Skip to content

Conversation

@tinevez
Copy link
Contributor

@tinevez tinevez commented Oct 19, 2017

Cursors that iterates over a circle or an ellipse exactly once over each pixel of the circle. Can be set to iterate over a nD in any 2D dimensions.
Simple addition to the region package.

Examples:

circle ellipse-1

incremented-1

circle iteration-1

ImageJ.main( args );
final int w = 256;

/*
 * Create a simple circle, setting the pixels values.
 */

final ArrayImg< UnsignedIntType, IntArray > img1 = ArrayImgs.unsignedInts( w, w );
final Point center = new Point( w / 2, w / 2 );
Circles.set( img1, center, w / 4, new UnsignedIntType( 70 ) );
// And an ellipse:
Ellipses.set( img1, center, w / 5, w / 10, new UnsignedIntType( 130 ) );
ImageJFunctions.show( img1, "Circle & Ellipse" );

/*
 * Circles by incrementing pixel values.
 */

final ArrayImg< UnsignedIntType, IntArray > img2 = ArrayImgs.unsignedInts( w, w );
final int nCircles = 100;
final Random ran = new Random( 0l );
for ( int i = 0; i < nCircles; i++ )
{
	final Point c = new Point( ran.nextInt( ( int ) img2.dimension( 0 ) ), ran.nextInt( ( int ) img2.dimension( 1 ) ) );
	final int rx = ran.nextInt( ( int ) ( img2.dimension( 0 ) / 4 ) );
	final int ry = ran.nextInt( ( int ) ( img2.dimension( 0 ) / 4 ) );
	Ellipses.inc( Views.extendZero( img2 ), c, rx, ry );
}
ImageJFunctions.show( img2, "Incremented" );

/*
 * Iterate over a circle.
 */

final ArrayImg< UnsignedIntType, IntArray > img3 = ArrayImgs.unsignedInts( w, w );
final CircleCursor< UnsignedIntType > circleCursor = new CircleCursor<>( img3, center, w / 3 );
int val = 0;
while ( circleCursor.hasNext() )
	circleCursor.next().set( val++ );

ImageJFunctions.show( img3, "Circle iteration" );

tinevez added 7 commits April 6, 2017 16:44
Each point of the circle is iterated exactly once, and
there is no "hole" in the circle. Contrary to the algorithm
in Wikipedia, the circles generated by this cursor are "slim":
each pixel of the circle is connected with 8-connectivity.
For instance, a bitmap excerpt from such a circle looks like this:

 ..........
 OOO.......
 ...OO.....
 .....O....
 .....O....
@tpietzsch tpietzsch self-assigned this Nov 6, 2017
@tpietzsch
Copy link
Member

@tinevez This started as a feature request and then it came out as just a comment...
There are some requests at the very end though :-)

This will fit nicely with some discrete imglib2-roi stuff I'm working on. The scenario there would be that

  1. you make a Circle (which is a RandomAccessibleInterval<BoolType> with true pixels on the circle),
  2. then Regions.sample() your output image with it (gives you a IterableInterval<T>)
  3. you iterate that to set the pixels or whatever.

The equivalent of your first example

Circles.set( img1, center, w / 4, new UnsignedIntType( 70 ) );

above would be

Regions.sample( new Circle( center, w / 4 ), img1 ).forEach( t -> t.set( 70 ) );

The Regions framework would pick up that the source region provides specialized cursors (for sampling RAI<T>) which would be your CircleCursor<T>.
So exactly the same would happen, except this same pattern applies to all bitmasks (and Circle just happens to be one with better-than-generic cursors).

This is part of a larger cleanup that will also get rid of the neighborhood framework (Shape etc).
You would just do Regions.neighborhoods(bitmask, img), where again HyperSphere etc are just specialized bitmasks with better-than-generic behaviour.

So requests:
For this to happen, the future Circle bitmask will need to know its size() and be RandomAccessible. Therefore,

  1. Is there an easy way to query for a given coordinate whether or not it is on the circle (will be touched by the CircleCursor)?
  2. Is there an easy way to compute without counting how many pixels will be visited by the CircleCursor?

@tpietzsch tpietzsch merged commit 912451b into imglib:master Nov 6, 2017
@tinevez
Copy link
Contributor Author

tinevez commented Nov 6, 2017

So requests:
For this to happen, the future Circle bitmask will need to know its size() and be RandomAccessible. Therefore,

  • Is there an easy way to query for a given coordinate whether or not it is on the circle (will be touched by the CircleCursor)?

Not an easy one for sure. I think we need to play with this question with a proper Computer Scientist.

  • Is there an easy way to compute without counting how many pixels will be visited by the CircleCursor?

Yes! I was puzzled by that question too a while ago. A StackOverflow discussion ensued:
https://stackoverflow.com/questions/8200843/predict-number-of-points-returned-mid-point-circle-algorithm

@tpietzsch
Copy link
Member

Is there an easy way to compute without counting how many pixels will be visited by the CircleCursor?
Yes! I was puzzled by that question too a while ago. A StackOverflow discussion ensued:
https://stackoverflow.com/questions/8200843/predict-number-of-points-returned-mid-point-circle-algorithm

Hmm, from the answers I don't really get whether any of the solutions is actually exact?

@tinevez
Copy link
Contributor Author

tinevez commented Nov 6, 2017

int npixels = 8 * floor((sqrt(2)*(radius-1)+4)/2)

@imagejan
Copy link
Member

imagejan commented Nov 6, 2017

What about the comment:

Considering octant only, returns actual count or one greater until a radius of 16557 and then growing to 1523 more than actual at a radius of 32767 (the graphics max).

?

@tinevez
Copy link
Contributor Author

tinevez commented Nov 6, 2017

What about the comment:

I do not understand said comment 😐

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants