diff --git a/README.md b/README.md index 0d688956d88c01c940486a4d7165cb51a3c6804d..6061b3880cf122e4f19c8d3071aed428b649b151 100644 --- a/README.md +++ b/README.md @@ -13,4 +13,6 @@ This is list of things, which have to be done first: - [ ] One master file and more *secondary files* files (our current `CziFile` class kinda support that situations, so keep going that way) Later on, we can extend our program to handle more things from the file, like: -- [ ] Parse metadata according to XML schemas \ No newline at end of file +- [ ] Parse metadata according to XML schemas +- [ ] Take a look on binary reader, can it be fastened up? +- [ ] Parse segments from memory buffer rather than from file stream. (*Disk bottleneck*) \ No newline at end of file diff --git a/czi-format/czi-parser/czi_file.cpp b/czi-format/czi-parser/czi_file.cpp index 4a2dd567c1fe3b48668e6e1c374fb5c104fed603..b754e68fd4169721d3f97846de4b844d5ed27687 100644 --- a/czi-format/czi-parser/czi_file.cpp +++ b/czi-format/czi-parser/czi_file.cpp @@ -16,19 +16,120 @@ void print_segment_header(const SegmentHeader &segmentHeader) printf("-------------------------------\n"); } -// void print_bytes(const std::vector<byte> &bytes) -// { -// std::stringstream ss; - -// for (const byte &b : bytes) -// { -// ss << (int)b; -// ss << "-"; -// //char a = (char)b; -// //printf("%x\n", b); -// } -// printf("%s\n", ss.str().c_str()); -// } +const char *pixel_type_str(const PixelType px) +{ + + switch (px) + { + case PixelType::Gray8: + return "Gray8"; + case PixelType::Gray16: + return "Gray16"; + case PixelType::Gray32Float: + return "Gray32Float"; + case PixelType::Bgr24: + return "Bgr24"; + case PixelType::Bgr48: + return "Bgr48"; + case PixelType::Bgr96Float: + return "Bgr96Float"; + case PixelType::Bgra32: + return "Bgra32"; + case PixelType::Gray64ComplexFloat: + return "Gray64ComplexFloat"; + case PixelType::Bgr192ComplexFloat: + return "Bgr192ComplexFloat"; + case PixelType::Gray32: + return "Gray32"; + case PixelType::Gray64: + return "Gray64"; + default: + assert("Bad pixel type." && false); + break; + } +} + +const char *pyramid_type_str(const PyramidType pt) +{ + switch (pt) + { + case PyramidType::None: + return "None"; + case PyramidType::SingleSubBlock: + return "SingleSubBlock"; + case PyramidType::MultiSubBlock: + return "MultiSubBlock"; + break; + default: + { + assert("Bad pyramid type." && false); + break; + } + } +} + +const char *compression_type_str(const CompressionType ct) +{ + switch (ct) + { + case CompressionType::Uncompressed: + return "Uncompressed"; + case CompressionType::LZW: + return "LZW"; + case CompressionType::JpgFile: + return "JpgFile"; + case CompressionType::JpegXrFile: + return "JpegXrFile"; + case CompressionType::Camera: + return "Camera"; + case CompressionType::System: + return "System"; + break; + default: + { + assert("Bad compression type." && false); + break; + } + } +} + +const char *dimension_type_str(const Dimension d) +{ + switch (d) + { + case Dimension::X: + return "X"; + case Dimension::Y: + return "Y"; + case Dimension::C: + return "C"; + case Dimension::Z: + return "Z"; + case Dimension::T: + return "T"; + case Dimension::R: + return "R"; + case Dimension::S: + return "S"; + case Dimension::I: + return "I"; + case Dimension::B: + return "B"; + case Dimension::M: + return "M"; + case Dimension::H: + return "H"; + case Dimension::V: + return "V"; + + break; + default: + { + assert("Bad dimension type." && false); + break; + } + } +} void CziFile::report() const { @@ -46,4 +147,32 @@ void CziFile::report() const printf("%-25s %15li\n", "MetadataPosition", header.metadataPosition); printf("%-25s %15li\n", "AttachmentDirPosition", header.attachmentDirectoryPosition); printf("===============================\n"); + + printf("==========SubBlock directory==========\n"); + print_segment_header(subBlockDirectory.header); + printf("%-25s %15i\n", "EntryCount", subBlockDirectory.entryCount); + + for (size_t entry = 0; entry < subBlockDirectory.entryCount; entry++) + { + printf("-------SubBlock directory entry %i-------\n", (int)entry); + printf("%-25s %15s\n", "PixelType", pixel_type_str(subBlockDirectory.entries[entry].pixelType)); + printf("%-25s %15li\n", "FilePosition", subBlockDirectory.entries[entry].filePosition); + printf("%-25s %15i\n", "FilePart", subBlockDirectory.entries[entry].filePart); + printf("%-25s %15s\n", "Compression", compression_type_str(subBlockDirectory.entries[entry].compression)); + printf("%-25s %15s\n", "PyramidType", pyramid_type_str(subBlockDirectory.entries[entry].pyramidType)); + printf("%-25s %15i\n", "DimensionCount", subBlockDirectory.entries[entry].dimensionCount); + + for (size_t dim = 0; dim < subBlockDirectory.entries[entry].dimensionCount; dim++) + { + printf("-------SubBlock %i dimension %i-------\n", (int)entry, (int)dim); + printf("%-25s %15s\n", "Dimension", dimension_type_str(subBlockDirectory.entries[entry].dimensions[dim].dimension)); + printf("%-25s %15i\n", "Start", subBlockDirectory.entries[entry].dimensions[dim].start); + printf("%-25s %15i\n", "Size", subBlockDirectory.entries[entry].dimensions[dim].size); + printf("%-25s %15i\n", "StoredSize", subBlockDirectory.entries[entry].dimensions[dim].storedSize); + } + + printf("-----------------------------------------\n"); + } + + printf("======================================\n"); } diff --git a/czi-format/czi-parser/czi_file.h b/czi-format/czi-parser/czi_file.h index eafbc9ad74fea6e9ee1c19d235c9c6832d4c803d..b135885e53f7101a9212d7257bd4f86383422ab9 100644 --- a/czi-format/czi-parser/czi_file.h +++ b/czi-format/czi-parser/czi_file.h @@ -6,6 +6,7 @@ struct CziFile std::string fileName; FileHeaderSegment header; MetadataSegment metadata; + SubBlockDirectory subBlockDirectory; bool is_master_file() const; void report() const; diff --git a/czi-format/czi-parser/czi_parser.cpp b/czi-format/czi-parser/czi_parser.cpp index e1f9d6530ffa276662bce2d8c46d796125b4ca28..eb4d96608e87ce11c9fa243a6733485a55495286 100644 --- a/czi-format/czi-parser/czi_parser.cpp +++ b/czi-format/czi-parser/czi_parser.cpp @@ -14,9 +14,8 @@ CziFile CziParser::parse_czi_file(const std::string &file) parsedFile.header = parse_file_header(cziStream); - assert(parsedFile.header.metadataPosition > 0); - cziStream.move_to(parsedFile.header.metadataPosition); - parsedFile.metadata = parse_metadata(cziStream); + parsedFile.metadata = parse_metadata(cziStream, parsedFile.header.metadataPosition); + parsedFile.subBlockDirectory = parse_subblock_directory(cziStream, parsedFile.header.subBlockDirectoryPosition); return parsedFile; } @@ -59,10 +58,15 @@ FileHeaderSegment CziParser::parse_file_header(BinaryStream &cziStream) return result; } -MetadataSegment CziParser::parse_metadata(BinaryStream &cziStream) +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 == "ZISRAWMETADATA"); + result.xmlSize = cziStream.consume_int(); result.attachmentSize = cziStream.consume_int(); @@ -71,6 +75,166 @@ MetadataSegment CziParser::parse_metadata(BinaryStream &cziStream) cziStream.move_by(248); result.xmlString = utf8bytes_to_string(cziStream.consume_bytes(result.xmlSize)); - printf("%s\n", result.xmlString.c_str()); + //printf("%s\n", result.xmlString.c_str()); + return result; +} + +SubBlockDirectory CziParser::parse_subblock_directory(BinaryStream &cziStream, const long position) +{ + assert(position > 0); + cziStream.move_to(position); + + SubBlockDirectory result = {}; + result.header = parse_segment_header(cziStream); + assert(result.header.sId == "ZISRAWDIRECTORY"); + + 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)); + } + + 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++) + { + result.dimensions.push_back(parse_dimension_entry(cziStream)); + } + + 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(); + //TODO: We are skipping float because we don't know how to parse it yet + cziStream.move_by(4); // result.startCoordinate = cziStream.consume_float(); + result.storedSize = cziStream.consume_int(); + + return result; +} + +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; + } + } return result; } \ No newline at end of file diff --git a/czi-format/czi-parser/czi_parser.h b/czi-format/czi-parser/czi_parser.h index e240c796d606babb96363a94c2c55ee69fe3930a..0de2731338c06e286a63b95533a1a46fdef01816 100644 --- a/czi-format/czi-parser/czi_parser.h +++ b/czi-format/czi-parser/czi_parser.h @@ -7,7 +7,14 @@ class CziParser private: SegmentHeader parse_segment_header(BinaryStream &cziStream); FileHeaderSegment parse_file_header(BinaryStream &cziStream); - MetadataSegment parse_metadata(BinaryStream &cziStream); + MetadataSegment parse_metadata(BinaryStream &cziStream, const long position); + SubBlockDirectory parse_subblock_directory(BinaryStream &cziStream, const long position); + DirectoryEntryDV parse_subblock_directory_entry(BinaryStream &cziStream); + DimensionEntryDV1 parse_dimension_entry(BinaryStream &cziStream); + PixelType to_pixel_type(const int value); + PyramidType to_pyramid_type(const byte value); + CompressionType to_compression_type(const int value); + Dimension to_dimension_type(const std::vector<byte> &bytes); public: CziParser(); diff --git a/czi-format/czi-parser/czi_parts/dimension.h b/czi-format/czi-parser/czi_parts/dimension.h index 68d841e59d7cc24b4cd8fa18c7116957dae7fc09..873ff5575b13176f3b01b9104954008158178221 100644 --- a/czi-format/czi-parser/czi_parts/dimension.h +++ b/czi-format/czi-parser/czi_parts/dimension.h @@ -2,27 +2,27 @@ enum Dimension { // Pixel index / offset in the X direction. Used for tiled images. - X, + X = 'X', // Pixel index / offset in the Y direction. Used for tiled images. - Y, + Y = 'Y', // Channel in a Multi-Channel data set. - C, + C = 'C', // Slice index (Z – direction). - Z, + Z = 'Z', // Time point in a sequentially acquired series of data. - T, + T = 'T', // Rotation – used in acquisition modes where the data is recorded from various angles. - R, + R = 'R', // Scene – for clustering items in X/Y direction (data belonging to contiguous regions of interests in a mosaic image). - S, + S = 'S', // Illumination - illumination direction index (e.g. from left=0, from right=1). - I, + I = 'I', // (Acquisition) Block index in segmented experiments. Note: This index has been dropped. Instead of the B index multiple single CZI images will be generated when saving segmented experiments. - B, + B = 'B', // Mosaic tile index – this index uniquely identifies all tiles in a specific plane. - M, + M = 'M', // Phase index – for specific acquisition methods. - H, + H = 'H', // View index (for multi – view images, e.g. SPIM) - V + V = 'V' }; \ No newline at end of file diff --git a/czi-format/czi-parser/czi_parts/directory_entry_dv.h b/czi-format/czi-parser/czi_parts/directory_entry_dv.h index d4ac1681763a790b55701dab6d0e1474327b2c3d..32ca08ffbb6a539ac14e16f5b286a042113380a5 100644 --- a/czi-format/czi-parser/czi_parts/directory_entry_dv.h +++ b/czi-format/czi-parser/czi_parts/directory_entry_dv.h @@ -8,20 +8,14 @@ struct DirectoryEntryDV { // Probably constant 'DV' ? std::vector<byte> schemaType; - // Parsed pixel type value. - int pixelTypeValue; // The type of the image pixels. PixelType pixelType; // Position of this subblock in file. long filePosition; // Reserved? int filePart; - // Parsed compression value; - int CompressionValue; // Compression applied to image. CompressionType compression; - // Contains information for automatic image pyramids using SubBlocks of different resolution, current values are: None = 0, SingleSubblock = 1, MultiSubblock = 2 - byte pyramidValue; // Type of image pyramid. PyramidType pyramidType; // Number of dimension entries. Minimally one. diff --git a/czi-format/czi-parser/czi_parts/segments.h b/czi-format/czi-parser/czi_parts/segments.h index e9f17268d6201666802eb59855fdeb699270b6e4..81f2e2000b4eded1844c95d6b567d6f51dad5df6 100644 --- a/czi-format/czi-parser/czi_parts/segments.h +++ b/czi-format/czi-parser/czi_parts/segments.h @@ -2,4 +2,5 @@ // Agregation of all segments. #include "file_header_segment.h" -#include "metadata_segment.h" \ No newline at end of file +#include "metadata_segment.h" +#include "subblock_directory.h" \ No newline at end of file diff --git a/czi-format/czi-parser/czi_parts/subblock_directory.h b/czi-format/czi-parser/czi_parts/subblock_directory.h new file mode 100644 index 0000000000000000000000000000000000000000..1efd91be00ef0add595a7a6c944d880013678923 --- /dev/null +++ b/czi-format/czi-parser/czi_parts/subblock_directory.h @@ -0,0 +1,13 @@ +#pragma once +#include "segment_header.h" +#include "directory_entry_dv.h" + +struct SubBlockDirectory +{ + // SubBlock directory segment header. + SegmentHeader header; + // Number of entries in directory. + int entryCount; + // Collection of DirectoryEntryDV, they are the exact same copy as in SubBlock segment. + std::vector<DirectoryEntryDV> entries; +}; \ No newline at end of file diff --git a/czi-format/czi-parser/czi_parts/sub_block_segment.h b/czi-format/czi-parser/czi_parts/subblock_segment.h similarity index 100% rename from czi-format/czi-parser/czi_parts/sub_block_segment.h rename to czi-format/czi-parser/czi_parts/subblock_segment.h