Skip to content

Commit 184558d

Browse files
committed
Add downsample block algorithm
The net.imglib2.algorithm.blocks.downsample package provides operators for downsampling blocks of (primitive arrays corresponding to) standard ImgLib2 RealTypes. Downsampling is always factor of 2 (in one ore more dimensions). There are versions for downsampling 2x2x2... blocks with half-pixel offset and 3x3x3... blocks on pixel centers.
1 parent 55ab022 commit 184558d

10 files changed

+1489
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
package net.imglib2.algorithm.blocks.downsample;
2+
3+
import java.util.Arrays;
4+
import java.util.function.Supplier;
5+
import net.imglib2.Interval;
6+
import net.imglib2.algorithm.blocks.BlockProcessor;
7+
import net.imglib2.algorithm.blocks.util.BlockProcessorSourceInterval;
8+
import net.imglib2.blocks.TempArray;
9+
import net.imglib2.type.PrimitiveType;
10+
import net.imglib2.util.CloseableThreadLocal;
11+
import net.imglib2.util.Intervals;
12+
13+
abstract class AbstractDownsample< T extends AbstractDownsample< T, P >, P > implements BlockProcessor< P, P >
14+
{
15+
PrimitiveType primitiveType;
16+
17+
final int n;
18+
final int[] destSize;
19+
final long[] sourcePos;
20+
final int[] sourceSize;
21+
22+
final boolean[] downsampleInDim;
23+
final int[] downsampleDims;
24+
final int steps;
25+
26+
// sources for every per-dimension downsampling step.
27+
// dest is the tempArray of the next step, or final dest for the last step.
28+
// tempArrays[0] can be used to copy the source block into.
29+
private final TempArray< P > tempArrays[];
30+
final int[] tempArraySizes;
31+
32+
private final BlockProcessorSourceInterval sourceInterval;
33+
34+
Supplier< T > threadSafeSupplier;
35+
36+
AbstractDownsample( final boolean[] downsampleInDim, final PrimitiveType primitiveType )
37+
{
38+
n = downsampleInDim.length;
39+
this.primitiveType = primitiveType;
40+
destSize = new int[ n ];
41+
sourceSize = new int[ n ];
42+
sourcePos = new long[ n ];
43+
44+
this.downsampleInDim = downsampleInDim;
45+
downsampleDims = downsampleDimIndices( downsampleInDim );
46+
steps = downsampleDims.length;
47+
48+
tempArrays = createTempArrays( steps, primitiveType );
49+
tempArraySizes = new int[ steps ];
50+
51+
sourceInterval = new BlockProcessorSourceInterval( this );
52+
}
53+
54+
private static int[] downsampleDimIndices( final boolean[] downsampleInDim )
55+
{
56+
final int n = downsampleInDim.length;
57+
final int[] dims = new int[ n ];
58+
int j = 0;
59+
for ( int i = 0; i < n; i++ )
60+
if ( downsampleInDim[ i ] )
61+
dims[ j++ ] = i;
62+
return Arrays.copyOf( dims, j );
63+
}
64+
65+
private static < P > TempArray< P >[] createTempArrays( final int steps, final PrimitiveType primitiveType )
66+
{
67+
final TempArray< P > tempArrays[] = new TempArray[ steps ];
68+
tempArrays[ 0 ] = TempArray.forPrimitiveType( primitiveType );
69+
if ( steps >= 2 )
70+
{
71+
tempArrays[ 1 ] = TempArray.forPrimitiveType( primitiveType );
72+
if ( steps >= 3 )
73+
{
74+
tempArrays[ 2 ] = TempArray.forPrimitiveType( primitiveType );
75+
for ( int i = 3; i < steps; ++i )
76+
tempArrays[ i ] = tempArrays[ i - 2 ];
77+
}
78+
}
79+
return tempArrays;
80+
}
81+
82+
AbstractDownsample( T downsample )
83+
{
84+
// re-use
85+
primitiveType = downsample.primitiveType;
86+
n = downsample.n;
87+
downsampleInDim = downsample.downsampleInDim;
88+
downsampleDims = downsample.downsampleDims;
89+
steps = downsample.steps;
90+
threadSafeSupplier = downsample.threadSafeSupplier;
91+
92+
// init empty
93+
destSize = new int[ n ];
94+
sourcePos = new long[ n ];
95+
sourceSize = new int[ n ];
96+
tempArraySizes = new int[ steps ];
97+
98+
// init new instance
99+
tempArrays = createTempArrays( steps, primitiveType );
100+
sourceInterval = new BlockProcessorSourceInterval( this );
101+
}
102+
103+
abstract T newInstance();
104+
105+
@Override
106+
public synchronized Supplier< T > threadSafeSupplier()
107+
{
108+
if ( threadSafeSupplier == null )
109+
threadSafeSupplier = CloseableThreadLocal.withInitial( this::newInstance )::get;
110+
return threadSafeSupplier;
111+
}
112+
113+
@Override
114+
public void setTargetInterval( final Interval interval )
115+
{
116+
boolean destSizeChanged = false;
117+
for ( int d = 0; d < n; ++d )
118+
{
119+
final long tpos = interval.min( d );
120+
sourcePos[ d ] = downsampleInDim[ d ] ? tpos * 2 - 1 : tpos;
121+
122+
final int tdim = safeInt( interval.dimension( d ) );
123+
if ( tdim != destSize[ d ] )
124+
{
125+
destSize[ d ] = tdim;
126+
sourceSize[ d ] = downsampleInDim[ d ] ? tdim * 2 + 1 : tdim;
127+
destSizeChanged = true;
128+
}
129+
}
130+
131+
if ( destSizeChanged )
132+
{
133+
int size = safeInt( Intervals.numElements( sourceSize ) );
134+
tempArraySizes[ 0 ] = size;
135+
for ( int i = 1; i < steps; ++i )
136+
{
137+
final int d = downsampleDims[ i - 1 ];
138+
size = size / sourceSize[ d ] * destSize[ d ];
139+
tempArraySizes[ i ] = size;
140+
}
141+
}
142+
}
143+
144+
static int safeInt( final long value )
145+
{
146+
if ( value > Integer.MAX_VALUE )
147+
throw new IllegalArgumentException( "value too large" );
148+
return ( int ) value;
149+
}
150+
151+
@Override
152+
public int[] getSourceSize()
153+
{
154+
return sourceSize;
155+
}
156+
157+
@Override
158+
public long[] getSourcePos()
159+
{
160+
return sourcePos;
161+
}
162+
163+
@Override
164+
public Interval getSourceInterval()
165+
{
166+
return sourceInterval;
167+
}
168+
169+
// optional. also other arrays can be passed to compute()
170+
@Override
171+
public P getSourceBuffer()
172+
{
173+
return getSourceBuffer( 0 );
174+
}
175+
176+
private P getSourceBuffer( int i )
177+
{
178+
return tempArrays[ i ].get( tempArraySizes[ i ] );
179+
}
180+
181+
@Override
182+
public void compute( final P src, final P dest )
183+
{
184+
P itSrc = src;
185+
final int[] itDestSize = sourceSize.clone();
186+
for ( int i = 0; i < steps; ++i )
187+
{
188+
final int d = downsampleDims[ i ];
189+
itDestSize[ d ] = destSize[ d ];
190+
final boolean lastStep = ( i == steps - 1 );
191+
final P itDest = lastStep ? dest : getSourceBuffer( i + 1 );
192+
downsample( itSrc, itDestSize, itDest, d );
193+
itSrc = itDest;
194+
}
195+
}
196+
197+
abstract void downsample( final P source, final int[] destSize, final P dest, final int dim );
198+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package net.imglib2.algorithm.blocks.downsample;
2+
3+
import net.imglib2.Interval;
4+
import net.imglib2.type.PrimitiveType;
5+
import net.imglib2.util.Intervals;
6+
7+
abstract class AbstractDownsampleHalfPixel< T extends AbstractDownsampleHalfPixel< T, P >, P > extends AbstractDownsample< T, P >
8+
{
9+
AbstractDownsampleHalfPixel( final boolean[] downsampleInDim, final PrimitiveType primitiveType )
10+
{
11+
super( downsampleInDim, primitiveType );
12+
}
13+
14+
AbstractDownsampleHalfPixel( T downsample )
15+
{
16+
super( downsample );
17+
}
18+
19+
@Override
20+
public void setTargetInterval( final Interval interval )
21+
{
22+
boolean destSizeChanged = false;
23+
for ( int d = 0; d < n; ++d )
24+
{
25+
final long tpos = interval.min( d );
26+
sourcePos[ d ] = downsampleInDim[ d ] ? tpos * 2 : tpos;
27+
28+
final int tdim = safeInt( interval.dimension( d ) );
29+
if ( tdim != destSize[ d ] )
30+
{
31+
destSize[ d ] = tdim;
32+
sourceSize[ d ] = downsampleInDim[ d ] ? tdim * 2 : tdim;
33+
destSizeChanged = true;
34+
}
35+
}
36+
37+
if ( destSizeChanged )
38+
{
39+
int size = safeInt( Intervals.numElements( sourceSize ) );
40+
tempArraySizes[ 0 ] = size;
41+
for ( int i = 1; i < steps; ++i )
42+
{
43+
final int d = downsampleDims[ i - 1 ];
44+
size = size / sourceSize[ d ] * destSize[ d ];
45+
tempArraySizes[ i ] = size;
46+
}
47+
}
48+
}
49+
50+
}

0 commit comments

Comments
 (0)