/* * #%L * OME Bio-Formats package for reading and converting biological file formats. * %% * Copyright (C) 2005 - 2015 Open Microscopy Environment: * - Board of Regents of the University of Wisconsin-Madison * - Glencoe Software, Inc. * - University of Dundee * %% * 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 loci.formats.in; import java.io.IOException; import java.util.ArrayList; import loci.common.RandomAccessInputStream; import loci.common.Region; import loci.formats.CoreMetadata; import loci.formats.FormatException; import loci.formats.FormatReader; import loci.formats.FormatTools; import loci.formats.MetadataTools; import loci.formats.meta.MetadataStore; import ome.units.UNITS; import ome.units.quantity.Length; import ome.xml.model.primitives.Color; import ome.xml.model.primitives.NonNegativeInteger; import ome.xml.model.primitives.PositiveFloat; /** * Reader for IMOD binary files. * * See http://bio3d.colorado.edu/imod/doc/binspec.html */ public class IMODReader extends FormatReader { // -- Constants -- private static final String MAGIC_STRING = "IMODV1.2"; // -- Fields -- private float[][][][] points; private byte[][] colors; // -- Constructor -- /** Constructs a new IMOD reader. */ public IMODReader() { super("IMOD", "mod"); domains = new String[] {FormatTools.UNKNOWN_DOMAIN}; } // -- IFormatReader API methods -- /* @see loci.formats.IFormatReader#isThisType(RandomAccessInputStream) */ @Override public boolean isThisType(RandomAccessInputStream stream) throws IOException { final int blockLen = 8; if (!FormatTools.validStream(stream, blockLen, false)) return false; return stream.readString(blockLen).equals(MAGIC_STRING); } /** * @see loci.formats.IFormatReader#openBytes(int, byte[], int, int, int, int) */ @Override public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h) throws FormatException, IOException { FormatTools.checkPlaneParameters(this, no, buf.length, x, y, w, h); // draw points given for each contour /* Region image = new Region(x, y, w, h); int pixel = getRGBChannelCount() * FormatTools.getBytesPerPixel(getPixelType()); for (int obj=0; obj<points.length; obj++) { for (int contour=0; contour<points[obj].length; contour++) { for (int point=0; point<points[obj][contour].length; point++) { if (points[obj][contour][point][2] == no) { int xc = (int) points[obj][contour][point][0]; int yc = getSizeY() - (int) points[obj][contour][point][1] - 1; if (image.containsPoint(xc, yc)) { xc -= x; yc -= y; int index = pixel * (yc * w + xc); System.arraycopy(colors[obj], 0, buf, index, colors[obj].length); } } } } } */ return buf; } /* @see loci.formats.IFormatReader#close(boolean) */ @Override public void close(boolean fileOnly) throws IOException { super.close(fileOnly); if (!fileOnly) { points = null; } } // -- Internal FormatReader API methods -- /* @see loci.formats.FormatReader#initFile(String) */ @Override protected void initFile(String id) throws FormatException, IOException { super.initFile(id); in = new RandomAccessInputStream(id); String check = in.readString(8); if (!check.equals(MAGIC_STRING)) { throw new FormatException("Invalid file ID: " + check); } CoreMetadata m = core.get(0); String filename = in.readString(128); m.sizeX = in.readInt(); m.sizeY = in.readInt(); m.sizeZ = in.readInt(); int nObjects = in.readInt(); points = new float[nObjects][][][]; colors = new byte[nObjects][3]; int flags = in.readInt(); int drawMode = in.readInt(); int mouseMode = in.readInt(); int blackLevel = in.readInt(); int whiteLevel = in.readInt(); float xOffset = in.readFloat(); float yOffset = in.readFloat(); float zOffset = in.readFloat(); float xScale = in.readFloat(); float yScale = in.readFloat(); float zScale = in.readFloat(); int currentObject = in.readInt(); int currentContour = in.readInt(); int currentPoint = in.readInt(); int res = in.readInt(); int thresh = in.readInt(); float pixSize = in.readFloat(); int pixSizeUnits = in.readInt(); int checksum = in.readInt(); float alpha = in.readFloat(); float beta = in.readFloat(); float gamma = in.readFloat(); addGlobalMeta("Model name", filename); addGlobalMeta("Model flags", flags); addGlobalMeta("Model drawing mode", drawMode); addGlobalMeta("Mouse mode", mouseMode); addGlobalMeta("Black level", blackLevel); addGlobalMeta("White level", whiteLevel); addGlobalMeta("X offset", xOffset); addGlobalMeta("Y offset", yOffset); addGlobalMeta("Z offset", zOffset); addGlobalMeta("X scale", xScale); addGlobalMeta("Y scale", yScale); addGlobalMeta("Z scale", zScale); addGlobalMeta("Alpha", alpha); addGlobalMeta("Beta", beta); addGlobalMeta("Gamma", gamma); MetadataStore store = makeFilterMetadata(); ArrayList<String> roiIDs = new ArrayList<String>(); for (int obj=0; obj<nObjects; obj++) { String objt = in.readString(4); while (!objt.equals("OBJT") && in.getFilePointer() < in.length()) { String prefix = "Object #" + obj + " "; if (objt.equals("IMAT")) { addGlobalMeta(prefix + "ambient", in.read()); addGlobalMeta(prefix + "diffuse", in.read()); addGlobalMeta(prefix + "specular", in.read()); addGlobalMeta(prefix + "shininess", in.read()); addGlobalMeta(prefix + "fill red", in.read()); addGlobalMeta(prefix + "fill green", in.read()); addGlobalMeta(prefix + "fill blue", in.read()); addGlobalMeta(prefix + "sphere quality", in.read()); in.skipBytes(4); addGlobalMeta(prefix + "black level", in.read()); addGlobalMeta(prefix + "white level", in.read()); in.skipBytes(2); } objt = in.readString(4); } if (!objt.equals("OBJT")) { break; } String objName = in.readString(64); in.skipBytes(64); // unused int nContours = in.readInt(); points[obj] = new float[nContours][][]; int objFlags = in.readInt(); int axis = in.readInt(); int objDrawMode = in.readInt(); float red = in.readFloat(); float green = in.readFloat(); float blue = in.readFloat(); colors[obj][0] = (byte) (red * 255); colors[obj][1] = (byte) (green * 255); colors[obj][2] = (byte) (blue * 255); int pixelRadius = in.readInt(); int pixelSymbol = in.read(); int symbolSize = in.read(); int lineWidth2D = in.read(); int lineWidth3D = in.read(); int lineStyle = in.read(); int symbolFlags = in.read(); int symbolPadding = in.read(); int transparency = in.read(); int nMeshes = in.readInt(); int nSurfaces = in.readInt(); if (getMetadataOptions().getMetadataLevel() == MetadataLevel.ALL) { String roiID = MetadataTools.createLSID("ROI", obj); store.setROIID(roiID, obj); store.setROIName(objName, obj); roiIDs.add(roiID); } int nextShape = 0; for (int contour=0; contour<nContours; contour++) { in.skipBytes(4); // CONT int nPoints = in.readInt(); int contourFlags = in.readInt(); int timeIndex = in.readInt(); int surface = in.readInt(); if (nPoints > in.length() || nPoints < 0) { while (!"CONT".equals(in.readString(4))) { in.seek(in.getFilePointer() - 8); } nPoints = in.readInt(); contourFlags = in.readInt(); timeIndex = in.readInt(); surface = in.readInt(); } points[obj][contour] = new float[nPoints][3]; for (int p=0; p<nPoints; p++) { for (int i=0; i<points[obj][contour][p].length; i++) { points[obj][contour][p][i] = in.readFloat(); } } if (getMetadataOptions().getMetadataLevel() == MetadataLevel.ALL) { boolean wild = (contourFlags & 0x10) == 0x10; Length l; if (wild) { int r = colors[obj][0] & 0xff; int g = colors[obj][1] & 0xff; int b = colors[obj][2] & 0xff; for (int i=0; i<nPoints; i++) { String shapeID = MetadataTools.createLSID("Shape", obj, nextShape); store.setPointID(shapeID, obj, nextShape); store.setPointStrokeColor( new Color(r, g, b, 0xff), obj, nextShape); l = new Length(new Double(lineWidth2D), UNITS.PIXEL); store.setPointStrokeWidth(l, obj, nextShape); if (lineStyle == 1) { store.setPointStrokeDashArray("5", obj, nextShape); } store.setPointX( new Double(points[obj][contour][i][0]), obj, nextShape); store.setPointY( new Double(points[obj][contour][i][1]), obj, nextShape); if (points[obj][contour][i][2] >= 0) { store.setPointTheZ(new NonNegativeInteger( (int) points[obj][contour][i][2]), obj, nextShape); } nextShape++; } } else { String shapeID = MetadataTools.createLSID("Shape", obj, nextShape); boolean closed = (contourFlags & 0x8) == 0; int r = colors[obj][0] & 0xff; int g = colors[obj][1] & 0xff; int b = colors[obj][2] & 0xff; StringBuffer sb = new StringBuffer(); for (int i=0; i<nPoints; i++) { sb.append(points[obj][contour][i][0]); sb.append(","); sb.append(points[obj][contour][i][1]); if (i < nPoints - 1) { sb.append(" "); } } if (closed) { store.setPolygonID(shapeID, obj, nextShape); store.setPolygonStrokeColor( new Color(r, g, b, 0xff), obj, nextShape); l = new Length(new Double(lineWidth2D), UNITS.PIXEL); store.setPolygonStrokeWidth(l, obj, nextShape); if (lineStyle == 1) { store.setPolygonStrokeDashArray("5", obj, nextShape); } if (nPoints > 0 && points[obj][contour][0][2] >= 0) { store.setPolygonTheZ(new NonNegativeInteger( (int) points[obj][contour][0][2]), obj, nextShape); } store.setPolygonPoints(sb.toString(), obj, nextShape); } else { store.setPolylineID(shapeID, obj, nextShape); store.setPolylineStrokeColor( new Color(r, g, b, 0xff), obj, nextShape); l = new Length(new Double(lineWidth2D), UNITS.PIXEL); store.setPolylineStrokeWidth(l, obj, nextShape); if (lineStyle == 1) { store.setPolylineStrokeDashArray("5", obj, nextShape); } if (nPoints > 0 && points[obj][contour][0][2] >= 0) { store.setPolylineTheZ(new NonNegativeInteger( (int) points[obj][contour][0][2]), obj, nextShape); } store.setPolylinePoints(sb.toString(), obj, nextShape); } nextShape++; } } } for (int mesh=0; mesh<nMeshes; mesh++) { in.skipBytes(4); // MESH int vsize = in.readInt(); int lsize = in.readInt(); int meshFlags = in.readInt(); int timeIndex = in.readShort(); int surface = in.readShort(); // TODO in.skipBytes(12 * vsize + 4 * lsize); } } double physicalX = 0d, physicalY = 0d, physicalZ = 0d; while (in.getFilePointer() + 4 < in.length()) { // check if there is any extra metadata at the end of the file String chunkType = in.readString(4); if (chunkType.equals("IMAT")) { in.skipBytes(20); } else if (chunkType.equals("VIEW")) { in.skipBytes(4); if (in.readInt() != 1) { in.skipBytes(176); int bytesPerView = in.readInt(); in.skipBytes(bytesPerView); } } else if (chunkType.equals("MINX")) { in.skipBytes(40); // skip old transformation values physicalX = in.readFloat(); physicalY = in.readFloat(); physicalZ = in.readFloat(); } } m.sizeT = 1; m.sizeC = 3; m.rgb = true; m.interleaved = true; m.imageCount = getSizeT() * getSizeZ(); m.littleEndian = false; m.dimensionOrder = "XYCZT"; m.pixelType = FormatTools.UINT8; MetadataTools.populatePixels(store, this); if (getMetadataOptions().getMetadataLevel() == MetadataLevel.ALL) { for (int i=0; i<roiIDs.size(); i++) { store.setROIID(roiIDs.get(i), i); store.setImageROIRef(roiIDs.get(i), 0, i); } } if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) { if (physicalX > 0) { store.setPixelsPhysicalSizeX( FormatTools.createLength(adjustForUnits(pixSizeUnits, physicalX), UNITS.MICROM), 0); } if (physicalY > 0) { store.setPixelsPhysicalSizeY( FormatTools.createLength(adjustForUnits(pixSizeUnits, physicalY), UNITS.MICROM), 0); } if (physicalZ > 0) { store.setPixelsPhysicalSizeZ( FormatTools.createLength(adjustForUnits(pixSizeUnits, physicalZ), UNITS.MICROM), 0); } } } // -- Helper methods -- private double adjustForUnits(int units, double value) { switch (units) { case 0: // pixels return value; case 1: // m return value * 100000000.0; case 3: // km return value * 100000000000.0; case -2: // cm return value * 1000000.0; case -3: // mm return value * 1000.0; case -6: // µm return value; case -9: // nm return value / 1000.0; case -10: // Å return value / 10000.0; case -12: // pm return value / 1000000.0; } return value; } }