/*-
* #%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%
*/
///////////////////////////////////////////////////////////////////////////////
//FILE: MultipageTiffReader.java
//PROJECT: Micro-Manager
//SUBSYSTEM: mmstudio
//-----------------------------------------------------------------------------
//
// AUTHOR: Henry Pinkard, henry.pinkard@gmail.com, 2012
// Stephan Preibisch, stephan.preibisch@gmx.de 2015
// Nico Stuurman 2015
//
// COPYRIGHT: University of California, San Francisco, 2012
//
// LICENSE: This file is distributed under the BSD license.
// License text is included with the source distribution.
//
// This file 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.
//
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES.
//
package spim.fiji.spimdata.imgloaders;
import ij.ImageJ;
import ij.ImagePlus;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import mpicbg.spim.io.IOFunctions;
import net.imglib2.img.array.ArrayImgs;
import net.imglib2.img.display.imagej.ImageJFunctions;
import net.imglib2.util.Util;
import spim.fiji.ImgLib2Temp.Pair;
import spim.fiji.ImgLib2Temp.ValuePair;
/**
* Adapted version from the MicroManager codebase that supports the new diSPIM related tags
* and multiple-file reading.
*/
public class MultipageTiffReader
{
private static final long BIGGEST_INT_BIT = (long) Math.pow(2, 31);
public static final int INDEX_MAP_HEADER = 3453623;
public static final int DISPLAY_SETTINGS_OFFSET_HEADER = 483765892;
public static final int DISPLAY_SETTINGS_HEADER = 347834724;
public static final int INDEX_MAP_OFFSET_HEADER = 54773648;
public static final int SUMMARY_MD_HEADER = 2355492;
public static final int COMMENTS_OFFSET_HEADER = 99384722;
public static final int COMMENTS_HEADER = 84720485;
public static final char BITS_PER_SAMPLE = 258;
public static final char STRIP_OFFSETS = 273;
public static final char SAMPLES_PER_PIXEL = 277;
public static final char STRIP_BYTE_COUNTS = 279;
public static final char IMAGE_DESCRIPTION = 270;
public static final char MM_METADATA = 51123;
public static String lastDisplayedFile;
private ByteOrder byteOrder_;
private List< File > files;
private List< RandomAccessFile > raFiles;
private List< FileChannel > fileChannels;
/*
* Contains pixel size, etc.
*/
private HashMap< String, Object > summaryMetadata_;
private int byteDepth_ = 0;;
private boolean rgb_;
protected String unit = "um";
protected double calX = Double.NaN;
protected double calY = Double.NaN;
protected double calZ = Double.NaN;
protected double[] rotAxis = null;
protected boolean applyAxis = true;
protected List< String > angleNames = null;
protected List< String > channelNames = null;
private HashMap< String, Pair< Long, FileChannel > > indexMap_;
/**
* This constructor is used for opening datasets that have already been
* saved
*/
public MultipageTiffReader( final File file ) throws IOException
{
this.files = new ArrayList< File >();
this.raFiles = new ArrayList< RandomAccessFile >();
this.fileChannels = new ArrayList< FileChannel >();
int i = 0;
try
{
this.files.add( file );
// looking for other files
if ( file.getAbsolutePath().toLowerCase().endsWith( ".ome.tif" ) || file.getAbsolutePath().toLowerCase().endsWith( ".ome.tiff" ) )
{
final String begin = file.getName().substring( 0, file.getName().toLowerCase().indexOf( ".ome.tif" ) );
final File dir = file.getParentFile();
final String[] list = dir.list();
Arrays.sort( list );
for ( final String fn : list )
if ( !fn.equals( file.getName() ) && fn.startsWith( begin ) && fn.toLowerCase().endsWith( ".ome.tif" ) || fn.toLowerCase().endsWith( ".ome.tiff" ) )
this.files.add( new File( dir, fn ) );
}
if ( lastDisplayedFile == null )
lastDisplayedFile = "";
if ( !lastDisplayedFile.equals( file.getAbsolutePath() ) )
IOFunctions.printlnSafe( "Using the following files for the MicroManager ImgLoader: " );
for ( i = 0; i < this.files.size(); ++i )
{
final File f = this.files.get( i );
if ( !lastDisplayedFile.equals( file.getAbsolutePath() ) )
IOFunctions.printlnSafe( f.getAbsolutePath() );
this.raFiles.add( new RandomAccessFile( f, "rw" ) );
this.fileChannels.add( this.raFiles.get( this.raFiles.size() - 1 ).getChannel() );
}
lastDisplayedFile = file.getAbsolutePath();
}
catch ( Exception e )
{
e.printStackTrace();
throw new IOException( "Can't successfully open file: " + this.files.get( i ).getName() + ": " + e );
}
// Assuming byteorders to be the same, where the first image has the last word
for ( i = this.fileChannels.size() - 1; i >= 0; --i )
readHeader( this.fileChannels.get( i ) );
summaryMetadata_ = new HashMap< String, Object >();
// updating the summary metadata object, the first image has the last word if duplicate entries are present
for ( i = this.fileChannels.size() - 1; i >= 0; --i )
readSummaryMD( this.fileChannels.get( i ), summaryMetadata_ );
if ( summaryMetadata_ == null )
throw new IOException( "Could not read metadata" );
this.indexMap_ = new HashMap< String, Pair< Long, FileChannel > >();
try
{
for ( i = 0; i < this.files.size(); ++i )
readIndexMap( this.fileChannels.get( i ), indexMap_ );
}
catch ( Exception e )
{
e.printStackTrace();
throw new IOException( "Reading of dataset unsuccessful for file: " + this.files.get( i ).getName() );
}
}
public String getPixelType()
{
try
{
if ( summaryMetadata_ != null )
return summaryMetadata_.get( "PixelType" ).toString();
}
catch ( Exception e)
{
try
{
int ijType = Integer.parseInt( summaryMetadata_.get( "IJType" ).toString() );
if (ijType == ImagePlus.GRAY8) {
return "GRAY8";
} else if (ijType == ImagePlus.GRAY16) {
return "GRAY16";
} else if (ijType == ImagePlus.GRAY32) {
return "GRAY32";
} else if (ijType == ImagePlus.COLOR_RGB) {
return "RGB32";
} else {
throw new RuntimeException("Can't figure out pixel type");
}
// There is no IJType for RGB64.
} catch ( Exception e2 ) {
throw new RuntimeException("Can't figure out pixel type");
}
}
return "";
}
public void setAngleNames( final List< String > names ) { this.angleNames = names; }
public void setChannelNames( final List< String > names ) { this.channelNames = names; }
private void getRGBAndByteDepth( final HashMap< String, Object > map )
{
try {
String pixelType = getPixelType();
rgb_ = pixelType.startsWith("RGB");
if (pixelType.equals("RGB32") || pixelType.equals("GRAY8")) {
byteDepth_ = 1;
} else {
byteDepth_ = 2;
}
} catch (Exception ex) {
IOFunctions.printlnSafe(ex);
}
}
public HashMap< String, Object > getSummaryMetadata() { return summaryMetadata_; }
public Pair< Object, HashMap< String, Object > > readImage( final String label )
{
if ( indexMap_.containsKey( label ) )
{
final FileChannel fileChannel = indexMap_.get( label ).getB();
if ( fileChannel == null )
{
IOFunctions.printlnSafe( "Attempted to read image on FileChannel that is null" );
return null;
}
try
{
final long byteOffset = indexMap_.get( label ).getA();
final IFDData data = readIFD( byteOffset, fileChannel );
return readTaggedImage( data, fileChannel );
}
catch ( IOException ex )
{
IOFunctions.printlnSafe(ex);
return null;
}
}
else
{
IOFunctions.printlnSafe( "Exception: label '" + label + "' not in present in hashmap, cannot read data." );
// label not in map--either writer hasnt finished writing it
return null;
}
}
public Set<String> getIndexKeys() {
if (indexMap_ == null)
return null;
return indexMap_.keySet();
}
private boolean readSummaryMD( final FileChannel fileChannel, final HashMap< String, Object > summaryMD_ )
{
try
{
final ByteBuffer mdInfo = ByteBuffer.allocate(8).order(byteOrder_);
fileChannel.read( mdInfo, 32 );
final int header = mdInfo.getInt( 0 );
final int length = mdInfo.getInt( 4 );
if ( header != SUMMARY_MD_HEADER )
{
IOFunctions.printlnSafe( "Summary Metadata Header Incorrect" );
return false;
}
final ByteBuffer mdBuffer = ByteBuffer.allocate( length ).order( byteOrder_ );
fileChannel.read( mdBuffer, 40 );
final HashMap< String, Object > summaryMD = parseJSONSimple( getString( mdBuffer ) );
if ( summaryMD == null )
IOFunctions.printlnSafe( "Couldn't read summary Metadata from file: " + getFileForFileChannel( fileChannel ).getName() );
// MVRotationAxis = 0_1_0
// MVRotations = 0_90_0_90
// >>> channels increasing, angles increasing faster
// fake the multiviewdata if necessary
if ( !summaryMD.containsKey( "MVRotationAxis" ) )
summaryMD.put( "MVRotationAxis", "0_1_0" );
if ( !summaryMD.containsKey( "MVRotations" ) )
{
final int numChannels = Integer.parseInt( summaryMD.get( "Channels" ).toString() );
if ( numChannels == 2 )
summaryMD.put( "MVRotations", "0_90" );
else if ( numChannels == 4 )
summaryMD.put( "MVRotations", "0_90_0_90" );
else if ( numChannels == 6 )
summaryMD.put( "MVRotations", "0_90_0_90_0_90" );
}
summaryMD_.putAll( summaryMD );
return true;
}
catch (Exception ex)
{
ex.printStackTrace();
IOFunctions.printlnSafe( "Couldn't read summary Metadata from file: " + getFileForFileChannel( fileChannel ).getName() );
return false;
}
}
private File getFileForFileChannel( final FileChannel fileChannel )
{
File file = null;
// same order
for ( int i = 0; i < this.fileChannels.size(); ++i )
if ( this.fileChannels.get( i ) == fileChannel )
{
file = this.files.get( i );
break;
}
return file;
}
protected HashMap< String, Object > parseJSONSimple( final String json )
{
String jsonString = json.trim();
jsonString = jsonString.substring( 1, jsonString.length() - 1 );
final HashMap< String, Object > map = new HashMap< String, Object >();
do
{
if ( !jsonString.startsWith( "\"" ) )
{
IOFunctions.printlnSafe( "Failed to parse json string: " + json );
return null;
}
final String key = jsonString.substring( 1, jsonString.indexOf( '\"', 1 ) );
int valueStart = jsonString.indexOf( "\":" ) + 2;
int valueEnd;
if ( jsonString.charAt( valueStart ) == '[' )
{
valueEnd = jsonString.indexOf( ']' ) + 1;
}
else if ( jsonString.charAt( valueStart ) == '{' )
{
valueEnd = jsonString.indexOf( '}' ) + 1;
}
else if ( jsonString.charAt( valueStart ) == '\"' )
{
++valueStart;
valueEnd = jsonString.indexOf( '\"', valueStart );
}
else
{
valueEnd = jsonString.indexOf( ',' );
if ( valueEnd == -1 )
valueEnd = jsonString.length();
}
final String value = jsonString.substring( valueStart, valueEnd );
final int nextComma = jsonString.indexOf( ',', valueEnd );
if ( nextComma == -1 )
jsonString = "";
else
jsonString = jsonString.substring( nextComma + 1, jsonString.length() );
map.put( key, value );
}
while ( jsonString.length() > 0 );
return map;
}
private ByteBuffer readIntoBuffer( final long position, final int length, final FileChannel fileChannel_ ) throws IOException
{
final ByteBuffer buffer = ByteBuffer.allocate(length).order( byteOrder_ );
fileChannel_.read( buffer, position );
return buffer;
}
private long readOffsetHeaderAndOffset( final int offsetHeaderVal, final int startOffset, final FileChannel fileChannel_ ) throws IOException
{
final ByteBuffer buffer1 = readIntoBuffer( startOffset, 8, fileChannel_ );
final int offsetHeader = buffer1.getInt( 0 );
if ( offsetHeader != offsetHeaderVal )
{
throw new IOException( "Offset header incorrect, expected: " + offsetHeaderVal + " found: " + offsetHeader );
}
return unsignInt( buffer1.getInt( 4 ) );
}
public static String generateLabel( final int channel, final int slice, final int frame, final int position )
{
return NumberUtils.intToCoreString(channel) + "_"
+ NumberUtils.intToCoreString(slice) + "_"
+ NumberUtils.intToCoreString(frame) + "_"
+ NumberUtils.intToCoreString(position);
}
private void readIndexMap( final FileChannel fileChannel, final HashMap< String, Pair< Long, FileChannel > > indexMap_ ) throws IOException
{
final long offset = readOffsetHeaderAndOffset( INDEX_MAP_OFFSET_HEADER, 8, fileChannel );
final ByteBuffer header = readIntoBuffer( offset, 8, fileChannel );
if ( header.getInt(0) != INDEX_MAP_HEADER )
throw new RuntimeException( "Error reading index map header" );
final int numMappings = header.getInt( 4 );
final ByteBuffer mapBuffer = readIntoBuffer( offset + 8, 20 * numMappings, fileChannel );
for ( int i = 0; i < numMappings; ++i )
{
final int channel = mapBuffer.getInt( i * 20 );
final int slice = mapBuffer.getInt( i * 20 + 4 );
final int frame = mapBuffer.getInt( i * 20 + 8 );
final int position = mapBuffer.getInt( i * 20 + 12 );
final long imageOffset = unsignInt( mapBuffer.getInt( i * 20 + 16 ) );
if ( imageOffset == 0 )
break; // end of index map reached
// If a duplicate label is read, forget about the previous one
// if data has been intentionally overwritten, this gives the most
// current version
final String label = generateLabel( channel, slice, frame, position );
if ( indexMap_.containsKey( label ) )
IOFunctions.printlnSafe( "ERROR!!! Label: " + label + " already present." );
//System.out.println( label + " " + getFileForFileChannel( fileChannel ).getName() );
indexMap_.put( label, new ValuePair< Long, FileChannel >( imageOffset, fileChannel ) );
}
}
private IFDData readIFD( final long byteOffset, final FileChannel fileChannel ) throws IOException
{
final ByteBuffer buff = readIntoBuffer( byteOffset, 2, fileChannel );
final int numEntries = buff.getChar( 0 );
final ByteBuffer entries = readIntoBuffer( byteOffset + 2, numEntries * 12 + 4, fileChannel ).order( byteOrder_ );
final IFDData data = new IFDData();
for ( int i = 0; i < numEntries; ++i )
{
final IFDEntry entry = readDirectoryEntry( i * 12, entries );
if ( entry.tag == MM_METADATA )
{
data.mdOffset = entry.value;
data.mdLength = entry.count;
}
else if (entry.tag == STRIP_OFFSETS)
{
data.pixelOffset = entry.value;
}
else if (entry.tag == STRIP_BYTE_COUNTS)
{
data.bytesPerImage = entry.value;
}
}
data.nextIFD = unsignInt( entries.getInt( numEntries * 12 ) );
data.nextIFDOffsetLocation = byteOffset + 2 + numEntries * 12;
return data;
}
private String getString(ByteBuffer buffer) {
try {
return new String(buffer.array(), "UTF-8");
} catch (UnsupportedEncodingException ex) {
IOFunctions.printlnSafe(ex);
return "";
}
}
private Pair< Object, HashMap< String, Object > > readTaggedImage( final IFDData data, final FileChannel fileChannel ) throws IOException
{
final ByteBuffer pixelBuffer = ByteBuffer.allocate( (int)data.bytesPerImage).order( byteOrder_ );
final ByteBuffer mdBuffer = ByteBuffer.allocate( (int)data.mdLength).order( byteOrder_ );
fileChannel.read( pixelBuffer, data.pixelOffset );
fileChannel.read( mdBuffer, data.mdOffset );
final HashMap< String, Object > md = parseJSONSimple( getString( mdBuffer ) );
if ( byteDepth_ == 0 )
getRGBAndByteDepth( md );
if ( rgb_ )
{
IOFunctions.printlnSafe( "RGB types not supported." );
return null;
}
else
{
if (byteDepth_ == 1)
{
return new ValuePair<Object, HashMap< String, Object >>( pixelBuffer.array(), md );
}
else
{
final short[] pix = new short[ pixelBuffer.capacity() / 2 ];
for ( int i = 0; i < pix.length; ++i )
pix[ i ] = pixelBuffer.getShort( i * 2 );
return new ValuePair<Object, HashMap< String, Object >>( pix, md );
}
}
}
private IFDEntry readDirectoryEntry( final int offset, final ByteBuffer buffer ) throws IOException
{
final char tag = buffer.getChar(offset);
final char type = buffer.getChar(offset + 2);
final long count = unsignInt(buffer.getInt(offset + 4));
final long value;
if (type == 3 && count == 1)
value = buffer.getChar(offset + 8);
else
value = unsignInt(buffer.getInt(offset + 8));
return ( new IFDEntry( tag, type, count, value ) );
}
// returns byteoffset of first IFD
private long readHeader( final FileChannel fileChannel ) throws IOException
{
final ByteBuffer tiffHeader = ByteBuffer.allocate( 8 );
fileChannel.read( tiffHeader, 0 );
final char zeroOne = tiffHeader.getChar( 0 );
if (zeroOne == 0x4949)
byteOrder_ = ByteOrder.LITTLE_ENDIAN;
else if (zeroOne == 0x4d4d)
byteOrder_ = ByteOrder.BIG_ENDIAN;
else
throw new IOException("Error reading Tiff header");
tiffHeader.order( byteOrder_ );
final short twoThree = tiffHeader.getShort( 2 );
if (twoThree != 42)
throw new IOException("Tiff identifier code incorrect");
return unsignInt( tiffHeader.getInt( 4 ) );
}
public void close() throws IOException
{
for ( final FileChannel fileChannel : this.fileChannels )
if ( fileChannel != null )
fileChannel.close();
this.fileChannels.clear();
for ( final RandomAccessFile raFile : this.raFiles )
if ( raFile != null )
raFile.close();
this.raFiles.clear();
}
public void setApplyAxis( final boolean apply ) { this.applyAxis = apply; }
public boolean applyAxis() { return applyAxis; }
public void setCalX( final double cal ) { this.calX = cal; }
public void setCalY( final double cal ) { this.calY = cal; }
public void setCalZ( final double cal ) { this.calZ = cal; }
public void setCalUnit( final String unit ) { this.unit = unit; }
public int width() { return Integer.parseInt( summaryMetadata_.get( "Width" ).toString() ); }
public int height() { return Integer.parseInt( summaryMetadata_.get( "Height" ).toString() ); }
public int depth() { return Integer.parseInt( summaryMetadata_.get( "Slices" ).toString() ); }
public double calX()
{
if ( Double.isNaN( calX ) )
{
final double x = Double.parseDouble( summaryMetadata_.get( "PixelSize_um" ).toString() );
if ( x <= 0 )
return 1;
else
return x;
}
else
return calX;
}
public double calY()
{
if ( Double.isNaN( calY ) )
{
final double y = Double.parseDouble( summaryMetadata_.get( "PixelSize_um" ).toString() );
if ( y <= 0 )
return 1;
else
return y;
}
else
return calY;
}
public double calZ()
{
if ( Double.isNaN( calZ ) )
{
final Object o = summaryMetadata_.get( "z-step_um" );
if ( o == null )
return 1.0;
else
{
final double z = Double.parseDouble( o.toString() );
if ( z <= 0 )
return 1;
else
return z;
}
}
else
{
return calZ;
}
}
public String calUnit() { return unit; }
public int numTimepoints() { return Integer.parseInt( summaryMetadata_.get( "Frames" ).toString() ); }
public int numPositions() { return Integer.parseInt( summaryMetadata_.get( "Positions" ).toString() ); }
public int numChannelsAndAngles() { return Integer.parseInt( summaryMetadata_.get( "Channels" ).toString() ); }
public int numChannels()
{
final int totalNum = numChannelsAndAngles();
final int numAngles = numAngles();
if ( totalNum % numAngles != 0 )
throw new RuntimeException( "Channels & Angle number is not symmetric. This is not supported. TotalNumCh=" + totalNum + ", numAngles=" + numAngles );
return totalNum / numAngles();
}
public int numAngles()
{
// MVRotations = 0_90_0_90
// >>> channels increasing, angles increasing faster
final String ac = summaryMetadata_.get( "MVRotations" ).toString().trim();
final String[] entries = ac.split( "_" );
final HashSet< Integer > uniqueAngles = new HashSet< Integer >();
for ( int i = 0; i < entries.length; ++i )
uniqueAngles.add( Integer.parseInt( entries[ i ] ) );
return uniqueAngles.size();
}
public String rotationAngle( final int angleId )
{
if ( angleId < 0 || angleId >= numAngles() )
{
IOFunctions.printlnSafe( "No angle with id " + angleId + ", there are only " + numAngles() + " angles." );
return String.valueOf( angleId );
}
if ( this.angleNames != null )
return this.angleNames.get( angleId );
// MVRotations = 0_90_0_90
// >>> channels increasing, angles increasing faster
try
{
final String ac = summaryMetadata_.get( "MVRotations" ).toString().trim();
final String[] entries = ac.split( "_" );
return entries[ angleId ];
}
catch ( Exception e )
{
IOFunctions.printlnSafe( "Failed to get rotation angle: " + e );
return "0";
}
}
public int interleavedId( final int channelId, final int angleId )
{
return channelId * numAngles() + angleId;
}
public String channelName( final int channelId )
{
if ( channelId < 0 || channelId >= numChannels() )
{
IOFunctions.printlnSafe( "No channel with id " + channelId + ", there are only " + numChannels() + " channels." );
return String.valueOf( channelId );
}
if ( this.channelNames != null )
return this.channelNames.get( channelId );
// ChNames: ["Camera_A-561","Camera_B-561","Camera_A-488","Camera_B-488"]
try
{
String as = summaryMetadata_.get( "ChNames" ).toString().trim();
if ( as.startsWith( "[" ) && as.endsWith( "]") )
as = as.substring( 1, as.length() - 1 );
final String[] entries = as.split( "," );
if ( entries.length != numChannelsAndAngles() )
{
IOFunctions.printlnSafe( "Number of entries in " + summaryMetadata_.get( "ChNames" ).toString().trim() + " does not match numAngles()*numChannels()=" + numChannelsAndAngles() );
return String.valueOf( channelId );
}
String entry = entries[ interleavedId( channelId, 0 ) ];
if ( entry.indexOf( '-' ) > 0 )
return entry.substring( entry.indexOf( '-' ) + 1, entry.length() - 1 );
else
return entry.substring( 1, entry.length() - 1 );
}
catch ( Exception e )
{
IOFunctions.printlnSafe( "Failed to parse channel name: " + e );
return String.valueOf( channelId );
}
}
public String rotationAxisName()
{
final double[] r = rotationAxis();
if ( r[ 0 ] == 1 && r[ 1 ] == 0 && r[ 2 ] == 0 )
return "X-Axis";
else if ( r[ 0 ] == 0 && r[ 1 ] == 1 && r[ 2 ] == 0 )
return "Y-Axis";
else if ( r[ 0 ] == 0 && r[ 0 ] == 1 && r[ 1 ] == 0 )
return "Z-Axis";
else
return "Rotation Axis Vector: " + Util.printCoordinates( r );
}
public int rotationAxisIndex()
{
final double[] r = rotationAxis();
if ( r[ 0 ] == 1 && r[ 1 ] == 0 && r[ 2 ] == 0 )
return 0;
else if ( r[ 0 ] == 0 && r[ 1 ] == 1 && r[ 2 ] == 0 )
return 1;
else if ( r[ 0 ] == 0 && r[ 0 ] == 1 && r[ 1 ] == 0 )
return 2;
else
return -1;
}
public void setRotAxis( final double[] axis ) { this.rotAxis = axis; }
public double[] rotationAxis()
{
// MVRotationAxis = 0_1_0
if ( this.rotAxis == null )
{
String as = summaryMetadata_.get( "MVRotationAxis" ).toString();
String[] v = as.split( "_" );
double x = Double.parseDouble( v[ 0 ].trim() );
double y = Double.parseDouble( v[ 1 ].trim() );
double z = Double.parseDouble( v[ 2 ].trim() );
return new double[]{ x, y, z };
}
else
{
return rotAxis;
}
}
final private static long unsignInt( final int i )
{
long val = Integer.MAX_VALUE & i;
if (i < 0)
val += BIGGEST_INT_BIT;
return val;
}
public class IFDData
{
public long pixelOffset;
public long bytesPerImage;
public long mdOffset;
public long mdLength;
public long nextIFD;
public long nextIFDOffsetLocation;
}
public class IFDEntry
{
final public char tag, type;
final public long count, value;
public IFDEntry( final char tg, final char typ, final long cnt, final long val )
{
this.tag = tg;
this.type = typ;
this.count = cnt;
this.value = val;
}
}
public static void main(String[] args) throws IOException
{
final File f;
f = new File( "/Users/preibischs/Documents/Microscopy/SPIM/561D", "MMStack_Pos0.ome.tif" );
//f = new File( "/Users/preibischs/Documents/Microscopy/SPIM/BeadVolume2ch_1", "MMStack_Pos0.ome.tif" );
MultipageTiffReader r = new MultipageTiffReader( f );
for ( final String k : r.getSummaryMetadata().keySet() )
System.out.println( k + ": " + r.getSummaryMetadata().get( k ) );
final String pixelType = r.getPixelType();
if ( pixelType.toUpperCase().startsWith( "RGB" ) )
{
IOFunctions.println( "RGB not supported." );
return;
}
System.out.println( "width: " + r.width() );
System.out.println( "height: " + r.height() );
System.out.println( "depth: " + r.depth() );
System.out.println( "calX: " + r.calX() );
System.out.println( "calY: " + r.calY() );
System.out.println( "calZ: " + r.calZ() );
System.out.println( "numAngles: " + r.numAngles() );
System.out.println( "rotation axis: " + Util.printCoordinates( r.rotationAxis() ) );
for ( int i = 0; i < r.numAngles(); ++i )
System.out.println( "angle " + i + " rotation: " + r.rotationAngle( i ) );
System.out.println( "numChannels: " + r.numChannels() );
for ( int i = 0; i < r.numChannels(); ++i )
System.out.println( "channel " + i + " name: " + r.channelName( i ) );
System.out.println( "numTimepoints: " + r.numTimepoints() );
System.out.println( "numPositions: " + r.numPositions() );
/*
ArrayList< String > indices = new ArrayList< String >();
for ( final String k : r.getIndexKeys() )
indices.add( k );
Collections.sort( indices );
for ( final String k : indices )
System.out.println( k );
*/
final int c = 0;
final int a = 1;
final int s = 20;
final int t = 0;
final int p = 0;
String label = generateLabel( r.interleavedId( c, a ), s, t, p );
Object o = r.readImage( label ).getA();
System.out.println( label );
System.out.println( r.indexMap_.get( label ) );
new ImageJ();
if ( o instanceof byte[] )
{
//ImageJFunctions.show( ArrayImgs.unsignedBytes( (byte[])o, r.width(), r.height() ) );
}
else if ( o instanceof short[] )
{
System.out.println( ((short[])o).length );
ImageJFunctions.show( ArrayImgs.unsignedShorts( (short[])o, r.width(), r.height() ) );
}
}
}