Skip to content

Commit 10bddc5

Browse files
authored
Merge pull request #58 from hanslovsky/fix-floodfill-interface-rebase
Update FloodFill interface
2 parents 0454000 + 3d824d1 commit 10bddc5

File tree

5 files changed

+254
-18
lines changed

5 files changed

+254
-18
lines changed

src/main/java/net/imglib2/algorithm/fill/Filter.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,16 @@
3535
package net.imglib2.algorithm.fill;
3636

3737
/**
38-
* Interface for comparing {@link T} t and {@link U} u and accepting
39-
* them as equivalent in a sense specified by implementation thereof.
38+
* Interface for comparing {@link T} t and {@link U} u and accepting them as
39+
* equivalent in a sense specified by implementation thereof.
4040
*
4141
* @author Philipp Hanslovsky
4242
* @author Stephan Saalfeld
4343
*
4444
* @param <T>
4545
* @param <U>
4646
*/
47+
@Deprecated
4748
public interface Filter< T, U >
4849
{
4950
boolean accept( T t, U u );

src/main/java/net/imglib2/algorithm/fill/FloodFill.java

Lines changed: 189 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434

3535
package net.imglib2.algorithm.fill;
3636

37+
import java.util.function.BiPredicate;
38+
import java.util.function.Consumer;
39+
3740
import gnu.trove.list.TLongList;
3841
import gnu.trove.list.array.TLongArrayList;
3942
import net.imglib2.Cursor;
@@ -58,6 +61,182 @@ public class FloodFill
5861
// int or long? current TLongList cannot store more than Integer.MAX_VALUE
5962
private static final int CLEANUP_THRESHOLD = ( int ) 1e5;
6063

64+
/**
65+
* Iterative n-dimensional flood fill for arbitrary neighborhoods: Starting
66+
* at seed location, write fillLabel into target at current location and
67+
* continue for each pixel in neighborhood defined by shape if neighborhood
68+
* pixel is in the same connected component and fillLabel has not been
69+
* written into that location yet (comparator evaluates to 0).
70+
*
71+
* Convenience call to
72+
* {@link FloodFill#fill(RandomAccessible, RandomAccessible, Localizable, Object, Type, Shape, BiPredicate)}.
73+
* seedLabel is extracted from source at seed location.
74+
*
75+
* @param source
76+
* input
77+
* @param target
78+
* {@link RandomAccessible} to be written into. May be the same
79+
* as input.
80+
* @param seed
81+
* Start flood fill at this location.
82+
* @param fillLabel
83+
* Immutable. Value to be written into valid flood fill
84+
* locations.
85+
* @param shape
86+
* Defines neighborhood that is considered for connected
87+
* components, e.g.
88+
* {@link net.imglib2.algorithm.neighborhood.DiamondShape}
89+
* @param <T>
90+
* input pixel type
91+
* @param <U>
92+
* fill label type
93+
*/
94+
public static < T extends Type< T >, U extends Type< U > > void fill(
95+
final RandomAccessible< T > source,
96+
final RandomAccessible< U > target,
97+
final Localizable seed,
98+
final U fillLabel,
99+
final Shape shape )
100+
{
101+
final RandomAccess< T > access = source.randomAccess();
102+
access.setPosition( seed );
103+
final T seedValue = access.get().copy();
104+
final BiPredicate< T, U > filter = ( t, u ) -> t.valueEquals( seedValue ) && !u.valueEquals( fillLabel );
105+
fill( source, target, seed, fillLabel, shape, filter );
106+
}
107+
108+
/**
109+
* Iterative n-dimensional flood fill for arbitrary neighborhoods: Starting
110+
* at seed location, write fillLabel into target at current location and
111+
* continue for each pixel in neighborhood defined by shape if neighborhood
112+
* pixel is in the same connected component and fillLabel has not been
113+
* written into that location yet (comparator evaluates to 0).
114+
*
115+
* Convenience call to
116+
* {@link FloodFill#fill(RandomAccessible, RandomAccessible, Localizable, Object, Object, Shape, BiPredicate, Consumer)}
117+
* with {@link TypeWriter} as writer.
118+
*
119+
* @param source
120+
* input
121+
* @param target
122+
* {@link RandomAccessible} to be written into. May be the same
123+
* as input.
124+
* @param seed
125+
* Start flood fill at this location.
126+
* @param fillLabel
127+
* Immutable. Value to be written into valid flood fill
128+
* locations.
129+
* @param shape
130+
* Defines neighborhood that is considered for connected
131+
* components, e.g.
132+
* {@link net.imglib2.algorithm.neighborhood.DiamondShape}
133+
* @param filter
134+
* Returns true if pixel has not been visited yet and should be
135+
* written into. Returns false if target pixel has been visited
136+
* or source pixel is not part of the same connected component.
137+
* @param <T>
138+
* input pixel type
139+
* @param <U>
140+
* fill label type
141+
*/
142+
public static < T, U extends Type< U > > void fill(
143+
final RandomAccessible< T > source,
144+
final RandomAccessible< U > target,
145+
final Localizable seed,
146+
final U fillLabel,
147+
final Shape shape,
148+
final BiPredicate< T, U > filter )
149+
{
150+
fill( source, target, seed, shape, filter, targetPixel -> targetPixel.set( fillLabel ) );
151+
}
152+
153+
/**
154+
*
155+
* Iterative n-dimensional flood fill for arbitrary neighborhoods: Starting
156+
* at seed location, write fillLabel into target at current location and
157+
* continue for each pixel in neighborhood defined by shape if neighborhood
158+
* pixel is in the same connected component and fillLabel has not been
159+
* written into that location yet (comparator evaluates to 0).
160+
*
161+
* @param source
162+
* input
163+
* @param target
164+
* {@link RandomAccessible} to be written into. May be the same
165+
* as input.
166+
* @param seed
167+
* Start flood fill at this location.
168+
* @param shape
169+
* Defines neighborhood that is considered for connected
170+
* components, e.g.
171+
* {@link net.imglib2.algorithm.neighborhood.DiamondShape}
172+
* @param filter
173+
* Returns true if pixel has not been visited yet and should be
174+
* written into. Returns false if target pixel has been visited
175+
* or source pixel is not part of the same connected component.
176+
* @param writer
177+
* Defines how fill label is written into target at current
178+
* location.
179+
* @param <T>
180+
* input pixel type
181+
* @param <U>
182+
* fill label type
183+
*/
184+
public static < T, U > void fill(
185+
final RandomAccessible< T > source,
186+
final RandomAccessible< U > target,
187+
final Localizable seed,
188+
final Shape shape,
189+
final BiPredicate< T, U > filter,
190+
final Consumer< U > writer )
191+
{
192+
final int n = source.numDimensions();
193+
194+
final RandomAccessible< Pair< T, U > > paired = Views.pair( source, target );
195+
196+
TLongList coordinates = new TLongArrayList();
197+
for ( int d = 0; d < n; ++d )
198+
{
199+
coordinates.add( seed.getLongPosition( d ) );
200+
}
201+
202+
final int cleanupThreshold = n * CLEANUP_THRESHOLD;
203+
204+
final RandomAccessible< Neighborhood< Pair< T, U > > > neighborhood = shape.neighborhoodsRandomAccessible( paired );
205+
final RandomAccess< Neighborhood< Pair< T, U > > > neighborhoodAccess = neighborhood.randomAccess();
206+
207+
final RandomAccess< U > targetAccess = target.randomAccess();
208+
targetAccess.setPosition( seed );
209+
writer.accept( targetAccess.get() );
210+
211+
for ( int i = 0; i < coordinates.size(); i += n )
212+
{
213+
for ( int d = 0; d < n; ++d )
214+
neighborhoodAccess.setPosition( coordinates.get( i + d ), d );
215+
216+
final Cursor< Pair< T, U > > neighborhoodCursor = neighborhoodAccess.get().cursor();
217+
218+
while ( neighborhoodCursor.hasNext() )
219+
{
220+
final Pair< T, U > p = neighborhoodCursor.next();
221+
if ( filter.test( p.getA(), p.getB() ) )
222+
{
223+
writer.accept( p.getB() );
224+
for ( int d = 0; d < n; ++d )
225+
coordinates.add( neighborhoodCursor.getLongPosition( d ) );
226+
}
227+
}
228+
229+
if ( i > cleanupThreshold )
230+
{
231+
// TODO should it start from i + n?
232+
coordinates = coordinates.subList( i, coordinates.size() );
233+
i = 0;
234+
}
235+
236+
}
237+
238+
}
239+
61240
/**
62241
* Iterative n-dimensional flood fill for arbitrary neighborhoods: Starting
63242
* at seed location, write fillLabel into target at current location and
@@ -84,10 +263,11 @@ public class FloodFill
84263
* written into. Returns false if target pixel has been visited
85264
* or source pixel is not part of the same connected component.
86265
* @param <T>
87-
* T implements {@code Type<U>}.
266+
* input pixel type
88267
* @param <U>
89-
* U implements {@code Type<U>}.
268+
* fill label type
90269
*/
270+
@Deprecated
91271
public static < T extends Type< T >, U extends Type< U > > void fill( final RandomAccessible< T > source, final RandomAccessible< U > target, final Localizable seed, final U fillLabel, final Shape shape, final Filter< Pair< T, U >, Pair< T, U > > filter )
92272
{
93273
final RandomAccess< T > access = source.randomAccess();
@@ -123,10 +303,11 @@ public static < T extends Type< T >, U extends Type< U > > void fill( final Rand
123303
* written into. Returns false if target pixel has been visited
124304
* or source pixel is not part of the same connected component.
125305
* @param <T>
126-
* No restrictions on {@link T}.
306+
* input pixel type
127307
* @param <U>
128-
* {@link U} implements {@code Type<U>}.
308+
* fill label type
129309
*/
310+
@Deprecated
130311
public static < T, U extends Type< U > > void fill( final RandomAccessible< T > source, final RandomAccessible< U > target, final Localizable seed, final T seedLabel, final U fillLabel, final Shape shape, final Filter< Pair< T, U >, Pair< T, U > > filter )
131312
{
132313
fill( source, target, seed, seedLabel, fillLabel, shape, filter, new TypeWriter< U >() );
@@ -164,17 +345,16 @@ public static < T, U extends Type< U > > void fill( final RandomAccessible< T >
164345
* Defines how fillLabel is written into target at current
165346
* location.
166347
* @param <T>
167-
* No restrictions on T. Appropriate comparator is the only
168-
* requirement.
348+
* input pixel type
169349
* @param <U>
170-
* No restrictions on U. Appropriate comparator and writer is the
171-
* only requirement.
350+
* fill label type
172351
*/
352+
@Deprecated
173353
public static < T, U > void fill( final RandomAccessible< T > source, final RandomAccessible< U > target, final Localizable seed, final T seedLabel, final U fillLabel, final Shape shape, final Filter< Pair< T, U >, Pair< T, U > > filter, final Writer< U > writer )
174354
{
175355
final int n = source.numDimensions();
176356

177-
final ValuePair< T, U > reference = new ValuePair< T, U >( seedLabel, fillLabel );
357+
final ValuePair< T, U > reference = new ValuePair<>( seedLabel, fillLabel );
178358

179359
final RandomAccessible< Pair< T, U > > paired = Views.pair( source, target );
180360

src/main/java/net/imglib2/algorithm/fill/TypeWriter.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
* @author Philipp Hanslovsky
4343
* @author Stephan Saalfeld
4444
*/
45+
@Deprecated
4546
public class TypeWriter< T extends Type< T > > implements Writer< T >
4647
{
4748
@Override

src/main/java/net/imglib2/algorithm/fill/Writer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
*
4343
* @param <U>
4444
*/
45+
@Deprecated
4546
public interface Writer< U >
4647
{
4748
void write( final U source, final U target );

src/test/java/net/imglib2/algorithm/fill/FloodFillTest.java

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public class FloodFillTest
6161

6262
private static final int[] N_DIMS = { 1, 2, 3, 4 };
6363

64-
private static final int SIZE_OF_EACH_DIM = 60;
64+
private static final int SIZE_OF_EACH_DIM = 24;
6565

6666
private static < T extends IntegerType< T > > void runTest( final int nDim, final int sizeOfEachDim, final ImgFactory< T > imageFactory, final T t )
6767
{
@@ -112,14 +112,66 @@ else if ( i.getLongPosition( 0 ) - c[ 0 ] > divisionLine )
112112

113113
final ExtendedRandomAccessibleInterval< T, Img< T > > extendedImg = Views.extendValue( img, fillLabel );
114114

115-
final Filter< Pair< T, T >, Pair< T, T > > filter = new Filter< Pair< T, T >, Pair< T, T > >()
115+
FloodFill.fill( extendedImg, extendedImg, new Point( c ), fillLabel, new DiamondShape( 1 ) );
116+
117+
for ( Cursor< T > imgCursor = img.cursor(), refCursor = refImg.cursor(); imgCursor.hasNext(); )
118+
{
119+
Assert.assertEquals( refCursor.next(), imgCursor.next() );
120+
}
121+
122+
}
123+
124+
@Deprecated
125+
private static < T extends IntegerType< T > > void runTestDeprecated( final int nDim, final int sizeOfEachDim, final ImgFactory< T > imageFactory, final T t )
126+
{
127+
final long[] dim = new long[ nDim ];
128+
final long[] c = new long[ nDim ];
129+
final long r = sizeOfEachDim / 4;
130+
for ( int d = 0; d < nDim; ++d )
116131
{
117-
@Override
118-
public boolean accept( final Pair< T, T > p1, final Pair< T, T > p2 )
132+
dim[ d ] = sizeOfEachDim;
133+
c[ d ] = sizeOfEachDim / 3;
134+
}
135+
136+
final long divisionLine = r / 3;
137+
138+
final Img< T > img = imageFactory.create( dim, t.copy() );
139+
final Img< T > refImg = imageFactory.create( dim, t.copy() );
140+
141+
for ( Cursor< T > i = img.cursor(), ref = refImg.cursor(); i.hasNext(); )
142+
{
143+
i.fwd();
144+
ref.fwd();
145+
long diffSum = 0;
146+
for ( int d = 0; d < nDim; ++d )
119147
{
120-
return ( p1.getB().getIntegerLong() != p2.getB().getIntegerLong() ) && ( p1.getA().getIntegerLong() == p2.getA().getIntegerLong() );
148+
final long diff = i.getLongPosition( d ) - c[ d ];
149+
diffSum += diff * diff;
150+
151+
}
152+
153+
if ( ( diffSum < r * r ) )
154+
{
155+
if ( ( i.getLongPosition( 0 ) - c[ 0 ] < divisionLine ) )
156+
{
157+
i.get().setInteger( START_LABEL );
158+
ref.get().setInteger( FILL_LABEL );
159+
}
160+
else if ( i.getLongPosition( 0 ) - c[ 0 ] > divisionLine )
161+
{
162+
i.get().setInteger( START_LABEL );
163+
ref.get().setInteger( START_LABEL );
164+
}
121165
}
122-
};
166+
167+
}
168+
169+
final T fillLabel = t.createVariable();
170+
fillLabel.setInteger( FILL_LABEL );
171+
172+
final ExtendedRandomAccessibleInterval< T, Img< T > > extendedImg = Views.extendValue( img, fillLabel );
173+
174+
final Filter< Pair< T, T >, Pair< T, T > > filter = ( p1, p2 ) -> ( p1.getB().getIntegerLong() != p2.getB().getIntegerLong() ) && ( p1.getA().getIntegerLong() == p2.getA().getIntegerLong() );
123175

124176
FloodFill.fill( extendedImg, extendedImg, new Point( c ), fillLabel, new DiamondShape( 1 ), filter );
125177

@@ -135,7 +187,8 @@ public void runTests()
135187
{
136188
for ( final int nDim : N_DIMS )
137189
{
138-
runTest( nDim, SIZE_OF_EACH_DIM, new ArrayImgFactory< LongType >(), new LongType() );
190+
runTestDeprecated( nDim, SIZE_OF_EACH_DIM, new ArrayImgFactory<>(), new LongType() );
191+
runTest( nDim, SIZE_OF_EACH_DIM, new ArrayImgFactory<>(), new LongType() );
139192
}
140193
}
141194

0 commit comments

Comments
 (0)