Skip to content

Commit 0454000

Browse files
tibuchhanslovsky
authored andcommitted
Differences (#44)
* Add forward and backward difference. * PartialDerivative: add tests * POM: bump parent to pom-imglib2 10.0.2 This makes the class LoopBuilder available. * PartialDerivative: use LoopBuilder to get clean code * Add formulae for finite diffierence methods Addresses comments in #44 Also adds formulae for central difference * Update author list of PartialDerivative.java Added: - @tibuch - @maarzt
1 parent 2b71c6d commit 0454000

File tree

3 files changed

+170
-70
lines changed

3 files changed

+170
-70
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>net.imglib2</groupId>
77
<artifactId>pom-imglib2</artifactId>
8-
<version>10.0.1</version>
8+
<version>10.0.2</version>
99
<relativePath />
1010
</parent>
1111

src/main/java/net/imglib2/algorithm/gradient/PartialDerivative.java

Lines changed: 69 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@
4343

4444
import net.imglib2.Cursor;
4545
import net.imglib2.FinalInterval;
46-
import net.imglib2.RandomAccess;
4746
import net.imglib2.RandomAccessible;
4847
import net.imglib2.RandomAccessibleInterval;
48+
import net.imglib2.loops.LoopBuilder;
4949
import net.imglib2.type.numeric.NumericType;
5050
import net.imglib2.util.Intervals;
5151
import net.imglib2.view.IntervalView;
@@ -54,13 +54,18 @@
5454
/**
5555
* @author Tobias Pietzsch
5656
* @author Philipp Hanslovsky
57+
* @author Tim-Oliver Buchholz
58+
* @author Matthias Arzt
5759
*
5860
*/
5961
public class PartialDerivative
6062
{
6163
// nice version...
6264
/**
63-
* Compute the partial derivative of source in a particular dimension.
65+
* Compute the partial derivative (central difference approximation) of source
66+
* in a particular dimension:
67+
* {@code d_f( x ) = ( f( x + e ) - f( x - e ) ) / 2},
68+
* where {@code e} is the unit vector along that dimension.
6469
*
6570
* @param source
6671
* source image, has to provide valid data in the interval of the
@@ -85,7 +90,10 @@ public static < T extends NumericType< T > > void gradientCentralDifference2( fi
8590

8691
// parallel version...
8792
/**
88-
* Compute the partial derivative of source in a particular dimension.
93+
* Compute the partial derivative (central difference approximation) of source
94+
* in a particular dimension:
95+
* {@code d_f( x ) = ( f( x + e ) - f( x - e ) ) / 2},
96+
* where {@code e} is the unit vector along that dimension.
8997
*
9098
* @param source
9199
* source image, has to provide valid data in the interval of the
@@ -157,81 +165,73 @@ public static < T extends NumericType< T > > void gradientCentralDifferenceParal
157165

158166
// fast version
159167
/**
160-
* Compute the partial derivative of source in a particular dimension.
168+
* Compute the partial derivative (central difference approximation) of source
169+
* in a particular dimension:
170+
* {@code d_f( x ) = ( f( x + e ) - f( x - e ) ) / 2},
171+
* where {@code e} is the unit vector along that dimension.
161172
*
162173
* @param source
163174
* source image, has to provide valid data in the interval of the
164175
* gradient image plus a one pixel border in dimension.
165-
* @param gradient
176+
* @param result
166177
* output image
167178
* @param dimension
168179
* along which dimension the partial derivatives are computed
169180
*/
170-
public static < T extends NumericType< T > > void gradientCentralDifference( final RandomAccessible< T > source, final RandomAccessibleInterval< T > gradient, final int dimension )
181+
public static < T extends NumericType< T > > void gradientCentralDifference( final RandomAccessible< T > source,
182+
final RandomAccessibleInterval< T > result, final int dimension )
171183
{
172-
final int n = gradient.numDimensions();
173-
174-
final long[] min = new long[ n ];
175-
gradient.min( min );
176-
final long[] max = new long[ n ];
177-
gradient.max( max );
178-
final long[] shiftback = new long[ n ];
179-
for ( int d = 0; d < n; ++d )
180-
shiftback[ d ] = min[ d ] - max[ d ];
181-
182-
final RandomAccess< T > result = gradient.randomAccess();
183-
final RandomAccess< T > back = source.randomAccess( Intervals.translate( gradient, -1, dimension ) );
184-
final RandomAccess< T > front = source.randomAccess( Intervals.translate( gradient, 1, dimension ) );
185-
186-
result.setPosition( min );
187-
back.setPosition( min );
188-
back.bck( dimension );
189-
front.setPosition( min );
190-
front.fwd( dimension );
191-
192-
final long max0 = max[ 0 ];
193-
while ( true )
194-
{
195-
// process pixel
196-
final T t = result.get();
197-
t.set( front.get() );
198-
t.sub( back.get() );
199-
t.mul( 0.5 );
184+
final RandomAccessibleInterval< T > back = Views.interval( source, Intervals.translate( result, -1, dimension ) );
185+
final RandomAccessibleInterval< T > front = Views.interval( source, Intervals.translate( result, 1, dimension ) );
186+
187+
LoopBuilder.setImages( result, back, front ).forEachPixel( ( r, b, f ) -> {
188+
r.set( f );
189+
r.sub( b );
190+
r.mul( 0.5 );
191+
} );
192+
}
200193

201-
// move to next pixel
202-
// check dimension 0 separately to avoid the loop over d in most
203-
// iterations
204-
if ( result.getLongPosition( 0 ) == max0 )
205-
{
206-
if ( n == 1 )
207-
return;
208-
result.move( shiftback[ 0 ], 0 );
209-
back.move( shiftback[ 0 ], 0 );
210-
front.move( shiftback[ 0 ], 0 );
211-
// now check the remaining dimensions
212-
for ( int d = 1; d < n; ++d )
213-
if ( result.getLongPosition( d ) == max[ d ] )
214-
{
215-
result.move( shiftback[ d ], d );
216-
back.move( shiftback[ d ], d );
217-
front.move( shiftback[ d ], d );
218-
if ( d == n - 1 )
219-
return;
220-
}
221-
else
222-
{
223-
result.fwd( d );
224-
back.fwd( d );
225-
front.fwd( d );
226-
break;
227-
}
228-
}
229-
else
230-
{
231-
result.fwd( 0 );
232-
back.fwd( 0 );
233-
front.fwd( 0 );
234-
}
235-
}
194+
/**
195+
* Compute the backward difference of source in a particular dimension:
196+
* {@code d_f( x ) = ( f( x ) - f( x - e ) )}
197+
* where {@code e} is the unit vector along that dimension
198+
*
199+
* @param source source image, has to provide valid data in the interval of
200+
* the gradient image plus a one pixel border in dimension.
201+
* @param result output image
202+
* @param dimension along which dimension the partial derivatives are computed
203+
*/
204+
public static < T extends NumericType< T > > void gradientBackwardDifference( final RandomAccessible< T > source,
205+
final RandomAccessibleInterval< T > result, final int dimension )
206+
{
207+
final RandomAccessibleInterval< T > back = Views.interval( source, Intervals.translate( result, -1, dimension ) );
208+
final RandomAccessibleInterval< T > front = Views.interval( source, result );
209+
210+
LoopBuilder.setImages( result, back, front ).forEachPixel( ( r, b, f ) -> {
211+
r.set( f );
212+
r.sub( b );
213+
} );
214+
}
215+
216+
/**
217+
* Compute the forward difference of source in a particular dimension:
218+
* {@code d_f( x ) = ( f( x + e ) - f( x ) )}
219+
* where {@code e} is the unit vector along that dimension
220+
221+
* @param source source image, has to provide valid data in the interval of
222+
* the gradient image plus a one pixel border in dimension.
223+
* @param result output image
224+
* @param dimension along which dimension the partial derivatives are computed
225+
*/
226+
public static < T extends NumericType< T > > void gradientForwardDifference( final RandomAccessible< T > source,
227+
final RandomAccessibleInterval< T > result, final int dimension )
228+
{
229+
final RandomAccessibleInterval< T > back = Views.interval( source, result );
230+
final RandomAccessibleInterval< T > front = Views.interval( source, Intervals.translate( result, 1, dimension ) );
231+
232+
LoopBuilder.setImages( result, back, front ).forEachPixel( ( r, b, f ) -> {
233+
r.set( f );
234+
r.sub( b );
235+
} );
236236
}
237237
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package net.imglib2.algorithm.gradient;
2+
3+
import net.imglib2.Interval;
4+
import net.imglib2.RandomAccessibleInterval;
5+
import net.imglib2.img.array.ArrayImgs;
6+
import net.imglib2.type.numeric.real.DoubleType;
7+
import net.imglib2.util.Intervals;
8+
import net.imglib2.view.Views;
9+
import org.junit.Test;
10+
11+
import java.util.ArrayList;
12+
import java.util.List;
13+
14+
import static junit.framework.TestCase.assertTrue;
15+
import static org.junit.Assert.assertArrayEquals;
16+
17+
/**
18+
* Tests {@link PartialDerivative}.
19+
*
20+
* @author Matthias Arzt
21+
*/
22+
public class PartialDerivativeTest
23+
{
24+
25+
private final double DELTA = 0.0001;
26+
27+
private final RandomAccessibleInterval< DoubleType > testImage =
28+
createImage2dMinSizeValues( /* min: */ 0, 0, /* size: */ 5, 3, /* values: */
29+
0.0, 0.0, 0.0, 0.0, 0.0,
30+
0.0, 2.0, 0.0, -1.0, 0.0,
31+
0.0, 0.0, 5.0, 0.0, 0.0
32+
);
33+
34+
private final RandomAccessibleInterval< DoubleType > centralDifferenceExpected =
35+
createImage2dMinSizeValues( /* min: */ 1, 0, /* size: */ 3, 3, /* values: */
36+
0.0, 0.0, 0.0,
37+
0.0, -1.5, 0.0,
38+
2.5, 0.0, -2.5
39+
);
40+
41+
private final RandomAccessibleInterval< DoubleType > backwardDifferenceExpected =
42+
createImage2dMinSizeValues( /* min: */ 1, 0, /* size: */ 4, 3, /* values: */
43+
0.0, 0.0, 0.0, 0.0,
44+
2.0, -2.0, -1.0, 1.0,
45+
0.0, 5.0, -5.0, 0.0
46+
);
47+
48+
public static RandomAccessibleInterval< DoubleType > createImage2dMinSizeValues( long minX, long minY, long sizeX, long sizeY, double... values )
49+
{
50+
Interval interval = Intervals.createMinSize( minX, minY, sizeX, sizeY );
51+
return Views.translate( ArrayImgs.doubles( values, Intervals.dimensionsAsLongArray( interval ) ),
52+
Intervals.minAsLongArray( interval ) );
53+
}
54+
55+
@Test
56+
public void testGradientCentralDifferenceX()
57+
{
58+
RandomAccessibleInterval< DoubleType > data = testImage;
59+
RandomAccessibleInterval< DoubleType > result = emptyArrayImg( centralDifferenceExpected );
60+
PartialDerivative.gradientCentralDifference( data, result, 0 );
61+
assertImagesEqual( centralDifferenceExpected, result );
62+
}
63+
64+
@Test
65+
public void testGradientBackwardDifferenceX()
66+
{
67+
RandomAccessibleInterval< DoubleType > data = testImage;
68+
RandomAccessibleInterval< DoubleType > result = emptyArrayImg( backwardDifferenceExpected );
69+
PartialDerivative.gradientBackwardDifference( data, result, 0 );
70+
assertImagesEqual( backwardDifferenceExpected, result );
71+
}
72+
73+
@Test
74+
public void testForwardDifferenceX()
75+
{
76+
RandomAccessibleInterval< DoubleType > data = testImage;
77+
RandomAccessibleInterval< DoubleType > expected = Views.translate( backwardDifferenceExpected, -1, 0 );
78+
RandomAccessibleInterval< DoubleType > result = emptyArrayImg( expected );
79+
PartialDerivative.gradientForwardDifference( data, result, 0 );
80+
assertImagesEqual( expected, result );
81+
}
82+
83+
private void assertImagesEqual( RandomAccessibleInterval< DoubleType > expected, RandomAccessibleInterval< DoubleType > actual )
84+
{
85+
assertTrue( Intervals.equals( expected, actual ) );
86+
assertArrayEquals( imageAsArray( expected ), imageAsArray( actual ), DELTA );
87+
}
88+
89+
private double[] imageAsArray( RandomAccessibleInterval< DoubleType > image )
90+
{
91+
List< Double > values = new ArrayList<>();
92+
Views.iterable( image ).forEach( x -> values.add( x.getRealDouble() ) );
93+
return values.stream().mapToDouble( x -> x ).toArray();
94+
}
95+
96+
private RandomAccessibleInterval< DoubleType > emptyArrayImg( Interval interval )
97+
{
98+
return Views.translate( ArrayImgs.doubles( Intervals.dimensionsAsLongArray( interval ) ), Intervals.minAsLongArray( interval ) );
99+
}
100+
}

0 commit comments

Comments
 (0)