Skip to content
Snippets Groups Projects
czi_parser.cpp 8.52 KiB
Newer Older
  • Learn to ignore specific revisions
  • theazgra's avatar
    theazgra committed
    #include "czi_parser.h"
    
    CziParser::CziParser()
    
    theazgra's avatar
    theazgra committed
    {
    
    }
    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);
    
    theazgra's avatar
    theazgra committed
    
    
        for (size_t entryId = 0; entryId < parsedFile.subBlockDirectory.entryCount; entryId++)
        {
            //TODO: Support multi-file scenario.
            int filePart = parsedFile.subBlockDirectory.entries[entryId].filePart;
            assert(filePart == 0 && "We are currently supporting only single-file scenario.");
            long subBlockPosition = parsedFile.subBlockDirectory.entries[entryId].filePosition;
            int entrySize = parsedFile.subBlockDirectory.entries[entryId].entrySize;
    
            parsedFile.subBlockDirectory.entries[entryId].subBlock = parse_subblock(cziStream, subBlockPosition, entryId, entrySize);
        }
    
    
        return parsedFile;
    
    }
    
    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();
    
        return result;
    }
    
    
    FileHeaderSegment CziParser::parse_file_header(BinaryStream &cziStream)
    
        FileHeaderSegment result;
    
        result.header = parse_segment_header(cziStream);
        assert(result.header.sId == "ZISRAWFILE");
    
    
        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();
    
        return result;
    }
    
    
    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();
    
        // 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());
        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++)
        {
    
            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;
    
        result.entrySize = 32 + (result.dimensionCount * DimensionEntryDV1Size);
    
        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();
    
        return result;
    }
    
    
    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 == "ZISRAWSUBBLOCK");
    
        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);
    
    
    theazgra's avatar
    theazgra committed
        // Metadata bytes
    
        auto metadataBytes = cziStream.consume_bytes(result.metadataSize);
    
        result.metadataString = utf8bytes_to_string(metadataBytes);
    
    theazgra's avatar
    theazgra committed
        //printf("%s\n", result.metadataString.c_str());
    
        // For now we won't read image into memory, we will just save its location in file.
        result.dataLocation = cziStream.get_position();
    
    
    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;
    
    theazgra's avatar
    theazgra committed
    }