I am attempting to use the bio-format library to write large (potentially very large) images to disk.
The format I am attempting to write to is .ome.tiff. I am using the FileExport example as a reference, but I am unable to do things correctly, so I must be missing something.
Here is the basic layout for composing/writing the image
1) With list of images, I use a blend class for doing an average blend for each image. Each image has been wrapped into its own Array2DView, which represents a 2d representation of the one-dimensional pixel array.
- Code: Select all
public class AverageBlendScifio implements JavaBlender {
private float[][]sums;
private int[][]counts;
private int height;
private int width;
private FileExport exporter;
@Override
public void init(int width, int height, int type, String fileName) {
exporter = new FileExport(fileName, width, height, type);
sums = new float[height][width];
counts = new int[height][width];
this.height = height;
this.width = width;
}
@Override
public void blend(int x, int y, Array2DView pixels) {
for(int row = 0; row < pixels.getViewHeight(); row++ ){
for(int col = 0; col < pixels.getViewWidth(); col++){
sums[row+y][col+x] += pixels.get(row, col);
counts[row+y][col+x]++;
}
}
}
@Override
public BufferedImage getResult() {
return null;
}
@Override
public void postProcess() {
byte[] vals = new byte[2*width*height];
System.out.println("Writing pixels");
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
short value = (short)((float)sums[row][col] / (float)counts[row][col]);
vals[row*width+col] = (byte)(value & 0xFF00);
vals[row*width+col+1] = (byte)(value & 0x00FF);
}
}
exporter.export(vals, width, height);
exporter.cleanup();
}
}
Here is my FileExporter class, which I adapted from the omexml file export example:
- Code: Select all
package visualize.export;
/*
* #%L
* OME Bio-Formats package for reading and converting biological file formats.
* %%
* Copyright (C) 2005 - 2013 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%
*/
import java.io.IOException;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import loci.common.services.DependencyException;
import loci.common.services.ServiceException;
import loci.common.services.ServiceFactory;
import loci.formats.FormatException;
import loci.formats.FormatTools;
import loci.formats.ImageWriter;
import loci.formats.meta.IMetadata;
import loci.formats.services.OMEXMLService;
import ome.xml.model.enums.DimensionOrder;
import ome.xml.model.enums.EnumerationException;
import ome.xml.model.enums.PixelType;
import ome.xml.model.primitives.PositiveInteger;
/**
* Example class that shows how to export raw pixel data to OME-TIFF using
* Bio-Formats version 4.2 or later.
*/
public class FileExport {
/** The file writer. */
private ImageWriter writer;
private int width;
private int height;
/** The name of the output file. */
private String outputFile;
/**
* Construct a new FileExport that will save to the specified file.
*
* @param outputFile the file to which we will export
*/
public FileExport(String outputFile, int width, int height, int pixelType) {
BasicConfigurator.configure();
Logger.getLogger("loci.common.services.ServiceFactory").setLevel(Level.ALL);
this.outputFile = outputFile;
IMetadata omexml = initializeMetadata(width, height, FormatTools.UINT16);
initializeWriter(omexml);
this.width = width;
this.height = height;
}
public void export(byte[] vals)
{
try {
writer.saveBytes(0, vals, 0, 0, width, height);
} catch (FormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void exportShort(short value, int x, int y)
{
byte b0 = (byte)(value & 0xFF00);
byte b1 = (byte)(value & 0x00FF);
try {
System.out.println("Writing x: " + x + " y: " + y + " val: " + value);
writer.saveBytes(0, new byte[] {b0, b1}, x, y, 1, 1);
} catch (FormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Set up the file writer.
*
* @param omexml the IMetadata object that is to be associated with the writer
* @return true if the file writer was successfully initialized; false if an
* error occurred
*/
private boolean initializeWriter(IMetadata omexml) {
// create the file writer and associate the OME-XML metadata with it
writer = new ImageWriter();
writer.setMetadataRetrieve(omexml);
Exception exception = null;
try {
writer.setId(outputFile);
}
catch (FormatException e) {
exception = e;
}
catch (IOException e) {
exception = e;
}
if (exception != null) {
System.err.println("Failed to initialize file writer.");
exception.printStackTrace();
}
return exception == null;
}
/**
* Populate the minimum amount of metadata required to export an image.
*
* @param width the width (in pixels) of the image
* @param height the height (in pixels) of the image
* @param pixelType the pixel type of the image; @see loci.formats.FormatTools
*/
private IMetadata initializeMetadata(int width, int height, int pixelType) {
Exception exception = null;
try {
// create the OME-XML metadata storage object
ServiceFactory factory = new ServiceFactory();
OMEXMLService service = factory.getInstance(OMEXMLService.class);
IMetadata meta = service.createOMEXMLMetadata();
meta.createRoot();
// define each stack of images - this defines a single stack of images
meta.setImageID("Image:0", 0);
meta.setPixelsID("Pixels:0", 0);
// specify that the pixel data is stored in big-endian format
// change 'TRUE' to 'FALSE' to specify little-endian format
meta.setPixelsBinDataBigEndian(Boolean.TRUE, 0, 0);
// specify that the images are stored in ZCT order
meta.setPixelsDimensionOrder(DimensionOrder.XYZCT, 0);
// specify that the pixel type of the images
meta.setPixelsType(
PixelType.fromString(FormatTools.getPixelTypeString(pixelType)), 0);
// specify the dimensions of the images
meta.setPixelsSizeX(new PositiveInteger(width), 0);
meta.setPixelsSizeY(new PositiveInteger(height), 0);
meta.setPixelsSizeZ(new PositiveInteger(1), 0);
meta.setPixelsSizeC(new PositiveInteger(1), 0);
meta.setPixelsSizeT(new PositiveInteger(1), 0);
// define each channel and specify the number of samples in the channel
// the number of samples is 3 for RGB images and 1 otherwise
meta.setChannelID("Channel:0:0", 0, 0);
meta.setChannelSamplesPerPixel(new PositiveInteger(1), 0, 0);
System.out.println("Completed initializing meta data");
return meta;
}
catch (DependencyException e) {
exception = e;
}
catch (ServiceException e) {
exception = e;
}
catch (EnumerationException e) {
exception = e;
}
System.err.println("Failed to populate OME-XML metadata object.");
exception.printStackTrace();
return null;
}
/**
* Generate a random plane of pixel data and save it to the output file.
*
* @param width the width of the image in pixels
* @param height the height of the image in pixels
* @param pixelType the pixel type of the image; @see loci.formats.FormatTools
*/
public void savePlane(int width, int height, int pixelType) {
byte[] plane = createImage(width, height, pixelType);
Exception exception = null;
try {
writer.saveBytes(0, plane);
}
catch (FormatException e) {
exception = e;
}
catch (IOException e) {
exception = e;
}
if (exception != null) {
System.err.println("Failed to save plane.");
exception.printStackTrace();
}
}
/**
* Generate a random plane of pixel data.
*
* @param width the width of the image in pixels
* @param height the height of the image in pixels
* @param pixelType the pixel type of the image; @see loci.formats.FormatTools
*/
private byte[] createImage(int width, int height, int pixelType) {
// create a blank image of the specified size
byte[] img =
new byte[width * height * FormatTools.getBytesPerPixel(pixelType)];
// fill it with random data
for (int i=0; i<img.length; i++) {
img[i] = (byte) (256 * Math.random());
}
return img;
}
/** Close the file writer. */
public void cleanup() {
try {
writer.close();
}
catch (IOException e) {
System.err.println("Failed to close file writer.");
e.printStackTrace();
}
}
}
I get the following error when trying to export the bytes to the file:
- Code: Select all
1320 [SwingWorker-pool-1-thread-1] DEBUG loci.formats.tiff.TiffSaver - Reading IFD from 8 in non-sequential write.
1322 [SwingWorker-pool-1-thread-1] ERROR loci.formats.tiff.TiffParser - Error reading IFD type at: 14
1323 [SwingWorker-pool-1-thread-1] DEBUG loci.formats.tiff.TiffParser -
loci.common.enumeration.EnumException: Unable to find IFDType with code: 1024
at loci.formats.tiff.IFDType.get(IFDType.java:98)
at loci.formats.tiff.TiffParser.readTiffIFDEntry(TiffParser.java:1166)
at loci.formats.tiff.TiffParser.getIFD(TiffParser.java:404)
at loci.formats.tiff.TiffSaver.writeImageIFD(TiffSaver.java:434)
at loci.formats.tiff.TiffSaver.writeImage(TiffSaver.java:388)
at loci.formats.tiff.TiffSaver.writeImage(TiffSaver.java:270)
at loci.formats.out.TiffWriter.saveBytes(TiffWriter.java:191)
at loci.formats.out.OMETiffWriter.saveBytes(OMETiffWriter.java:201)
at loci.formats.out.OMETiffWriter.saveBytes(OMETiffWriter.java:187)
at loci.formats.ImageWriter.saveBytes(ImageWriter.java:212)
at visualize.export.FileExport.export(FileExport.java:82)
at visualize.java.blend.AverageBlendScifio.postProcess(AverageBlendScifio.java:57)
at visualize.export.LargeImageExporter.exportImage(LargeImageExporter.java:152)
at visualize.export.LargeImageExporter.exportImage(LargeImageExporter.java:182)
at app.gui.StitchingExecutor.outputGrid(StitchingExecutor.java:587)
at app.gui.StitchingExecutor.launchFFTWStitching(StitchingExecutor.java:413)
at app.gui.StitchingExecutor.doInBackground(StitchingExecutor.java:189)
at app.gui.StitchingExecutor.doInBackground(StitchingExecutor.java:1)
at javax.swing.SwingWorker$1.call(Unknown Source)
at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at javax.swing.SwingWorker.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
I was also wondering if it would be possible to use the exportShort function that I wrote in the FileExporter, but first I need to fix this IFD error, because I get it when trying to use exportShort.
Here is a basic example of how to begin executing:
- Code: Select all
AverageBlendScifio blend = new AverageBlendScifio();
File outputFile = new File(outputDir, imageName);
blend.init(width, height, BufferedImage.TYPE_USHORT_GRAY, outputFile.getAbsolutePath());
LargeImageExporter.exportImage(grid, 0, 0, width, height, blend, outputFile, progress);
The LargeImageExporter basically loops through each image and sends the pixel data to the blend function and then calls postProcess on the blender.
Thank you for any comments/suggestions regarding this issue.