//
// CellomicsReader.java
//
/*
OME Bio-Formats package for reading and converting biological file formats.
Copyright (C) 2005-@year@ UW-Madison LOCI and Glencoe Software, Inc.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package loci.formats.in;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import loci.common.Location;
import loci.common.RandomAccessInputStream;
import loci.formats.CoreMetadata;
import loci.formats.FormatException;
import loci.formats.FormatReader;
import loci.formats.FormatTools;
import loci.formats.MetadataTools;
import loci.formats.UnsupportedCompressionException;
import loci.formats.codec.ZlibCodec;
import loci.formats.meta.MetadataStore;
import ome.xml.model.primitives.PositiveFloat;
import ome.xml.model.enums.NamingConvention;
import ome.xml.model.primitives.NonNegativeInteger;
/**
* Reader for Cellomics C01 files.
*
* <dl><dt><b>Source code:</b></dt>
* <dd><a href="http://trac.openmicroscopy.org.uk/ome/browser/bioformats.git/components/bio-formats/src/loci/formats/in/CellomicsReader.java">Trac</a>,
* <a href="http://git.openmicroscopy.org/?p=bioformats.git;a=blob;f=components/bio-formats/src/loci/formats/in/CellomicsReader.java;hb=HEAD">Gitweb</a></dd></dl>
*/
public class CellomicsReader extends FormatReader {
// -- Constants --
public static final int C01_MAGIC_BYTES = 16;
// -- Fields --
private String[] files;
// -- Constructor --
/** Constructs a new Cellomics reader. */
public CellomicsReader() {
super("Cellomics C01", new String[] {"c01", "dib"});
domains = new String[] {FormatTools.LM_DOMAIN, FormatTools.HCS_DOMAIN};
datasetDescription = "One or more .c01 files";
}
// -- IFormatReader API methods --
/* @see loci.formats.IFormatReader#isThisType(RandomAccessInputStream) */
public boolean isThisType(RandomAccessInputStream stream) throws IOException {
final int blockLen = 4;
if (!FormatTools.validStream(stream, blockLen, false)) return false;
return stream.readInt() == C01_MAGIC_BYTES;
}
/* @see loci.formats.IFormatReader#getDomains() */
public String[] getDomains() {
FormatTools.assertId(currentId, true, 1);
return new String[] {FormatTools.HCS_DOMAIN};
}
/**
* @see loci.formats.IFormatReader#openBytes(int, byte[], int, int, int, int)
*/
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);
String file = files[getSeries()];
RandomAccessInputStream s = getDecompressedStream(file);
int planeSize = FormatTools.getPlaneSize(this);
s.seek(52 + no * planeSize);
readPlane(s, x, y, w, h, buf);
s.close();
return buf;
}
/* @see loci.formats.IFormatReader#close(boolean) */
public void close(boolean fileOnly) throws IOException {
super.close(fileOnly);
if (!fileOnly) {
files = null;
}
}
// -- Internal FormatReader API methods --
/* @see loci.formats.FormatReader#initFile(String) */
protected void initFile(String id) throws FormatException, IOException {
super.initFile(id);
// look for files with similar names
Location baseFile = new Location(id).getAbsoluteFile();
Location parent = baseFile.getParentFile();
String[] list = parent.list(true);
ArrayList<String> pixelFiles = new ArrayList<String>();
String plateName = getPlateName(baseFile.getName());
if (plateName != null && isGroupFiles()) {
for (String f : list) {
if (plateName.equals(getPlateName(f)) &&
(checkSuffix(f, "c01") || checkSuffix(f, "dib")))
{
pixelFiles.add(new Location(parent, f).getAbsolutePath());
}
}
}
else pixelFiles.add(id);
files = pixelFiles.toArray(new String[pixelFiles.size()]);
Arrays.sort(files);
int wellRows = 0;
int wellColumns = 0;
int fields = 0;
ArrayList<String> uniqueRows = new ArrayList<String>();
ArrayList<String> uniqueCols = new ArrayList<String>();
ArrayList<String> uniqueFields = new ArrayList<String>();
for (String f : files) {
String wellRow = getWellRow(f);
String wellCol = getWellColumn(f);
String field = getField(f);
if (!uniqueRows.contains(wellRow)) uniqueRows.add(wellRow);
if (!uniqueCols.contains(wellCol)) uniqueCols.add(wellCol);
if (!uniqueFields.contains(field)) uniqueFields.add(field);
}
fields = uniqueFields.size();
wellRows = uniqueRows.size();
wellColumns = uniqueCols.size();
if (fields * wellRows * wellColumns > files.length) {
files = new String[] {id};
}
core = new CoreMetadata[files.length];
for (int i=0; i<core.length; i++) {
core[i] = new CoreMetadata();
}
in = getDecompressedStream(id);
LOGGER.info("Reading header data");
in.order(true);
in.skipBytes(4);
int x = in.readInt();
int y = in.readInt();
int nPlanes = in.readShort();
int nBits = in.readShort();
int compression = in.readInt();
if (x * y * nPlanes * (nBits / 8) + 52 > in.length()) {
throw new UnsupportedCompressionException(
"Compressed pixel data is not yet supported.");
}
in.skipBytes(4);
int pixelWidth = 0, pixelHeight = 0;
if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) {
pixelWidth = in.readInt();
pixelHeight = in.readInt();
int colorUsed = in.readInt();
int colorImportant = in.readInt();
LOGGER.info("Populating metadata hashtable");
addGlobalMeta("Image width", x);
addGlobalMeta("Image height", y);
addGlobalMeta("Number of planes", nPlanes);
addGlobalMeta("Bits per pixel", nBits);
addGlobalMeta("Compression", compression);
addGlobalMeta("Pixels per meter (X)", pixelWidth);
addGlobalMeta("Pixels per meter (Y)", pixelHeight);
addGlobalMeta("Color used", colorUsed);
addGlobalMeta("Color important", colorImportant);
}
LOGGER.info("Populating core metadata");
for (int i=0; i<getSeriesCount(); i++) {
core[i].sizeX = x;
core[i].sizeY = y;
core[i].sizeZ = nPlanes;
core[i].sizeT = 1;
core[i].sizeC = 1;
core[i].imageCount = getSizeZ();
core[i].littleEndian = true;
core[i].dimensionOrder = "XYCZT";
core[i].pixelType =
FormatTools.pixelTypeFromBytes(nBits / 8, false, false);
}
LOGGER.info("Populating metadata store");
MetadataStore store = makeFilterMetadata();
MetadataTools.populatePixels(store, this);
MetadataTools.setDefaultCreationDate(store, id, 0);
store.setPlateID(MetadataTools.createLSID("Plate", 0), 0);
store.setPlateName(plateName, 0);
store.setPlateRowNamingConvention(NamingConvention.LETTER, 0);
store.setPlateColumnNamingConvention(NamingConvention.NUMBER, 0);
int realRows = wellRows;
int realCols = wellColumns;
if (realRows <= 8 && realCols <= 12) {
realRows = 8;
realCols = 12;
}
else {
realRows = 16;
realCols = 24;
}
for (int row=0; row<realRows; row++) {
for (int col=0; col<realCols; col++) {
int well = row * realCols + col;
store.setWellID(MetadataTools.createLSID("Well", 0, well), 0, well);
store.setWellRow(new NonNegativeInteger(row), 0, well);
store.setWellColumn(new NonNegativeInteger(col), 0, well);
}
}
for (int i=0; i<getSeriesCount(); i++) {
String file = files[i];
String field = getField(file);
String wellRow = getWellRow(file);
String wellColumn = getWellColumn(file);
int row = wellRow.toUpperCase().charAt(0) - 'A';
int col = Integer.parseInt(wellColumn) - 1;
String imageID = MetadataTools.createLSID("Image", i);
store.setImageID(imageID, i);
if (row < realRows && col < realCols) {
int wellIndex = row * realCols + col;
int fieldIndex = i % fields;
String wellSampleID =
MetadataTools.createLSID("WellSample", 0, wellIndex, fieldIndex);
store.setWellSampleID(wellSampleID, 0, wellIndex, fieldIndex);
store.setWellSampleIndex(
new NonNegativeInteger(fieldIndex), 0, wellIndex, fieldIndex);
store.setWellSampleImageRef(imageID, 0, wellIndex, fieldIndex);
}
store.setImageName(
"Well " + wellRow + wellColumn + ", Field #" + field, i);
}
if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) {
// physical dimensions are stored as pixels per meter - we want them
// in microns per pixel
double width = pixelWidth == 0 ? 0.0 : 1000000.0 / pixelWidth;
double height = pixelHeight == 0 ? 0.0 : 1000000.0 / pixelHeight;
for (int i=0; i<getSeriesCount(); i++) {
if (width > 0) {
store.setPixelsPhysicalSizeX(new PositiveFloat(width), 0);
}
if (height > 0) {
store.setPixelsPhysicalSizeY(new PositiveFloat(height), 0);
}
}
}
}
// -- Helper methods --
private String getPlateName(String filename) {
int underscore = filename.lastIndexOf("_");
if (underscore < 0) return null;
return filename.substring(0, underscore);
}
private String getWellName(String filename) {
String wellName = filename.substring(filename.lastIndexOf("_") + 1);
while (!Character.isLetter(wellName.charAt(0)) ||
!Character.isDigit(wellName.charAt(1)))
{
wellName = wellName.substring(1, wellName.length());
}
return wellName;
}
private String getWellRow(String filename) {
return getWellName(filename).substring(0, 1);
}
private String getWellColumn(String filename) {
return getWellName(filename).substring(1, 3);
}
private String getField(String filename) {
String well = getWellName(filename);
int start = well.indexOf("f") + 1;
int end = start + 2;
return well.substring(start, end);
}
private RandomAccessInputStream getDecompressedStream(String filename)
throws FormatException, IOException
{
RandomAccessInputStream s = new RandomAccessInputStream(filename);
if (checkSuffix(filename, "c01")) {
LOGGER.info("Decompressing file");
s.seek(4);
ZlibCodec codec = new ZlibCodec();
byte[] file = codec.decompress(s, null);
s.close();
return new RandomAccessInputStream(file);
}
return s;
}
}