Newer
Older
}
CziParser::~CziParser()
{
}
CziFile CziParser::parse_czi_file(const std::string &file)
{
BinaryStream cziStream(file);
CziFile parsedFile;
parsedFile.fileName = file;
parsedFile.header = parse_file_header(cziStream);
parsedFile.metadata = parse_metadata(cziStream, parsedFile.header.metadataPosition);
parsedFile.subBlockDirectory = parse_subblock_directory(cziStream, parsedFile.header.subBlockDirectoryPosition);
for (size_t subBlockId = 0; subBlockId < parsedFile.subBlockDirectory.entryCount; subBlockId++)
int filePart = parsedFile.subBlockDirectory.entries[subBlockId].filePart;
assert(filePart == 0 && "We are currently supporting only single-file scenario.");
long subBlockPosition = parsedFile.subBlockDirectory.entries[subBlockId].filePosition;
int entrySize = parsedFile.subBlockDirectory.entries[subBlockId].entrySize;
parsedFile.subBlockDirectory.entries[subBlockId].subBlock = parse_subblock(cziStream, subBlockPosition, subBlockId, entrySize);
}
// AttachmentDirectory and Attachments
parsedFile.attachmentDirectory = parse_attachment_directory(cziStream, parsedFile.header.attachmentDirectoryPosition);
for (size_t attachId = 0; attachId < parsedFile.attachmentDirectory.entryCount; attachId++)
{
long attachPos = parsedFile.attachmentDirectory.entries[attachId].filePosition;
parsedFile.attachmentDirectory.entries[attachId].attachment = parse_attachment_segment(cziStream, attachPos, attachId);
}
SegmentHeader CziParser::parse_segment_header(BinaryStream &cziStream)
{
SegmentHeader result = {};
// 16 B for segment id.
result.sId = utf8bytes_to_string(cziStream.consume_bytes(16), 0, 16);
// 8 B for allocated size.
result.allocatedSize = cziStream.consume_long();
// 8 B for used size.
result.usedSize = cziStream.consume_long();
#if VERBOSE_PARSER
printf("Parsed segment header: %s.\n", result.sId.c_str());
#endif // VERBOSE_PARSER
FileHeaderSegment CziParser::parse_file_header(BinaryStream &cziStream)
result.header = parse_segment_header(cziStream);
assert(result.header.sId == FileHeaderSegmentSID);
result.fileVersion = {};
result.fileVersion.major = cziStream.consume_int();
result.fileVersion.minor = cziStream.consume_int();
// 8 next bytes are reserved and not used for anythint ATM.
cziStream.move_by(8);
result.masterFileGuid = cziStream.consume_bytes(16);
result.fileGuid = cziStream.consume_bytes(16);
result.filePart = cziStream.consume_int();
result.subBlockDirectoryPosition = cziStream.consume_long();
result.metadataPosition = cziStream.consume_long();
result.updatePending = cziStream.consume_bool(4);
result.attachmentDirectoryPosition = cziStream.consume_long();
#if VERBOSE_PARSER
printf("Parsed FileHeader segment\n");
#endif // VERBOSE_PARSER
MetadataSegment CziParser::parse_metadata(BinaryStream &cziStream, const long position)
assert(position > 0);
cziStream.move_to(position);
MetadataSegment result = {};
result.header = parse_segment_header(cziStream);
assert(result.header.sId == MetadataSegmentSID);
result.xmlSize = cziStream.consume_int();
result.attachmentSize = cziStream.consume_int();
// 248 B are spare, so not used atm?
// Skipping 248 spared bytes.
cziStream.move_by(248);
result.xmlString = utf8bytes_to_string(cziStream.consume_bytes(result.xmlSize));
//printf("%s\n", result.xmlString.c_str());
#if VERBOSE_PARSER
printf("Parsed Metadata, containing string of length: %li.\n", result.xmlString.size());
#endif // VERBOSE_PARSER
SubBlockDirectorySegment CziParser::parse_subblock_directory(BinaryStream &cziStream, const long position)
{
assert(position > 0);
cziStream.move_to(position);
result.header = parse_segment_header(cziStream);
assert(result.header.sId == SubBlockDirectorySegmentSID);
result.entryCount = cziStream.consume_int();
// 124 B are reserved, skipping.
cziStream.move_by(124);
result.entries.reserve(result.entryCount);
for (size_t entry = 0; entry < result.entryCount; entry++)
{
result.entries.push_back(parse_subblock_directory_entry(cziStream));
}
#if VERBOSE_PARSER
printf("Parsed SubBlockDirectory with %i entries.\n", result.entryCount);
#endif // VERBOSE_PARSER
return result;
}
DirectoryEntryDV CziParser::parse_subblock_directory_entry(BinaryStream &cziStream)
{
DirectoryEntryDV result = {};
result.schemaType = cziStream.consume_bytes(2);
assert(result.schemaType.size() == 2 && result.schemaType[0] == 'D' && result.schemaType[1] == 'V');
result.pixelType = to_pixel_type(cziStream.consume_int());
result.filePosition = cziStream.consume_long();
result.filePart = cziStream.consume_int();
result.compression = to_compression_type(cziStream.consume_int());
result.pyramidType = to_pyramid_type(cziStream.consume_byte());
// 5 next bytes are spare, reserved, skipping them.
cziStream.move_by(5);
result.dimensionCount = cziStream.consume_int();
assert(result.dimensionCount > 0);
result.dimensions.reserve(result.dimensionCount);
for (size_t dim = 0; dim < result.dimensionCount; dim++)
{
auto dimEntry = parse_dimension_entry(cziStream);
result.dimensions.push_back(dimEntry);
if (dimEntry.dimension == Dimension::X)
result.width = dimEntry.size;
if (dimEntry.dimension == Dimension::Y)
result.height = dimEntry.size;
#if VERBOSE_PARSER
printf("Parsed SubBlockDirectoryEntry with %i dimensions.\n", result.dimensionCount);
#endif // VERBOSE_PARSER
result.entrySize = 32 + (result.dimensionCount * DimensionEntryDV1Size);
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
return result;
}
AttachmentDirectorySegment CziParser::parse_attachment_directory(BinaryStream &cziStream, const long position)
{
assert(position > 0);
cziStream.move_to(position);
AttachmentDirectorySegment result = {};
result.header = parse_segment_header(cziStream);
assert(result.header.sId == AttachmentDirectorySegmentSID);
result.entryCount = cziStream.consume_int();
// Next 252 B are reserved.
cziStream.move_by(252);
result.entries.reserve(result.entryCount);
for (size_t entry = 0; entry < result.entryCount; entry++)
{
result.entries.push_back(parse_attachment_entry(cziStream));
}
#if VERBOSE_PARSER
printf("Parsed AttachmentDirectory with %i entries.\n", result.entryCount);
#endif // VERBOSE_PARSER
return result;
}
AttachmentEntryA1 CziParser::parse_attachment_entry(BinaryStream &cziStream)
{
AttachmentEntryA1 result = {};
result.schemaType = cziStream.consume_bytes(2);
assert(result.schemaType.size() == 2 && result.schemaType[0] == 'A' && result.schemaType[1] == '1');
// Next 10 bytes are reserved.
cziStream.move_by(10);
result.filePosition = cziStream.consume_long();
result.filePart = cziStream.consume_int();
result.contentGUID = cziStream.consume_bytes(16);
result.contentFileTypeBytes = cziStream.consume_bytes(8);
result.contentFileTypeString = utf8bytes_to_string(result.contentFileTypeBytes);
result.nameBytes = cziStream.consume_bytes(80);
result.name = utf8bytes_to_string(result.nameBytes);
#if VERBOSE_PARSER
printf("Parsed AttachmentEntry: %s - %s.\n", result.contentFileTypeString.c_str(), result.name.c_str());
#endif // VERBOSE_PARSER
return result;
}
DimensionEntryDV1 CziParser::parse_dimension_entry(BinaryStream &cziStream)
{
DimensionEntryDV1 result = {};
result.dimensionBytes = cziStream.consume_bytes(4);
result.dimension = to_dimension_type(result.dimensionBytes);
result.start = cziStream.consume_int();
result.size = cziStream.consume_int();
result.startCoordinate = cziStream.consume_float();
result.storedSize = cziStream.consume_int();
#if VERBOSE_PARSER
printf("Parsed DimensionEntry: %i.\n", result.dimension);
#endif // VERBOSE_PARSER
SubBlockSegment CziParser::parse_subblock(BinaryStream &cziStream, const long position, const int entryIndex, const int entrySize)
{
assert(position > 0);
cziStream.move_to(position);
SubBlockSegment result = {};
result.header = parse_segment_header(cziStream);
assert(result.header.sId == SubBlockSegmentSID);
result.metadataSize = cziStream.consume_int();
result.attachmentSize = cziStream.consume_int();
result.dataSize = cziStream.consume_long();
result.directoryEntryIndex = entryIndex;
// Metadata, Data, Attachments are offsetted by DirectoryEntrySize + Fill size;
int fillOffset = entrySize + 16;
int div = 256 - fillOffset;
int fillSize = (div > 0) ? div : 0;
int distance = entrySize + fillSize;
cziStream.move_by(distance);
auto metadataBytes = cziStream.consume_bytes(result.metadataSize);
result.metadataString = utf8bytes_to_string(metadataBytes);
// Data bytes
// For now we won't read image into memory, we will just save its location in file.
result.dataLocation = cziStream.get_position();
// Attachments bytes
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
result.attachmentLocation = result.dataLocation + result.dataSize;
#if VERBOSE_PARSER
printf("Parsed SubBlock, Metadata size: %i; data size: %li; Attachment size: %i\n", result.metadataSize, result.dataSize, result.attachmentSize);
#endif // VERBOSE_PARSER
return result;
}
AttachmentSegment CziParser::parse_attachment_segment(BinaryStream &cziStream, const long position, const int entryIndex)
{
assert(position > 0);
cziStream.move_to(position);
AttachmentSegment result = {};
result.directoryEntryIndex = entryIndex;
result.header = parse_segment_header(cziStream);
assert(result.header.sId == AttachmentSegmentSID);
result.dataSize = cziStream.consume_int();
// 12 B reserved next 128 B are for AttachmentEntry which is saved under 'entryIndex' in
// AttachmentDirectorySegment. After that, there are 112 B reserved.
const int dataOffset = 12 + 128 + 112;
result.dataLocation = cziStream.get_position() + dataOffset;
#if VERBOSE_PARSER
printf("Parsed AttachmentSegment. Attachment data size: %i.\n", result.dataSize);
#endif // VERBOSE_PARSER
return result;
}
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
PixelType CziParser::to_pixel_type(const int value)
{
PixelType result = static_cast<PixelType>(value);
switch (result)
{
case PixelType::Gray8:
case PixelType::Gray16:
case PixelType::Gray32Float:
case PixelType::Bgr24:
case PixelType::Bgr48:
case PixelType::Bgr96Float:
case PixelType::Bgra32:
case PixelType::Gray64ComplexFloat:
case PixelType::Bgr192ComplexFloat:
case PixelType::Gray32:
case PixelType::Gray64:
{
// Correct pixel types.
break;
}
default:
assert("Bad pixel type." && false);
break;
}
return result;
}
PyramidType CziParser::to_pyramid_type(const byte value)
{
PyramidType result = static_cast<PyramidType>(value);
switch (result)
{
case PyramidType::None:
case PyramidType::SingleSubBlock:
case PyramidType::MultiSubBlock:
break;
default:
{
assert("Bad pyramid type." && false);
break;
}
}
return result;
}
CompressionType CziParser::to_compression_type(const int value)
{
CompressionType result = static_cast<CompressionType>(value);
switch (result)
{
case CompressionType::Uncompressed:
case CompressionType::LZW:
case CompressionType::JpgFile:
case CompressionType::JpegXrFile:
case CompressionType::Camera:
case CompressionType::System:
break;
default:
{
assert("Bad compression type." && false);
break;
}
}
return result;
}
Dimension CziParser::to_dimension_type(const std::vector<byte> &bytes)
{
assert(bytes.size() == 4);
Dimension result = static_cast<Dimension>((char)bytes[0]);
switch (result)
{
case Dimension::X:
case Dimension::Y:
case Dimension::C:
case Dimension::Z:
case Dimension::T:
case Dimension::R:
case Dimension::S:
case Dimension::I:
case Dimension::B:
case Dimension::M:
case Dimension::H:
case Dimension::V:
break;
default:
{
assert("Bad dimension type." && false);
break;
}
}