Skip to content
Snippets Groups Projects
czi_file.cpp 11.84 KiB
#include "czi_file.h"

bool CziFile::is_master_file() const
{
    return ((header.filePart == 0) && (header.fileGuid == header.masterFileGuid));
}

void CziFile::print_segment_header(const SegmentHeader &segmentHeader) const
{
    printf("---------SegmentHeader---------\n");
    printf("%-25s %15s\n", "SID", segmentHeader.sId.c_str());
    printf("%-25s %15li\n", "AllocatedSize", segmentHeader.allocatedSize);
    printf("%-25s %15li\n", "UsedSize", segmentHeader.usedSize);
    printf("-------------------------------\n");
}

const char *CziFile::pixel_type_str(const PixelType px) const
{

    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:
        always_assert("Bad pixel type." && false);
        break;
    }

    return nullptr;
}

const char *CziFile::pyramid_type_str(const PyramidType pt) const
{
    switch (pt)
    {
    case PyramidType::None:
        return "None";
    case PyramidType::SingleSubBlock:
        return "SingleSubBlock";
    case PyramidType::MultiSubBlock:
        return "MultiSubBlock";
        break;
    default:
    {
        always_assert("Bad pyramid type." && false);
        break;
    }
    }
    return nullptr;
}

const char *CziFile::compression_type_str(const CompressionType ct) const
{
    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:
    {
        always_assert("Bad compression type." && false);
        break;
    }
    }
    return nullptr;
}

const char *CziFile::dimension_type_str(const Dimension &d) const
{
    switch (d)
    {
    case Dimension::X:
        return "X (width)";
    case Dimension::Y:
        return "Y (height)";
    case Dimension::C:
        return "C (channels)";
    case Dimension::Z:
        return "Z (Z-slices)";
    case Dimension::T:
        return "T (timestamps)";
    case Dimension::R:
        return "R (rotation)";
    case Dimension::S:
        return "S (scene)";
    case Dimension::I:
        return "I (illumination)";
    case Dimension::B:
        return "B (block index)";
    case Dimension::M:
        return "M (mosaic)";
    case Dimension::H:
        return "H (phase)";
    case Dimension::V:
        return "V (views)";

        break;
    default:
    {
        always_assert("Bad dimension type." && false);
        break;
    }
    }
    return nullptr;
}

const char *CziFile::dimension_char(const Dimension &d) const
{
    std::string result;
    switch (d)
    {
    case Dimension::X:
        result = "X";
        break;
    case Dimension::Y:
        result = "Y";
        break;
    case Dimension::C:
        result = "C";
        break;
    case Dimension::Z:
        result = "Z";
        break;
    case Dimension::T:
        result = "T";
        break;
    case Dimension::R:
        result = "R";
        break;
    case Dimension::S:
        result = "S";
        break;
    case Dimension::I:
        result = "I";
        break;
    case Dimension::B:
        result = "B";
        break;
    case Dimension::M:
        result = "M";
        break;
    case Dimension::H:
        result = "H";
        break;
    case Dimension::V:
        result = "V";
        break;
    default:
    {
        always_assert("Bad dimension type." && false);
        break;
    }
    }
    return result.c_str();
}

const char *CziFile::dimension_stack_str(const std::vector<DimensionEntryDV1> &dims, bool includeSize) const
{

    std::string result;
    for (const DimensionEntryDV1 &entry : dims)
    {
        result.append(dimension_char(entry.dimension) + (includeSize ? ("(" + std::to_string(entry.size) + ")-") : ""));
    }

    return (includeSize ? result.substr(0, result.length() - 1) : result).c_str();
}

void CziFile::report_verbose() const
{
    report();

    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("SubBlockId: %i\tPixelType: %s\tCompression: %s\tDimensionStack: %s\tPyramidType: %s\n",
               (int)entry,
               pixel_type_str(subBlockDirectory.entries[entry].pixelType),
               compression_type_str(subBlockDirectory.entries[entry].compression),
               dimension_stack_str(subBlockDirectory.entries[entry].dimensions, true),
               pyramid_type_str(subBlockDirectory.entries[entry].pyramidType));

        // 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");
}

std::string byte_array_str(const std::vector<byte> &bytes)
{
    std::string result = "";

    for (size_t i = 0; i < bytes.size(); i++)
    {
        result.append(std::to_string(bytes[i]));
        if (i % 4 == 0 && i != bytes.size() - 1)
            result.append("-");
    }
    return result;
}

void CziFile::report() const
{
    printf("Loaded CZI FILE: %s\n", fileName.c_str());

    printf("==========Summary report==========\n");
    print_segment_header(header.header);
    printf("%-25s %13i.%i\n", "FileVersion", header.fileVersion.major, header.fileVersion.minor);

    printf("%-25s %15i\n", "FilePart", header.filePart);

    printf("%-25s %30s\n", "MasterGUID", byte_array_str(header.masterFileGuid).c_str());
    printf("%-25s %30s\n", "FileGUID", byte_array_str(header.masterFileGuid).c_str());

    //TODO: Replace with GUID print.
    printf("%-25s %15i\n", "GUIDs are matching", are_same_vectors(header.masterFileGuid, header.fileGuid));
    printf("%-25s %15i\n", "UpdatePending", header.updatePending);
    printf("%-25s %15li\n", "SubblockDirPosition", header.subBlockDirectoryPosition);
    printf("%-25s %15li\n", "MetadataPosition", header.metadataPosition);
    printf("%-25s %15li\n", "AttachmentDirPosition", header.attachmentDirectoryPosition);
    printf("%-25s %15i\n", "SubBlockCount", subBlockDirectory.entryCount);

    printf("%-25s %15i\n", "AttachmentCount", attachmentDirectory.entryCount);

    if (subBlockDirectory.entryCount > 0)
    {
        {
            std::map<PixelType, int> pixelTypeMap;
            std::map<CompressionType, int> compressionMap;
            std::map<std::string, int> dimensionMap;

            for (size_t sbId = 0; sbId < subBlockDirectory.entryCount; sbId++)
            {
                const DirectoryEntryDV entry = subBlockDirectory.entries[sbId];

                if (pixelTypeMap.count(entry.pixelType))
                    pixelTypeMap[entry.pixelType]++;
                else
                    pixelTypeMap.emplace(std::make_pair(entry.pixelType, 1));

                if (compressionMap.count(entry.compression))
                    compressionMap[entry.compression]++;
                else
                    compressionMap.emplace(std::make_pair(entry.compression, 1));

                std::string dimStackStr = dimension_stack_str(entry.dimensions, false);
                if (dimensionMap.count(dimStackStr))
                    dimensionMap[dimStackStr]++;
                else
                    dimensionMap.emplace(std::make_pair(dimStackStr, 1));
            }

            printf("Pixel type distribution:\n");
            for (auto &&ptPair : pixelTypeMap)
            {
                printf("\t%s %ix\n", pixel_type_str(ptPair.first), ptPair.second);
            }
            printf("Compression type distribution:\n");
            for (auto &&ctPair : compressionMap)
            {
                printf("\t%s %ix\n", compression_type_str(ctPair.first), ctPair.second);
            }
            printf("Dimension stack  distribution:\n");
            for (auto &&dimPair : dimensionMap)
            {
                printf("\t%s %ix\n", dimPair.first.c_str(), dimPair.second);
            }
        }

        //printf("%-25s %15i\n", "DimensionCount", subBlockDirectory.entries[0].dimensionCount);
        //printf("%-25s %15s\n", "PixelType", pixel_type_str(subBlockDirectory.entries[0].pixelType));
    }

    printf("==================================\n");
}

std::vector<byte> CziFile::get_image_data(const int subblockId)
{
    always_assert((uint)subblockId < subBlockDirectory.entries.size());
    auto entry = subBlockDirectory.entries[subblockId];
    always_assert(entry.width > 0 && entry.height > 0);

    BinaryFileStream cziStream(fileName);
    return cziStream.move_and_consume_bytes(entry.subBlock.dataLocation, entry.subBlock.dataSize);
}

void CziFile::extract_images(const std::string &baseName) const
{
    BinaryFileStream cziStream(fileName);
    int imageIndex = 0;
    for (const DirectoryEntryDV &entry : subBlockDirectory.entries)
    {
        always_assert(entry.width > 0 && entry.height > 0);

        auto imageBytes = cziStream.move_and_consume_bytes(entry.subBlock.dataLocation, entry.subBlock.dataSize);
        std::string imageFileName = baseName + std::to_string(imageIndex++) + ".pnm";
        create_image_file(imageFileName.c_str(), imageBytes, entry.pixelType, entry.width, entry.height);
    }
}
void CziFile::dump_image_data(const std::string &baseName) const
{
    BinaryFileStream cziStream(fileName);
    int imageIndex = 0;
    for (const DirectoryEntryDV &entry : subBlockDirectory.entries)
    {
        always_assert(entry.width > 0 && entry.height > 0);
        std::string binaryFileName = baseName + std::to_string(imageIndex++) + ".bin";

        auto imageBytes = cziStream.move_and_consume_bytes(entry.subBlock.dataLocation, entry.subBlock.dataSize);

        std::ofstream binaryStream(binaryFileName, std::ios::binary | std::ios::out);
        always_assert(binaryStream.is_open());
        binaryStream.write(reinterpret_cast<const char *>(imageBytes.data()), imageBytes.size());

        printf("Wrote %s\n", binaryFileName.c_str());
    }
}