From f0e32d77ec6f9d294b62153c52ea9e9c84cd9ca3 Mon Sep 17 00:00:00 2001
From: theazgra <theazgra@gmail.com>
Date: Fri, 8 Mar 2019 11:48:59 +0100
Subject: [PATCH] Small refactoring and start of image difference.

---
 czi-format/czi-parser/binary_converter.cpp    |  71 +++++--
 czi-format/czi-parser/binary_converter.h      |   7 +-
 .../czi-parser/compression/compressors.h      |  31 +--
 czi-format/czi-parser/custom_types.h          |   1 +
 czi-format/czi-parser/czi_file.cpp            | 192 ++++++------------
 czi-format/czi-parser/czi_file.h              |   9 +-
 .../czi-parser/czi_parts/compression_type.h   |  28 ++-
 czi-format/czi-parser/czi_parts/dimension.h   |  81 +++++++-
 czi-format/czi-parser/czi_parts/pixel_type.h  |  98 +++++++++
 .../czi-parser/czi_parts/pyramid_type.h       |  22 +-
 czi-format/czi-parser/image/image_matrix.cpp  |   2 +-
 czi-format/czi-parser/image/image_matrix.h    |   2 +-
 .../czi-parser/image/pixel_structures.h       | 100 +--------
 czi-format/czi-parser/main.cpp                |  15 +-
 .../czi-parser/utilities/vector_utilities.h   |  17 +-
 15 files changed, 404 insertions(+), 272 deletions(-)

diff --git a/czi-format/czi-parser/binary_converter.cpp b/czi-format/czi-parser/binary_converter.cpp
index b723cb7..81200fd 100644
--- a/czi-format/czi-parser/binary_converter.cpp
+++ b/czi-format/czi-parser/binary_converter.cpp
@@ -6,13 +6,13 @@
 #include "binary_converter.h"
 short bytes_to_short(const ByteArray &bytes, const ulong fromIndex)
 {
-    always_assert(bytes.size() >= 2);
-    always_assert(fromIndex <= bytes.size() - 2);
+    always_assert(bytes.size() >= sizeof(short));
+    always_assert(fromIndex <= bytes.size() - sizeof(short));
 
     // Little endian
     auto bit = &bytes[fromIndex];
 
-    if (fromIndex % 2 == 0)
+    if (fromIndex % 2 == sizeof(short))
     {
         return (*((short *)bit));
     }
@@ -24,13 +24,13 @@ short bytes_to_short(const ByteArray &bytes, const ulong fromIndex)
 
 int bytes_to_int(const ByteArray &bytes, const ulong fromIndex)
 {
-    always_assert(bytes.size() >= 4);
-    always_assert(fromIndex <= bytes.size() - 4);
+    always_assert(bytes.size() >= sizeof(int));
+    always_assert(fromIndex <= bytes.size() - sizeof(int));
 
     // Little endian
     auto bit = &bytes[fromIndex];
 
-    if (fromIndex % 4 == 0)
+    if (fromIndex % sizeof(int) == 0)
     {
         return (*((int *)bit));
     }
@@ -42,12 +42,12 @@ int bytes_to_int(const ByteArray &bytes, const ulong fromIndex)
 
 int bytes_to_long(const ByteArray &bytes, const ulong fromIndex)
 {
-    always_assert(bytes.size() >= 8);
-    always_assert(fromIndex <= bytes.size() - 8);
+    always_assert(bytes.size() >= sizeof(long));
+    always_assert(fromIndex <= bytes.size() - sizeof(long));
 
     // Little endian
     auto bit = &bytes[fromIndex];
-    if (fromIndex % 8 == 0)
+    if (fromIndex % sizeof(long) == 0)
     {
         return (*((long *)bit));
     }
@@ -61,21 +61,21 @@ int bytes_to_long(const ByteArray &bytes, const ulong fromIndex)
 
 float bytes_to_float(const ByteArray &bytes, const ulong fromIndex)
 {
-    always_assert(bytes.size() >= 4);
-    always_assert(fromIndex <= bytes.size() - 4);
+    always_assert(bytes.size() >= sizeof(float));
+    always_assert(fromIndex <= bytes.size() - sizeof(float));
 
     float result;
-    memcpy(&result, (bytes.data() + fromIndex), 4);
+    memcpy(&result, (bytes.data() + fromIndex), sizeof(float));
     return result;
 }
 
 double bytes_to_double(const ByteArray &bytes, const ulong fromIndex)
 {
-    always_assert(bytes.size() >= 8);
-    always_assert(fromIndex <= bytes.size() - 8);
+    always_assert(bytes.size() >= sizeof(double));
+    always_assert(fromIndex <= bytes.size() - sizeof(double));
 
     double result;
-    memcpy(&result, (bytes.data() + fromIndex), 8);
+    memcpy(&result, (bytes.data() + fromIndex), sizeof(double));
     return result;
 }
 
@@ -107,4 +107,45 @@ std::string utf8bytes_to_string(const ByteArray &bytes, const ulong fromIndex, c
     ByteArray stringBytes(fromIt, fromIt + byteCount);
     std::string result = boost::locale::conv::from_utf<char>((char *)stringBytes.data(), "UTF-8");
     return result;
+}
+
+ByteArray int_to_bytes(const int &i)
+{
+    ByteArray intBytes;
+    intBytes.resize(sizeof(int));
+
+    std::memcpy(intBytes.data(), &i, sizeof(int));
+    return intBytes;
+}
+
+void int_to_bytes(const int &i, ByteArray &buffer, size_t bufferPosition)
+{
+    always_assert(buffer.size() - bufferPosition >= sizeof(int));
+    std::memcpy((buffer.data() + bufferPosition), &i, sizeof(int));
+}
+
+std::vector<ushort> bytes_to_ushort_array(const ByteArray &data)
+{
+    std::vector<ushort> result;
+    result.resize(data.size() / sizeof(ushort));
+
+    size_t index = 0;
+    for (size_t i = 0; i < data.size(); i += 2)
+    {
+        result[index++] = bytes_to_ushort(data, i);
+    }
+
+    return result;
+}
+
+ByteArray int_array_to_bytes(const std::vector<int> &data)
+{
+    ByteArray result;
+    result.resize(data.size() * sizeof(int));
+
+    for (size_t i = 0; i < data.size(); i++)
+    {
+        int_to_bytes(data[i], result, i * sizeof(int));
+    }
+    return result;
 }
\ No newline at end of file
diff --git a/czi-format/czi-parser/binary_converter.h b/czi-format/czi-parser/binary_converter.h
index 91bbb97..8945f88 100644
--- a/czi-format/czi-parser/binary_converter.h
+++ b/czi-format/czi-parser/binary_converter.h
@@ -35,7 +35,12 @@ ulong bytes_to_ulong(const ByteArray &bytes, const ulong fromIndex = 0);
 std::string utf8bytes_to_string(const ByteArray &bytes);
 // Convert number of UTF-8  bytes, from specified index, to string.
 std::string utf8bytes_to_string(const ByteArray &bytes, const ulong fromIndex, const uint byteCount);
+// Convert int into 4 bytes.
+ByteArray int_to_bytes(const int &i);
+// Convert int into 4 bytes, which will be added into buffer.
+void int_to_bytes(const int &i, ByteArray &buffer, size_t bufferPosition);
 
-//char get_hex_value(int x);
+std::vector<ushort> bytes_to_ushort_array(const ByteArray &data);
+ByteArray int_array_to_bytes(const std::vector<int> &data);
 
 #include "binary_converter.cpp"
\ No newline at end of file
diff --git a/czi-format/czi-parser/compression/compressors.h b/czi-format/czi-parser/compression/compressors.h
index ff91671..2a75b3c 100644
--- a/czi-format/czi-parser/compression/compressors.h
+++ b/czi-format/czi-parser/compression/compressors.h
@@ -436,26 +436,27 @@ ByteArray lz_decode(ByteArray &compressed)
 
 CompressionResult test_compression_method(const ByteArray &data, CompressionMethod method, int compressionLevel)
 {
-    ByteArray compressedData;
     CompressionResult result = {};
-
-    switch (method)
     {
-    case CompressionMethod_GZIP:
-        compressedData = gzip_encode(data, compressionLevel, result);
-        break;
-    case CompressionMethod_LZMA:
-        compressedData = lzma_encode(data, compressionLevel, result);
-        break;
-    case CompressionMethod_BZIP2:
-        compressedData = bzip2_encode(data, compressionLevel, result);
-        break;
-    default:
-        INVALID_CASE;
+        ByteArray compressedData;
+        switch (method)
+        {
+        case CompressionMethod_GZIP:
+            compressedData = gzip_encode(data, compressionLevel, result);
+            break;
+        case CompressionMethod_LZMA:
+            compressedData = lzma_encode(data, compressionLevel, result);
+            break;
+        case CompressionMethod_BZIP2:
+            compressedData = bzip2_encode(data, compressionLevel, result);
+            break;
+        default:
+            INVALID_CASE;
+        }
+        result.compressedSize = compressedData.size();
     }
 
     result.originalSize = data.size();
-    result.compressedSize = compressedData.size();
     result.compressionRatio = compression_ratio((float)result.originalSize, (float)result.compressedSize);
     result.percentageOfOriginalSize = ((float)result.compressedSize / (float)result.originalSize) * 100.0f;
 
diff --git a/czi-format/czi-parser/custom_types.h b/czi-format/czi-parser/custom_types.h
index 28d8ae7..22a8910 100644
--- a/czi-format/czi-parser/custom_types.h
+++ b/czi-format/czi-parser/custom_types.h
@@ -24,6 +24,7 @@ typedef unsigned long ulong;
 constexpr uint UINT_MAX = std::numeric_limits<uint>::max();
 constexpr ulong ULONG_MAX = std::numeric_limits<ulong>::max();
 constexpr short SHORT_MAX = std::numeric_limits<short>::max();
+constexpr short USHORT_MAX = std::numeric_limits<ushort>::max();
 
 #define TagType std::string, std::string
 typedef std::pair<TagType> Tag;
diff --git a/czi-format/czi-parser/czi_file.cpp b/czi-format/czi-parser/czi_file.cpp
index 0e4d065..0e274fb 100644
--- a/czi-format/czi-parser/czi_file.cpp
+++ b/czi-format/czi-parser/czi_file.cpp
@@ -49,131 +49,6 @@ const char *CziFile::pixel_type_str(const PixelType px) const
     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:
-        return "X";
-        break;
-    case Dimension_Y:
-        return "Y";
-        break;
-    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";
-    default:
-    {
-        always_assert("Bad dimension type." && false);
-        break;
-    }
-    }
-    return nullptr;
-}
-
 std::string CziFile::dimension_stack_str(const std::vector<DimensionEntryDV1> &dims, bool includeSize) const
 {
 
@@ -495,7 +370,7 @@ void CziFile::dump_continuous_data(const std::string &dir) const
     }
 }
 
-void CziFile::differences_between_next(const std::string baseName) const
+void CziFile::absolutu_difference_between_frames(const std::string baseName) const
 {
     if (subBlockDirectory.entries.size() <= 1)
     {
@@ -510,7 +385,7 @@ void CziFile::differences_between_next(const std::string baseName) const
         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);
+        ImageMatrix diff_ref_next = ref.create_absolute_difference_matrix(next);
 
         diff_ref_next.save_as_ppm(fName.c_str());
 
@@ -616,4 +491,67 @@ void CziFile::test_compression(CompressionMethod method, bool verbose, int compr
     printf("Original size: %8lu B Compressed size: %8lu B Compressed Z-Order size: %8lu B\n", overallN.originalSize, overallN.compressedSize, overallZ.compressedSize);
     printf("Original size (%%): 100%% Compressed: %5.5f%% Compressed Z-Order: %5.5f%%\n", overallN.percentageOfOriginalSize, overallZ.percentageOfOriginalSize);
     printf("Average compression time: %f ms\n", overallN.compressionTimeMS);
+}
+
+std::vector<std::pair<uint, std::vector<uint>>> CziFile::get_subblocks_grouped_by_channels() const
+{
+    std::vector<std::pair<uint, std::vector<uint>>> result;
+    result.reserve(subBlockDirectory.channelCount);
+
+    for (uint channel = 0; channel < subBlockDirectory.channelCount; channel++)
+    {
+        std::vector<uint> subblocksInChannel;
+        for (uint subblock = 0; subblock < subBlockDirectory.entryCount; subblock++)
+        {
+            int subblockChannel = subBlockDirectory.entries[subblock].channel;
+            always_assert(subblockChannel >= 0);
+            if ((uint)subblockChannel == channel)
+            {
+                subblocksInChannel.push_back(subblock);
+            }
+        }
+        result.push_back(std::make_pair(channel, subblocksInChannel));
+    }
+
+    return result;
+}
+
+void CziFile::frames_difference() const
+{
+    // TODO: This will now handle only Gray16 pixels!
+
+    auto framesByChannels = get_subblocks_grouped_by_channels();
+
+    for (const std::pair<uint, std::vector<uint>> &channelGroup : framesByChannels)
+    {
+        printf("Starting channel %u\n", channelGroup.first);
+        for (size_t i = 1; i < channelGroup.second.size(); i++)
+        {
+            // This assertion will fail if pixel type isn't Gray16
+            always_assert(subBlockDirectory.entries[channelGroup.second[i - 1]].pixelType == PixelType_Gray16);
+            always_assert(subBlockDirectory.entries[channelGroup.second[i]].pixelType == PixelType_Gray16);
+
+            auto prevFrameData = get_image_data(channelGroup.second[i - 1], false);
+            auto currentFrameData = get_image_data(channelGroup.second[i], false);
+            always_assert(prevFrameData.size() == currentFrameData.size());
+
+            std::vector<ushort> prevFrameValues = bytes_to_ushort_array(prevFrameData);
+            std::vector<ushort> currentFrameValues = bytes_to_ushort_array(currentFrameData);
+            always_assert(prevFrameValues.size() == currentFrameValues.size());
+
+            std::vector<int> differenceArray = vecUtil::diff_vectors<ushort, int>(prevFrameValues, currentFrameValues);
+
+            ByteArray intBytes = int_array_to_bytes(differenceArray);
+
+            auto frameCompResult = test_compression_method(currentFrameData, CompressionMethod_BZIP2, 6);
+            auto diffCompResult = test_compression_method(intBytes, CompressionMethod_BZIP2, 6);
+
+            float diffSizeDif = ((float)diffCompResult.compressedSize / (float)frameCompResult.compressedSize) * 100.0f;
+
+            printf("Frame [%u | %u]: Size %7lu Compression ratio: %7f\n",
+                   channelGroup.second[i - 1], channelGroup.second[i], frameCompResult.compressedSize, frameCompResult.compressionRatio);
+
+            printf(" Diff:           Size %7lu Compression ratio: %7f\n Size: %f\n\n", diffCompResult.compressedSize, diffCompResult.compressionRatio, diffSizeDif);
+        }
+    }
 }
\ No newline at end of file
diff --git a/czi-format/czi-parser/czi_file.h b/czi-format/czi-parser/czi_file.h
index a1d0ca5..c5ae40e 100644
--- a/czi-format/czi-parser/czi_file.h
+++ b/czi-format/czi-parser/czi_file.h
@@ -14,10 +14,6 @@ class CziFile
 {
 private:
   void print_segment_header(const SegmentHeader &segmentHeader) const;
-  const char *pyramid_type_str(const PyramidType pt) const;
-  const char *compression_type_str(const CompressionType ct) const;
-  const char *dimension_type_str(const Dimension &d) const;
-  const char *dimension_char(const Dimension &d) const;
   std::string dimension_stack_str(const std::vector<DimensionEntryDV1> &dims, bool includeSize) const;
 
 public:
@@ -52,9 +48,12 @@ public:
   // Dump all images to graphics files.
   void dump_images(const std::string &baseName) const;
   // Save differences between next images, so 0x1;1x2;...
-  void differences_between_next(const std::string baseName) const;
+  void absolutu_difference_between_frames(const std::string baseName) const;
   // Report compression ratios of images in normal and Z order.
   void test_compression(CompressionMethod method, bool verbose, int compressionLevel) const;
+  // Get pairs of subblocks in different channels.
+  std::vector<std::pair<uint, std::vector<uint>>> get_subblocks_grouped_by_channels() const;
+  void frames_difference() const;
 };
 
 #include "czi_file.cpp"
\ No newline at end of file
diff --git a/czi-format/czi-parser/czi_parts/compression_type.h b/czi-format/czi-parser/czi_parts/compression_type.h
index d7770cb..d5c3a17 100644
--- a/czi-format/czi-parser/czi_parts/compression_type.h
+++ b/czi-format/czi-parser/czi_parts/compression_type.h
@@ -11,4 +11,30 @@ enum CompressionType
     CompressionType_Camera,
     // System specific RAW data.
     CompressionType_System
-};
\ No newline at end of file
+};
+
+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:
+    {
+        always_assert("Bad compression type." && false);
+        break;
+    }
+    }
+    return nullptr;
+}
\ No newline at end of file
diff --git a/czi-format/czi-parser/czi_parts/dimension.h b/czi-format/czi-parser/czi_parts/dimension.h
index 4ab01c8..a85a36d 100644
--- a/czi-format/czi-parser/czi_parts/dimension.h
+++ b/czi-format/czi-parser/czi_parts/dimension.h
@@ -25,4 +25,83 @@ enum Dimension
     Dimension_H = 'H',
     // View index (for multi – view images, e.g. SPIM)
     Dimension_V = 'V'
-};
\ No newline at end of file
+};
+
+const char *dimension_type_str(const Dimension &d)
+{
+    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 *dimension_char(const Dimension &d)
+{
+    std::string result;
+    switch (d)
+    {
+    case Dimension_X:
+        return "X";
+        break;
+    case Dimension_Y:
+        return "Y";
+        break;
+    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";
+    default:
+    {
+        always_assert("Bad dimension type." && false);
+        break;
+    }
+    }
+    return nullptr;
+}
\ No newline at end of file
diff --git a/czi-format/czi-parser/czi_parts/pixel_type.h b/czi-format/czi-parser/czi_parts/pixel_type.h
index 8b61fe4..d8a4d15 100644
--- a/czi-format/czi-parser/czi_parts/pixel_type.h
+++ b/czi-format/czi-parser/czi_parts/pixel_type.h
@@ -1,4 +1,5 @@
 #pragma once
+#include "../custom_types.h"
 enum PixelType
 {
     // 8 bit unsigned.
@@ -26,3 +27,100 @@ enum PixelType
     // For internal usage.
     PixelType_None
 };
+
+// Get number of bytes needed to encode specific `PixelType`
+int get_bytes_per_pixel_type(const PixelType &pt)
+{
+    switch (pt)
+    {
+    case PixelType_Gray8:
+        return 1;
+    case PixelType_Gray16:
+        return 2;
+    case PixelType_Gray32Float:
+        return 4;
+    case PixelType_Bgr24:
+        return 3;
+    case PixelType_Bgr48:
+        return 6;
+    case PixelType_Bgr96Float:
+        return 12;
+    case PixelType_Bgra32:
+        return 4;
+    case PixelType_Gray64ComplexFloat:
+        return 8;
+    case PixelType_Bgr192ComplexFloat:
+        return 24;
+    case PixelType_Gray32:
+        return 4;
+    case PixelType_Gray64:
+        return 8;
+
+    default:
+        always_assert("Bad pixel type." && false);
+        break;
+    }
+    return -1;
+}
+
+// Get number of bytes, by which ByteArray can be reordered in Z-Order.
+uint get_component_size_of_pixel(const PixelType &pt)
+{
+    switch (pt)
+    {
+    case PixelType_Gray8:
+        return 1;
+    case PixelType_Gray16:
+        return 2;
+    case PixelType_Gray32Float:
+        return 4;
+    case PixelType_Bgr24:
+        return 1; // Channels will be separated.
+    case PixelType_Bgr48:
+        return 2; // Channels will be separated.
+    case PixelType_Bgr96Float:
+        return 4; // Channels will be separated.
+    case PixelType_Bgra32:
+        return 1; // Channels will be separated.
+    case PixelType_Gray64ComplexFloat:
+        return 4; // Channels will be separated.
+    case PixelType_Bgr192ComplexFloat:
+        return 4; // Channels will be separated.
+    case PixelType_Gray32:
+        return 4;
+    case PixelType_Gray64:
+        return 8;
+    default:
+        INVALID_CASE;
+        break;
+    }
+    return -1;
+}
+
+// Get number of separable components in specific `PixelType`.
+uint get_count_of_components_of_pixel(const PixelType &pt)
+{
+    switch (pt)
+    {
+    case PixelType_Gray8:
+    case PixelType_Gray16:
+    case PixelType_Gray32Float:
+    case PixelType_Gray32:
+    case PixelType_Gray64:
+        return 1;
+    case PixelType_Gray64ComplexFloat:
+        return 2;
+    case PixelType_Bgr24:
+    case PixelType_Bgr48:
+    case PixelType_Bgr96Float:
+        return 3;
+    case PixelType_Bgra32:
+        return 4;
+    case PixelType_Bgr192ComplexFloat:
+        return 6;
+    default:
+        INVALID_CASE;
+        break;
+    }
+    return -1;
+}
\ No newline at end of file
diff --git a/czi-format/czi-parser/czi_parts/pyramid_type.h b/czi-format/czi-parser/czi_parts/pyramid_type.h
index 339dfa5..cac3d51 100644
--- a/czi-format/czi-parser/czi_parts/pyramid_type.h
+++ b/czi-format/czi-parser/czi_parts/pyramid_type.h
@@ -5,4 +5,24 @@ enum PyramidType
     PyramidType_None = 0,
     PyramidType_SingleSubBlock = 1,
     PyramidType_MultiSubBlock = 2
-};
\ No newline at end of file
+};
+
+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:
+    {
+        always_assert("Bad pyramid type." && false);
+        break;
+    }
+    }
+    return nullptr;
+}
\ No newline at end of file
diff --git a/czi-format/czi-parser/image/image_matrix.cpp b/czi-format/czi-parser/image/image_matrix.cpp
index 267824a..eff1ef6 100644
--- a/czi-format/czi-parser/image/image_matrix.cpp
+++ b/czi-format/czi-parser/image/image_matrix.cpp
@@ -207,7 +207,7 @@ void ImageMatrix::fill(std::shared_ptr<BasePixel> px)
         data[i] = px;
 }
 
-ImageMatrix ImageMatrix::create_difference_matrix(const ImageMatrix &other)
+ImageMatrix ImageMatrix::create_absolute_difference_matrix(const ImageMatrix &other)
 {
     ImageMatrix diff(colCount, rowCount, PixelType_Gray8);
 
diff --git a/czi-format/czi-parser/image/image_matrix.h b/czi-format/czi-parser/image/image_matrix.h
index b6308bd..b6280f3 100644
--- a/czi-format/czi-parser/image/image_matrix.h
+++ b/czi-format/czi-parser/image/image_matrix.h
@@ -53,7 +53,7 @@ public:
   void save_as_ppm(const char *fName) const;
   // Set all pixels to one value of `px`.
   void fill(std::shared_ptr<BasePixel> px);
-  ImageMatrix create_difference_matrix(const ImageMatrix &other);
+  ImageMatrix create_absolute_difference_matrix(const ImageMatrix &other);
 };
 
 #include "image_matrix.cpp"
\ No newline at end of file
diff --git a/czi-format/czi-parser/image/pixel_structures.h b/czi-format/czi-parser/image/pixel_structures.h
index 4c771bd..7c442c2 100644
--- a/czi-format/czi-parser/image/pixel_structures.h
+++ b/czi-format/czi-parser/image/pixel_structures.h
@@ -1,5 +1,4 @@
 #pragma once
-#include "../czi_parts/pixel_type.h"
 #include "../custom_types.h"
 
 template <typename Number>
@@ -603,101 +602,4 @@ struct Bgr192ComplexFloatPixel : BasePixel
 
         return *this;
     }
-};
-
-// Get number of bytes needed to encode specific `PixelType`
-int get_bytes_per_pixel_type(const PixelType &pt)
-{
-    switch (pt)
-    {
-    case PixelType_Gray8:
-        return 1;
-    case PixelType_Gray16:
-        return 2;
-    case PixelType_Gray32Float:
-        return 4;
-    case PixelType_Bgr24:
-        return 3;
-    case PixelType_Bgr48:
-        return 6;
-    case PixelType_Bgr96Float:
-        return 12;
-    case PixelType_Bgra32:
-        return 4;
-    case PixelType_Gray64ComplexFloat:
-        return 8;
-    case PixelType_Bgr192ComplexFloat:
-        return 24;
-    case PixelType_Gray32:
-        return 4;
-    case PixelType_Gray64:
-        return 8;
-
-    default:
-        always_assert("Bad pixel type." && false);
-        break;
-    }
-    return -1;
-}
-
-// Get number of bytes, by which ByteArray can be reordered in Z-Order.
-uint get_component_size_of_pixel(const PixelType &pt)
-{
-    switch (pt)
-    {
-    case PixelType_Gray8:
-        return 1;
-    case PixelType_Gray16:
-        return 2;
-    case PixelType_Gray32Float:
-        return 4;
-    case PixelType_Bgr24:
-        return 1; // Channels will be separated.
-    case PixelType_Bgr48:
-        return 2; // Channels will be separated.
-    case PixelType_Bgr96Float:
-        return 4; // Channels will be separated.
-    case PixelType_Bgra32:
-        return 1; // Channels will be separated.
-    case PixelType_Gray64ComplexFloat:
-        return 4; // Channels will be separated.
-    case PixelType_Bgr192ComplexFloat:
-        return 4; // Channels will be separated.
-    case PixelType_Gray32:
-        return 4;
-    case PixelType_Gray64:
-        return 8;
-    default:
-        INVALID_CASE;
-        break;
-    }
-    return -1;
-}
-
-// Get number of separable components in specific `PixelType`.
-uint get_count_of_components_of_pixel(const PixelType &pt)
-{
-    switch (pt)
-    {
-    case PixelType_Gray8:
-    case PixelType_Gray16:
-    case PixelType_Gray32Float:
-    case PixelType_Gray32:
-    case PixelType_Gray64:
-        return 1;
-    case PixelType_Gray64ComplexFloat:
-        return 2;
-    case PixelType_Bgr24:
-    case PixelType_Bgr48:
-    case PixelType_Bgr96Float:
-        return 3;
-    case PixelType_Bgra32:
-        return 4;
-    case PixelType_Bgr192ComplexFloat:
-        return 6;
-    default:
-        INVALID_CASE;
-        break;
-    }
-    return -1;
-}
\ No newline at end of file
+};
\ No newline at end of file
diff --git a/czi-format/czi-parser/main.cpp b/czi-format/czi-parser/main.cpp
index 5372f05..55655b2 100644
--- a/czi-format/czi-parser/main.cpp
+++ b/czi-format/czi-parser/main.cpp
@@ -20,8 +20,9 @@ int main(int argc, char **argv)
     args::Flag compressionTestMethod(mainMethodGroup, "compression-test", "Compress subblocks and output compression ratios <compression method>", {"compression-test"});
     args::Flag versionMethod(mainMethodGroup, "Version test", "Check version", {"version", 'V'});
     args::Flag benchmarkMethod(mainMethodGroup, "Compression benchmark", "Start compression benchmark for selected CZI file. <report-file> [continuos] [verbose].", {"benchmark"});
-    args::Flag exportDataMethod(mainMethodGroup, "Export binary dat", "Export raw image data <folder> [continuos]", {"dump-data"});
+    args::Flag exportDataMethod(mainMethodGroup, "Export binary data", "Export raw image data <folder> [continuos]", {"dump-data"});
     args::Flag exportImagesMethod(mainMethodGroup, "Export images", "Export images to ppm files <folder>", {"dump-images"});
+    args::Flag frameDiffMethod(mainMethodGroup, "Frame difference", "TODO", {"frame-diff"});
 
     // Options
     args::Flag dontParseMetadataOption(optionsGroup, "no metadata", "Dont read metadata byte", {"no-metadata"});
@@ -29,9 +30,9 @@ int main(int argc, char **argv)
     args::ValueFlag<int> compressionLevelOption(optionsGroup, "Compression level", "Compression level", {'l', "level"});
     args::ValueFlag<std::string> folderOption(optionsGroup, "Folder", "Folder to which save exported items.", {'f', "folder"});
     args::ValueFlag<std::string> reportFileOption(optionsGroup, "Report file", "File where to write report.", {'r', "report"});
-    args::Flag gzipCompressionOption(compressionGroup, "GZIP", "GZIP (zlib) compression", {"gzip"});
-    args::Flag lzmaCompressionOption(compressionGroup, "LZMA", "LZMA (2?) compression", {"lzma"});
-    args::Flag bzip2CompressionOption(compressionGroup, "BZIP2", "BZip2 compression", {"bzip2"});
+    args::Flag gzipCompressionOption(compressionGroup, "GZIP", "gzip (zlib) compression", {"gzip"});
+    args::Flag lzmaCompressionOption(compressionGroup, "LZMA", "lzma compression", {"lzma"});
+    args::Flag bzip2CompressionOption(compressionGroup, "BZIP2", "bzip2 compression", {"bzip2"});
     args::Flag continuousCompressionOption(compressionGroup, "Continuos compression", "Compress continuos array of image data into one buffer.", {'c'});
 
     try
@@ -91,6 +92,12 @@ int main(int argc, char **argv)
         return 0;
     }
 
+    if (frameDiffMethod)
+    {
+        parsedFile.frames_difference();
+        return 0;
+    }
+
     // Test compression method.
     if (compressionTestMethod)
     {
diff --git a/czi-format/czi-parser/utilities/vector_utilities.h b/czi-format/czi-parser/utilities/vector_utilities.h
index a8529b5..76482d5 100644
--- a/czi-format/czi-parser/utilities/vector_utilities.h
+++ b/czi-format/czi-parser/utilities/vector_utilities.h
@@ -44,4 +44,19 @@ inline bool contains(const std::vector<T> &vec, const T &elem)
     return (std::find(vec.begin(), vec.end(), elem) != vec.end());
 }
 
-}; // namespace vecUtil
\ No newline at end of file
+template <typename T, typename DiffType>
+std::vector<DiffType> diff_vectors(const std::vector<T> &a, const std::vector<T> &b)
+{
+    always_assert(a.size() == b.size());
+
+    std::vector<DiffType> result;
+    result.resize(a.size());
+    for (size_t i = 0; i < a.size(); i++)
+    {
+        result[i] = (DiffType)(b[i] - a[i]);
+    }
+
+    return result;
+}
+
+}; // namespace vecUtil
-- 
GitLab