35
35
package net .imglib2 .imagej ;
36
36
37
37
import ij .ImagePlus ;
38
- import net .imglib2 .Cursor ;
39
38
import net .imglib2 .cache .Cache ;
40
39
import net .imglib2 .cache .ref .SoftRefLoaderCache ;
41
- import net .imglib2 .converter .Converter ;
42
- import net .imglib2 .imagej .imageplus .*;
43
- import net .imglib2 .img .Img ;
44
- import net .imglib2 .img .basictypeaccess .array .ArrayDataAccess ;
40
+ import net .imglib2 .imagej .imageplus .ByteImagePlus ;
41
+ import net .imglib2 .imagej .imageplus .FloatImagePlus ;
42
+ import net .imglib2 .imagej .imageplus .IntImagePlus ;
43
+ import net .imglib2 .imagej .imageplus .ShortImagePlus ;
44
+ import net .imglib2 .img .basictypeaccess .array .*;
45
45
import net .imglib2 .img .planar .PlanarImg ;
46
- import net .imglib2 .type .Type ;
46
+ import net .imglib2 .type .NativeType ;
47
+ import net .imglib2 .type .NativeTypeFactory ;
47
48
import net .imglib2 .type .numeric .ARGBType ;
48
- import net .imglib2 .type .numeric .ComplexType ;
49
49
import net .imglib2 .type .numeric .integer .UnsignedByteType ;
50
- import net .imglib2 .type .numeric .integer .UnsignedIntType ;
51
50
import net .imglib2 .type .numeric .integer .UnsignedShortType ;
52
51
import net .imglib2 .type .numeric .real .FloatType ;
52
+ import net .imglib2 .util .Fraction ;
53
53
54
54
import java .util .AbstractList ;
55
+ import java .util .List ;
55
56
import java .util .concurrent .ExecutionException ;
56
57
import java .util .function .Function ;
58
+ import java .util .stream .LongStream ;
57
59
58
60
/**
59
61
* Provides convenience functions to wrap ImageJ 1.x data structures as ImgLib2
63
65
* @author Stephan Preibisch
64
66
* @author Stephan Saalfeld
65
67
* @author Matthias Arzt
68
+ * @author Gabriel Selzer
66
69
*/
67
70
public class ImagePlusToImg
68
71
{
69
72
70
- public static PlanarImg < ?, ? > wrap ( final ImagePlus imp )
73
+ /**
74
+ * Wraps an {@link ImagePlus} into a {@link PlanarImg}.
75
+ * <p>
76
+ * Under the hood, each {@link ij.process.ImageProcessor}'s backing array is
77
+ * wrapped into an {@link ArrayDataAccess}. The resulting {@link List} of
78
+ * {@link ArrayDataAccess}es is used to form a {@link PlanarImg}.
79
+ * </p>
80
+ *
81
+ * @param imp the {@link ImagePlus} to wrap
82
+ * @return a {@link PlanarImg} directly wrapping {@code imp}
83
+ */
84
+ public static PlanarImg < ?, ? > wrapDirect (final ImagePlus imp )
71
85
{
72
86
switch ( imp .getType () )
73
87
{
74
88
case ImagePlus .GRAY8 :
75
- return wrapByte ( imp );
89
+ return wrapByteDirect ( imp );
76
90
case ImagePlus .GRAY16 :
77
- return wrapShort ( imp );
91
+ return wrapShortDirect ( imp );
78
92
case ImagePlus .GRAY32 :
79
- return wrapFloat ( imp );
93
+ return wrapFloatDirect ( imp );
80
94
case ImagePlus .COLOR_RGB :
81
- return wrapRGBA ( imp );
95
+ return wrapRGBADirect ( imp );
82
96
default :
83
97
throw new RuntimeException ( "Only 8, 16, 32-bit and RGB supported!" );
84
98
}
85
99
}
86
100
87
- public static PlanarImg < UnsignedByteType , ? > wrapByte (final ImagePlus imp )
101
+ /**
102
+ * Wraps an {@link ImagePlus} into a {@link PlanarImg} of unsigned bytes.
103
+ * <p>
104
+ * Under the hood, each {@link ij.process.ImageProcessor}'s backing array is
105
+ * wrapped into an {@link ByteArray}. The resulting {@link List} of
106
+ * {@link ArrayDataAccess}es is used to form a {@link PlanarImg}.
107
+ * </p>
108
+ *
109
+ * @param imp the {@link ImagePlus} to wrap
110
+ * @return a {@link PlanarImg} of unsigned bytes directly wrapping {@code imp}
111
+ */
112
+ public static PlanarImg < UnsignedByteType , ? > wrapByteDirect (final ImagePlus imp )
88
113
{
89
114
if ( imp .getType () != ImagePlus .GRAY8 )
90
- return null ;
115
+ throw new IllegalArgumentException ( imp + " does not contain unsigned bytes!" ) ;
91
116
92
117
final ByteImagePlus < UnsignedByteType > container = new ByteImagePlus <>( imp );
93
118
@@ -100,10 +125,21 @@ public class ImagePlusToImg
100
125
return container ;
101
126
}
102
127
103
- public static PlanarImg < UnsignedShortType , ? > wrapShort (final ImagePlus imp )
128
+ /**
129
+ * Wraps an {@link ImagePlus} into a {@link PlanarImg} of unsigned shorts.
130
+ * <p>
131
+ * Under the hood, each {@link ij.process.ImageProcessor}'s backing array is
132
+ * wrapped into an {@link ShortArray}. The resulting {@link List} of
133
+ * {@link ArrayDataAccess}es is used to form a {@link PlanarImg}.
134
+ * </p>
135
+ *
136
+ * @param imp the {@link ImagePlus} to wrap
137
+ * @return a {@link PlanarImg} of unsigned shorts directly wrapping {@code imp}
138
+ */
139
+ public static PlanarImg < UnsignedShortType , ? > wrapShortDirect (final ImagePlus imp )
104
140
{
105
141
if ( imp .getType () != ImagePlus .GRAY16 )
106
- return null ;
142
+ throw new IllegalArgumentException ( imp + " does not contain unsigned shorts!" ) ;
107
143
108
144
final ShortImagePlus < UnsignedShortType > container = new ShortImagePlus <>( imp );
109
145
@@ -116,26 +152,21 @@ public class ImagePlusToImg
116
152
return container ;
117
153
}
118
154
119
- public static PlanarImg < UnsignedIntType , ? > wrapInt (final ImagePlus imp )
120
- {
121
- if ( imp .getType () != ImagePlus .COLOR_RGB )
122
- return null ;
123
-
124
- final IntImagePlus < UnsignedIntType > container = new IntImagePlus <>( imp );
125
-
126
- // create a Type that is linked to the container
127
- final UnsignedIntType linkedType = new UnsignedIntType ( container );
128
-
129
- // pass it to the DirectAccessContainer
130
- container .setLinkedType ( linkedType );
131
-
132
- return container ;
133
- }
134
-
135
- public static PlanarImg < ARGBType , ? > wrapRGBA ( final ImagePlus imp )
155
+ /**
156
+ * Wraps an {@link ImagePlus} into a {@link PlanarImg} of RGBA tuples.
157
+ * <p>
158
+ * Under the hood, each {@link ij.process.ImageProcessor}'s backing array is
159
+ * wrapped into an {@link IntArray}. The resulting {@link List} of
160
+ * {@link ArrayDataAccess}es is used to form a {@link PlanarImg}.
161
+ * </p>
162
+ *
163
+ * @param imp the {@link ImagePlus} to wrap
164
+ * @return a {@link PlanarImg} of ARGB tuples directly wrapping {@code imp}
165
+ */
166
+ public static PlanarImg < ARGBType , ? > wrapRGBADirect (final ImagePlus imp )
136
167
{
137
168
if ( imp .getType () != ImagePlus .COLOR_RGB )
138
- return null ;
169
+ throw new IllegalArgumentException ( imp + " does not contain RGB tuples!" ) ;
139
170
140
171
final IntImagePlus < ARGBType > container = new IntImagePlus <>( imp );
141
172
@@ -148,10 +179,21 @@ public class ImagePlusToImg
148
179
return container ;
149
180
}
150
181
151
- public static PlanarImg < FloatType , ? > wrapFloat (final ImagePlus imp )
182
+ /**
183
+ * Wraps an {@link ImagePlus} into a {@link PlanarImg} of floats.
184
+ * <p>
185
+ * Under the hood, each {@link ij.process.ImageProcessor}'s backing array is
186
+ * wrapped into an {@link FloatArray}. The resulting {@link List} of
187
+ * {@link ArrayDataAccess}es is used to form a {@link PlanarImg}.
188
+ * </p>
189
+ *
190
+ * @param imp the {@link ImagePlus} to wrap
191
+ * @return a {@link PlanarImg} of floats directly wrapping {@code imp}
192
+ */
193
+ public static PlanarImg < FloatType , ? > wrapFloatDirect (final ImagePlus imp )
152
194
{
153
195
if ( imp .getType () != ImagePlus .GRAY32 )
154
- return null ;
196
+ throw new IllegalArgumentException ( imp + " does not contain floats!" ) ;
155
197
156
198
final FloatImagePlus < FloatType > container = new FloatImagePlus <>( imp );
157
199
@@ -164,61 +206,99 @@ public class ImagePlusToImg
164
206
return container ;
165
207
}
166
208
167
- public static PlanarImg < FloatType , ? > convertFloat ( final ImagePlus imp )
209
+ /**
210
+ * Wraps an 8 bit {@link ImagePlus}, into an {@link PlanarImg}, that is backed
211
+ * by a {@link PlanarImg}. The {@link PlanarImg} loads the planes only if
212
+ * needed, and caches them.
213
+ * @param image the {@link ImagePlus} to wrap. Must contain unsigned bytes.
214
+ * @return a {@link PlanarImg} wrapping {@code image}.
215
+ */
216
+ public static PlanarImg < UnsignedByteType , ByteArray > wrapByteCached (final ImagePlus image )
168
217
{
169
-
170
- switch ( imp .getType () )
171
- {
172
- case ImagePlus .GRAY8 :
173
- return convertToFloat ( wrapByte ( imp ), new NumberToFloatConverter < UnsignedByteType >() );
174
- case ImagePlus .GRAY16 :
175
- return convertToFloat ( wrapShort ( imp ), new NumberToFloatConverter < UnsignedShortType >() );
176
- case ImagePlus .GRAY32 :
177
- return wrapFloat ( imp );
178
- case ImagePlus .COLOR_RGB :
179
- return convertToFloat ( wrapRGBA ( imp ), new ARGBtoFloatConverter () );
180
- default :
181
- throw new RuntimeException ( "Only 8, 16, 32-bit and RGB supported!" );
182
- }
218
+ return internWrap ( image , ImagePlus .GRAY8 , new UnsignedByteType (), array -> new ByteArray ( ( byte [] ) array ) );
183
219
}
184
220
185
- static private class ARGBtoFloatConverter implements Converter < ARGBType , FloatType >
221
+ /**
222
+ * Wraps a 16 bit {@link ImagePlus}, into an {@link PlanarImg}, that is backed
223
+ * by a {@link PlanarImg}. The {@link PlanarImg} loads the planes only if
224
+ * needed, and caches them.
225
+ * @param image the {@link ImagePlus} to wrap. Must contain unsigned shorts.
226
+ * @return a {@link PlanarImg} wrapping {@code image}.
227
+ */
228
+ public static PlanarImg < UnsignedShortType , ShortArray > wrapShortCached (final ImagePlus image )
186
229
{
187
- /** Luminance times alpha. */
188
- @ Override
189
- public void convert ( final ARGBType input , final FloatType output )
190
- {
191
- final int v = input .get ();
192
- output .setReal ( ( ( v >> 24 ) & 0xff ) * ( ( ( v >> 16 ) & 0xff ) * 0.299 + ( ( v >> 8 ) & 0xff ) * 0.587 + ( v & 0xff ) * 0.144 ) );
193
- }
230
+ return internWrap ( image , ImagePlus .GRAY16 , new UnsignedShortType (), array -> new ShortArray ( ( short [] ) array ) );
194
231
}
195
232
196
- static private class NumberToFloatConverter < T extends ComplexType < T > > implements Converter < T , FloatType >
233
+ /**
234
+ * Wraps a 32 bit {@link ImagePlus}, into an {@link PlanarImg}, that is backed
235
+ * by a {@link PlanarImg}. The {@link PlanarImg} loads the planes only if
236
+ * needed, and caches them.
237
+ * @param image the {@link ImagePlus} to wrap. Must contain floats.
238
+ * @return a {@link PlanarImg} wrapping {@code image}.
239
+ */
240
+ public static PlanarImg < FloatType , FloatArray > wrapFloatCached (final ImagePlus image )
197
241
{
198
- @ Override
199
- public void convert ( final T input , final FloatType output )
200
- {
201
- output .setReal ( input .getRealFloat () );
202
- }
242
+ return internWrap ( image , ImagePlus .GRAY32 , new FloatType (), array -> new FloatArray ( ( float [] ) array ) );
203
243
}
204
244
205
- protected static < T extends Type < T > > PlanarImg < FloatType , ?> convertToFloat (
206
- final Img < T > input , final Converter < T , FloatType > c )
245
+ /**
246
+ * Wraps a 24 bit {@link ImagePlus}, into an {@link PlanarImg}, that is backed
247
+ * by a {@link PlanarImg}. The {@link PlanarImg} loads the planes only if
248
+ * needed, and caches them.
249
+ * @param image the {@link ImagePlus} to wrap. Must contain RGB tuples.
250
+ * @return a {@link PlanarImg} wrapping {@code image}.
251
+ */
252
+ public static PlanarImg < ARGBType , IntArray > wrapRGBACached (final ImagePlus image )
207
253
{
208
- final ImagePlusImg < FloatType , ? > output = new ImagePlusImgFactory <>( new FloatType () ).create ( input );
209
-
210
- final Cursor < T > in = input .cursor ();
211
- final Cursor < FloatType > out = output .cursor ();
254
+ return internWrap ( image , ImagePlus .COLOR_RGB , new ARGBType (), array -> new IntArray ( ( int [] ) array ) );
255
+ }
212
256
213
- while ( in .hasNext () )
257
+ /**
258
+ * Wraps an {@link ImagePlus}, into an {@link PlanarImg}, that is backed by a
259
+ * {@link PlanarImg}. The {@link PlanarImg} loads the planes only if needed,
260
+ * and caches them. The pixel type of the returned image depends on the type
261
+ * of the ImagePlus.
262
+ * @param image the {@link ImagePlus} to wrap
263
+ * @return a {@link PlanarImg} wrapping {@code image}.
264
+ */
265
+ public static PlanarImg < ?, ? > wrapCached (final ImagePlus image )
266
+ {
267
+ switch ( image .getType () )
214
268
{
215
- in .fwd ();
216
- out .fwd ();
217
-
218
- c .convert ( in .get (), out .get () );
269
+ case ImagePlus .GRAY8 :
270
+ return wrapByteCached ( image );
271
+ case ImagePlus .GRAY16 :
272
+ return wrapShortCached ( image );
273
+ case ImagePlus .GRAY32 :
274
+ return wrapFloatCached ( image );
275
+ case ImagePlus .COLOR_RGB :
276
+ return wrapRGBACached ( image );
219
277
}
278
+ throw new RuntimeException ( "Only 8, 16, 32-bit and RGB supported!" );
279
+ }
220
280
221
- return output ;
281
+ private static < T extends NativeType < T >, A extends ArrayDataAccess < A > > PlanarImg < T , A > internWrap (
282
+ final ImagePlus image ,
283
+ final int expectedType ,
284
+ final T type ,
285
+ final Function < Object , A > createArrayAccess
286
+ ) {
287
+ if ( image .getType () != expectedType )
288
+ throw new IllegalArgumentException ();
289
+ final ImagePlusLoader < A > loader = new ImagePlusLoader <>( image , createArrayAccess );
290
+ final long [] dimensions = getNonTrivialDimensions ( image );
291
+ final PlanarImg < T , A > cached = new PlanarImg <>( loader , dimensions , new Fraction () );
292
+ cached .setLinkedType ( ( (NativeTypeFactory < T , A >) type .getNativeTypeFactory () ).createLinkedType ( cached ) );
293
+ // TODO: Preserve metadata
294
+ return cached ;
295
+ }
296
+
297
+ private static long [] getNonTrivialDimensions (final ImagePlus image )
298
+ {
299
+ final LongStream xy = LongStream .of ( image .getWidth (), image .getHeight () );
300
+ final LongStream czt = LongStream .of ( image .getNChannels (), image .getNSlices (), image .getNFrames () );
301
+ return LongStream .concat ( xy , czt .filter ( x -> x > 1 ) ).toArray ();
222
302
}
223
303
224
304
private static class ImagePlusLoader < A extends ArrayDataAccess < A >> extends AbstractList < A >
@@ -260,6 +340,4 @@ public int size()
260
340
return image .getStackSize ();
261
341
}
262
342
}
263
-
264
- //
265
343
}
0 commit comments