Skip to content

Commit 92b42e0

Browse files
committed
Add PositionFieldFunction and DisplacementFieldBlockSupplier
This allows to use a displacement field to look up values with an arbitrary function. A use case for this is creating blending masks in multiview-reconstruction.
1 parent 7e298fb commit 92b42e0

2 files changed

Lines changed: 292 additions & 0 deletions

File tree

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
/*-
2+
* #%L
3+
* ImgLib2: a general-purpose, multidimensional image processing library.
4+
* %%
5+
* Copyright (C) 2009 - 2025 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld,
6+
* John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke,
7+
* Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner,
8+
* Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert,
9+
* Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin,
10+
* Jean-Yves Tinevez and Michael Zinsmaier.
11+
* %%
12+
* Redistribution and use in source and binary forms, with or without
13+
* modification, are permitted provided that the following conditions are met:
14+
*
15+
* 1. Redistributions of source code must retain the above copyright notice,
16+
* this list of conditions and the following disclaimer.
17+
* 2. Redistributions in binary form must reproduce the above copyright notice,
18+
* this list of conditions and the following disclaimer in the documentation
19+
* and/or other materials provided with the distribution.
20+
*
21+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
25+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31+
* POSSIBILITY OF SUCH DAMAGE.
32+
* #L%
33+
*/
34+
package net.imglib2.algorithm.blocks.dfield;
35+
36+
import net.imglib2.Interval;
37+
import net.imglib2.algorithm.blocks.AbstractBlockSupplier;
38+
import net.imglib2.algorithm.blocks.BlockSupplier;
39+
import net.imglib2.blocks.BlockInterval;
40+
import net.imglib2.blocks.TempArray;
41+
import net.imglib2.realtransform.AffineGet;
42+
import net.imglib2.realtransform.AffineTransform2D;
43+
import net.imglib2.realtransform.AffineTransform3D;
44+
import net.imglib2.type.NativeType;
45+
import net.imglib2.type.PrimitiveType;
46+
import net.imglib2.type.numeric.RealType;
47+
import net.imglib2.util.Intervals;
48+
49+
import static net.imglib2.algorithm.blocks.transform.Transform.Interpolation.NLINEAR;
50+
import static net.imglib2.algorithm.blocks.transform.Transform.invert;
51+
import static net.imglib2.util.Util.safeInt;
52+
53+
/**
54+
* A {@code BlockSupplier} that combines a {@code
55+
* AbstractDispFieldAffineProcessor} and a {@code PositionFieldFunction} to
56+
* compute (blocks of) the transformation of a function with a displacement
57+
* field.
58+
* <p>
59+
* The {@code AbstractDispFieldAffineProcessor} interpolates and affine
60+
* transforms the displacement field to get a position field.
61+
* <p>
62+
* The {@code PositionFieldFunction} computes target values using the position
63+
* field vectors.
64+
*
65+
* @param <D>
66+
* displacement field type
67+
* @param <T>
68+
* target pixel type
69+
*/
70+
public class DisplacementFieldBlockSupplier< D extends NativeType< D > & RealType< D >, T extends NativeType< T > > extends AbstractBlockSupplier< T >
71+
{
72+
/**
73+
* Create a {@code BlockSupplier} that transforms {@code displacementField}
74+
* into a position field and produces target values by applying
75+
* {@code positionFieldFunction} to the resulting position vectors.
76+
* <p>
77+
* {@code transformFromField} is an affine transform from {@code
78+
* displacementField} coordinates to target coordinates. For example, this
79+
* can be used to upscale a downsampled displacement field.
80+
*
81+
* @param transformFromField
82+
* a 2D or 3D affine transform from displacementField coordinates to
83+
* target coordinates
84+
* @param displacementField
85+
* the (normalized) displacement field
86+
* @param positionFieldFunction
87+
* maps position vectors to target values
88+
* @param <D>
89+
* displacement field type
90+
* @param <T>
91+
* the target type
92+
*
93+
* @return a {@code BlockSupplier} that transforms {@code displacementField}
94+
* into a position field and produces target values by applying
95+
* positionFieldFunction to the resulting position vectors
96+
*/
97+
public static < D extends NativeType< D > & RealType< D >, T extends NativeType< T > >
98+
BlockSupplier< T > create(
99+
final AffineGet transformFromField,
100+
final DisplacementField< D > displacementField,
101+
final PositionFieldFunction< D, T, ?, ? > positionFieldFunction )
102+
{
103+
final int n = transformFromField.numDimensions();
104+
if ( n < 2 || n > 3 )
105+
{
106+
throw new IllegalArgumentException( "Only 2D and 3D affine transforms are supported currently" );
107+
}
108+
if ( displacementField.numDimensions() != n )
109+
{
110+
throw new IllegalArgumentException( "Number of dimension must be the same for the affine transform and the displacement field" );
111+
}
112+
113+
final AffineGet transformToField = invert( transformFromField );
114+
final PrimitiveType dfieldPrimitiveType = displacementField.getType().getNativeTypeFactory().getPrimitiveType();
115+
final double[] scale = displacementField.scale();
116+
final double[] translation = displacementField.translation();
117+
final BlockSupplier< D > displacements = displacementField.displacements();
118+
final AbstractDispFieldAffineProcessor< ? > fieldProcessor = ( n == 2 )
119+
? new DispFieldAffine2DProcessor<>( ( AffineTransform2D ) transformToField, scale, translation, NLINEAR, dfieldPrimitiveType )
120+
: new DispFieldAffine3DProcessor<>( ( AffineTransform3D ) transformToField, scale, translation, NLINEAR, dfieldPrimitiveType );
121+
return new DisplacementFieldBlockSupplier<>( n, fieldProcessor, displacements, positionFieldFunction );
122+
}
123+
124+
private final T type;
125+
126+
private final int numDimensions;
127+
128+
@SuppressWarnings( "rawtypes" )
129+
private final AbstractDispFieldAffineProcessor fieldProcessor;
130+
131+
@SuppressWarnings( "rawtypes" )
132+
private final PositionFieldFunction positionFieldFunction;
133+
134+
private final BlockSupplier< D > displacementField;
135+
136+
private final TempArray< ? > tempArrayPositionField;
137+
138+
/**
139+
*
140+
* @param numDimensions
141+
* number of dimensions (source and target) of this operator
142+
* @param fieldProcessor
143+
* interpolates and affine-transforms the {@code displacementField} to get a position field
144+
* @param displacementField
145+
* a normalized displacement field and its mapping to the source image
146+
* @param positionFieldFunction
147+
* maps position vectors to target values
148+
*/
149+
DisplacementFieldBlockSupplier(
150+
int numDimensions,
151+
AbstractDispFieldAffineProcessor< ? > fieldProcessor,
152+
BlockSupplier< D > displacementField,
153+
PositionFieldFunction< D, T, ?, ? > positionFieldFunction )
154+
{
155+
this.type = positionFieldFunction.getType();
156+
this.numDimensions = numDimensions;
157+
this.fieldProcessor = fieldProcessor;
158+
this.positionFieldFunction = positionFieldFunction;
159+
this.displacementField = displacementField;
160+
tempArrayPositionField = TempArray.forPrimitiveType( displacementField.getType().getNativeTypeFactory().getPrimitiveType() );
161+
}
162+
163+
private DisplacementFieldBlockSupplier(DisplacementFieldBlockSupplier< D, T > op )
164+
{
165+
this.type = op.type;
166+
this.numDimensions = op.numDimensions;
167+
this.fieldProcessor = op.fieldProcessor.independentCopy();
168+
this.positionFieldFunction = op.positionFieldFunction.independentCopy();
169+
this.displacementField = op.displacementField.independentCopy();
170+
this.tempArrayPositionField = op.tempArrayPositionField.newInstance();
171+
}
172+
173+
@SuppressWarnings( "unchecked" )
174+
@Override
175+
public void copy( Interval interval, Object dest )
176+
{
177+
final int destLength = ( int ) Intervals.numElements( interval );
178+
179+
fieldProcessor.setTargetInterval( interval );
180+
final Object bufField = fieldProcessor.getSourceBuffer();
181+
displacementField.copy( fieldProcessor.getSourceInterval(), bufField );
182+
final Object positions = tempArrayPositionField.get( safeInt( numDimensions() * ( long ) destLength ) );
183+
fieldProcessor.compute( bufField, positions );
184+
185+
final double[] offset = fieldProcessor.getInputOffset();
186+
// NB: We don't operate on a translated interval of the input, but use
187+
// input coordinates directly. Therefore, we need to undo the shift to
188+
// inputBounds.min().
189+
final BlockInterval bounds = fieldProcessor.getInputBounds();
190+
for ( int d = 0; d < numDimensions; d++ )
191+
offset[ d ] += bounds.min( d );
192+
193+
positionFieldFunction.compute( dest, destLength, positions, offset );
194+
}
195+
196+
@Override
197+
public int numDimensions() {
198+
return numDimensions;
199+
}
200+
201+
@Override
202+
public T getType() {
203+
return type;
204+
}
205+
206+
@Override
207+
public BlockSupplier<T> independentCopy() {
208+
return new DisplacementFieldBlockSupplier<>(this);
209+
}
210+
}
211+
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*-
2+
* #%L
3+
* ImgLib2: a general-purpose, multidimensional image processing library.
4+
* %%
5+
* Copyright (C) 2009 - 2025 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld,
6+
* John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke,
7+
* Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner,
8+
* Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert,
9+
* Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin,
10+
* Jean-Yves Tinevez and Michael Zinsmaier.
11+
* %%
12+
* Redistribution and use in source and binary forms, with or without
13+
* modification, are permitted provided that the following conditions are met:
14+
*
15+
* 1. Redistributions of source code must retain the above copyright notice,
16+
* this list of conditions and the following disclaimer.
17+
* 2. Redistributions in binary form must reproduce the above copyright notice,
18+
* this list of conditions and the following disclaimer in the documentation
19+
* and/or other materials provided with the distribution.
20+
*
21+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
25+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31+
* POSSIBILITY OF SUCH DAMAGE.
32+
* #L%
33+
*/
34+
package net.imglib2.algorithm.blocks.dfield;
35+
36+
import net.imglib2.algorithm.blocks.BlockSupplier;
37+
import net.imglib2.type.NativeType;
38+
import net.imglib2.type.numeric.RealType;
39+
40+
/**
41+
* A function that maps position vectors to {@code T}.
42+
*
43+
* @param <D>
44+
* position component type. should be {@code DoubleType} or {@code FloatType}
45+
* @param <T>
46+
* target pixel type
47+
* @param <F>
48+
* corresponding position field array type (must be float[] or double[])
49+
* @param <P>
50+
* corresponding target primitive array type (i.e., float[] or double[])
51+
*/
52+
public interface PositionFieldFunction< D extends NativeType< D > & RealType< D >, T extends NativeType< T >, F, P >
53+
{
54+
/**
55+
* Compute a block of target values from a block of position vectors.
56+
* (The components of the position vectors are flattened in dimension 0.)
57+
*
58+
* @param dest
59+
* block of output values to fill
60+
* @param length
61+
* number of output values (and number of positionField vectors)
62+
* @param positionField
63+
* position field block
64+
* @param positionOffset
65+
* offset to add to {@code positionField} vectors
66+
*/
67+
void compute( P dest, int length, F positionField, double[] positionOffset );
68+
69+
/**
70+
* Returns an instance of this {@link PositionFieldFunction} that can be
71+
* used independently, e.g., in another thread.
72+
*/
73+
PositionFieldFunction< D, T, F, P > independentCopy();
74+
75+
/**
76+
* Returns an instance of the target pixel type.
77+
*
78+
* @return target pixel type
79+
*/
80+
T getType();
81+
}

0 commit comments

Comments
 (0)