3535package net .imglib2 .imagej ;
3636
3737import ij .ImagePlus ;
38- import net .imglib2 .Cursor ;
3938import net .imglib2 .cache .Cache ;
4039import 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 .*;
4545import net .imglib2 .img .planar .PlanarImg ;
46- import net .imglib2 .type .Type ;
46+ import net .imglib2 .type .NativeType ;
47+ import net .imglib2 .type .NativeTypeFactory ;
4748import net .imglib2 .type .numeric .ARGBType ;
48- import net .imglib2 .type .numeric .ComplexType ;
4949import net .imglib2 .type .numeric .integer .UnsignedByteType ;
50- import net .imglib2 .type .numeric .integer .UnsignedIntType ;
5150import net .imglib2 .type .numeric .integer .UnsignedShortType ;
5251import net .imglib2 .type .numeric .real .FloatType ;
52+ import net .imglib2 .util .Fraction ;
5353
5454import java .util .AbstractList ;
55+ import java .util .List ;
5556import java .util .concurrent .ExecutionException ;
5657import java .util .function .Function ;
58+ import java .util .stream .LongStream ;
5759
5860/**
5961 * Provides convenience functions to wrap ImageJ 1.x data structures as ImgLib2
6365 * @author Stephan Preibisch
6466 * @author Stephan Saalfeld
6567 * @author Matthias Arzt
68+ * @author Gabriel Selzer
6669 */
6770public class ImagePlusToImg
6871{
6972
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 )
7185 {
7286 switch ( imp .getType () )
7387 {
7488 case ImagePlus .GRAY8 :
75- return wrapByte ( imp );
89+ return wrapByteDirect ( imp );
7690 case ImagePlus .GRAY16 :
77- return wrapShort ( imp );
91+ return wrapShortDirect ( imp );
7892 case ImagePlus .GRAY32 :
79- return wrapFloat ( imp );
93+ return wrapFloatDirect ( imp );
8094 case ImagePlus .COLOR_RGB :
81- return wrapRGBA ( imp );
95+ return wrapRGBADirect ( imp );
8296 default :
8397 throw new RuntimeException ( "Only 8, 16, 32-bit and RGB supported!" );
8498 }
8599 }
86100
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 )
88113 {
89114 if ( imp .getType () != ImagePlus .GRAY8 )
90- return null ;
115+ throw new IllegalArgumentException ( imp + " does not contain unsigned bytes!" ) ;
91116
92117 final ByteImagePlus < UnsignedByteType > container = new ByteImagePlus <>( imp );
93118
@@ -100,10 +125,21 @@ public class ImagePlusToImg
100125 return container ;
101126 }
102127
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 )
104140 {
105141 if ( imp .getType () != ImagePlus .GRAY16 )
106- return null ;
142+ throw new IllegalArgumentException ( imp + " does not contain unsigned shorts!" ) ;
107143
108144 final ShortImagePlus < UnsignedShortType > container = new ShortImagePlus <>( imp );
109145
@@ -116,26 +152,21 @@ public class ImagePlusToImg
116152 return container ;
117153 }
118154
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 )
136167 {
137168 if ( imp .getType () != ImagePlus .COLOR_RGB )
138- return null ;
169+ throw new IllegalArgumentException ( imp + " does not contain RGB tuples!" ) ;
139170
140171 final IntImagePlus < ARGBType > container = new IntImagePlus <>( imp );
141172
@@ -148,10 +179,21 @@ public class ImagePlusToImg
148179 return container ;
149180 }
150181
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 )
152194 {
153195 if ( imp .getType () != ImagePlus .GRAY32 )
154- return null ;
196+ throw new IllegalArgumentException ( imp + " does not contain floats!" ) ;
155197
156198 final FloatImagePlus < FloatType > container = new FloatImagePlus <>( imp );
157199
@@ -164,61 +206,99 @@ public class ImagePlusToImg
164206 return container ;
165207 }
166208
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 )
168217 {
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 ) );
183219 }
184220
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 )
186229 {
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 ) );
194231 }
195232
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 )
197241 {
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 ) );
203243 }
204244
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 )
207253 {
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+ }
212256
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 () )
214268 {
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 );
219277 }
278+ throw new RuntimeException ( "Only 8, 16, 32-bit and RGB supported!" );
279+ }
220280
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 ();
222302 }
223303
224304 private static class ImagePlusLoader < A extends ArrayDataAccess < A >> extends AbstractList < A >
@@ -260,6 +340,4 @@ public int size()
260340 return image .getStackSize ();
261341 }
262342 }
263-
264- //
265343}
0 commit comments