use of loci.formats.codec.CodecOptions in project bioformats by openmicroscopy.
the class DicomReader method initFile.
// -- 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);
in.order(true);
CoreMetadata m = core.get(0);
// look for companion files
attachCompanionFiles();
helper = new DicomReader();
helper.setGroupFiles(false);
m.littleEndian = true;
location = 0;
isJPEG = false;
isRLE = false;
bigEndianTransferSyntax = false;
oddLocations = false;
inSequence = false;
bitsPerPixel = 0;
elementLength = 0;
vr = 0;
lut = null;
offsets = null;
inverted = false;
// some DICOM files have a 128 byte header followed by a 4 byte identifier
LOGGER.info("Verifying DICOM format");
MetadataLevel level = getMetadataOptions().getMetadataLevel();
in.seek(128);
if (in.readString(4).equals("DICM")) {
if (level != MetadataLevel.MINIMUM) {
// header exists, so we'll read it
in.seek(0);
addSeriesMeta("Header information", in.readString(128));
in.skipBytes(4);
}
location = 128;
} else
in.seek(0);
LOGGER.info("Reading tags");
long baseOffset = 0;
boolean decodingTags = true;
boolean signed = false;
String currentType = "";
while (decodingTags) {
if (in.getFilePointer() + 4 >= in.length()) {
break;
}
LOGGER.debug("Reading tag from {}", in.getFilePointer());
int tag = getNextTag(in);
if (elementLength <= 0)
continue;
oddLocations = (location & 1) != 0;
LOGGER.debug(" tag={} len={} fp=", new Object[] { tag, elementLength, in.getFilePointer() });
String s = null;
switch(tag) {
case TRANSFER_SYNTAX_UID:
// this tag can indicate which compression scheme is used
s = in.readString(elementLength);
addInfo(tag, s);
if (s.startsWith("1.2.840.10008.1.2.4.9"))
isJP2K = true;
else if (s.startsWith("1.2.840.10008.1.2.4"))
isJPEG = true;
else if (s.startsWith("1.2.840.10008.1.2.5"))
isRLE = true;
else if (s.equals("1.2.8.10008.1.2.1.99"))
isDeflate = true;
else if (s.indexOf("1.2.4") > -1 || s.indexOf("1.2.5") > -1) {
throw new UnsupportedCompressionException("Sorry, compression type " + s + " not supported");
}
if (s.indexOf("1.2.840.10008.1.2.2") >= 0) {
bigEndianTransferSyntax = true;
}
break;
case NUMBER_OF_FRAMES:
s = in.readString(elementLength);
addInfo(tag, s);
double frames = Double.parseDouble(s);
if (frames > 1.0)
imagesPerFile = (int) frames;
break;
case SAMPLES_PER_PIXEL:
addInfo(tag, in.readShort());
break;
case PLANAR_CONFIGURATION:
int config = in.readShort();
m.interleaved = config == 0;
addInfo(tag, config);
break;
case ROWS:
int y = in.readShort();
if (y > getSizeY()) {
m.sizeY = y;
}
addInfo(tag, getSizeY());
break;
case COLUMNS:
int x = in.readShort();
if (x > getSizeX()) {
m.sizeX = x;
}
addInfo(tag, getSizeX());
break;
case PHOTOMETRIC_INTERPRETATION:
case PIXEL_SPACING:
case SLICE_SPACING:
case RESCALE_INTERCEPT:
case WINDOW_CENTER:
String winCenter = in.readString(elementLength);
if (winCenter.trim().length() == 0)
centerPixelValue = -1;
else {
try {
centerPixelValue = new Double(winCenter).intValue();
} catch (NumberFormatException e) {
centerPixelValue = -1;
}
}
addInfo(tag, winCenter);
break;
case RESCALE_SLOPE:
addInfo(tag, in.readString(elementLength));
break;
case BITS_ALLOCATED:
if (bitsPerPixel == 0)
bitsPerPixel = in.readShort();
else
in.skipBytes(2);
addInfo(tag, bitsPerPixel);
break;
case PIXEL_REPRESENTATION:
case PIXEL_SIGN:
short ss = in.readShort();
signed = ss == 1;
addInfo(tag, ss);
break;
case 537262910:
case WINDOW_WIDTH:
String t = in.readString(elementLength);
if (t.trim().length() == 0)
maxPixelRange = -1;
else {
try {
maxPixelRange = new Double(t.trim()).intValue();
} catch (NumberFormatException e) {
maxPixelRange = -1;
}
}
addInfo(tag, t);
break;
case PIXEL_DATA:
case ITEM:
case 0xffee000:
if (elementLength != 0) {
baseOffset = in.getFilePointer();
addInfo(tag, location);
decodingTags = false;
} else
addInfo(tag, null);
break;
case 0x7f880010:
if (elementLength != 0) {
baseOffset = location + 4;
decodingTags = false;
}
break;
case 0x7fe00000:
in.skipBytes(elementLength);
break;
case 0:
in.seek(in.getFilePointer() - 4);
break;
case 0x41430:
currentType = getHeaderInfo(tag, s).trim();
break;
case 0x41500:
if (currentType.equals("IMAGE")) {
if (fileList == null) {
fileList = new HashMap<Integer, List<String>>();
}
int seriesIndex = 0;
if (originalInstance != null) {
try {
seriesIndex = Integer.parseInt(originalInstance);
} catch (NumberFormatException e) {
LOGGER.debug("Could not parse instance number: {}", originalInstance);
}
}
if (fileList.get(seriesIndex) == null) {
fileList.put(seriesIndex, new ArrayList<String>());
}
fileList.get(seriesIndex).add(getHeaderInfo(tag, s).trim());
} else {
companionFiles.add(getHeaderInfo(tag, s).trim());
}
currentType = "";
break;
default:
long oldfp = in.getFilePointer();
addInfo(tag, s);
in.seek(oldfp + elementLength);
}
if (in.getFilePointer() >= (in.length() - 4)) {
decodingTags = false;
}
}
if (imagesPerFile == 0)
imagesPerFile = 1;
if (id.endsWith("DICOMDIR")) {
String parent = new Location(currentId).getAbsoluteFile().getParent();
for (int q = 0; q < fileList.size(); q++) {
Integer[] fileKeys = fileList.keySet().toArray(new Integer[0]);
for (int i = 0; i < fileList.get(fileKeys[q]).size(); i++) {
String file = fileList.get(fileKeys[q]).get(i);
file = file.replace('\\', File.separatorChar);
file = file.replaceAll("/", File.separator);
fileList.get(fileKeys[q]).set(i, parent + File.separator + file);
}
}
for (int i = 0; i < companionFiles.size(); i++) {
String file = companionFiles.get(i);
file = file.replace('\\', File.separatorChar);
file = file.replaceAll("/", File.separator);
companionFiles.set(i, parent + File.separator + file);
}
companionFiles.add(new Location(currentId).getAbsolutePath());
initFile(fileList.get(0).get(0));
return;
}
m.bitsPerPixel = bitsPerPixel;
while (bitsPerPixel % 8 != 0) bitsPerPixel++;
if (bitsPerPixel == 24 || bitsPerPixel == 48) {
bitsPerPixel /= 3;
m.bitsPerPixel /= 3;
}
m.pixelType = FormatTools.pixelTypeFromBytes(bitsPerPixel / 8, signed, false);
int bpp = FormatTools.getBytesPerPixel(getPixelType());
int plane = getSizeX() * getSizeY() * (lut == null ? getSizeC() : 1) * bpp;
LOGGER.info("Calculating image offsets");
// calculate the offset to each plane
in.seek(baseOffset - 12);
int len = in.readInt();
if (len >= 0 && len + in.getFilePointer() < in.length()) {
in.skipBytes(len);
int check = in.readShort() & 0xffff;
if (check == 0xfffe) {
baseOffset = in.getFilePointer() + 2;
}
}
offsets = new long[imagesPerFile];
for (int i = 0; i < imagesPerFile; i++) {
if (isRLE) {
if (i == 0)
in.seek(baseOffset);
else {
in.seek(offsets[i - 1]);
CodecOptions options = new CodecOptions();
options.maxBytes = plane / bpp;
for (int q = 0; q < bpp; q++) {
new PackbitsCodec().decompress(in, options);
while (in.read() == 0) ;
in.seek(in.getFilePointer() - 1);
}
}
in.skipBytes(i == 0 ? 64 : 53);
while (in.read() == 0) ;
offsets[i] = in.getFilePointer() - 1;
} else if (isJPEG || isJP2K) {
// scan for next JPEG magic byte sequence
if (i == 0)
offsets[i] = baseOffset;
else
offsets[i] = offsets[i - 1] + 3;
byte secondCheck = isJPEG ? (byte) 0xd8 : (byte) 0x4f;
in.seek(offsets[i]);
byte[] buf = new byte[8192];
int n = in.read(buf);
boolean found = false;
while (!found) {
for (int q = 0; q < n - 2; q++) {
if (buf[q] == (byte) 0xff && buf[q + 1] == secondCheck && buf[q + 2] == (byte) 0xff) {
if (isJPEG || (isJP2K && buf[q + 3] == 0x51)) {
found = true;
offsets[i] = in.getFilePointer() + q - n;
break;
}
}
}
if (!found) {
for (int q = 0; q < 4; q++) {
buf[q] = buf[buf.length + q - 4];
}
n = in.read(buf, 4, buf.length - 4) + 4;
}
}
} else
offsets[i] = baseOffset + plane * i;
}
makeFileList();
LOGGER.info("Populating metadata");
int seriesCount = fileList.size();
Integer[] keys = fileList.keySet().toArray(new Integer[0]);
Arrays.sort(keys);
if (seriesCount > 1) {
core.clear();
}
for (int i = 0; i < seriesCount; i++) {
if (seriesCount == 1) {
CoreMetadata ms = core.get(i);
ms.sizeZ = imagesPerFile * fileList.get(keys[i]).size();
if (ms.sizeC == 0)
ms.sizeC = 1;
ms.rgb = ms.sizeC > 1;
ms.sizeT = 1;
ms.dimensionOrder = "XYCZT";
ms.metadataComplete = true;
ms.falseColor = false;
if (isRLE)
core.get(i).interleaved = false;
ms.imageCount = ms.sizeZ;
} else {
helper.close();
helper.setId(fileList.get(keys[i]).get(0));
CoreMetadata ms = helper.getCoreMetadataList().get(0);
ms.sizeZ *= fileList.get(keys[i]).size();
ms.imageCount = ms.sizeZ;
core.add(ms);
}
}
// The metadata store we're working with.
MetadataStore store = makeFilterMetadata();
MetadataTools.populatePixels(store, this, true);
String stamp = null;
if (date != null && time != null) {
stamp = date + " " + time;
stamp = DateTools.formatDate(stamp, "yyyy.MM.dd HH:mm:ss", ".");
}
if (stamp == null || stamp.trim().equals(""))
stamp = null;
for (int i = 0; i < core.size(); i++) {
if (stamp != null)
store.setImageAcquisitionDate(new Timestamp(stamp), i);
store.setImageName("Series " + i, i);
}
if (level != MetadataLevel.MINIMUM) {
for (int i = 0; i < core.size(); i++) {
store.setImageDescription(imageType, i);
// all physical sizes were stored in mm, so must be converted to um
if (pixelSizeX != null) {
Length x = FormatTools.getPhysicalSizeX(new Double(pixelSizeX), UNITS.MILLIMETER);
if (x != null) {
store.setPixelsPhysicalSizeX(x, i);
}
}
if (pixelSizeY != null) {
Length y = FormatTools.getPhysicalSizeY(new Double(pixelSizeY), UNITS.MILLIMETER);
if (y != null) {
store.setPixelsPhysicalSizeY(y, i);
}
}
if (pixelSizeZ != null) {
Length z = FormatTools.getPhysicalSizeZ(new Double(pixelSizeZ), UNITS.MILLIMETER);
if (z != null) {
store.setPixelsPhysicalSizeZ(z, i);
}
}
for (int p = 0; p < getImageCount(); p++) {
if (p < positionX.size()) {
if (positionX.get(p) != null) {
Length x = new Length(positionX.get(p), UNITS.MM);
if (x != null) {
store.setPlanePositionX(x, 0, p);
}
}
}
if (p < positionY.size()) {
if (positionY.get(p) != null) {
Length y = new Length(positionY.get(p), UNITS.MM);
if (y != null) {
store.setPlanePositionY(y, 0, p);
}
}
}
if (p < positionZ.size()) {
if (positionZ.get(p) != null) {
Length z = new Length(positionZ.get(p), UNITS.MM);
if (z != null) {
store.setPlanePositionZ(z, 0, p);
}
}
}
}
}
}
}
use of loci.formats.codec.CodecOptions in project bioformats by openmicroscopy.
the class OMEXMLReader method openBytes.
/**
* @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 {
if (binDataOffsets.size() == 0)
return buf;
FormatTools.checkPlaneParameters(this, no, buf.length, x, y, w, h);
int index = no;
int series = getSeries();
for (int i = 0; i < series; i++) {
index += core.get(i).imageCount;
}
if (index >= binDataOffsets.size()) {
index = binDataOffsets.size() - 1;
}
long offset = binDataOffsets.get(index).longValue();
String compress = compression.get(index);
in.seek(offset);
int depth = FormatTools.getBytesPerPixel(getPixelType());
int planeSize = getSizeX() * getSizeY() * depth;
CodecOptions options = new CodecOptions();
options.width = getSizeX();
options.height = getSizeY();
options.bitsPerSample = depth * 8;
options.channels = getRGBChannelCount();
options.maxBytes = planeSize;
options.littleEndian = isLittleEndian();
options.interleaved = isInterleaved();
String encoded = in.readString("<");
encoded = encoded.trim();
if (encoded.length() == 0 || encoded.equals("<")) {
LOGGER.debug("No pixel data for plane #{}", no);
return buf;
}
encoded = encoded.substring(0, encoded.length() - 1);
byte[] pixels = BaseEncoding.base64().decode(encoded);
// return a blank plane if no pixel data was stored
if (pixels.length == 0) {
LOGGER.debug("No pixel data for plane #{}", no);
return buf;
}
// TODO: Create a method uncompress to handle all compression methods
if (compress.equals("bzip2")) {
byte[] tempPixels = pixels;
pixels = new byte[tempPixels.length - 2];
System.arraycopy(tempPixels, 2, pixels, 0, pixels.length);
ByteArrayInputStream bais = new ByteArrayInputStream(pixels);
CBZip2InputStream bzip = new CBZip2InputStream(bais);
pixels = new byte[planeSize];
bzip.read(pixels, 0, pixels.length);
tempPixels = null;
bais.close();
bzip.close();
bais = null;
bzip = null;
} else if (compress.equals("zlib")) {
pixels = new ZlibCodec().decompress(pixels, options);
} else if (compress.equals("J2K")) {
pixels = new JPEG2000Codec().decompress(pixels, options);
} else if (compress.equals("JPEG")) {
pixels = new JPEGCodec().decompress(pixels, options);
}
for (int row = 0; row < h; row++) {
int off = (row + y) * getSizeX() * depth + x * depth;
System.arraycopy(pixels, off, buf, row * w * depth, w * depth);
}
pixels = null;
return buf;
}
use of loci.formats.codec.CodecOptions in project bioformats by openmicroscopy.
the class CompressDecompressTest method assertCompression.
/**
* Tests the writing of the tiles.
* @param compression The compression to use.
* @param lossy whether or not this is a lossy compression type
*/
private void assertCompression(TiffCompression compression, boolean lossy) throws Exception {
IFD ifd = new IFD();
int w = 64;
int h = 64;
int bpp = 8;
ifd.put(IFD.IMAGE_WIDTH, w);
ifd.put(IFD.IMAGE_LENGTH, h);
ifd.put(IFD.BITS_PER_SAMPLE, new int[] { bpp });
ifd.put(IFD.SAMPLES_PER_PIXEL, 1);
ifd.put(IFD.LITTLE_ENDIAN, Boolean.TRUE);
byte[] plane = new byte[w * h * (bpp / 8)];
for (int i = 0; i < plane.length; i++) {
plane[i] = (byte) i;
}
String beforeCompression, afterCompression, afterDecompression;
CodecOptions options = compression.getCompressionCodecOptions(ifd);
byte[] compressed;
beforeCompression = Hashing.md5().hashBytes(plane).toString();
compressed = compression.compress(plane, options);
afterCompression = Hashing.md5().hashBytes(compressed).toString();
if (compression.equals(TiffCompression.UNCOMPRESSED)) {
if (!beforeCompression.equals(afterCompression)) {
fail("Compression: " + compression.getCodecName() + " " + String.format("Compression MD5 %s != %s", beforeCompression, afterCompression));
}
afterDecompression = Hashing.md5().hashBytes(compression.decompress(compressed, options)).toString();
if (!beforeCompression.equals(afterDecompression)) {
fail("Compression: " + compression.getCodecName() + " " + String.format("Decompression MD5 %s != %s", beforeCompression, afterDecompression));
}
} else {
if (beforeCompression.equals(afterCompression)) {
fail("Compression: " + compression.getCodecName() + " " + String.format("Compression MD5 %s != %s", beforeCompression, afterCompression));
}
afterDecompression = Hashing.md5().hashBytes(compression.decompress(compressed, options)).toString();
if (!lossy && !beforeCompression.equals(afterDecompression)) {
fail("Compression: " + compression.getCodecName() + " " + String.format("Decompression MD5 %s != %s", beforeCompression, afterDecompression));
}
}
}
use of loci.formats.codec.CodecOptions in project bioformats by openmicroscopy.
the class NativeND2Reader method initFile.
/* @see loci.formats.FormatReader#initFile(String) */
@Override
protected void initFile(String id) throws FormatException, IOException {
super.initFile(id);
// using a 32KB buffer instead of the default 1MB gives
// better performance with the seek/skip pattern used here
in = new RandomAccessInputStream(id, BUFFER_SIZE);
boolean useChunkMap = useChunkMap();
LOGGER.debug("Attempting to use chunk map = {}", useChunkMap);
channelColors = new HashMap<String, Integer>();
if (in.read() == -38 && in.read() == -50) {
// newer version of ND2 - doesn't use JPEG2000
LOGGER.info("Searching for blocks");
isJPEG = false;
in.seek(0);
in.order(true);
// assemble offsets to each block
ArrayList<String> imageNames = new ArrayList<String>();
ArrayList<Long> imageOffsets = new ArrayList<Long>();
ArrayList<int[]> imageLengths = new ArrayList<int[]>();
ArrayList<Long> customDataOffsets = new ArrayList<Long>();
ArrayList<int[]> customDataLengths = new ArrayList<int[]>();
// order matters when working with the text blocks, which is
// why two ArrayLists are used instead of a HashMap
ArrayList<String> textStrings = new ArrayList<String>();
ArrayList<Boolean> validDimensions = new ArrayList<Boolean>();
ByteArrayHandle xml = new ByteArrayHandle();
final StringBuilder name = new StringBuilder();
int extraZDataCount = 0;
boolean foundMetadata = false;
boolean foundAttributes = false;
boolean useLastText = false;
int blockCount = 0;
TreeMap<Long, ChunkMapEntry> allChunkPositions = new TreeMap<Long, ChunkMapEntry>();
if (useChunkMap) {
/* In modern ND2 files, the chunk map is stored near the end, and contains
* a list of blocks and their offsets. By using these offsets instead of
* scanning through the whole file, an enormous speed up can be achieved.
*
* Implementation: Read the chunk map beforehand, process the file as normally,
* once the first ImageDataSeq block is reached, add all images and skip past the
* image data to process remaining metadata.
* I haven't read through all of NativeND2Reader, but I hope to have the least
* chance of inadvertedly breaking something by this approach.
*/
String chunkMapSignature = "ND2 CHUNK MAP SIGNATURE 0000001";
in.seek(in.length() - 40);
if (!in.readString(chunkMapSignature.length()).equals(chunkMapSignature)) {
useChunkMap = false;
LOGGER.info("ND2 Warning: No chunk map found!");
} else {
in.skipBytes(1);
long chunkMapPosition = in.readLong();
in.seek(chunkMapPosition);
int tmpLenOne = in.readInt();
int tmpLenTwo = in.readInt();
long chunkMapLength = in.readLong();
chunkMapPosition += 16 + tmpLenTwo;
in.seek(chunkMapPosition);
long chunkMapEnd = chunkMapPosition + chunkMapLength;
int imageDataCount = 0;
int maxImageIndex = -1;
while (in.getFilePointer() + 1 + 16 < chunkMapEnd) {
char b = (char) in.readByte();
while (b != '!') {
name.append(b);
b = (char) in.readByte();
}
ChunkMapEntry entry = new ChunkMapEntry();
entry.name = name.toString();
name.delete(0, name.length());
if (entry.name.equals(chunkMapSignature))
break;
entry.position = in.readLong();
entry.length = in.readLong();
if (entry.name.startsWith("ImageDataSeq|")) {
imageDataCount++;
int imageIndex = -1;
try {
imageIndex = Integer.parseInt(entry.name.substring("ImageDataSeq|".length()));
if (imageIndex > maxImageIndex) {
maxImageIndex = imageIndex;
}
} catch (NumberFormatException e) {
LOGGER.trace(entry.name, e);
}
}
allChunkPositions.put(entry.position, entry);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("ND2 {}", entry.toString());
}
}
if (imageDataCount != maxImageIndex + 1) {
LOGGER.warn("Discarding chunk map; image data count = {}, max index = {}", imageDataCount, maxImageIndex);
useChunkMap = false;
}
}
in.seek(0);
}
// search for blocks
// 0xDACEBE0A
byte[] sigBytes = { -38, -50, -66, 10 };
byte[] buf = new byte[BUFFER_SIZE];
if (useChunkMap) {
long checkEvery = in.length() / 10;
long nextCheck = 0;
for (ChunkMapEntry entry : allChunkPositions.values()) {
if (!entry.name.startsWith("ImageDataSeq")) {
continue;
}
if (entry.position > nextCheck) {
in.seek(entry.position);
in.read(buf, 0, sigBytes.length);
if (!(buf[0] == sigBytes[0] && buf[1] == sigBytes[1] && buf[2] == sigBytes[2] && buf[3] == sigBytes[3])) {
LOGGER.warn("Broken ND2 File detected! Disabling chunk map processing.");
useChunkMap = false;
break;
}
nextCheck = entry.position + checkEvery;
}
}
}
int chunkmapSkips = 0;
in.seek(0);
while (in.getFilePointer() < in.length() - 1 && in.getFilePointer() >= 0) {
int foundIndex = -1;
in.read(buf, 0, sigBytes.length);
while (foundIndex == -1 && in.getFilePointer() < in.length()) {
int n = in.read(buf, sigBytes.length, buf.length - sigBytes.length);
for (int i = 0; i < buf.length - sigBytes.length; i++) {
for (int j = 0; j < sigBytes.length; j++) {
if (buf[i + j] != sigBytes[j])
break;
if (j == sigBytes.length - 1)
foundIndex = i;
}
if (foundIndex != -1)
break;
}
if (foundIndex == -1) {
// likely faster than System.arraycopy
buf[0] = buf[buf.length - 4];
buf[1] = buf[buf.length - 3];
buf[2] = buf[buf.length - 2];
buf[3] = buf[buf.length - 1];
} else if (in.getFilePointer() - n + foundIndex < in.length()) {
in.seek(in.getFilePointer() - n + foundIndex);
}
}
if (in.getFilePointer() > in.length() - 24 || foundIndex == -1) {
break;
}
// Remember starting position
Long helper = in.getFilePointer();
// Length of the block name
int nameLength = in.readInt();
// Length of the data
long dataLength = in.readLong();
// Read the name
String nameAttri = in.readString(nameLength).trim();
// Where this block ends
Long stop = helper + (dataLength + nameLength);
// Only 1 MetadataSeq is needed
boolean seq = false;
// (we are interested only in xxxxLV - LV = light variant)
if (nameAttri.contains("MetadataLV") || nameAttri.contains("CalibrationLV") || (nameAttri.contains("MetadataSeqLV") && !seq)) {
// prevent position count from being doubled
if (nameAttri.equals("ImageMetadataLV!")) {
positionCount = 0;
}
iterateIn(in, stop);
}
// (others should be same, and time is saved elsewhere)
if (nameAttri.contains("MetadataSeqLV")) {
seq = true;
}
// Return to starting position
in.seek(helper + 12);
int len = (int) (nameLength + dataLength);
long fp = in.getFilePointer();
String blockType = in.readString(12);
int percent = (int) (100 * fp / in.length());
LOGGER.info("Parsing block '{}' {}%", blockType, percent);
blockCount++;
int skip = len - 12 - nameLength * 2;
if (skip <= 0)
skip += nameLength * 2;
if (blockType.endsWith("Calibra")) {
long veryStart = in.getFilePointer();
// ImageCalibra|tionLV
in.skipBytes(12);
long endFP = in.getFilePointer() + len - 24;
while (in.read() == 0) ;
while (in.getFilePointer() < endFP) {
int nameLen = in.read();
if (nameLen == 0) {
in.seek(in.getFilePointer() - 3);
nameLen = in.read();
}
if (nameLen < 0) {
break;
}
// Get data
String attributeName = DataTools.stripString(in.readString(nameLen * 2));
double valueOrLength = in.readDouble();
if (attributeName.equals("dCalibration")) {
if (valueOrLength > 0) {
addGlobalMeta(attributeName, valueOrLength);
if (trueSizeX == 0) {
trueSizeX = valueOrLength;
} else if (trueSizeY == 0) {
trueSizeY = valueOrLength;
}
}
// Done with calibration
break;
}
}
// For old nd2 files
in.seek(veryStart);
}
if (blockType.startsWith("ImageDataSeq")) {
if (foundMetadata && foundAttributes) {
imageOffsets.clear();
imageNames.clear();
imageLengths.clear();
customDataOffsets.clear();
customDataLengths.clear();
foundMetadata = false;
foundAttributes = false;
extraZDataCount = 0;
useLastText = true;
}
if (useChunkMap && chunkmapSkips == 0) {
ChunkMapEntry lastImage = null;
// sanity check: see if the chunk we just found is actually in the chunkmap ...
long lookupPosition = in.getFilePointer() - 28;
Long lookupResult = allChunkPositions.floorKey(lookupPosition);
if (lookupResult == null || lookupResult != lookupPosition) {
// if not, deactivate chunkmap processing and try classic
useChunkMap = false;
in.seek(lookupPosition);
continue;
}
for (ChunkMapEntry entry : allChunkPositions.values()) {
if ((entry.position + 28) < in.getFilePointer()) {
continue;
}
if (!entry.name.startsWith("ImageDataSeq")) {
break;
}
if (lastImage != null) {
chunkmapSkips = (int) (((entry.position - lastImage.position) / entry.length) - 1);
if (chunkmapSkips > 0) {
break;
}
}
lastImage = entry;
imageOffsets.add(new Long(entry.position + 16));
int realLength = (int) Math.max(entry.name.length() + 1, nameLength);
imageLengths.add(new int[] { realLength, (int) (entry.length - nameLength - 16), getSizeX() * getSizeY() });
imageNames.add(entry.name.substring(12));
blockCount++;
percent = (int) (100 * entry.position / in.length());
LOGGER.info("Parsing block '{}' {}%", "ImageDataSeq", percent);
}
// one was already added by the outer blockCount ++;
blockCount--;
if (lastImage.position + lastImage.length >= in.length()) {
in.seek(lastImage.position + 16);
} else {
in.seek(lastImage.position + lastImage.length);
}
continue;
}
if (chunkmapSkips > 0) {
chunkmapSkips -= 1;
}
dataLength -= 31;
LOGGER.debug("Adding non-chunkmap offset {}, nameLength = {}, dataLength = {}", fp, nameLength, dataLength);
imageOffsets.add(fp);
imageLengths.add(new int[] { nameLength, (int) dataLength, getSizeX() * getSizeY() });
char b = (char) in.readByte();
while (b != '!') {
name.append(b);
b = (char) in.readByte();
}
imageNames.add(name.toString());
name.setLength(0);
} else if (blockType.startsWith("ImageText")) {
foundMetadata = true;
in.skipBytes(6);
while (in.read() == 0) ;
long startFP = in.getFilePointer();
in.seek(startFP - 1);
String textString = DataTools.stripString(in.readString((int) dataLength));
textStrings.add(textString);
validDimensions.add(blockCount > 2);
if (!textString.startsWith("<")) {
skip = 0;
}
} else if (blockType.startsWith("Image") || blockType.startsWith("CustomDataVa")) {
if (blockType.equals("ImageAttribu")) {
foundAttributes = true;
in.skipBytes(6);
long endFP = in.getFilePointer() + len - 18;
while (in.read() == 0) ;
boolean canBeLossless = true;
while (in.getFilePointer() < endFP) {
int nameLen = in.read();
if (nameLen == 0) {
in.seek(in.getFilePointer() - 3);
nameLen = in.read();
}
if (nameLen < 0) {
break;
}
long start = in.getFilePointer();
String attributeName = DataTools.stripString(in.readString(nameLen * 2));
if (attributeName.startsWith("xml ") || attributeName.startsWith("ml version") || attributeName.startsWith("l version") || attributeName.startsWith("version")) {
if (attributeName.startsWith("xml ")) {
in.seek(start - 2);
} else if (attributeName.startsWith("ml version")) {
in.seek(start - 3);
} else if (attributeName.startsWith("l version")) {
in.seek(start - 4);
} else {
in.seek(start - 6);
}
attributeName = in.readCString();
String xmlString = XMLTools.sanitizeXML(attributeName.trim());
xmlString = xmlString.substring(0, xmlString.lastIndexOf(">") + 1);
if (xmlString.startsWith("<?xml")) {
xmlString = xmlString.substring(xmlString.indexOf('>') + 1);
}
if (!xmlString.endsWith("</variant>")) {
xmlString += "</variant>";
}
if (getDimensionOrder() == null) {
core.get(0).dimensionOrder = "";
}
try {
ND2Handler handler = new ND2Handler(core, imageOffsets.size());
XMLTools.parseXML(xmlString, handler);
xmlString = null;
core = handler.getCoreMetadataList();
if (backupHandler == null) {
backupHandler = handler;
}
} catch (IOException e) {
LOGGER.debug("Could not parse XML", e);
}
in.seek(in.getFilePointer() - 8);
break;
}
int valueOrLength = in.readInt();
addGlobalMeta(attributeName, valueOrLength);
if (attributeName.equals("uiWidth")) {
core.get(0).sizeX = valueOrLength;
} else if (attributeName.equals("uiHeight")) {
core.get(0).sizeY = valueOrLength;
} else if (attributeName.equals("uiComp")) {
core.get(0).sizeC = valueOrLength;
} else if (attributeName.equals("uiBpcInMemory")) {
core.get(0).pixelType = FormatTools.pixelTypeFromBytes(valueOrLength / 8, false, false);
} else if (attributeName.equals("uiBpcSignificant")) {
core.get(0).bitsPerPixel = valueOrLength;
} else if (attributeName.equals("dCompressionParam")) {
isLossless = valueOrLength >= 0;
} else if (attributeName.equals("eCompression")) {
canBeLossless = valueOrLength <= 0;
} else if (attributeName.equals("SLxImageAttributes")) {
int toSkip = valueOrLength - 5;
if ((toSkip % 2) == 1) {
toSkip++;
}
in.skipBytes(toSkip);
} else if (attributeName.endsWith("Desc")) {
in.seek(in.getFilePointer() - 2);
} else if (attributeName.equals("SLxExperiment")) {
in.skipBytes(8);
} else if (attributeName.equals("wsCameraName")) {
in.seek(in.getFilePointer() - 4);
byte[] b = new byte[2];
in.read(b);
StringBuilder value = new StringBuilder();
while (b[0] != 0) {
value.append(b[0]);
in.read(b);
}
addGlobalMeta(attributeName, value.toString());
} else if (attributeName.equals("uLoopPars")) {
int v2 = in.readInt();
int v3 = in.readInt();
addGlobalMeta(attributeName, valueOrLength + ", " + v2 + ", " + v3);
} else if (attributeName.equals("pPeriod")) {
in.skipBytes(22);
} else if (attributeName.equals("dPeriod") || attributeName.equals("dDuration")) {
in.skipBytes(4);
} else if (attributeName.equals("bDurationPref")) {
in.seek(in.getFilePointer() - 3);
}
in.skipBytes(1);
}
if (in.getFilePointer() > endFP) {
in.seek(endFP);
}
isLossless = isLossless && canBeLossless;
} else {
if (blockType.startsWith("ImageMetadat")) {
foundMetadata = true;
}
int length = len - 12;
byte[] b = new byte[length];
in.read(b);
// strip out invalid characters
int off = 0;
for (int j = 0; j < length; j++) {
char c = (char) b[j];
if ((off == 0 && c == '!') || c == 0)
off = j + 1;
if (Character.isISOControl(c) || !Character.isDefined(c)) {
b[j] = (byte) ' ';
}
}
if (length - off >= 5 && b[off] == '<' && b[off + 1] == '?' && b[off + 2] == 'x' && b[off + 3] == 'm' && b[off + 4] == 'l') {
boolean endBracketFound = false;
while (!endBracketFound) {
if (b[off++] == '>') {
endBracketFound = true;
}
}
xml.write(b, off, b.length - off);
}
}
skip = 0;
} else if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) {
int nDoubles = len / 8;
int nInts = len / 4;
long doubleOffset = fp + 8 * (nDoubles - imageOffsets.size());
long intOffset = fp + 4 * (nInts - imageOffsets.size());
if (blockType.startsWith("CustomData|A")) {
customDataOffsets.add(fp);
customDataLengths.add(new int[] { nameLength, (int) dataLength });
} else if (blockType.startsWith("CustomData|Z")) {
if (zOffset == 0) {
zOffset = doubleOffset;
}
extraZDataCount++;
} else if (blockType.startsWith("CustomData|X")) {
xOffset = doubleOffset;
} else if (blockType.startsWith("CustomData|Y")) {
yOffset = doubleOffset;
} else if (blockType.startsWith("CustomData|P")) {
if (pfsOffset == 0) {
pfsOffset = intOffset;
} else if (pfsStateOffset == 0) {
pfsStateOffset = intOffset;
}
}
}
if (skip > 0 && skip + in.getFilePointer() <= in.length()) {
in.skipBytes(skip);
}
}
// parse text blocks
int nChannelNames = textChannelNames.size();
for (int i = 0; i < textStrings.size(); i++) {
parseText(textStrings.get(i), imageOffsets.size(), validDimensions.get(i));
}
if (textChannelNames.size() > nChannelNames) {
int diff = textChannelNames.size() - nChannelNames;
while (textChannelNames.size() > diff) {
textChannelNames.remove(0);
}
}
// parse XML blocks
String xmlString = new String(xml.getBytes(), 0, (int) xml.length(), Constants.ENCODING);
xml = null;
xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><ND2>" + xmlString + "</ND2>";
xmlString = XMLTools.sanitizeXML(xmlString);
core.get(0).dimensionOrder = "";
ND2Handler handler = new ND2Handler(core, getSizeX() == 0, imageOffsets.size());
XMLTools.parseXML(xmlString, handler);
xmlString = null;
if (handler.getChannelColors().size() > 0) {
channelColors = handler.getChannelColors();
}
if (!isLossless) {
isLossless = handler.isLossless();
}
fieldIndex = handler.getFieldIndex();
core = handler.getCoreMetadataList();
final Map<String, Object> globalMetadata = handler.getMetadata();
nXFields = handler.getXFields();
if (nXFields > 6) {
nXFields = 0;
}
for (String key : globalMetadata.keySet()) {
addGlobalMeta(key, globalMetadata.get(key));
if (key.equals("ChannelCount")) {
for (int i = 0; i < getSeriesCount(); i++) {
CoreMetadata ms = core.get(i);
if (ms.sizeC == 0) {
ms.sizeC = Integer.parseInt(globalMetadata.get(key).toString());
if (ms.sizeC > 1) {
ms.rgb = true;
}
}
}
} else if (key.equals("uiBpcInMemory")) {
int bpc = Integer.parseInt(globalMetadata.get(key).toString());
core.get(0).pixelType = FormatTools.pixelTypeFromBytes(bpc / 8, false, false);
}
}
int planeCount = core.size() * getSizeZ() * getSizeT();
if (!textData && planeCount < imageOffsets.size() && planeCount > 0 && (imageOffsets.size() % (planeCount / core.size())) == 0) {
int seriesCount = imageOffsets.size() / (planeCount / core.size());
core = new ArrayList<CoreMetadata>();
for (int i = 0; i < seriesCount; i++) {
core.add(handler.getCoreMetadataList().get(0));
}
}
int numSeries = core.size();
if (numSeries == 0)
numSeries = 1;
else if (numSeries == 1 && positionCount > 1) {
for (int i = 1; i < positionCount; i++) {
core.add(core.get(0));
}
numSeries = core.size();
}
if (getSizeZ() == 0) {
for (int i = 0; i < getSeriesCount(); i++) {
core.get(i).sizeZ = 1;
}
if (getSizeT() == 0) {
for (int i = 0; i < getSeriesCount(); i++) {
core.get(i).sizeT = imageOffsets.size() / getSeriesCount();
}
}
}
if (getSizeT() == 0) {
for (int i = 0; i < getSeriesCount(); i++) {
core.get(i).sizeT = 1;
}
}
if (getSizeC() == 0) {
for (int i = 0; i < getSeriesCount(); i++) {
core.get(i).sizeC = 1;
}
}
if (getSizeZ() * getSizeT() == imageOffsets.size() && core.size() > 1) {
CoreMetadata ms0 = core.get(0);
core = new ArrayList<CoreMetadata>();
core.add(ms0);
}
if (positionCount != getSeriesCount() && (getSizeZ() == imageOffsets.size() || (extraZDataCount > 1 && getSizeZ() == 1 && (extraZDataCount == getSizeC())) || (handler.getXPositions().size() == 0 && (xOffset == 0 && getSizeZ() != getSeriesCount()))) && getSeriesCount() > 1) {
CoreMetadata ms0 = core.get(0);
if (getSeriesCount() > ms0.sizeZ) {
ms0.sizeZ = getSeriesCount();
}
core = new ArrayList<CoreMetadata>();
core.add(ms0);
}
// make sure the channel count is reasonable
// sometimes the XML will indicate that there are multiple channels,
// when in fact there is only one channel
long firstOffset = imageOffsets.get(0);
long secondOffset = imageOffsets.size() > 1 ? imageOffsets.get(1) : in.length();
long availableBytes = secondOffset - firstOffset;
// make sure that we have the compression setting correct
// it's not always easy to tell from the metadata
isLossless = true;
long fp = in.getFilePointer();
int[] firstLengths = imageLengths.get(0);
in.seek(firstOffset + firstLengths[0] + 8);
if (codec == null)
codec = createCodec(false);
try {
CodecOptions options = new CodecOptions();
options.littleEndian = isLittleEndian();
options.interleaved = true;
options.maxBytes = (int) secondOffset;
byte[] t = codec.decompress(in, options);
if (t.length == 2 * getSizeX() * getSizeY() && getPixelType() == FormatTools.INT8) {
core.get(0).pixelType = FormatTools.UINT16;
}
availableBytes = t.length;
} catch (IOException e) {
isLossless = false;
}
boolean allEqual = true;
long offsetDiff = imageOffsets.get(0) - imageLengths.get(0)[1];
for (int i = 1; i < imageOffsets.size(); i++) {
long nextOffsetDiff = imageOffsets.get(i) - imageLengths.get(i)[1];
if (imageLengths.get(i)[1] != imageLengths.get(0)[1] && offsetDiff != nextOffsetDiff) {
allEqual = false;
break;
}
}
if (!allEqual && !isLossless && imageOffsets.size() > 1) {
int plane = (getSizeX() + getScanlinePad()) * getSizeY();
boolean fixByteCounts = false;
if (plane > 0) {
for (int i = 0; i < imageOffsets.size(); i++) {
int check = imageLengths.get(i)[2];
int length = imageLengths.get(i)[1] - 8;
if ((length % plane != 0 && length % (getSizeX() * getSizeY()) != 0) || (check > 0 && plane != check)) {
if (imageOffsets.get(i) - length != offsetDiff + 8) {
if (i == 0) {
fixByteCounts = true;
}
imageOffsets.remove(i);
imageLengths.remove(i);
i--;
}
}
}
}
if (fixByteCounts) {
firstOffset = imageOffsets.get(0);
secondOffset = imageOffsets.size() > 1 ? imageOffsets.get(1) : in.length();
availableBytes = secondOffset - firstOffset;
if (isLossless) {
firstLengths = imageLengths.get(0);
in.seek(firstOffset + firstLengths[0] + 8);
CodecOptions options = new CodecOptions();
options.littleEndian = isLittleEndian();
options.interleaved = true;
options.maxBytes = (int) secondOffset;
byte[] t = codec.decompress(in, options);
availableBytes = t.length;
}
}
}
in.seek(fp);
long planeSize = getSizeX() * getSizeY() * getSizeC() * FormatTools.getBytesPerPixel(getPixelType());
if (availableBytes < planeSize) {
LOGGER.debug("Correcting SizeC: was {}", getSizeC());
LOGGER.debug("plane size = {}", planeSize);
LOGGER.debug("available bytes = {}", availableBytes);
core.get(0).sizeC = (int) (availableBytes / (planeSize / getSizeC()));
if (getSizeC() == 0) {
core.get(0).sizeC = 1;
}
planeSize = getSizeX() * getSizeY() * getSizeC() * FormatTools.getBytesPerPixel(getPixelType());
}
if (planeSize > 0 && availableBytes % planeSize != 0) {
// an extra 4K block of zeros may have been appended
if ((availableBytes - 4096) % planeSize == 0) {
availableBytes -= 4096;
}
}
if (planeSize > 0 && imageOffsets.size() > 1 && availableBytes > DataTools.safeMultiply64(planeSize, 3)) {
if (availableBytes < DataTools.safeMultiply64(planeSize, 6)) {
core.get(0).sizeC = 3;
core.get(0).rgb = true;
if (getPixelType() == FormatTools.INT8) {
core.get(0).pixelType = availableBytes > planeSize * 5 ? FormatTools.UINT16 : FormatTools.UINT8;
}
}
} else if (((planeSize > 0 && availableBytes >= DataTools.safeMultiply64(planeSize, 2)) || getSizeC() > 3) && getPixelType() == FormatTools.INT8) {
core.get(0).pixelType = FormatTools.UINT16;
planeSize *= 2;
if (getSizeC() > 3 && availableBytes % planeSize != 0 && planeSize > availableBytes) {
core.get(0).sizeC = 3;
core.get(0).rgb = true;
}
} else if (getSizeC() == 2 && getPixelType() == FormatTools.INT8 && availableBytes >= planeSize * 2) {
core.get(0).pixelType = FormatTools.UINT16;
} else if (getPixelType() == FormatTools.INT8) {
core.get(0).pixelType = FormatTools.UINT8;
}
if (getSizeX() == 0) {
core.get(0).sizeX = (int) Math.sqrt(availableBytes / (getSizeC() * FormatTools.getBytesPerPixel(getPixelType())));
core.get(0).sizeY = getSizeX();
}
int rowSize = getSizeX() * FormatTools.getBytesPerPixel(getPixelType()) * getSizeC();
long sizeY = availableBytes / rowSize;
if (sizeY < getSizeY()) {
core.get(0).sizeY = (int) sizeY;
}
if (getSizeT() == imageOffsets.size() && getSeriesCount() > 1) {
CoreMetadata firstCore = core.get(0);
core = new ArrayList<CoreMetadata>();
core.add(firstCore);
}
// calculate the image count
for (int i = 0; i < getSeriesCount(); i++) {
CoreMetadata ms = core.get(i);
ms.imageCount = getSizeZ() * getSizeT() * getSizeC();
if (imageOffsets.size() / getSeriesCount() < ms.imageCount) {
ms.imageCount /= getSizeC();
}
if (ms.imageCount > imageOffsets.size() / getSeriesCount()) {
int diff = imageOffsets.size() - ms.imageCount;
if (diff >= 0 && diff < ms.sizeZ && diff < ms.sizeT) {
CoreMetadata ms0 = core.get(0);
core = new ArrayList<CoreMetadata>();
core.add(ms0);
numSeries = 1;
break;
} else if (imageOffsets.size() % ms.sizeT == 0) {
ms.imageCount = imageOffsets.size() / getSeriesCount();
ms.sizeZ = ms.imageCount / ms.sizeT;
ms.dimensionOrder = "CZT";
} else {
ms.imageCount = imageOffsets.size() / getSeriesCount();
ms.sizeZ = 1;
ms.sizeT = ms.imageCount;
}
}
}
if (numSeries * getImageCount() == 1 && imageOffsets.size() > 1) {
for (int i = 0; i < getSeriesCount(); i++) {
core.get(i).imageCount = imageOffsets.size() / getSeriesCount();
core.get(i).sizeZ = getImageCount();
core.get(i).sizeT = 1;
}
}
if (getSizeZ() * getSizeT() * (split ? 1 : getSizeC()) < imageOffsets.size() / getSeriesCount()) {
split = getSizeC() > 1;
int count = imageOffsets.size() / getSeriesCount();
if (!split && count >= getSizeC()) {
count /= getSizeC();
}
int diff = count - getSizeZ() * getSizeT();
if (diff == getSizeZ()) {
core.get(0).sizeT++;
} else if (getSizeT() > getSizeZ()) {
core.get(0).sizeZ = 1;
core.get(0).sizeT = count;
} else {
core.get(0).sizeT = 1;
core.get(0).sizeZ = count;
}
if (useZ != null && !useZ) {
CoreMetadata original = core.get(0);
int nSeries = imageOffsets.size() / (getSizeZ() * getSizeT());
for (int i = 1; i < nSeries; i++) {
core.add(original);
}
numSeries = core.size();
}
if (getSizeZ() * getSizeT() * (split ? 1 : getSizeC()) < imageOffsets.size() / getSeriesCount() && getSizeC() > 4) {
core.get(0).sizeZ = 1;
core.get(0).sizeT = imageOffsets.size() / getSeriesCount();
}
core.get(0).imageCount = getSizeZ() * getSizeT() * getSizeC();
}
if (getDimensionOrder().equals("T")) {
fieldIndex = 0;
} else if (getDimensionOrder().equals("ZT") && fieldIndex == 2) {
fieldIndex--;
}
if (getSizeC() > 1 && getDimensionOrder().indexOf('C') == -1) {
core.get(0).dimensionOrder = "C" + getDimensionOrder();
fieldIndex++;
}
core.get(0).dimensionOrder = "XY" + getDimensionOrder();
if (getDimensionOrder().indexOf('Z') == -1)
core.get(0).dimensionOrder += 'Z';
if (getDimensionOrder().indexOf('C') == -1)
core.get(0).dimensionOrder += 'C';
if (getDimensionOrder().indexOf('T') == -1)
core.get(0).dimensionOrder += 'T';
if (getSizeZ() == 0) {
core.get(0).sizeZ = 1;
}
if (getSizeT() == 0) {
core.get(0).sizeT = 1;
}
if (getSizeC() == 0) {
core.get(0).sizeC = 1;
}
core.get(0).imageCount = getSizeZ() * getSizeT();
if (!isRGB()) {
core.get(0).imageCount *= getSizeC();
}
posX = handler.getXPositions();
posY = handler.getYPositions();
posZ = handler.getZPositions();
int uniqueX = 0, uniqueY = 0, uniqueZ = 0;
if (posX.size() == 0 && xOffset != 0) {
in.seek(xOffset);
for (int i = 0; i < imageOffsets.size(); i++) {
final Double number = Double.valueOf(in.readDouble());
final Length x = new Length(number, UNITS.REFERENCEFRAME);
if (!posX.contains(x)) {
uniqueX++;
}
posX.add(x);
}
}
if (posY.size() == 0 && yOffset != 0) {
in.seek(yOffset);
for (int i = 0; i < imageOffsets.size(); i++) {
final Double number = Double.valueOf(in.readDouble());
final Length y = new Length(number, UNITS.REFERENCEFRAME);
if (!posY.contains(y)) {
uniqueY++;
}
posY.add(y);
}
}
if (posZ.size() == 0 && zOffset != 0) {
in.seek(zOffset);
for (int i = 0; i < imageOffsets.size(); i++) {
final Double number = Double.valueOf(in.readDouble());
final Length z = new Length(number, UNITS.REFERENCEFRAME);
if (!posZ.contains(z)) {
boolean unique = true;
for (int q = 0; q < posZ.size(); q++) {
// account for potential stage drift
final double z1 = z.value(UNITS.REFERENCEFRAME).doubleValue();
final double z2 = posZ.get(q).value(UNITS.REFERENCEFRAME).doubleValue();
if (Math.abs(z1 - z2) <= 0.05) {
unique = false;
break;
}
}
if (unique) {
uniqueZ++;
}
}
posZ.add(z);
}
}
if (pfsOffset != 0) {
in.seek(pfsOffset);
for (int i = 0; i < imageOffsets.size(); i++) {
addGlobalMetaList("PFS Offset", in.readInt());
}
}
if (pfsStateOffset != 0) {
in.seek(pfsStateOffset);
for (int i = 0; i < imageOffsets.size(); i++) {
addGlobalMetaList("PFS Status", in.readInt());
}
}
if (core.size() == 1 && ((uniqueX == getSizeT() && uniqueY == getSizeT()) || uniqueZ == getSizeT())) {
int count = getSizeT();
core.get(0).imageCount /= count;
core.get(0).sizeT = 1;
for (int i = 1; i < count; i++) {
core.add(core.get(0));
}
numSeries = core.size();
}
// when the offsets array is allocated
if (getImageCount() == imageOffsets.size() && numSeries > 1 && getSizeC() == 1) {
CoreMetadata first = core.get(0);
core.clear();
core.add(first);
numSeries = 1;
}
offsets = new long[numSeries][getImageCount()];
int[] lengths = new int[4];
int nextChar = 2;
for (int i = 0; i < lengths.length; i++) {
if (i == fieldIndex)
lengths[i] = core.size();
else {
char axis = getDimensionOrder().charAt(nextChar++);
if (axis == 'Z')
lengths[i] = getSizeZ();
else if (axis == 'C')
lengths[i] = 1;
else if (axis == 'T')
lengths[i] = getSizeT();
}
}
int[] zctLengths = new int[4];
System.arraycopy(lengths, 0, zctLengths, 0, lengths.length);
zctLengths[fieldIndex] = 1;
boolean oneIndexed = false;
for (int i = 0; i < imageOffsets.size(); i++) {
long offset = imageOffsets.get(i).longValue();
int[] p = imageLengths.get(i);
int length = p[0] + p[1];
if (getSizeC() == 0) {
int sizeC = length / (getSizeX() * getSizeY() * FormatTools.getBytesPerPixel(getPixelType()));
for (int q = 0; q < getSeriesCount(); q++) {
core.get(q).sizeC = sizeC;
}
}
String imageName = imageNames.get(i);
int ndx = Integer.parseInt(imageName.replaceAll("\\D", ""));
if (ndx == 1 && i == 0) {
oneIndexed = true;
}
if (oneIndexed) {
ndx--;
}
int[] pos = FormatTools.rasterToPosition(lengths, ndx);
int seriesIndex = pos[fieldIndex];
pos[fieldIndex] = 0;
int plane = FormatTools.positionToRaster(zctLengths, pos);
if (seriesIndex < offsets.length && plane < offsets[seriesIndex].length) {
offsets[seriesIndex][plane] = offset + p[0] + 8;
}
}
ArrayList<long[]> tmpOffsets = new ArrayList<long[]>();
for (int i = 0; i < offsets.length; i++) {
if (offsets[i].length > 0 && offsets[i][0] > 0) {
tmpOffsets.add(offsets[i]);
}
}
offsets = new long[tmpOffsets.size()][];
for (int i = 0; i < tmpOffsets.size(); i++) {
offsets[i] = tmpOffsets.get(i);
}
if (offsets.length != getSeriesCount()) {
int x = getSizeX();
int y = getSizeY();
int c = getSizeC();
int pixelType = getPixelType();
int bitsPerPixel = getBitsPerPixel();
boolean rgb = isRGB();
String order = getDimensionOrder();
core = new ArrayList<CoreMetadata>();
for (int i = 0; i < offsets.length; i++) {
CoreMetadata ms = new CoreMetadata();
core.add(ms);
ms.sizeX = x;
ms.sizeY = y;
ms.sizeC = c == 0 ? 1 : c;
ms.pixelType = pixelType;
ms.bitsPerPixel = bitsPerPixel;
ms.rgb = rgb;
ms.sizeZ = 1;
ms.dimensionOrder = order;
int invalid = 0;
for (int q = 0; q < offsets[i].length; q++) {
if (offsets[i][q] == 0)
invalid++;
}
ms.imageCount = offsets[i].length - invalid;
ms.sizeT = ms.imageCount / (rgb ? 1 : ms.sizeC);
if (ms.sizeT == 0)
ms.sizeT = 1;
}
} else {
for (int i = 0; i < getSeriesCount(); i++) {
CoreMetadata ms = core.get(i);
ms.sizeX = getSizeX();
ms.sizeY = getSizeY();
ms.sizeC = getSizeC() == 0 ? 1 : getSizeC();
ms.sizeZ = getSizeZ() == 0 ? 1 : getSizeZ();
ms.sizeT = getSizeT() == 0 ? 1 : getSizeT();
ms.imageCount = getImageCount();
ms.pixelType = getPixelType();
ms.bitsPerPixel = getBitsPerPixel();
ms.dimensionOrder = getDimensionOrder();
}
}
boolean hasColor = false;
for (String ch : channelColors.keySet()) {
Integer color = channelColors.get(ch);
// look for a color that is neither black nor white
if (color != 0xffffff && color != 0) {
hasColor = true;
break;
}
}
split = getSizeC() > 1;
for (int i = 0; i < getSeriesCount(); i++) {
CoreMetadata ms = core.get(i);
ms.rgb = false;
ms.littleEndian = true;
ms.interleaved = false;
ms.indexed = channelColors.size() > 0 && hasColor;
ms.falseColor = true;
ms.metadataComplete = true;
ms.imageCount = ms.sizeZ * ms.sizeT * ms.sizeC;
}
if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) {
if (customDataOffsets.size() > 0) {
in.seek(customDataOffsets.get(0).longValue());
int[] p = customDataLengths.get(0);
int len = p[0] + p[1];
int timestampBytes = imageOffsets.size() * 8;
in.skipBytes(len - timestampBytes);
for (int series = 0; series < getSeriesCount(); series++) {
setSeries(series);
int count = split ? getImageCount() / getSizeC() : getImageCount();
for (int plane = 0; plane < count; plane++) {
// timestamps are stored in ms; we want them in seconds
double time = in.readDouble() / 1000;
tsT.add(time);
addSeriesMetaList("timestamp", time);
}
}
setSeries(0);
}
}
populateMetadataStore(handler);
return;
} else
in.seek(0);
// older version of ND2 - uses JPEG 2000 compression
isJPEG = true;
LOGGER.info("Calculating image offsets");
ArrayList<Long> vs = new ArrayList<Long>();
long pos = in.getFilePointer();
boolean lastBoxFound = false;
int length = 0;
int box = 0;
// assemble offsets to each plane
int x = 0, y = 0, c = 0, type = 0;
while (!lastBoxFound) {
pos = in.getFilePointer();
length = in.readInt();
long nextPos = pos + length;
if (nextPos < 0 || nextPos >= in.length() || length == 0) {
lastBoxFound = true;
}
box = in.readInt();
pos = in.getFilePointer();
length -= 8;
if (box == 0x6a703263) {
vs.add(pos);
} else if (box == 0x6a703268) {
in.skipBytes(4);
String s = in.readString(4);
if (s.equals("ihdr")) {
y = in.readInt();
x = in.readInt();
c = in.readShort();
type = in.readInt();
if (type == 0xf070100 || type == 0xf070000)
type = FormatTools.UINT16;
else
type = FormatTools.UINT8;
}
}
if (!lastBoxFound && box != 0x6a703268)
in.skipBytes(length);
}
LOGGER.info("Finding XML metadata");
// read XML metadata from the end of the file
in.seek(vs.get(vs.size() - 1).longValue());
boolean found = false;
long off = -1;
byte[] buf = new byte[8192];
while (!found && in.getFilePointer() < in.length()) {
int read = 0;
if (in.getFilePointer() == vs.get(vs.size() - 1).longValue()) {
read = in.read(buf);
} else {
System.arraycopy(buf, buf.length - 10, buf, 0, 10);
read = in.read(buf, 10, buf.length - 10);
}
if (read == buf.length)
read -= 10;
for (int i = 0; i < read + 9; i++) {
if (buf[i] == (byte) 0xff && buf[i + 1] == (byte) 0xd9) {
found = true;
off = in.getFilePointer() - (read + 10) + i;
i = buf.length;
break;
}
}
}
buf = null;
LOGGER.info("Parsing XML");
ArrayList<Long> zs = new ArrayList<Long>();
ArrayList<Long> ts = new ArrayList<Long>();
int numSeries = 0;
ND2Handler handler = null;
if (off > 0 && off < in.length() - 5 && (in.length() - off - 5) > 14) {
in.seek(off + 4);
StringBuilder sb = new StringBuilder();
// stored XML doesn't have a root node - add one, so that we can parse
// using SAX
sb.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><NIKON>");
String s = null;
int blockLength = 0;
while (in.getFilePointer() < in.length()) {
blockLength = in.readShort();
if (blockLength < 2)
break;
blockLength -= 2;
if (blockLength + in.getFilePointer() >= in.length()) {
blockLength = (int) (in.length() - in.getFilePointer());
}
s = in.readString(blockLength);
// remove comments
s = s.replaceAll("<!--.+?>", "");
int openBracket = s.indexOf('<');
if (openBracket == -1)
continue;
int closedBracket = s.lastIndexOf(">") + 1;
if (closedBracket < openBracket)
continue;
s = s.substring(openBracket, closedBracket).trim();
if (s.indexOf("CalibrationSeq") == -1 && s.indexOf("VCAL") == -1 && s.indexOf("jp2cLUNK") == -1) {
sb.append(s);
}
}
s = null;
sb.append("</NIKON>");
LOGGER.info("Finished assembling XML string");
// strip out invalid characters
int offset = 0;
int len = sb.length();
for (int i = 0; i < len; i++) {
char ch = sb.charAt(i);
if (offset == 0 && ch == '!')
offset = i + 1;
if (Character.isISOControl(ch) || !Character.isDefined(ch)) {
sb.setCharAt(i, ' ');
}
}
core.get(0).dimensionOrder = "";
if (len - offset < offset) {
offset = 0;
}
String xml = sb.substring(offset, len);
sb = null;
handler = new ND2Handler(core, vs.size());
try {
xml = XMLTools.sanitizeXML(xml);
XMLTools.parseXML(xml, handler);
} catch (IOException e) {
}
xml = null;
isLossless = handler.isLossless();
fieldIndex = handler.getFieldIndex();
zs = handler.getZSections();
ts = handler.getTimepoints();
numSeries = handler.getSeriesCount();
core = handler.getCoreMetadataList();
final Map<String, Object> globalMetadata = handler.getMetadata();
for (final Map.Entry<String, Object> entry : globalMetadata.entrySet()) {
addGlobalMeta(entry.getKey(), entry.getValue());
}
}
LOGGER.info("Populating metadata");
core.get(0).pixelType = FormatTools.UINT8;
offsets = new long[1][2];
offsets[0][0] = vs.get(0).longValue();
if (offsets[0].length > 1 && vs.size() > 1) {
offsets[0][1] = vs.get(1).longValue();
}
in.seek(offsets[0][0]);
if (getSizeC() == 0)
core.get(0).sizeC = 1;
int numBands = c;
c = numBands > 1 ? numBands : getSizeC();
if (numBands == 1 && getImageCount() == 1)
c = 1;
for (int i = 0; i < getSeriesCount(); i++) {
CoreMetadata ms = core.get(i);
ms.sizeC = c;
ms.rgb = numBands > 1;
ms.pixelType = type;
}
if (getDimensionOrder() == null)
core.get(0).dimensionOrder = "";
if (getSizeC() > 1) {
core.get(0).dimensionOrder = getDimensionOrder().replaceAll("C", "");
core.get(0).dimensionOrder = "C" + getDimensionOrder();
fieldIndex++;
}
if (getDimensionOrder().indexOf('Z') == -1)
core.get(0).dimensionOrder += 'Z';
if (getDimensionOrder().indexOf('C') == -1)
core.get(0).dimensionOrder += 'C';
if (getDimensionOrder().indexOf('T') == -1)
core.get(0).dimensionOrder += 'T';
core.get(0).dimensionOrder = "XY" + getDimensionOrder();
if (getImageCount() == 0) {
core.get(0).imageCount = vs.size();
core.get(0).sizeZ = (int) Math.max(zs.size(), 1);
core.get(0).sizeT = (int) Math.max(ts.size(), 1);
int channels = isRGB() ? 1 : getSizeC();
if (channels * getSizeZ() * getSizeT() != getImageCount()) {
core.get(0).sizeZ = 1;
core.get(0).sizeT = getImageCount() / channels;
core.get(0).imageCount = getSizeZ() * getSizeT() * channels;
}
}
if (getSizeZ() == 0)
core.get(0).sizeZ = 1;
if (getSizeT() == 0)
core.get(0).sizeT = 1;
for (int i = 0; i < getSeriesCount(); i++) {
CoreMetadata ms = core.get(i);
ms.sizeZ = getSizeZ();
ms.sizeT = getSizeT();
ms.imageCount = getSizeZ() * getSizeT() * (isRGB() ? 1 : getSizeC());
ms.dimensionOrder = getDimensionOrder();
ms.sizeX = x;
ms.sizeY = y;
ms.interleaved = false;
ms.littleEndian = false;
ms.metadataComplete = true;
}
int nplanes = getSizeZ() * getEffectiveSizeC();
if (numSeries == 0)
numSeries = 1;
if (numSeries * nplanes * getSizeT() > vs.size()) {
numSeries = vs.size() / (nplanes * getSizeT());
}
offsets = new long[numSeries][getImageCount()];
for (int i = 0; i < getSizeT(); i++) {
for (int j = 0; j < numSeries; j++) {
for (int q = 0; q < nplanes; q++) {
offsets[j][i * nplanes + q] = vs.remove(0).longValue();
}
}
}
populateMetadataStore(handler);
}
use of loci.formats.codec.CodecOptions in project bioformats by openmicroscopy.
the class OpenlabReader method openBytes.
/**
* @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);
lastPlane = no;
PlaneInfo planeInfo = null;
if (specialPlateNames) {
planeInfo = getPlane(getZCTCoords(no));
} else if (no < planeOffsets[getSeries()].length) {
int index = planeOffsets[getSeries()][no];
planeInfo = planes[index];
}
if (planeInfo == null)
return buf;
long first = planeInfo.planeOffset;
long last = first + FormatTools.getPlaneSize(this) * 2;
int bpp = FormatTools.getBytesPerPixel(getPixelType());
if (!planeInfo.pict) {
if (version == 2) {
in.seek(first);
readPlane(in, x, y, w, h, buf);
} else {
in.seek(first + 16);
int bytes = bpp * getRGBChannelCount();
byte[] b = new byte[(int) (last - first)];
in.read(b);
CodecOptions options = new CodecOptions();
options.width = getSizeX();
options.height = getSizeY();
options.bitsPerSample = bytes * 8;
options.maxBytes = getSizeX() * getSizeY() * bytes;
b = new LZOCodec().decompress(b, options);
if (getSizeX() * getSizeY() * 4 <= b.length) {
for (int yy = y; yy < h + y; yy++) {
for (int xx = x; xx < w + x; xx++) {
System.arraycopy(b, (yy * (getSizeX() + 4) + xx) * 4 + 1, buf, ((yy - y) * w + xx - x) * 3, 3);
}
}
} else {
int src = b.length / getSizeY();
if (src - (getSizeX() * bytes) != 16)
src = getSizeX() * bytes;
int dest = w * bytes;
for (int row = 0; row < h; row++) {
System.arraycopy(b, (row + y) * src + x * bytes, buf, row * dest, dest);
}
}
b = null;
}
if (planeInfo.volumeType == MAC_256_GREYS || planeInfo.volumeType == MAC_256_COLORS) {
for (int i = 0; i < buf.length; i++) {
buf[i] = (byte) (~buf[i] & 0xff);
}
}
} else {
// PICT plane
Exception exc = null;
if (getPixelType() == FormatTools.UINT8) {
in.seek(first);
byte[] b = new byte[(int) (last - first) + 512];
in.read(b, 512, b.length - 512);
IFormatReader r = getRGBChannelCount() == 1 ? new ChannelSeparator(pict) : pict;
try {
Location.mapFile("OPENLAB_PICT", new ByteArrayHandle(b));
r.setId("OPENLAB_PICT");
if (getPixelType() != r.getPixelType()) {
throw new FormatException("Pixel type of inner PICT does not " + "match pixel type of Openlab file");
}
if (isIndexed()) {
int index = no;
if (getSeries() < planeOffsets.length) {
index = planeOffsets[getSeries()][lastPlane];
}
luts.set(index, pict.get8BitLookupTable());
}
r.openBytes(0, buf, x, y, w, h);
} catch (FormatException e) {
exc = e;
} catch (IOException e) {
exc = e;
} finally {
r.close();
// remove file from map
Location.mapFile("OPENLAB_PICT", null);
}
b = null;
}
if (exc != null || getPixelType() != FormatTools.UINT8) {
LOGGER.debug("", exc);
in.seek(planeInfo.planeOffset - 298);
if (in.readByte() == 1)
in.skipBytes(297);
else
in.skipBytes(169);
int size = 0, expectedBlock = 0, totalBlocks = -1, pixPos = 0;
byte[] plane = new byte[FormatTools.getPlaneSize(this)];
while (expectedBlock != totalBlocks && pixPos < plane.length && in.getFilePointer() + 32 < last) {
findNextBlock();
if (in.getFilePointer() + 4 >= in.length())
break;
int num = in.readInt();
if (num != expectedBlock) {
throw new FormatException("Expected iPic block not found");
}
expectedBlock++;
if (totalBlocks == -1) {
totalBlocks = in.readInt();
in.skipBytes(8);
} else
in.skipBytes(12);
size = in.readInt();
in.skipBytes(4);
if (size + pixPos > plane.length)
size = plane.length - pixPos;
in.read(plane, pixPos, size);
pixPos += size;
}
RandomAccessInputStream pix = new RandomAccessInputStream(plane);
readPlane(pix, x, y, w, h, buf);
pix.close();
plane = null;
}
}
return buf;
}
Aggregations