private byte[] decodeTile(int no, int row, int col) throws FormatException, IOException { if (tileMap[getCoreIndex()] == null) { return new byte[getTileSize()]; } int[] zct = getZCTCoords(no); TileCoordinate t = new TileCoordinate(nDimensions[getCoreIndex()]); t.coordinate[0] = col; t.coordinate[1] = row; for (String dim : dimensionOrdering.keySet()) { int index = dimensionOrdering.get(dim) + 2; if (dim.equals("Z")) { t.coordinate[index] = zct[0]; } else if (dim.equals("C")) { t.coordinate[index] = zct[1]; } else if (dim.equals("T")) { t.coordinate[index] = zct[2]; } } Integer index = (Integer) tileMap[getCoreIndex()].get(t); if (index == null) { return new byte[getTileSize()]; } Long offset = tileOffsets[getCoreIndex()][index]; RandomAccessInputStream ets = new RandomAccessInputStream(usedFiles[getCoreIndex()]); ets.seek(offset); CodecOptions options = new CodecOptions(); options.interleaved = isInterleaved(); options.littleEndian = isLittleEndian(); int tileSize = getTileSize(); if (tileSize == 0) { tileSize = tileX[getCoreIndex()] * tileY[getCoreIndex()] * 10; } options.maxBytes = (int) (offset + tileSize); byte[] buf = null; long end = index < tileOffsets[getCoreIndex()].length - 1 ? tileOffsets[getCoreIndex()][index + 1] : ets.length(); IFormatReader reader = null; String file = null; switch (compressionType[getCoreIndex()]) { case RAW: buf = new byte[tileSize]; ets.read(buf); break; case JPEG: Codec codec = new JPEGCodec(); buf = codec.decompress(ets, options); break; case JPEG_2000: codec = new JPEG2000Codec(); buf = codec.decompress(ets, options); break; case PNG: file = "tile.png"; reader = new APNGReader(); case BMP: if (reader == null) { file = "tile.bmp"; reader = new BMPReader(); } byte[] b = new byte[(int) (end - offset)]; ets.read(b); Location.mapFile(file, new ByteArrayHandle(b)); reader.setId(file); buf = reader.openBytes(0); Location.mapFile(file, null); break; } if (reader != null) { reader.close(); } ets.close(); return buf; }
/** * Reads in an imglib {@link Image} from the given initialized {@link IFormatReader}, using the * given {@link ImageFactory}. */ public <T extends RealType<T>> Image<T> openImage(IFormatReader r, ImageFactory<T> imageFactory) throws FormatException, IOException { final String[] dimTypes = getDimTypes(r); final int[] dimLengths = getDimLengths(r); // TEMP - make suffix out of dimension types, until imglib supports them final String id = r.getCurrentFile(); final File idFile = new File(id); String name = idFile.exists() ? idFile.getName() : id; name = encodeName(name, dimTypes); // create image object final Image<T> img = imageFactory.createImage(dimLengths, name); // set calibration of the image img.setCalibration(getCalibration(r, dimLengths)); // TODO - create better container types; either: // 1) an array container type using one byte array per plane // 2) as #1, but with an IFormatReader reference reading planes on demand // 3) as PlanarContainer, but with an IFormatReader reference // reading planes on demand // PlanarContainer is useful for efficient access to pixels in ImageJ // (e.g., getPixels) // #1 is useful for efficient Bio-Formats import, and useful for tools // needing byte arrays (e.g., BufferedImage Java3D texturing by reference) // #2 is useful for efficient memory use for tools wanting matching // primitive arrays (e.g., virtual stacks in ImageJ) // #3 is useful for efficient memory use // get container final PlanarAccess<?> planarAccess = getPlanarAccess(img); final T inputType = makeType(r.getPixelType()); final T outputType = imageFactory.createType(); final boolean compatibleTypes = outputType.getClass().isAssignableFrom(inputType.getClass()); final long startTime = System.currentTimeMillis(); // populate planes final int planeCount = r.getImageCount(); if (planarAccess == null || !compatibleTypes) { // use cursor to populate planes // NB: This solution is general and works regardless of container, // but at the expense of performance both now and later. final LocalizablePlaneCursor<T> cursor = img.createLocalizablePlaneCursor(); byte[] plane = null; for (int no = 0; no < planeCount; no++) { notifyListeners( new StatusEvent(no, planeCount, "Reading plane " + (no + 1) + "/" + planeCount)); if (plane == null) plane = r.openBytes(no); else r.openBytes(no, plane); populatePlane(r, no, plane, cursor); } cursor.close(); } else { // populate the values directly using PlanarAccess interface; // e.g., to a PlanarContainer byte[] plane = null; for (int no = 0; no < planeCount; no++) { notifyListeners( new StatusEvent(no, planeCount, "Reading plane " + (no + 1) + "/" + planeCount)); if (plane == null) plane = r.openBytes(no); else r.openBytes(no, plane); populatePlane(r, no, plane, planarAccess); } } r.close(); final long endTime = System.currentTimeMillis(); final float time = (endTime - startTime) / 1000f; notifyListeners( new StatusEvent( planeCount, planeCount, id + ": read " + planeCount + " planes in " + time + "s")); return img; }
/** * Attempts to import the given file IDs using Bio-Formats, as a single group. Pixels are saved to * the pixels file designated by OMEIS, and an OME-XML metadata block describing the successfully * imported data is dumped to standard output. */ public void importIds(int[] fileIds) throws OmeisException, FormatException, IOException { boolean doLittle = isLittleEndian(); Arrays.sort(fileIds); // set up file path mappings String[] ids = new String[fileIds.length]; for (int i = 0; i < fileIds.length; i++) { Hashtable fileInfo = getFileInfo(fileIds[i]); ids[i] = (String) fileInfo.get("Name"); String path = getLocalFilePath(fileIds[i]); Location.mapId(ids[i], path); } // read file group String id = ids[0]; String path = Location.getMappedId(id); if (DEBUG) log("Reading file '" + id + "' --> " + path); // verify that all given file IDs were grouped by the reader reader.setId(id); String[] used = reader.getUsedFiles(); if (used == null) { throw new FormatException("Invalid file list for " + path); } if (used.length != ids.length) { throw new FormatException( "File list length mismatch for " + path + ": used=" + a2s(used) + "; ids=" + a2s(ids)); } boolean[] done = new boolean[ids.length]; int numLeft = ids.length; for (int i = 0; i < used.length; i++) { for (int j = 0; j < ids.length; j++) { if (done[j]) continue; if (used[i].equals(ids[j])) { done[j] = true; numLeft--; break; } } } if (numLeft > 0) { throw new FormatException("File list does not correspond to ID list for " + path); } int seriesCount = reader.getSeriesCount(); // get DOM and Pixels elements for the file's OME-XML metadata OMENode ome = (OMENode) omexmlMeta.getRoot(); Document omeDoc = ome.getDOMElement().getOwnerDocument(); Vector pix = DOMUtil.findElementList("Pixels", omeDoc); if (pix.size() != seriesCount) { throw new FormatException( "Pixels element count (" + pix.size() + ") does not match series count (" + seriesCount + ") for '" + id + "'"); } if (DEBUG) log(seriesCount + " series detected."); for (int s = 0; s < seriesCount; s++) { reader.setSeries(s); // gather pixels information for this series int sizeX = reader.getSizeX(); int sizeY = reader.getSizeY(); int sizeZ = reader.getSizeZ(); int sizeC = reader.getSizeC(); int sizeT = reader.getSizeT(); int pixelType = reader.getPixelType(); int bytesPerPixel; boolean isSigned, isFloat; switch (pixelType) { case FormatTools.INT8: bytesPerPixel = 1; isSigned = true; isFloat = false; break; case FormatTools.UINT8: bytesPerPixel = 1; isSigned = false; isFloat = false; break; case FormatTools.INT16: bytesPerPixel = 2; isSigned = true; isFloat = false; break; case FormatTools.UINT16: bytesPerPixel = 2; isSigned = false; isFloat = false; break; case FormatTools.INT32: bytesPerPixel = 4; isSigned = true; isFloat = false; break; case FormatTools.UINT32: bytesPerPixel = 4; isSigned = false; isFloat = false; break; case FormatTools.FLOAT: bytesPerPixel = 4; isSigned = true; isFloat = true; break; case FormatTools.DOUBLE: bytesPerPixel = 8; isSigned = true; isFloat = true; break; default: throw new FormatException( "Unknown pixel type for '" + id + "' series #" + s + ": " + pixelType); } boolean little = reader.isLittleEndian(); boolean swap = doLittle != little && bytesPerPixel > 1 && !isFloat; // ask OMEIS to allocate new pixels file int pixelsId = newPixels(sizeX, sizeY, sizeZ, sizeC, sizeT, bytesPerPixel, isSigned, isFloat); String pixelsPath = getLocalPixelsPath(pixelsId); if (DEBUG) { log("Series #" + s + ": id=" + pixelsId + ", path=" + pixelsPath); } // write pixels to file FileOutputStream out = new FileOutputStream(pixelsPath); int imageCount = reader.getImageCount(); if (DEBUG) { log( "Processing " + imageCount + " planes (sizeZ=" + sizeZ + ", sizeC=" + sizeC + ", sizeT=" + sizeT + "): "); } // OMEIS expects XYZCT order -- // interleaved RGB files will be handled a bit more slowly due to this // ordering (ChannelSeparator must read each plane three times), but // caching performed by the OS helps some for (int t = 0; t < sizeT; t++) { for (int c = 0; c < sizeC; c++) { for (int z = 0; z < sizeZ; z++) { int ndx = reader.getIndex(z, c, t); if (DEBUG) { log("Reading plane #" + ndx + ": z=" + z + ", c=" + c + ", t=" + t); } byte[] plane = reader.openBytes(ndx); if (swap) { // swap endianness for (int b = 0; b < plane.length; b += bytesPerPixel) { for (int k = 0; k < bytesPerPixel / 2; k++) { int i1 = b + k; int i2 = b + bytesPerPixel - k - 1; byte b1 = plane[i1]; byte b2 = plane[i2]; plane[i1] = b2; plane[i2] = b1; } } } out.write(plane); } } } out.close(); if (DEBUG) log("[done]"); // tell OMEIS we're done pixelsId = finishPixels(pixelsId); if (DEBUG) log("finishPixels called (new id=" + pixelsId + ")"); // get SHA1 hash for finished pixels String sha1 = getPixelsSHA1(pixelsId); if (DEBUG) log("SHA1=" + sha1); // inject important extra attributes into proper Pixels element Element pixels = (Element) pix.elementAt(s); pixels.setAttribute("FileSHA1", sha1); pixels.setAttribute("ImageServerID", "" + pixelsId); pixels.setAttribute("DimensionOrder", "XYZCT"); // ignored anyway String pType = pixels.getAttribute("PixelType"); if (pType.startsWith("u")) { pixels.setAttribute("PixelType", pType.replace('u', 'U')); } if (DEBUG) log("Pixel attributes injected."); } reader.close(); // accumulate XML into buffer ByteArrayOutputStream xml = new ByteArrayOutputStream(); try { DOMUtil.writeXML(xml, omeDoc); } catch (javax.xml.transform.TransformerException exc) { throw new FormatException(exc); } // output OME-XML to standard output xml.close(); String xmlString = new String(xml.toByteArray()); if (DEBUG) log(xmlString); if (http) printHttpResponseHeader(); System.out.println(xmlString); }