/*- * #%L * Fiji distribution of ImageJ for the life sciences. * %% * Copyright (C) 2007 - 2017 Fiji developers. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-2.0.html>. * #L% */ package spim.fiji.spimdata.imgloaders; import ij.IJ; import java.io.File; import java.io.IOException; import java.util.Date; import loci.common.services.DependencyException; import loci.common.services.ServiceException; import loci.common.services.ServiceFactory; import loci.formats.ChannelSeparator; import loci.formats.FormatTools; import loci.formats.IFormatReader; import loci.formats.meta.IMetadata; import loci.formats.services.OMEXMLService; import mpicbg.spim.data.generic.sequence.AbstractSequenceDescription; import mpicbg.spim.data.generic.sequence.BasicViewDescription; import mpicbg.spim.data.generic.sequence.BasicViewSetup; import mpicbg.spim.data.sequence.Angle; import mpicbg.spim.data.sequence.Channel; import mpicbg.spim.data.sequence.Illumination; import mpicbg.spim.data.sequence.TimePoint; import mpicbg.spim.data.sequence.ViewId; import mpicbg.spim.io.IOFunctions; import net.imglib2.Cursor; import net.imglib2.RandomAccessibleInterval; import net.imglib2.img.Img; import net.imglib2.img.ImgFactory; import net.imglib2.img.array.ArrayImg; import net.imglib2.type.NativeType; import net.imglib2.type.numeric.RealType; import net.imglib2.type.numeric.integer.UnsignedShortType; import net.imglib2.type.numeric.real.FloatType; import net.imglib2.view.Views; import spim.fiji.datasetmanager.LightSheetZ1; import spim.fiji.datasetmanager.LightSheetZ1MetaData; public class LegacyLightSheetZ1ImgLoader extends AbstractImgFactoryImgLoader { final File cziFile; final AbstractSequenceDescription<?, ?, ?> sequenceDescription; // once the metadata is loaded for one view, it is available for all other ones LightSheetZ1MetaData meta; boolean isClosed = true; public LegacyLightSheetZ1ImgLoader( final File cziFile, final ImgFactory< ? extends NativeType< ? > > imgFactory, final AbstractSequenceDescription<?, ?, ?> sequenceDescription ) { super(); this.cziFile = cziFile; this.sequenceDescription = sequenceDescription; setImgFactory( imgFactory ); } public File getCZIFile() { return cziFile; } @Override public RandomAccessibleInterval< FloatType > getFloatImage( final ViewId view, final boolean normalize ) { try { final Img< FloatType > img = openCZI( new FloatType(), view ); if ( img == null ) throw new RuntimeException( "Could not load '" + cziFile + "' viewId=" + view.getViewSetupId() + ", tpId=" + view.getTimePointId() ); if ( normalize ) normalize( img ); return img; } catch ( Exception e ) { throw new RuntimeException( "Could not load '" + cziFile + "' viewId=" + view.getViewSetupId() + ", tpId=" + view.getTimePointId() + ": " + e ); } } @Override public RandomAccessibleInterval< UnsignedShortType > getImage( final ViewId view ) { try { final Img< UnsignedShortType > img = openCZI( new UnsignedShortType(), view ); if ( img == null ) throw new RuntimeException( "Could not load '" + cziFile + "' viewId=" + view.getViewSetupId() + ", tpId=" + view.getTimePointId() ); return img; } catch ( Exception e ) { throw new RuntimeException( "Could not load '" + cziFile + "' viewId=" + view.getViewSetupId() + ", tpId=" + view.getTimePointId() + ": " + e ); } } @Override protected void loadMetaData( final ViewId view ) { IOFunctions.printlnSafe( new Date( System.currentTimeMillis() ) + ": Loading metadata for Lightsheet Z1 imgloader not necessary." ); } @Override public void finalize() { IOFunctions.printlnSafe( "Closing czi: " + cziFile ); try { if ( meta != null && meta.getReader() != null ) { meta.getReader().close(); isClosed = true; } } catch (IOException e) {} } protected < T extends RealType< T > & NativeType< T > > Img< T > openCZI( final T type, final ViewId view ) throws Exception { if ( meta == null ) { IOFunctions.printlnSafe( new Date( System.currentTimeMillis() ) + ": Investigating file '" + cziFile.getAbsolutePath() + "' (loading metadata)." ); meta = new LightSheetZ1MetaData(); if ( !meta.loadMetaData( cziFile, true ) ) { IOFunctions.printlnSafe( "Failed to analyze file: '" + cziFile.getAbsolutePath() + "'." ); meta = null; isClosed = true; return null; } else { isClosed = false; } } final BasicViewDescription< ? > vd = sequenceDescription.getViewDescriptions().get( view ); final BasicViewSetup vs = vd.getViewSetup(); final TimePoint t = vd.getTimePoint(); final Angle a = getAngle( vd ); final Channel c = getChannel( vd ); final Illumination i = getIllumination( vd ); final int[] dim; if ( vs.hasSize() ) { dim = new int[ vs.getSize().numDimensions() ]; for ( int d = 0; d < vs.getSize().numDimensions(); ++d ) dim[ d ] = (int)vs.getSize().dimension( d ); } else { dim = meta.imageSizes().get( a.getId() ); } final Img< T > img = imgFactory.imgFactory( type ).create( dim, type ); if ( img == null ) throw new RuntimeException( "Could not instantiate " + getImgFactory().getClass().getSimpleName() + " for '" + cziFile + "' viewId=" + view.getViewSetupId() + ", tpId=" + view.getTimePointId() + ", most likely out of memory." ); final boolean isLittleEndian = meta.isLittleEndian(); final boolean isArray = ArrayImg.class.isInstance( img ); final int pixelType = meta.pixelType(); final int width = dim[ 0 ]; final int height = dim[ 1 ]; final int depth = dim[ 2 ]; final int numPx = width * height; final IFormatReader r; // if we already loaded the metadata in this run, use the opened file if ( meta.getReader() == null ) r = LegacyLightSheetZ1ImgLoader.instantiateImageReader(); else r = meta.getReader(); final byte[] b = new byte[ numPx * meta.bytesPerPixel() ]; try { // open the file if not already done try { if ( meta.getReader() == null ) { IOFunctions.printlnSafe( new Date( System.currentTimeMillis() ) + ": Opening '" + cziFile.getName() + "' for reading image data." ); r.setId( cziFile.getAbsolutePath() ); } // set the right angle r.setSeries( a.getId() ); } catch ( IllegalStateException e ) { r.setId( cziFile.getAbsolutePath() ); r.setSeries( a.getId() ); } IOFunctions.printlnSafe( new Date( System.currentTimeMillis() ) + ": Reading image data from '" + cziFile.getName() + "' [" + dim[ 0 ] + "x" + dim[ 1 ] + "x" + dim[ 2 ] + " angle=" + a.getName() + " ch=" + c.getName() + " illum=" + i.getName() + " tp=" + t.getName() + " type=" + meta.pixelTypeString() + " img=" + img.getClass().getSimpleName() + "<" + type.getClass().getSimpleName() + ">]" ); // TODO: fix the channel/illum assignments, I think the lower one is correct but need example dataset // compute the right channel from channelId & illuminationId //int ch = c.getId() * meta.numIlluminations() + i.getId(); // c0( i0, i1 ), c1( i0, i1 ), c2( i0, i1 ) int ch = i.getId() * meta.numChannels() + c.getId(); // i0( c0, c1, c2 ), i1( c0, c1, c2 ) for ( int z = 0; z < depth; ++z ) { IJ.showProgress( (double)z / (double)depth ); final Cursor< T > cursor = Views.iterable( Views.hyperSlice( img, 2, z ) ).localizingCursor(); r.openBytes( r.getIndex( z, ch, t.getId() ), b ); if ( pixelType == FormatTools.UINT8 ) { if ( isArray ) readBytesArray( b, cursor, numPx ); else readBytes( b, cursor, width ); } else if ( pixelType == FormatTools.UINT16 ) { if ( isArray ) readUnsignedShortsArray( b, cursor, numPx, isLittleEndian ); else readUnsignedShorts( b, cursor, width, isLittleEndian ); } else if ( pixelType == FormatTools.INT16 ) { if ( isArray ) readSignedShortsArray( b, cursor, numPx, isLittleEndian ); else readSignedShorts( b, cursor, width, isLittleEndian ); } else if ( pixelType == FormatTools.UINT32 ) { //TODO: Untested if ( isArray ) readUnsignedIntsArray( b, cursor, numPx, isLittleEndian ); else readUnsignedInts( b, cursor, width, isLittleEndian ); } else if ( pixelType == FormatTools.FLOAT ) { if ( isArray ) readFloatsArray( b, cursor, numPx, isLittleEndian ); else readFloats( b, cursor, width, isLittleEndian ); } } IJ.showProgress( 1 ); } catch ( Exception e ) { IOFunctions.printlnSafe( "File '" + cziFile.getAbsolutePath() + "' could not be opened: " + e ); IOFunctions.printlnSafe( "Stopping" ); e.printStackTrace(); try { r.close(); } catch (IOException e1) { e1.printStackTrace(); } return null; } return img; } public static final < T extends RealType< T > > void readBytes( final byte[] b, final Cursor< T > cursor, final int width ) { while( cursor.hasNext() ) { cursor.fwd(); // otherwise the position is off below cursor.get().setReal( b[ cursor.getIntPosition( 0 ) + cursor.getIntPosition( 1 ) * width ] & 0xff ); } } public static final < T extends RealType< T > > void readBytesArray( final byte[] b, final Cursor< T > cursor, final int numPx ) { for ( int i = 0; i < numPx; ++i ) cursor.next().setReal( b[ i ] & 0xff ); } public static final < T extends RealType< T > > void readUnsignedShorts( final byte[] b, final Cursor< T > cursor, final int width, final boolean isLittleEndian ) { while( cursor.hasNext() ) { cursor.fwd(); cursor.get().setReal( LegacyStackImgLoaderLOCI.getShortValueInt( b, ( cursor.getIntPosition( 0 ) + cursor.getIntPosition( 1 ) * width ) * 2, isLittleEndian ) ); } } public static final < T extends RealType< T > > void readUnsignedShortsArray( final byte[] b, final Cursor< T > cursor, final int numPx, final boolean isLittleEndian ) { for ( int i = 0; i < numPx; ++i ) cursor.next().setReal( LegacyStackImgLoaderLOCI.getShortValueInt( b, i * 2, isLittleEndian ) ); } public static final < T extends RealType< T > > void readSignedShorts( final byte[] b, final Cursor< T > cursor, final int width, final boolean isLittleEndian ) { while( cursor.hasNext() ) { cursor.fwd(); cursor.get().setReal( LegacyStackImgLoaderLOCI.getShortValue( b, ( cursor.getIntPosition( 0 ) + cursor.getIntPosition( 1 ) * width ) * 2, isLittleEndian ) ); } } public static final < T extends RealType< T > > void readSignedShortsArray( final byte[] b, final Cursor< T > cursor, final int numPx, final boolean isLittleEndian ) { for ( int i = 0; i < numPx; ++i ) cursor.next().setReal( LegacyStackImgLoaderLOCI.getShortValue( b, i * 2, isLittleEndian ) ); } public static final < T extends RealType< T > > void readUnsignedInts( final byte[] b, final Cursor< T > cursor, final int width, final boolean isLittleEndian ) { while( cursor.hasNext() ) { cursor.fwd(); cursor.get().setReal( LegacyStackImgLoaderLOCI.getIntValue( b, ( cursor.getIntPosition( 0 ) + cursor.getIntPosition( 1 ) * width ) * 4, isLittleEndian ) ); } } public static final < T extends RealType< T > > void readUnsignedIntsArray( final byte[] b, final Cursor< T > cursor, final int numPx, final boolean isLittleEndian ) { for ( int i = 0; i < numPx; ++i ) cursor.next().setReal( LegacyStackImgLoaderLOCI.getIntValue( b, i * 4, isLittleEndian ) ); } public static final < T extends RealType< T > > void readFloats( final byte[] b, final Cursor< T > cursor, final int width, final boolean isLittleEndian ) { while( cursor.hasNext() ) { cursor.fwd(); cursor.get().setReal( LegacyStackImgLoaderLOCI.getFloatValue( b, ( cursor.getIntPosition( 0 ) + cursor.getIntPosition( 1 ) * width ) * 4, isLittleEndian ) ); } } public static final < T extends RealType< T > > void readFloatsArray( final byte[] b, final Cursor< T > cursor, final int numPx, final boolean isLittleEndian ) { for ( int i = 0; i < numPx; ++i ) cursor.next().setReal( LegacyStackImgLoaderLOCI.getFloatValue( b, i * 4, isLittleEndian ) ); } public static IFormatReader instantiateImageReader() { // should I use the ZeissCZIReader here directly? return new ChannelSeparator();// new ZeissCZIReader(); } public static boolean createOMEXMLMetadata( final IFormatReader r ) { try { final ServiceFactory serviceFactory = new ServiceFactory(); final OMEXMLService service = serviceFactory.getInstance( OMEXMLService.class ); final IMetadata omexmlMeta = service.createOMEXMLMetadata(); r.setMetadataStore(omexmlMeta); } catch (final ServiceException e) { e.printStackTrace(); return false; } catch (final DependencyException e) { e.printStackTrace(); return false; } return true; } protected static Angle getAngle( final AbstractSequenceDescription< ?, ?, ? > seqDesc, final ViewId view ) { return getAngle( seqDesc.getViewDescriptions().get( view ) ); } protected static Angle getAngle( final BasicViewDescription< ? > vd ) { final BasicViewSetup vs = vd.getViewSetup(); final Angle angle = vs.getAttribute( Angle.class ); if ( angle == null ) throw new RuntimeException( "This XML does not have the 'Angle' attribute for their ViewSetup. Cannot continue." ); return angle; } protected static Channel getChannel( final BasicViewDescription< ? > vd ) { final BasicViewSetup vs = vd.getViewSetup(); final Channel channel = vs.getAttribute( Channel.class ); if ( channel == null ) throw new RuntimeException( "This XML does not have the 'Channel' attribute for their ViewSetup. Cannot continue." ); return channel; } protected static Illumination getIllumination( final BasicViewDescription< ? > vd ) { final BasicViewSetup vs = vd.getViewSetup(); final Illumination illumination = vs.getAttribute( Illumination.class ); if ( illumination == null ) throw new RuntimeException( "This XML does not have the 'Illumination' attribute for their ViewSetup. Cannot continue." ); return illumination; } @Override public String toString() { return new LightSheetZ1().getTitle() + ", ImgFactory=" + imgFactory.getClass().getSimpleName(); } }