/* * #%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% */ /*! * \file WlzService.java * \author Bill Hill * \date August 2013 * \version $Id$ * \par * Address: * MRC Human Genetics Unit, * MRC Institute of Genetics and Molecular Medicine, * University of Edinburgh, * Western General Hospital, * Edinburgh, EH4 2XU, UK. * \par * Copyright (C), [2013], * The University Court of the University of Edinburgh, * Old College, Edinburgh, UK. * * 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, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * \brief Woolz service for bioformats. */ package loci.formats.services; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.StringTokenizer; import java.util.Vector; import loci.common.Constants; import loci.common.Location; import loci.common.services.AbstractService; import loci.common.services.ServiceException; import loci.formats.FormatTools; import loci.formats.FormatException; import loci.formats.MissingLibraryException; import uk.ac.mrc.hgu.Wlz.WlzException; import uk.ac.mrc.hgu.Wlz.WlzGreyType; import uk.ac.mrc.hgu.Wlz.WlzObjectType; import uk.ac.mrc.hgu.Wlz.WlzFileStream; import uk.ac.mrc.hgu.Wlz.WlzFileInputStream; import uk.ac.mrc.hgu.Wlz.WlzFileOutputStream; import uk.ac.mrc.hgu.Wlz.WlzIBox2; import uk.ac.mrc.hgu.Wlz.WlzIBox3; import uk.ac.mrc.hgu.Wlz.WlzIVertex2; import uk.ac.mrc.hgu.Wlz.WlzIVertex3; import uk.ac.mrc.hgu.Wlz.WlzDVertex2; import uk.ac.mrc.hgu.Wlz.WlzDVertex3; import uk.ac.mrc.hgu.Wlz.WlzObject; public class WlzServiceImpl extends AbstractService implements WlzService { // -- Constants -- public static final String WLZ_ORG_LABEL = "WoolzOrigin"; public static final String NO_WLZ_MSG = "\n" + "Woolz is required to read and write Woolz objects.\n" + "Please obtain the necessary JAR and native library files from:\n" + "http://www.emouseatlas.org/emap/analysis_tools_resources/software/woolz.html.\n" + "The source code for these is also available from:\n" + "https://github.com/ma-tech/Woolz."; public static final int WLZ_SERVICE_UNKNOWN = 0; public static final int WLZ_SERVICE_READ = 1; public static final int WLZ_SERVICE_WRITE = 2; /* * Fields */ private int state = WLZ_SERVICE_UNKNOWN; private int pixelType = FormatTools.UINT8; private int objType = WlzObjectType.WLZ_NULL; /* objGType is the Woolz value type, but may be WLZ_GREY_ERROR to indicate * an object with no values, just a domain. */ private int objGType = WlzGreyType.WLZ_GREY_ERROR; private String wlzVersion = new String("unknown"); private WlzIBox3 bBox = null; private WlzDVertex3 voxSz = null; private WlzObject wlzObj = null; private WlzFileStream wlzFP = null; /* * Default constructor. */ public WlzServiceImpl() { checkClassDependency(WlzObject.class); } @Override protected void checkClassDependency(Class<? extends Object> klass) { String v[] = new String[1]; WlzObject.WlzGetVersion(v); } /* * Service methods */ @Override public String getNoWlzMsg() { return(new String(NO_WLZ_MSG)); } @Override public String getWlzOrgLabelName() { return(new String(WLZ_ORG_LABEL)); } @Override public void open(String file, String rw) throws FormatException, IOException { try { String v[] = new String[1]; WlzObject.WlzGetVersion(v); wlzVersion = new String(v[0]); } catch (UnsatisfiedLinkError e) { throw new FormatException(NO_WLZ_MSG, e); } if(rw.equals("r")) { openRead(file); } else if(rw.equals("w")) { openWrite(file); } else { throw new IOException("Failed to open file " + file); } } @Override public int getSizeX() { int sz; if(bBox == null) { sz = 0; } else { sz = bBox.xMax - bBox.xMin + 1; } return(sz); } @Override public int getSizeY() { int sz; if(bBox == null) { sz = 0; } else { sz = bBox.yMax - bBox.yMin + 1; } return(sz); } @Override public int getSizeZ() { int sz; if(bBox == null) { sz = 0; } else { sz = bBox.zMax - bBox.zMin + 1; } return(sz); } @Override public int getSizeC() { int sz; if(objGType == WlzGreyType.WLZ_GREY_RGBA) { sz = 4; } else { sz = 1; } return(sz); } @Override public int getSizeT() { return(1); } @Override public boolean isRGB() { boolean rgb; if(objGType == WlzGreyType.WLZ_GREY_RGBA) { rgb = true; } else { rgb = false; } return(rgb); } @Override public double getVoxSzX() { double sz; if(voxSz == null) { sz = 1.0; } else { sz = voxSz.vtX; } return(sz); } @Override public double getVoxSzY() { double sz; if(voxSz == null) { sz = 1.0; } else { sz = voxSz.vtY; } return(sz); } @Override public double getVoxSzZ() { double sz; if(voxSz == null) { sz = 1.0; } else { sz = voxSz.vtZ; } return(sz); } @Override public double getOrgX() { int og; if(bBox == null) { og = 0; } else { og = bBox.xMin; } return(og); } @Override public double getOrgY() { int og; if(bBox == null) { og = 0; } else { og = bBox.yMin; } return(og); } @Override public double getOrgZ() { int og; if(bBox == null) { og = 0; } else { og = bBox.zMin; } return(og); } @Override public int[] getSupPixelTypes() { return new int[] {FormatTools.UINT8, FormatTools.INT16, FormatTools.INT32, FormatTools.FLOAT, FormatTools.DOUBLE}; } @Override public int getPixelType() { int pixType; switch(objGType) { case WlzGreyType.WLZ_GREY_SHORT: pixelType = FormatTools.INT16; break; case WlzGreyType.WLZ_GREY_INT: pixelType = FormatTools.INT32; break; case WlzGreyType.WLZ_GREY_FLOAT: pixelType = FormatTools.FLOAT; break; case WlzGreyType.WLZ_GREY_DOUBLE: pixelType = FormatTools.DOUBLE; break; default: pixelType = FormatTools.UINT8; break; } return(pixelType); } @Override public void setupWrite(int orgX, int orgY, int orgZ, int pixSzX, int pixSzY, int pixSzZ, int pixSzC, int pixSzT, double voxSzX, double voxSzY, double voxSzZ, int gType) throws FormatException { bBox = new WlzIBox3(); bBox.xMin = orgX; bBox.yMin = orgY; bBox.zMin = orgZ; bBox.xMax = orgX + pixSzX - 1; bBox.yMax = orgY + pixSzY - 1; bBox.zMax = orgZ + pixSzZ - 1; voxSz = new WlzDVertex3(voxSzX, voxSzY, voxSzZ); if((bBox.xMax < bBox.xMin) || (bBox.yMax < bBox.yMin) || (bBox.zMax < bBox.zMin) || (pixSzC <= 0) || (pixSzT <= 0)) { throw new FormatException("Invalid image size (" + (bBox.xMax - bBox.xMin + 1) + ", " + (bBox.yMax - bBox.yMin + 1) + ", " + (bBox.zMax - bBox.zMin + 1) + ", " + pixSzC + ", " + pixSzT + ")"); } switch(gType) { case FormatTools.UINT8: objGType = WlzGreyType.WLZ_GREY_UBYTE; break; case FormatTools.INT16: objGType = WlzGreyType.WLZ_GREY_SHORT; break; case FormatTools.INT32: objGType = WlzGreyType.WLZ_GREY_INT; break; case FormatTools.FLOAT: objGType = WlzGreyType.WLZ_GREY_FLOAT; break; case FormatTools.DOUBLE: objGType = WlzGreyType.WLZ_GREY_DOUBLE; break; default: throw new FormatException("Invalid image value type"); } if(bBox.zMax == bBox.zMin) { objType = WlzObjectType.WLZ_2D_DOMAINOBJ; } else { objType = WlzObjectType.WLZ_3D_DOMAINOBJ; } try { wlzObj = WlzObject.WlzMakeEmpty(); } catch (WlzException e) { throw new FormatException("Failed to create Woolz object", e); } } @Override public void close() throws IOException { if(wlzObj != null) { if(state == WLZ_SERVICE_WRITE) { try { if(objType == WlzObjectType.WLZ_3D_DOMAINOBJ) { WlzObject.WlzSetVoxelSize(wlzObj, voxSz.vtX, voxSz.vtY, voxSz.vtZ); } WlzObject.WlzWriteObj(wlzFP, wlzObj); } catch(WlzException e) { throw new IOException("Failed to write to Woolz object (" + e + ")"); } } // Object is freed by garbage collection. wlzObj = null; } if(wlzFP != null) { wlzFP.close(); wlzFP = null; } } @Override public byte[] readBytes(int no, byte[] buf, int x, int y, int w, int h) throws FormatException, IOException { if((wlzObj == null) || (state != WLZ_SERVICE_READ)) { throw new FormatException("Uninitialised Woolz service"); } else { try { switch(objType) { case WlzObjectType.WLZ_2D_DOMAINOBJ: buf = readBytes2DDomObj(buf, x, y, w, h); break; case WlzObjectType.WLZ_3D_DOMAINOBJ: buf = readBytes3DDomObj(buf, x, y, no, w, h); break; default: throw new FormatException("Unsupported Woolz object type " + objType); } } catch (WlzException e) { throw new FormatException( "Failed to copy bytes from Woolz object (" + e + ")"); } } return(buf); } @Override public void saveBytes(int no, byte[] buf, int x, int y, int w, int h) throws FormatException, IOException { if(state == WLZ_SERVICE_WRITE) { WlzIVertex3 og = new WlzIVertex3(x + bBox.xMin, y + bBox.yMin, no + bBox.zMin); WlzIVertex2 sz = new WlzIVertex2(w, h); try { wlzObj = WlzObject.WlzBuildObj3B(wlzObj, og, sz, objGType, buf.length, buf); } catch (WlzException e) { throw new FormatException("Failed save bytes to Woolz object", e); } } } /* * Helper methods. */ private void openRead(String file) throws FormatException, IOException { state = WLZ_SERVICE_READ; try { wlzFP = new WlzFileInputStream(file); wlzObj = WlzObject.WlzReadObj(wlzFP); bBox = WlzObject.WlzBoundingBox3I(wlzObj); objType = WlzObject.WlzGetObjectType(wlzObj); if(objType == WlzObjectType.WLZ_3D_DOMAINOBJ) { voxSz = WlzObject.WlzGetVoxelSize(wlzObj); } else { voxSz = new WlzDVertex3(1.0, 1.0, 1.0); } } catch (WlzException e) { throw new IOException("Failed to read Woolz object (" + e + ")", e); } try { if (objType == WlzObjectType.WLZ_COMPOUND_ARR_1 || objType == WlzObjectType.WLZ_COMPOUND_ARR_2) { int count = objType == WlzObjectType.WLZ_COMPOUND_ARR_1 ? 1 : 2; WlzObject[][] dest = new WlzObject[1][count]; WlzObject.WlzExplode(new int[] {count}, dest, wlzObj); wlzObj = dest[0][0]; bBox = WlzObject.WlzBoundingBox3I(wlzObj); objType = WlzObject.WlzGetObjectType(wlzObj); if (objType == WlzObjectType.WLZ_3D_DOMAINOBJ) { voxSz = WlzObject.WlzGetVoxelSize(wlzObj); } } if(WlzObject.WlzObjectValuesIsNull(wlzObj) != 0) { /* Here we use WLZ_GREY_ERROR to indicate that the object has no * values not an error. */ objGType = WlzGreyType.WLZ_GREY_ERROR; } else { // throw an exception here instead of segfaulting during readBytes* if (WlzObject.WlzGetObjectValuesType(wlzObj) > WlzObjectType.WLZ_GREY_TAB_TILED) { throw new FormatException("Value table data not supported"); } objGType = WlzObject.WlzGreyTypeFromObj(wlzObj); } } catch (WlzException e) { throw new FormatException( "Unable to determine Woolz object value type (" + e + ")", e); } switch(objGType) { case WlzGreyType.WLZ_GREY_UBYTE: break; case WlzGreyType.WLZ_GREY_SHORT: break; case WlzGreyType.WLZ_GREY_INT: break; case WlzGreyType.WLZ_GREY_FLOAT: break; case WlzGreyType.WLZ_GREY_DOUBLE: break; case WlzGreyType.WLZ_GREY_RGBA: break; case WlzGreyType.WLZ_GREY_ERROR: break; default: throw new FormatException( "Inappropriate Woolz object value type (type = " + objGType + ")"); } } private void openWrite(String file) throws FormatException, IOException { state = WLZ_SERVICE_WRITE; try { wlzFP = new WlzFileOutputStream(file); } catch (IOException e) { throw new IOException("Failed to open " + file + "for writing."); } } private byte[] readBytes2DDomObj(byte[] buf, int x, int y, int w, int h) throws WlzException { WlzIVertex2 og = new WlzIVertex2(x + bBox.xMin, y + bBox.yMin); WlzIVertex2 sz = new WlzIVertex2(w, h); WlzIVertex2 dstSz[] = new WlzIVertex2[1]; dstSz[0] = null; switch(objGType) { case WlzGreyType.WLZ_GREY_UBYTE: { byte dstDat[][][] = new byte[1][][]; WlzObject.WlzToUArray2D(dstSz, dstDat, wlzObj, og, sz, 0); for(int idY = 0; idY < h; ++idY) { int idYW = idY * w; for(int idX = 0; idX < w; ++idX) { buf[idYW + idX] = dstDat[0][idY][idX]; } } } break; case WlzGreyType.WLZ_GREY_SHORT: { short m = 0xff; short dstDat[][][] = new short[1][][]; WlzObject.WlzToSArray2D(dstSz, dstDat, wlzObj, og, sz, 0); for(int idY = 0; idY < h; ++idY) { int idYW = idY * w; for(int idX = 0; idX < w; ++idX) { short p = dstDat[0][idY][idX]; buf[2 * (idYW + idX)] = (byte )((p >>> 8) & m); buf[2 * (idYW + idX) + 1] = (byte )(p & m); } } } break; case WlzGreyType.WLZ_GREY_INT: { int m = 0xff; int dstDat[][][] = new int[1][][]; WlzObject.WlzToIArray2D(dstSz, dstDat, wlzObj, og, sz, 0); for(int idY = 0; idY < h; ++idY) { int idYW = idY * w; for(int idX = 0; idX < w; ++idX) { int p = dstDat[0][idY][idX]; buf[idYW + (4 * idX)] = (byte )((p >> 24) & m); buf[idYW + (4 * idX) + 1] = (byte )((p >> 16) & m); buf[idYW + (4 * idX) + 2] = (byte )((p >> 8) & m); buf[idYW + (4 * idX) + 3] = (byte )(p & m); } } } break; case WlzGreyType.WLZ_GREY_RGBA: { int m = 0xff; int cOff = h * w; int dstDat[][][] = new int[1][][]; WlzObject.WlzToRArray2D(dstSz, dstDat, wlzObj, og, sz, 0); for(int idY = 0; idY < h; ++idY) { int idYW = idY * w; for(int idX = 0; idX < w; ++idX) { int idYWX = idYW + idX; int p = dstDat[0][idY][idX]; buf[idYWX] = (byte )((p >> 0) & m); buf[idYWX + cOff] = (byte )((p >> 8) & m); buf[idYWX + (2 * cOff)] = (byte )((p >> 16) & m); buf[idYWX + (3 * cOff)] = (byte )((p >> 24) & m); } } } break; case WlzGreyType.WLZ_GREY_ERROR: /* Indicates no values. */ { int w8 = (w + 7) / 8; byte dstDat[][][] = new byte[1][][]; WlzObject.WlzToBArray2D(dstSz, dstDat, wlzObj, og, sz, 0); for(int idY = 0; idY < h; ++idY) { int idYW = idY * w; int idYW8 = idY * w8; for(int idX = 0; idX < w; ++idX) { byte p = dstDat[0][idY][idX / 8]; byte b = (byte )(p & (0x01 << (idX % 8))); if(b == 0){ buf[idYW + idX] = (byte )0x00; } else { buf[idYW + idX] = (byte )0xff; } } } } break; default: throw new WlzException("Unsupported pixel type"); } return(buf); } private byte[] readBytes3DDomObj(byte[] buf, int x, int y, int z, int w, int h) throws WlzException { WlzIVertex3 og = new WlzIVertex3(x + bBox.xMin, y + bBox.yMin, z + bBox.zMin); WlzIVertex3 sz = new WlzIVertex3(w, h, 1); WlzIVertex3 dstSz[] = new WlzIVertex3[1]; dstSz[0] = null; switch(objGType) { case WlzGreyType.WLZ_GREY_UBYTE: { byte dstDat[][][][] = new byte[1][][][]; WlzObject.WlzToUArray3D(dstSz, dstDat, wlzObj, og, sz, 0); for(int idY = 0; idY < h; ++idY) { int idYW = idY * w; for(int idX = 0; idX < w; ++idX) { buf[idYW + idX] = dstDat[0][0][idY][idX]; } } } break; case WlzGreyType.WLZ_GREY_SHORT: { short m = 0xff; short dstDat[][][][] = new short[1][][][]; WlzObject.WlzToSArray3D(dstSz, dstDat, wlzObj, og, sz, 0); for(int idY = 0; idY < h; ++idY) { int idYW = idY * w; for(int idX = 0; idX < w; ++idX) { short p = dstDat[0][0][idY][idX]; buf[2 * (idYW + idX)] = (byte )((p >>> 8) & m); buf[2 * (idYW + idX) + 1] = (byte )(p & m); } } } break; case WlzGreyType.WLZ_GREY_INT: { int m = 0xff; int dstDat[][][][] = new int[1][][][]; WlzObject.WlzToIArray3D(dstSz, dstDat, wlzObj, og, sz, 0); for(int idY = 0; idY < h; ++idY) { int idYW = idY * w; for(int idX = 0; idX < w; ++idX) { int p = dstDat[0][0][idY][idX]; buf[idYW + (4 * idX)] = (byte )((p >> 24) & m); buf[idYW + (4 * idX) + 1] = (byte )((p >> 16) & m); buf[idYW + (4 * idX) + 2] = (byte )((p >> 8) & m); buf[idYW + (4 * idX) + 3] = (byte )(p & m); } } } break; case WlzGreyType.WLZ_GREY_RGBA: { int m = 0xff; int cOff = h * w; int dstDat[][][][] = new int[1][][][]; WlzObject.WlzToRArray3D(dstSz, dstDat, wlzObj, og, sz, 0); for(int idY = 0; idY < h; ++idY) { int idYW = idY * w; for(int idX = 0; idX < w; ++idX) { int idYWX = idYW + idX; int p = dstDat[0][0][idY][idX]; buf[idYWX] = (byte )((p >> 0) & m); buf[idYWX + cOff] = (byte )((p >> 8) & m); buf[idYWX + (2 * cOff)] = (byte )((p >> 16) & m); buf[idYWX + (3 * cOff)] = (byte )((p >> 24) & m); } } } break; case WlzGreyType.WLZ_GREY_ERROR: /* Indicates no values. */ { int w8 = (w + 7) / 8; byte dstDat[][][][] = new byte[1][][][]; WlzObject.WlzToBArray3D(dstSz, dstDat, wlzObj, og, sz, 0); for(int idY = 0; idY < h; ++idY) { int idYW = idY * w; int idYW8 = idY * w8; for(int idX = 0; idX < w; ++idX) { byte p = dstDat[0][0][idY][idX / 8]; byte b = (byte )(p & (0x01 << (idX % 8))); if(b == 0){ buf[idYW + idX] = (byte )0x00; } else { buf[idYW + idX] = (byte )0xff; } } } } break; default: throw new WlzException("Unsupported pixel type"); } return(buf); } }