Skip to content
Snippets Groups Projects
czi_file.cpp 13.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • theazgra's avatar
    theazgra committed
    #include "czi_file.h"
    
    
    bool CziFile::is_master_file() const
    
    theazgra's avatar
    theazgra committed
    {
    
        return ((header.filePart == 0) && (header.fileGuid == header.masterFileGuid));
    
    theazgra's avatar
    theazgra committed
    }
    
    
    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
    
    theazgra's avatar
    theazgra committed
        case PixelType_Gray8:
    
            return "Gray8";
    
    theazgra's avatar
    theazgra committed
        case PixelType_Gray16:
    
            return "Gray16";
    
    theazgra's avatar
    theazgra committed
        case PixelType_Gray32Float:
    
            return "Gray32Float";
    
    theazgra's avatar
    theazgra committed
        case PixelType_Bgr24:
    
            return "Bgr24";
    
    theazgra's avatar
    theazgra committed
        case PixelType_Bgr48:
    
            return "Bgr48";
    
    theazgra's avatar
    theazgra committed
        case PixelType_Bgr96Float:
    
            return "Bgr96Float";
    
    theazgra's avatar
    theazgra committed
        case PixelType_Bgra32:
    
            return "Bgra32";
    
    theazgra's avatar
    theazgra committed
        case PixelType_Gray64ComplexFloat:
    
            return "Gray64ComplexFloat";
    
    theazgra's avatar
    theazgra committed
        case PixelType_Bgr192ComplexFloat:
    
            return "Bgr192ComplexFloat";
    
    theazgra's avatar
    theazgra committed
        case PixelType_Gray32:
    
            return "Gray32";
    
    theazgra's avatar
    theazgra committed
        case PixelType_Gray64:
    
            return "Gray64";
        default:
    
            always_assert("Bad pixel type." && false);
    
    const char *CziFile::pyramid_type_str(const PyramidType pt) const
    
    theazgra's avatar
    theazgra committed
        case PyramidType_None:
    
    theazgra's avatar
    theazgra committed
        case PyramidType_SingleSubBlock:
    
            return "SingleSubBlock";
    
    theazgra's avatar
    theazgra committed
        case PyramidType_MultiSubBlock:
    
            return "MultiSubBlock";
            break;
        default:
        {
    
            always_assert("Bad pyramid type." && false);
    
    const char *CziFile::compression_type_str(const CompressionType ct) const
    
    theazgra's avatar
    theazgra committed
        case CompressionType_Uncompressed:
    
            return "Uncompressed";
    
    theazgra's avatar
    theazgra committed
        case CompressionType_LZW:
    
    theazgra's avatar
    theazgra committed
        case CompressionType_JpgFile:
    
            return "JpgFile";
    
    theazgra's avatar
    theazgra committed
        case CompressionType_JpegXrFile:
    
            return "JpegXrFile";
    
    theazgra's avatar
    theazgra committed
        case CompressionType_Camera:
    
            return "Camera";
    
    theazgra's avatar
    theazgra committed
        case CompressionType_System:
    
            return "System";
            break;
        default:
        {
    
            always_assert("Bad compression type." && false);
    
    const char *CziFile::dimension_type_str(const Dimension &d) const
    
    theazgra's avatar
    theazgra committed
        case Dimension_X:
    
    theazgra's avatar
    theazgra committed
        case Dimension_Y:
    
    theazgra's avatar
    theazgra committed
        case Dimension_C:
    
            return "C (channels)";
    
    theazgra's avatar
    theazgra committed
        case Dimension_Z:
    
            return "Z (Z-slices)";
    
    theazgra's avatar
    theazgra committed
        case Dimension_T:
    
            return "T (timestamps)";
    
    theazgra's avatar
    theazgra committed
        case Dimension_R:
    
            return "R (rotation)";
    
    theazgra's avatar
    theazgra committed
        case Dimension_S:
    
    theazgra's avatar
    theazgra committed
        case Dimension_I:
    
            return "I (illumination)";
    
    theazgra's avatar
    theazgra committed
        case Dimension_B:
    
            return "B (block index)";
    
    theazgra's avatar
    theazgra committed
        case Dimension_M:
    
    theazgra's avatar
    theazgra committed
        case Dimension_H:
    
    theazgra's avatar
    theazgra committed
        case Dimension_V:
    
            always_assert("Bad dimension type." && false);
    
    const char *CziFile::dimension_char(const Dimension &d) const
    
    theazgra's avatar
    theazgra committed
    {
    
    theazgra's avatar
    theazgra committed
        switch (d)
        {
    
    theazgra's avatar
    theazgra committed
        case Dimension_X:
    
    theazgra's avatar
    theazgra committed
            return "X";
    
    theazgra's avatar
    theazgra committed
        case Dimension_Y:
    
    theazgra's avatar
    theazgra committed
            return "Y";
    
    theazgra's avatar
    theazgra committed
        case Dimension_C:
    
    theazgra's avatar
    theazgra committed
            return "C";
    
    theazgra's avatar
    theazgra committed
        case Dimension_Z:
    
    theazgra's avatar
    theazgra committed
            return "Z";
    
    theazgra's avatar
    theazgra committed
        case Dimension_T:
    
    theazgra's avatar
    theazgra committed
            return "T";
    
    theazgra's avatar
    theazgra committed
        case Dimension_R:
    
    theazgra's avatar
    theazgra committed
            return "R";
    
    theazgra's avatar
    theazgra committed
        case Dimension_S:
    
    theazgra's avatar
    theazgra committed
            return "S";
    
    theazgra's avatar
    theazgra committed
        case Dimension_I:
    
    theazgra's avatar
    theazgra committed
            return "I";
    
    theazgra's avatar
    theazgra committed
        case Dimension_B:
    
    theazgra's avatar
    theazgra committed
            return "B";
    
    theazgra's avatar
    theazgra committed
        case Dimension_M:
    
    theazgra's avatar
    theazgra committed
            return "M";
    
    theazgra's avatar
    theazgra committed
        case Dimension_H:
    
    theazgra's avatar
    theazgra committed
            return "H";
    
    theazgra's avatar
    theazgra committed
        case Dimension_V:
    
    theazgra's avatar
    theazgra committed
            return "V";
    
    theazgra's avatar
    theazgra committed
        default:
        {
            always_assert("Bad dimension type." && false);
            break;
        }
        }
    
    theazgra's avatar
    theazgra committed
        return nullptr;
    
    theazgra's avatar
    theazgra committed
    std::string CziFile::dimension_stack_str(const std::vector<DimensionEntryDV1> &dims, bool includeSize) const
    
    theazgra's avatar
    theazgra committed
    {
    
        std::string result;
        for (const DimensionEntryDV1 &entry : dims)
        {
    
    theazgra's avatar
    theazgra committed
            result.append(dimension_char(entry.dimension));
            // + std::string(includeSize ? () : ""));
            if (includeSize)
                result.append("(" + std::to_string(entry.size) + ")-");
        }
    
        if (includeSize)
        {
            result = result.substr(0, result.length() - 1);
    
    theazgra's avatar
    theazgra committed
        }
    
    theazgra's avatar
    theazgra committed
        return result;
    
    theazgra's avatar
    theazgra committed
    
    
    theazgra's avatar
    theazgra committed
        // return (includeSize ?: result).c_str();
    
    theazgra's avatar
    theazgra committed
    }
    
    void CziFile::report_verbose() const
    {
    
    theazgra's avatar
    theazgra committed
        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++)
        {
    
    theazgra's avatar
    theazgra committed
            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),
    
    theazgra's avatar
    theazgra committed
                   dimension_stack_str(subBlockDirectory.entries[entry].dimensions, true).c_str(),
    
    theazgra's avatar
    theazgra committed
                   pyramid_type_str(subBlockDirectory.entries[entry].pyramidType));
    
        }
    
        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;
    }
    
    
    theazgra's avatar
    theazgra committed
    void CziFile::report() const
    
    {
        printf("Loaded CZI FILE: %s\n", fileName.c_str());
    
    
    theazgra's avatar
    theazgra committed
        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", vecUtil::vector_eq(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);
    
    theazgra's avatar
    theazgra committed
        printf("%-25s %15i\n", "AttachmentCount", attachmentDirectory.entryCount);
    
    
    theazgra's avatar
    theazgra committed
        if (subBlockDirectory.entryCount > 0)
    
    theazgra's avatar
    theazgra committed
            {
                std::map<PixelType, int> pixelTypeMap;
                std::map<CompressionType, int> compressionMap;
                std::map<std::string, int> dimensionMap;
    
    theazgra's avatar
    theazgra committed
                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));
        }
    
    theazgra's avatar
    theazgra committed
    
        printf("==================================\n");
    
    ImageMatrix CziFile::get_image(const uint subblockId) const
    
    {
        // // subblockId is in bounds.
        // always_assert((uint)subblockId < subBlockDirectory.entries.size());
        // // Image is not empty.
        // always_assert(entry.width > 0 && entry.height > 0);
        // // Read image bytes.
        // std::vector<byte> imageBytes;
        // {
        //     BinaryFileStream cziStream(fileName);
        //     imageBytes = cziStream.move_and_consume_bytes(entry.subBlock.dataLocation, entry.subBlock.dataSize);
        // }
    
        auto imageBytes = get_image_data(subblockId, false);
        auto entry = subBlockDirectory.entries[subblockId];
    
        ImageMatrix image = ImageMatrix((uint)entry.width, (uint)entry.height, entry.pixelType, imageBytes);
        return image;
    }
    
    std::vector<byte> CziFile::get_image_data(const uint subblockId, const bool ZCurveOrdered) const
    
    theazgra's avatar
    theazgra committed
        // subblockId is in bounds.
    
    theazgra's avatar
    theazgra committed
        always_assert((uint)subblockId < subBlockDirectory.entries.size());
        auto entry = subBlockDirectory.entries[subblockId];
    
    theazgra's avatar
    theazgra committed
        // Image is not empty.
    
    theazgra's avatar
    theazgra committed
        always_assert(entry.width > 0 && entry.height > 0);
    
    theazgra's avatar
    theazgra committed
        // Read image bytes.
        std::vector<byte> imageBytes;
        {
            BinaryFileStream cziStream(fileName);
            imageBytes = cziStream.move_and_consume_bytes(entry.subBlock.dataLocation, entry.subBlock.dataSize);
        }
    
        // Check if read data are correct and not corrupted.
        int bytesPerPixel = get_bytes_per_pixel_type(entry.pixelType);
        ulong expectedSize = entry.width * entry.height * bytesPerPixel;
        always_assert(expectedSize == imageBytes.size());
    
        if (ZCurveOrdered)
        {
            auto zIndices = generate_ordered_z_order_indices(entry.width, entry.height);
            always_assert(zIndices.size() == (ulong)(entry.width * entry.height));
    
            std::vector<byte> zOrderedBytes = reoder_bytes_to_z_order(imageBytes, zIndices, bytesPerPixel);
            return zOrderedBytes;
        }
        else
        {
            return imageBytes;
        }
    
    void CziFile::differences_between_next(const std::string baseName) const
    
    {
        if (subBlockDirectory.entries.size() <= 1)
        {
            printf("There aren't 2 different images in this CZI file.\n");
            return;
        }
    
        std::string fName;
    
        ImageMatrix ref = get_image(0);
    
        for (size_t i = 1; i < subBlockDirectory.entries.size(); i++)
        {
            fName = baseName + "diff_" + std::to_string(i - 1) + "_" + std::to_string(i) + ".ppm";
    
            ImageMatrix next = get_image(i);
            ImageMatrix diff_ref_next = ref.create_difference_matrix(next);
    
            diff_ref_next.save_as_ppm(fName.c_str());
    
    
    void CziFile::extract_images(const std::string &baseName) const
    {
        std::string fName;
        for (size_t i = 0; i < subBlockDirectory.entries.size(); i++)
        {
            auto subblock = get_image(i);
            fName = baseName + "subblock_" + std::to_string(i) + ".ppm";
            subblock.save_as_ppm(fName.c_str());
        }
    }
    
    
    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";
    
    
    theazgra's avatar
    theazgra committed
            auto imageBytes = cziStream.move_and_consume_bytes(entry.subBlock.dataLocation, entry.subBlock.dataSize);
    
    
            write_bytes_to_file(imageBytes, binaryFileName.c_str());
    
    theazgra's avatar
    theazgra committed
    
    
            printf("Wrote %s\n", binaryFileName.c_str());
        }
    }
    
    theazgra's avatar
    theazgra committed
    
    void CziFile::test_rle_encode() const
    {
        float dataSize, dataZSize, rleDataSize, rleDataZSize, ratio, ratioZ;
        float overall = 0;
        float overallZ = 0;
        for (size_t i = 0; i < subBlockDirectory.entries.size(); i++)
        {
            auto data = get_image_data(i, false);
            auto dataZ = get_image_data(i, true);
    
            auto rle_data = rle_encode(data);
            auto rle_dataZ = rle_encode(dataZ);
    
            dataSize = (float)data.size();
            dataZSize = (float)dataZ.size();
            rleDataSize = (float)rle_data.size();
            rleDataZSize = (float)rle_dataZ.size();
    
            ratio = compression_ratio(dataSize, rleDataSize);
            ratioZ = compression_ratio(dataZSize, rleDataZSize);
    
            overall += ratio;
            overallZ += ratioZ;
    
            printf("Subblock %-3i Compression ratios: Normal: %8f Z-Ordered: %8f\n", (int)i, ratio, ratioZ);
        }
        float dataCount = (float)subBlockDirectory.entries.size();
        overall /= dataCount;
        overallZ /= dataCount;
    
        printf("Overall Normal %8f Z-Ordered: %8f\n", overall, overallZ);
    }