From 098581e93cdf277d2e059cfd7a057ecb477ca09e Mon Sep 17 00:00:00 2001 From: theazgra <theazgra@gmail.com> Date: Fri, 8 Feb 2019 11:10:10 +0100 Subject: [PATCH] Saving ImageMatrix. --- czi-format/czi-parser/czi_file.cpp | 26 +-- czi-format/czi-parser/czi_file.h | 3 +- czi-format/czi-parser/image.cpp | 63 ------ czi-format/czi-parser/{ => image}/CImg.h | 0 czi-format/czi-parser/image/cimg_wrapper.h | 73 +++++++ czi-format/czi-parser/image/image_matrix.cpp | 211 +++++++++++++++++++ czi-format/czi-parser/image/image_matrix.h | 199 +++-------------- czi-format/czi-parser/image_writer.h | 27 --- czi-format/czi-parser/main.cpp | 7 +- 9 files changed, 333 insertions(+), 276 deletions(-) delete mode 100644 czi-format/czi-parser/image.cpp rename czi-format/czi-parser/{ => image}/CImg.h (100%) create mode 100644 czi-format/czi-parser/image/cimg_wrapper.h create mode 100644 czi-format/czi-parser/image/image_matrix.cpp delete mode 100644 czi-format/czi-parser/image_writer.h diff --git a/czi-format/czi-parser/czi_file.cpp b/czi-format/czi-parser/czi_file.cpp index 5c9a9bd..ad0f063 100644 --- a/czi-format/czi-parser/czi_file.cpp +++ b/czi-format/czi-parser/czi_file.cpp @@ -322,19 +322,19 @@ ImageMatrix CziFile::get_image(const uint subblockId) return image; } -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::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 { diff --git a/czi-format/czi-parser/czi_file.h b/czi-format/czi-parser/czi_file.h index 407f6b3..0951831 100644 --- a/czi-format/czi-parser/czi_file.h +++ b/czi-format/czi-parser/czi_file.h @@ -3,7 +3,6 @@ #include "czi_parts/segments.h" #include "utilities/vector_utilities.h" #include "stream/binary_file_stream.h" -#include "image_writer.h" #include "image/image_matrix.h" //TODO: Handle multi-file scenarios. @@ -31,7 +30,7 @@ public: void report() const; ImageMatrix get_image(const uint subblockId); void dump_image_data(const std::string &baseName) const; - void extract_images(const std::string &baseName) const; + //void extract_images(const std::string &baseName) const; }; #include "czi_file.cpp" \ No newline at end of file diff --git a/czi-format/czi-parser/image.cpp b/czi-format/czi-parser/image.cpp deleted file mode 100644 index 6d4600e..0000000 --- a/czi-format/czi-parser/image.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#define cimg_display 0 -#include "CImg.h" -#include "image/pixel_structures.h" - -// struct RgbPixel -// { -// byte R; -// byte G; -// byte B; - -// RgbPixel() -// { -// R = 0; -// G = 0; -// B = 0; -// } - -// RgbPixel(byte r, byte g, byte b) -// { -// R = r; -// G = g; -// B = b; -// } - -// RgbPixel(byte value) -// { -// R = value; -// G = value; -// B = value; -// } -// }; - -inline void set_pixel(cimg_library::CImg<byte> &img, const uint &row, const uint &col, const Bgr24Pixel &bgr) -{ - img(col, row, 0) = bgr.r; - img(col, row, 1) = bgr.g; - img(col, row, 2) = bgr.b; -} - -inline void set_pixel(cimg_library::CImg<byte> &img, const uint &row, const uint &col, const byte &px) -{ - img(col, row, 0) = px; -} - -inline Bgr24Pixel get_pixel(cimg_library::CImg<byte> &img, const uint &row, const uint &col) -{ - Bgr24Pixel rgb(img(col, row, 2), img(col, row, 1), img(col, row, 0)); - return rgb; -} - -cimg_library::CImg<byte> create_grayscale_image(const uint width, const uint height) -{ - cimg_library::CImg<byte> image(width, height, 1, 1); - image.fill(0); - return image; -} - -cimg_library::CImg<byte> create_color_image(const uint width, const uint height) -{ - cimg_library::CImg<byte> image(width, height, 1, 3); - image.fill(0); - return image; -} \ No newline at end of file diff --git a/czi-format/czi-parser/CImg.h b/czi-format/czi-parser/image/CImg.h similarity index 100% rename from czi-format/czi-parser/CImg.h rename to czi-format/czi-parser/image/CImg.h diff --git a/czi-format/czi-parser/image/cimg_wrapper.h b/czi-format/czi-parser/image/cimg_wrapper.h new file mode 100644 index 0000000..4c74f56 --- /dev/null +++ b/czi-format/czi-parser/image/cimg_wrapper.h @@ -0,0 +1,73 @@ +#pragma once +#define cimg_display 0 + +#include "CImg.h" +#include "pixel_structures.h" + +namespace cimg_wrapper +{ + +// Set color pixel at row, col in image. +inline void set_color_pixel(cimg_library::CImg<byte> &img, const uint &row, const uint &col, const Bgr24Pixel &bgr) +{ + img(col, row, 0) = bgr.r; + img(col, row, 1) = bgr.g; + img(col, row, 2) = bgr.b; +} + +// Set grayscale pixel at row, col in image. +inline void set_grayscale_pixel(cimg_library::CImg<byte> &img, const uint &row, const uint &col, const byte &px) +{ + img(col, row, 0) = px; +} +// Get grayscale pixel at row, col from image. +inline Bgr24Pixel get_color_pixel(cimg_library::CImg<byte> &img, const uint &row, const uint &col) +{ + Bgr24Pixel rgb(img(col, row, 2), img(col, row, 1), img(col, row, 0)); + return rgb; +} +// Get grayscale pixel at row, col from image. +inline Gray8Pixel get_grayscale_pixel(cimg_library::CImg<byte> &img, const uint &row, const uint &col) +{ + Gray8Pixel px(img(col, row, 0)); + return px; +} + +// Create grayscale image. `Gray8` +cimg_library::CImg<byte> create_grayscale_image(const uint width, const uint height) +{ + cimg_library::CImg<byte> image(width, height, 1, 1); + image.fill(0); + return image; +} + +// Create color image. `Bgr24` +cimg_library::CImg<byte> create_color_image(const uint width, const uint height) +{ + cimg_library::CImg<byte> image(width, height, 1, 3); + image.fill(0); + return image; +} + +// void cimg_save_image(const char *fileName, ImageMatrix, const PixelType pixelType, const int width, const int height) +// { +// // This is not generally true, because it depends on BitsPerPixelType. +// // always_assert((width * height) == bytes.size()); +// cimg_library::CImg<byte> img = create_color_image((uint)width, (uint)height); + +// // Hard-Coded for uncompressed Bgr24 pixel. +// ulong current = 0; + +// for (int row = 0; row < height; row++) +// { +// for (int col = 0; col < width; col++) +// { +// Bgr24Pixel px(bytes[current + 1], bytes[current + 2], bytes[current + 3]); +// current += 3; +// set_pixel(img, row, col, px); +// } +// } +// printf("Finished painting %s\n", fileName); +// img.save_pnm(fileName); +// } +}; \ 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 new file mode 100644 index 0000000..3dda363 --- /dev/null +++ b/czi-format/czi-parser/image/image_matrix.cpp @@ -0,0 +1,211 @@ +#include "image_matrix.h" + +bool ImageMatrix::equals(const ImageMatrix &other) const +{ + if (pixelType != other.pixelType) + return false; + + if ((this->rowCount != other.rows()) || (this->colCount != other.cols())) + return false; + + size_t dataSize = this->data.size(); + for (size_t i = 0; i < dataSize; i++) + { + if (data[i]) + { + if (!this->data[i]->equals(other.data[i])) + return false; + } + else if (other.data[i]) + { + return false; + } + } + return true; +} + +// Returns function to parse bytes into corresponding Pixel structure based on `PixelType` +std::function<BasePixel *(const std::vector<byte> &, ulong &)> ImageMatrix::get_pixel_parse_function(const PixelType pixelType) +{ + switch (pixelType) + { + case PixelType_Gray8: + return bytes_to_Gray8Pixel; + case PixelType_Gray16: + return bytes_to_Gray16Pixel; + case PixelType_Gray32Float: + return bytes_to_Gray32FloatPixel; + case PixelType_Bgr24: + return bytes_to_Bgr24Pixel; + case PixelType_Bgr48: + return bytes_to_Bgr48Pixel; + case PixelType_Bgr96Float: + return bytes_to_Bgr96FloatPixel; + case PixelType_Bgra32: + return bytes_to_Bgra32Pixel; + case PixelType_Gray64ComplexFloat: + return bytes_to_Gray64ComplexFloatPixel; + case PixelType_Bgr192ComplexFloat: + return bytes_to_Bgr192ComplexFloatPixel; + case PixelType_Gray32: + return bytes_to_Gray32Pixel; + case PixelType_Gray64: + return bytes_to_Gray64Pixel; + default: + always_assert(false && "Wrong PixelType!"); + break; + } + return nullptr; +} + +// Get number of bytes needed to encode specific `PixelType` +int ImageMatrix::get_bytes_per_pixel_type(const PixelType &pt) const +{ + 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; +} + +void ImageMatrix::parse_bytes_to_image(const std::vector<byte> &bytes, const PixelType &pt) +{ + auto pixel_parse_function = get_pixel_parse_function(pt); + size_t count = rowCount * colCount; + ulong bufferIndex = 0; + for (size_t i = 0; i < count; i++) + { + data[i] = pixel_parse_function(bytes, bufferIndex); + } +} + +ImageMatrix::ImageMatrix() +{ + this->rowCount = 0; + this->colCount = 0; +} + +ImageMatrix::ImageMatrix(const uint width, const uint height, const PixelType pixelType, const std::vector<byte> &imageBytes) +{ + always_assert(width > 0 && height > 0 && pixelType != PixelType_None && imageBytes.size() > 0); + + this->rowCount = height; + this->colCount = width; + this->pixelType = pixelType; + + // Allocate memory. + data.resize(rowCount * colCount); + + int bytePerPixel = get_bytes_per_pixel_type(pixelType); + ulong requiredByteCount = (ulong)width * (ulong)height * (ulong)bytePerPixel; + always_assert(requiredByteCount == imageBytes.size() && "imageBytes size is wrong."); + + parse_bytes_to_image(imageBytes, pixelType); +} + +ImageMatrix::~ImageMatrix() +{ + for (size_t i = 0; i < data.size(); i++) + { + if (data[i]) + { + delete data[i]; + } + } +} + +// Get number of rows. +uint ImageMatrix::rows() const { return this->rowCount; } + +// Get number of cols. +uint ImageMatrix::cols() const { return this->colCount; } + +inline void ImageMatrix::set(const uint &row, const uint &col, BasePixel *px) +{ + data[((row * colCount) + col)] = px; +} + +inline const BasePixel *ImageMatrix::get(const uint &row, const uint &col) const +{ + return data[((row * colCount) + col)]; +} + +inline void ImageMatrix::set(const ulong index, BasePixel *px) +{ + data[index] = px; +} + +inline const BasePixel *ImageMatrix::get(const ulong index) const +{ + return data[index]; +} + +bool ImageMatrix::operator==(const ImageMatrix &other) const +{ + return equals(other); +} + +bool ImageMatrix::operator!=(const ImageMatrix &other) const +{ + return (!equals(other)); +} + +void ImageMatrix::save_as_ppm(std::string baseFileName) const +{ + always_assert((this->pixelType == PixelType_Gray8 || this->pixelType == PixelType_Bgr24) && "Currently only those pixel types are supported."); + + std::string fName = baseFileName + ".ppm"; + if (this->pixelType == PixelType_Gray8) + { + auto bwImage = cimg_wrapper::create_grayscale_image(colCount, rowCount); + ulong index = 0; + for (uint row = 0; row < this->rowCount; row++) + { + for (uint col = 0; col < this->colCount; col++) + { + cimg_wrapper::set_grayscale_pixel(bwImage, row, col, ((Gray8Pixel *)data[index++])->value); + } + } + bwImage.save_pnm(fName.c_str()); + } + else if (this->pixelType == PixelType_Bgr24) + { + auto colorImage = cimg_wrapper::create_color_image(colCount, rowCount); + ulong index = 0; + Bgr24Pixel *px; + for (uint row = 0; row < this->rowCount; row++) + { + for (uint col = 0; col < this->colCount; col++) + { + px = (Bgr24Pixel *)data[index++]; + cimg_wrapper::set_color_pixel(colorImage, row, col, *px); + } + } + colorImage.save_pnm(fName.c_str()); + } +} \ No newline at end of file diff --git a/czi-format/czi-parser/image/image_matrix.h b/czi-format/czi-parser/image/image_matrix.h index 4ca8143..248d1a4 100644 --- a/czi-format/czi-parser/image/image_matrix.h +++ b/czi-format/czi-parser/image/image_matrix.h @@ -1,7 +1,8 @@ +#pragma once + #include <functional> #include "pixel_parse_functions.h" - -#pragma once +#include "cimg_wrapper.h" class ImageMatrix { @@ -16,180 +17,38 @@ class ImageMatrix PixelType pixelType = PixelType_None; // Check equality of two ImageMatrices. - bool equals(const ImageMatrix &other) - { - if (pixelType != other.pixelType) - return false; - - if ((this->rowCount != other.rows()) || (this->colCount != other.cols())) - return false; - - size_t dataSize = this->data.size(); - for (size_t i = 0; i < dataSize; i++) - { - if (data[i]) - { - if (!this->data[i]->equals(other.data[i])) - return false; - } - else if (other.data[i]) - { - return false; - } - } - return true; - } - + bool equals(const ImageMatrix &other) const; // Returns function to parse bytes into corresponding Pixel structure based on `PixelType` - std::function<BasePixel *(const std::vector<byte> &, ulong &)> get_pixel_parse_function(const PixelType pixelType) - { - switch (pixelType) - { - case PixelType_Gray8: - return bytes_to_Gray8Pixel; - case PixelType_Gray16: - return bytes_to_Gray16Pixel; - case PixelType_Gray32Float: - return bytes_to_Gray32FloatPixel; - case PixelType_Bgr24: - return bytes_to_Bgr24Pixel; - case PixelType_Bgr48: - return bytes_to_Bgr48Pixel; - case PixelType_Bgr96Float: - return bytes_to_Bgr96FloatPixel; - case PixelType_Bgra32: - return bytes_to_Bgra32Pixel; - case PixelType_Gray64ComplexFloat: - return bytes_to_Gray64ComplexFloatPixel; - case PixelType_Bgr192ComplexFloat: - return bytes_to_Bgr192ComplexFloatPixel; - case PixelType_Gray32: - return bytes_to_Gray32Pixel; - case PixelType_Gray64: - return bytes_to_Gray64Pixel; - default: - always_assert(false && "Wrong PixelType!"); - break; - } - return nullptr; - } - + std::function<BasePixel *(const std::vector<byte> &, ulong &)> get_pixel_parse_function(const PixelType pixelType); // Get number of bytes needed to encode specific `PixelType` - int get_bytes_per_pixel_type(const PixelType &pt) const - { - 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; - } - - void parse_bytes_to_image(const std::vector<byte> &bytes, const PixelType &pt) - { - auto pixel_parse_function = get_pixel_parse_function(pt); - size_t count = rowCount * colCount; - ulong bufferIndex = 0; - for (size_t i = 0; i < count; i++) - { - data[i] = pixel_parse_function(bytes, bufferIndex); - } - } + int get_bytes_per_pixel_type(const PixelType &pt) const; + // Parse bytes to this matrix. + void parse_bytes_to_image(const std::vector<byte> &bytes, const PixelType &pt); public: // Default constructor for empty image. - ImageMatrix() - { - this->rowCount = 0; - this->colCount = 0; - } + ImageMatrix(); // Constuctor creating image matrix from image data. - ImageMatrix(const uint width, const uint height, const PixelType pixelType, const std::vector<byte> &imageBytes) - { - always_assert(width > 0 && height > 0 && pixelType != PixelType_None && imageBytes.size() > 0); - - this->rowCount = height; - this->colCount = width; - this->pixelType = pixelType; - - // Allocate memory. - data.resize(rowCount * colCount); - - int bytePerPixel = get_bytes_per_pixel_type(pixelType); - ulong requiredByteCount = (ulong)width * (ulong)height * (ulong)bytePerPixel; - always_assert(requiredByteCount == imageBytes.size() && "imageBytes size is wrong."); - - parse_bytes_to_image(imageBytes, pixelType); - } - - ~ImageMatrix() - { - for (size_t i = 0; i < data.size(); i++) - { - if (data[i]) - { - delete data[i]; - } - } - } - + ImageMatrix(const uint width, const uint height, const PixelType pixelType, const std::vector<byte> &imageBytes); + ~ImageMatrix(); // Get number of rows. - uint rows() const { return this->rowCount; } - + uint rows() const; // Get number of cols. - uint cols() const { return this->colCount; } - - inline void set(const uint &row, const uint &col, BasePixel *px) - { - data[((row * colCount) + col)] = px; - } - - inline const BasePixel *get(const uint &row, const uint &col) - { - return data[((row * colCount) + col)]; - } - - inline void set(const ulong index, BasePixel *px) - { - data[index] = px; - } - - inline const BasePixel *get(const ulong index) - { - return data[index]; - } - - bool operator==(const ImageMatrix &other) - { - return equals(other); - } - - bool operator!=(const ImageMatrix &other) - { - return (!equals(other)); - } + uint cols() const; + // Set pixel at row and col. + inline void set(const uint &row, const uint &col, BasePixel *px); + // Set pixel at raw index. + inline void set(const ulong index, BasePixel *px); + // Get pixel at row and col. + inline const BasePixel *get(const uint &row, const uint &col) const; + // Get pixel at raw index. + inline const BasePixel *get(const ulong index) const; + // Equal operator. + bool operator==(const ImageMatrix &other) const; + // Not equal operator. + bool operator!=(const ImageMatrix &other) const; + // Save Matrix as image, supports pixels: `Bgr24`, `Gray8` + void save_as_ppm(std::string baseFileName) const; }; + +#include "image_matrix.cpp" \ No newline at end of file diff --git a/czi-format/czi-parser/image_writer.h b/czi-format/czi-parser/image_writer.h deleted file mode 100644 index 8319c7c..0000000 --- a/czi-format/czi-parser/image_writer.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include <fstream> -#include "czi_parts/pixel_type.h" -#include "image/pixel_structures.h" -#include "image.cpp" - -void create_image_file(const char *fileName, const std::vector<byte> &bytes, const PixelType pixelType, const int width, const int height) -{ - // This is not generally true, because it depends on BitsPerPixelType. - // always_assert((width * height) == bytes.size()); - cimg_library::CImg<byte> img = create_color_image((uint)width, (uint)height); - - // Hard-Coded for uncompressed Bgr24 pixel. - ulong current = 0; - - for (int row = 0; row < height; row++) - { - for (int col = 0; col < width; col++) - { - Bgr24Pixel px(bytes[current + 1], bytes[current + 2], bytes[current + 3]); - current += 3; - set_pixel(img, row, col, px); - } - } - printf("Finished painting %s\n", fileName); - img.save_pnm(fileName); -} \ No newline at end of file diff --git a/czi-format/czi-parser/main.cpp b/czi-format/czi-parser/main.cpp index 0444278..616eda6 100644 --- a/czi-format/czi-parser/main.cpp +++ b/czi-format/czi-parser/main.cpp @@ -43,7 +43,12 @@ int main(int argc, char **argv) else if (dumpRawImageData) parseResult.dump_image_data(dumpName); else if (dumpImageData) - parseResult.extract_images(dumpName); + { + auto imgMat = parseResult.get_image(0); + imgMat.save_as_ppm("matrix"); + //TODO: Re-Enable it. + //parseResult.extract_images(dumpName); + } printf("Finished.\n"); -- GitLab