Skip to content

Commit 912451b

Browse files
authored
Merge pull request #47 from tinevez/hough-circle-transform
Bresenham circle and ellipse iterators.
2 parents 33e53d9 + 2a6b917 commit 912451b

File tree

6 files changed

+1298
-0
lines changed

6 files changed

+1298
-0
lines changed
Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
package net.imglib2.algorithm.region;
2+
3+
import net.imglib2.Cursor;
4+
import net.imglib2.FinalInterval;
5+
import net.imglib2.Interval;
6+
import net.imglib2.Localizable;
7+
import net.imglib2.RandomAccess;
8+
import net.imglib2.RandomAccessible;
9+
import net.imglib2.Sampler;
10+
11+
/**
12+
* Iterates over a Bresenham 2D circle using the mid-point algorithm.
13+
* <p>
14+
* Each point of the circle is iterated exactly once, and there is no "hole" in
15+
* the circle. Contrary to the algorithm linked below, the circles generated by
16+
* this cursor are "slim": each pixel of the circle is connected with
17+
* 8-connectivity. For instance, a bitmap excerpt from such a circle looks like
18+
* this:
19+
*
20+
* <pre>
21+
* ..........
22+
* OOO.......
23+
* ...OO.....
24+
* .....O....
25+
* .....O....
26+
* </pre>
27+
*
28+
* @author Jean-Yves Tinevez
29+
* @see <a href=
30+
* "https://en.wikipedia.org/wiki/Midpoint_circle_algorithm">Midpoint
31+
* circle algorithm on Wikipedia.</a>
32+
*
33+
*/
34+
public class CircleCursor< T > implements Cursor< T >
35+
{
36+
private final RandomAccessible< T > rai;
37+
38+
private final RandomAccess< T > ra;
39+
40+
private final Localizable center;
41+
42+
private final long radius;
43+
44+
private final int dimX;
45+
46+
private final int dimY;
47+
48+
private long x;
49+
50+
private long y;
51+
52+
private long dx;
53+
54+
private long dy;
55+
56+
private long f;
57+
58+
private Octant octant;
59+
60+
private boolean hasNext;
61+
62+
private static enum Octant
63+
{
64+
INIT, EAST, NORTH, WEST, SOUTH, O1, O2, O3, O4, O5, O6, O7, O8;
65+
}
66+
67+
/**
68+
* Iterates over a Bresenham circle in the target {@link RandomAccessible}.
69+
* Each point of the circle is iterated exactly once, and there is no "hole"
70+
* in the circle.
71+
*
72+
* @param rai
73+
* the random accessible. It is the caller responsibility to
74+
* ensure it can be accessed everywhere the circle will be
75+
* iterated.
76+
* @param center
77+
* the circle center. Must be at least of dimension 2. Dimensions
78+
* 0 and 1 are used to specify the circle center.
79+
* @param radius
80+
* the circle radius. The circle is written in a plane in
81+
* dimensions 0 and 1.
82+
*/
83+
public CircleCursor( final RandomAccessible< T > rai, final Localizable center, final long radius )
84+
{
85+
this( rai, center, radius, 0, 1 );
86+
}
87+
88+
/**
89+
* Iterates over a Bresenham circle in the target {@link RandomAccessible}.
90+
* Each point of the circle is iterated exactly once, and there is no "hole"
91+
* in the circle.
92+
*
93+
* @param rai
94+
* the random accessible. It is the caller responsibility to
95+
* ensure it can be accessed everywhere the circle will be
96+
* iterated.
97+
* @param center
98+
* the circle center. Must at least contain dimensions specified
99+
* <code>dimX</code> and <code>dimY</code>.
100+
* @param radius
101+
* the circle radius.
102+
* @param dimX
103+
* the first dimension of the plane in which to draw the circle.
104+
* @param dimY
105+
* the second dimension of the plane in which to draw the circle.
106+
*/
107+
public CircleCursor( final RandomAccessible< T > rai, final Localizable center, final long radius, final int dimX, final int dimY )
108+
{
109+
this.rai = rai;
110+
this.center = center;
111+
this.radius = radius;
112+
this.dimX = dimX;
113+
this.dimY = dimY;
114+
// Make an interval to signal where we will access data.
115+
final int numDimensions = rai.numDimensions();
116+
final long[] minmax = new long[ 2 * numDimensions ];
117+
for ( int d = 0; d < numDimensions; d++ )
118+
{
119+
if ( d == dimX || d == dimY )
120+
minmax[ d ] = center.getLongPosition( d ) - radius;
121+
else
122+
minmax[ d ] = center.getLongPosition( d );
123+
}
124+
for ( int d = 0; d < numDimensions; d++ )
125+
{
126+
if ( d == dimX || d == dimY )
127+
minmax[ d + numDimensions ] = center.getLongPosition( d ) + radius;
128+
else
129+
minmax[ d + numDimensions ] = center.getLongPosition( d );
130+
}
131+
final Interval interval = FinalInterval.createMinMax( minmax );
132+
this.ra = rai.randomAccess( interval );
133+
reset();
134+
}
135+
136+
@Override
137+
public void reset()
138+
{
139+
x = 0;
140+
y = radius;
141+
f = 1 - radius;
142+
dx = 1;
143+
dy = -2 * radius;
144+
octant = Octant.INIT;
145+
ra.setPosition( center );
146+
hasNext = true;
147+
}
148+
149+
@Override
150+
public void fwd()
151+
{
152+
switch ( octant )
153+
{
154+
default:
155+
case INIT:
156+
ra.setPosition( center.getLongPosition( dimY ) + radius, dimY );
157+
octant = Octant.NORTH;
158+
break;
159+
160+
case NORTH:
161+
ra.setPosition( center.getLongPosition( dimY ) - radius, dimY );
162+
octant = Octant.SOUTH;
163+
break;
164+
165+
case SOUTH:
166+
ra.setPosition( center.getLongPosition( dimX ) - radius, dimX );
167+
ra.setPosition( center.getLongPosition( dimY ), dimY );
168+
octant = Octant.WEST;
169+
break;
170+
171+
case WEST:
172+
ra.setPosition( center.getLongPosition( dimX ) + radius, dimX );
173+
octant = Octant.EAST;
174+
break;
175+
176+
case EAST:
177+
x = x + 1;
178+
dx = dx + 2;
179+
f = f + dx;
180+
ra.setPosition( center.getLongPosition( dimX ) + x, dimX );
181+
ra.setPosition( center.getLongPosition( dimY ) + y, dimY );
182+
octant = Octant.O1;
183+
break;
184+
185+
case O1:
186+
ra.setPosition( center.getLongPosition( dimX ) - x, dimX );
187+
octant = Octant.O2;
188+
break;
189+
190+
case O2:
191+
ra.setPosition( center.getLongPosition( dimY ) - y, dimY );
192+
octant = Octant.O3;
193+
break;
194+
195+
case O3:
196+
ra.setPosition( center.getLongPosition( dimX ) + x, dimX );
197+
octant = Octant.O4;
198+
// Stop here if x==y, lest the 45º will be iterated twice.
199+
if ( x >= y )
200+
hasNext = false;
201+
break;
202+
203+
case O4:
204+
ra.setPosition( center.getLongPosition( dimX ) + y, dimX );
205+
ra.setPosition( center.getLongPosition( dimY ) - x, dimY );
206+
octant = Octant.O5;
207+
break;
208+
209+
case O5:
210+
ra.setPosition( center.getLongPosition( dimX ) - y, dimX );
211+
octant = Octant.O6;
212+
break;
213+
214+
case O6:
215+
ra.setPosition( center.getLongPosition( dimY ) + x, dimY );
216+
octant = Octant.O7;
217+
break;
218+
219+
case O7:
220+
ra.setPosition( center.getLongPosition( dimX ) + y, dimX );
221+
octant = Octant.O8;
222+
// Stop here if dx would cross y.
223+
if ( x >= y - 1 )
224+
hasNext = false;
225+
break;
226+
227+
case O8:
228+
if ( f > 0 )
229+
{
230+
y = y - 1;
231+
dy = dy + 2;
232+
f = f + dy;
233+
}
234+
x = x + 1;
235+
dx = dx + 2;
236+
f = f + dx;
237+
ra.setPosition( center.getLongPosition( dimX ) + x, dimX );
238+
ra.setPosition( center.getLongPosition( dimY ) + y, dimY );
239+
octant = Octant.O1;
240+
break;
241+
}
242+
}
243+
244+
@Override
245+
public boolean hasNext()
246+
{
247+
return hasNext;
248+
}
249+
250+
@Override
251+
public void localize( final float[] position )
252+
{
253+
ra.localize( position );
254+
}
255+
256+
@Override
257+
public void localize( final double[] position )
258+
{
259+
ra.localize( position );
260+
}
261+
262+
@Override
263+
public float getFloatPosition( final int d )
264+
{
265+
return ra.getFloatPosition( d );
266+
}
267+
268+
@Override
269+
public double getDoublePosition( final int d )
270+
{
271+
return ra.getDoublePosition( d );
272+
}
273+
274+
@Override
275+
public int numDimensions()
276+
{
277+
return ra.numDimensions();
278+
}
279+
280+
@Override
281+
public T get()
282+
{
283+
return ra.get();
284+
}
285+
286+
@Override
287+
public Sampler< T > copy()
288+
{
289+
return ra.copy();
290+
}
291+
292+
@Override
293+
public void jumpFwd( final long steps )
294+
{
295+
for ( int i = 0; i < steps; i++ )
296+
fwd();
297+
}
298+
299+
@Override
300+
public T next()
301+
{
302+
fwd();
303+
return get();
304+
}
305+
306+
@Override
307+
public void localize( final int[] position )
308+
{
309+
ra.localize( position );
310+
}
311+
312+
@Override
313+
public void localize( final long[] position )
314+
{
315+
ra.localize( position );
316+
}
317+
318+
@Override
319+
public int getIntPosition( final int d )
320+
{
321+
return ra.getIntPosition( d );
322+
}
323+
324+
@Override
325+
public long getLongPosition( final int d )
326+
{
327+
return ra.getLongPosition( d );
328+
}
329+
330+
@Override
331+
public Cursor< T > copyCursor()
332+
{
333+
return new CircleCursor<>( rai, center, radius, dimX, dimY );
334+
}
335+
}

0 commit comments

Comments
 (0)