diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 1f211f9e5..81cbe4ce8 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -172,6 +172,7 @@ set(test_srcs test/LoggingTest.cpp test/SignalsTest.cpp test/IterationTest.cpp + test/TIFFIOTest.cpp ) # Add a test executable for each src diff --git a/core/include/vc/core/io/PointSetIO.hpp b/core/include/vc/core/io/PointSetIO.hpp index 8ddee6abd..270398ca1 100644 --- a/core/include/vc/core/io/PointSetIO.hpp +++ b/core/include/vc/core/io/PointSetIO.hpp @@ -450,14 +450,11 @@ class PointSetIO // Read data T t; - auto nbytes = header.dim * typeBytes; + std::size_t nbytes = header.width * header.dim * typeBytes; + std::vector points(header.width, 0); + points.reserve(header.width); for (size_t h = 0; h < header.height; ++h) { - std::vector points; - points.reserve(header.width); - for (size_t w = 0; w < header.width; ++w) { - infile.read(reinterpret_cast(t.val), nbytes); - points.push_back(t); - } + infile.read(reinterpret_cast(points.data()), nbytes); ps.pushRow(points); } diff --git a/core/include/vc/core/io/TIFFIO.hpp b/core/include/vc/core/io/TIFFIO.hpp index 33b82cb5d..658652a82 100644 --- a/core/include/vc/core/io/TIFFIO.hpp +++ b/core/include/vc/core/io/TIFFIO.hpp @@ -47,11 +47,37 @@ enum class Compression { JP2000 = 34712 }; +/** + * @brief Read a TIFF file + * + * Reads Gray, Gray+Alpha, RGB, and RGBA TIFF images. Supports 8, 16, and + * 32-bit integer types as well as 32-bit float types. 3 and 4 channel images + * will be returned with a BGR channel order, except for 8-bit and 16-bit + * signed integer types which will be returned with an RGB channel order. + * + * Only supports single image TIFF files with scanline encoding and a + * contiguous planar configuration (this matches the format written by + * WriteTIFF). Unless you need to read some obscure image type (e.g. 32-bit + * float or signed integer images), it's generally preferable to use cv::imread. + * + * If the raw size of the image (width x height x channels x bytes-per-sample) + * is >= 4GB, the TIFF will be written using the BigTIFF extension to the TIFF + * format. + * + * @param path Path to TIFF file + * @throws std::runtime_error Unrecoverable read errors + */ +auto ReadTIFF(const volcart::filesystem::path& path) -> cv::Mat; + /** * @brief Write a TIFF image to file * - * Supports writing floating point and signed integer TIFFs, in addition to - * unsigned 8 & 16 bit integer types. Also supports 1-4 channel images. + * Writes Gray, Gray+Alpha, RGB, and RGBA TIFF images. Supports 8, 16, and + * 32-bit integer types as well as 32-bit float types. 3 and 4 channel images + * are assumed to have a BGR channel order, except for 8-bit and 16-bit signed + * integer types which are not supported. + * + * @throws std::runtime_error All writing errors */ void WriteTIFF( const volcart::filesystem::path& path, diff --git a/core/src/PerPixelMap.cpp b/core/src/PerPixelMap.cpp index e8fb237f8..6fa9828ca 100644 --- a/core/src/PerPixelMap.cpp +++ b/core/src/PerPixelMap.cpp @@ -100,12 +100,18 @@ auto PerPixelMap::ReadPPM(const fs::path& path) -> PerPixelMap ppm.height_ = ppm.map_.height(); ppm.width_ = ppm.map_.width(); - ppm.mask_ = cv::imread(MaskPath(path).string(), cv::IMREAD_GRAYSCALE); + auto maskPath = MaskPath(path); + if (fs::exists(maskPath)) { + ppm.mask_ = cv::imread(maskPath.string(), cv::IMREAD_GRAYSCALE); + } if (ppm.mask_.empty()) { Logger()->warn("Failed to read mask: {}", MaskPath(path).string()); } - ppm.cellMap_ = cv::imread(CellMapPath(path).string(), cv::IMREAD_UNCHANGED); + auto cellMapPath = CellMapPath(path); + if (fs::exists(cellMapPath)) { + ppm.cellMap_ = tiffio::ReadTIFF(cellMapPath); + } if (ppm.cellMap_.empty()) { Logger()->warn( "Failed to read cell map: {}", CellMapPath(path).string()); diff --git a/core/src/TIFFIO.cpp b/core/src/TIFFIO.cpp index 957016a55..f0ebfce06 100644 --- a/core/src/TIFFIO.cpp +++ b/core/src/TIFFIO.cpp @@ -6,6 +6,7 @@ #include "vc/core/Version.hpp" #include "vc/core/io/FileExtensionFilter.hpp" +#include "vc/core/util/Logging.hpp" // Wrapping in a namespace to avoid define collisions namespace lt @@ -13,9 +14,124 @@ namespace lt #include } +namespace vc = volcart; namespace tio = volcart::tiffio; namespace fs = volcart::filesystem; +namespace +{ +// Return a CV Mat type using TIF type (signed, unsigned, float), +// bit-depth, and number of channels +auto GetCVMatType( + const uint16_t tifType, const uint16_t depth, const uint16_t channels) + -> int +{ + switch (depth) { + case 8: + if (tifType == SAMPLEFORMAT_INT) { + return CV_MAKETYPE(CV_8S, channels); + } else { + return CV_MAKETYPE(CV_8U, channels); + } + case 16: + if (tifType == SAMPLEFORMAT_INT) { + return CV_MAKETYPE(CV_16S, channels); + } else { + return CV_MAKETYPE(CV_16U, channels); + } + case 32: + if (tifType == SAMPLEFORMAT_INT) { + return CV_MAKETYPE(CV_32S, channels); + } else { + return CV_MAKETYPE(CV_32F, channels); + } + default: + return CV_8UC3; + } +} + +constexpr std::size_t MAX_TIFF_BYTES{4'294'967'296}; +constexpr std::size_t BITS_PER_BYTE{8}; + +inline auto NeedBigTIFF( + std::size_t w, std::size_t h, std::size_t cns, std::size_t bps) -> bool +{ + const std::size_t bytes = w * h * cns * bps / BITS_PER_BYTE; + return bytes >= MAX_TIFF_BYTES; +} + +} // namespace + +auto tio::ReadTIFF(const volcart::filesystem::path& path) -> cv::Mat +{ + // Make sure input file exists + if (!fs::exists(path)) { + throw std::runtime_error("File does not exist"); + } + + // Open the file read-only + lt::TIFF* tif = lt::TIFFOpen(path.c_str(), "r"); + if (tif == nullptr) { + throw std::runtime_error("Failed to open tif"); + } + + // Get metadata + uint32_t width = 0; + uint32_t height = 0; + uint16_t type = 1; + uint16_t depth = 1; + uint16_t channels = 1; + uint16_t config = 0; + TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width); + TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height); + TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &type); + TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &depth); + TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &channels); + TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &config); + auto cvType = ::GetCVMatType(type, depth, channels); + + // Construct the mat + auto h = static_cast(height); + auto w = static_cast(width); + cv::Mat img = cv::Mat::zeros(h, w, cvType); + + // Read the rows + auto bufferSize = static_cast(lt::TIFFScanlineSize(tif)); + std::vector buffer(bufferSize + 4); + if (config == PLANARCONFIG_CONTIG) { + for (auto row = 0; row < height; row++) { + lt::TIFFReadScanline(tif, &buffer[0], row); + std::memcpy(img.ptr(row), &buffer[0], bufferSize); + } + } else if (config == PLANARCONFIG_SEPARATE) { + std::runtime_error( + "Unsupported TIFF planar configuration: PLANARCONFIG_SEPARATE"); + } + + // Do channel conversion + auto cvtNeeded = img.channels() == 3 or img.channels() == 4; + auto cvtSupported = img.depth() != CV_8S and img.depth() != CV_16S and + img.depth() != CV_32S; + if (cvtNeeded) { + if (cvtSupported) { + if (img.channels() == 3) { + cv::cvtColor(img, img, cv::COLOR_RGB2BGR); + } else if (img.channels() == 4) { + cv::cvtColor(img, img, cv::COLOR_RGBA2BGRA); + } + } else { + vc::Logger()->warn( + "[TIFFIO] RGB->BGR conversion for signed 8-bit and 16-bit " + "images is not supported. Image will be loaded with RGB " + "element order."); + } + } + + lt::TIFFClose(tif); + + return img; +} + // Write a TIFF to a file. This implementation heavily borrows from how OpenCV's // TIFFEncoder writes to the TIFF void tio::WriteTIFF( @@ -88,8 +204,34 @@ void tio::WriteTIFF( throw std::runtime_error("Unsupported number of channels"); } + // Get working copy with converted channels if an RGB-type image + auto cvtNeeded = img.channels() == 3 or img.channels() == 4; + auto cvtSupported = img.depth() != CV_8S and img.depth() != CV_16S and + img.depth() != CV_32S; + cv::Mat imgCopy; + if (cvtNeeded and cvtSupported) { + if (img.channels() == 3) { + cv::cvtColor(img, imgCopy, cv::COLOR_BGR2RGB); + } else if (img.channels() == 4) { + cv::cvtColor(img, imgCopy, cv::COLOR_BGRA2RGBA); + } + } else if (cvtNeeded) { + throw std::runtime_error( + "BGR->RGB conversion for signed 8-bit and 16-bit images is not " + "supported."); + } else { + imgCopy = img; + } + + // Estimated file size in bytes + auto useBigTIFF = ::NeedBigTIFF(width, height, channels, bitsPerSample); + if (useBigTIFF) { + Logger()->warn("File estimate >= 4GB. Writing as BigTIFF."); + } + // Open the file - auto out = lt::TIFFOpen(path.c_str(), "w"); + const std::string mode = (useBigTIFF) ? "w8" : "w"; + auto* out = lt::TIFFOpen(path.c_str(), mode.c_str()); if (out == nullptr) { throw std::runtime_error("Failed to open file for writing"); } @@ -122,16 +264,6 @@ void tio::WriteTIFF( auto bufferSize = static_cast(lt::TIFFScanlineSize(out)); std::vector buffer(bufferSize + 32); - // Get working copy with converted channels if an RGB-type image - cv::Mat imgCopy; - if (img.channels() == 3) { - cv::cvtColor(img, imgCopy, cv::COLOR_BGR2RGB); - } else if (img.channels() == 4) { - cv::cvtColor(img, imgCopy, cv::COLOR_BGRA2RGBA); - } else { - imgCopy = img; - } - // For each row for (unsigned row = 0; row < height; row++) { std::memcpy(&buffer[0], imgCopy.ptr(row), bufferSize); diff --git a/core/test/PerPixelMapTest.cpp b/core/test/PerPixelMapTest.cpp index 7160d4b5e..881004c2b 100644 --- a/core/test/PerPixelMapTest.cpp +++ b/core/test/PerPixelMapTest.cpp @@ -7,14 +7,24 @@ using namespace volcart; TEST(PerPixelMap, WriteRead) { // Build a PPM - PerPixelMap ppm(10, 10); + PerPixelMap ppm(100, 100); + cv::Mat mask = cv::Mat::zeros(100, 100, CV_8UC1); + cv::Mat cellMap = cv::Mat(100, 100, CV_32SC1); + cellMap = cv::Scalar::all(-1); for (auto y = 0; y < 10; ++y) { for (auto x = 0; x < 10; ++x) { + auto outY = 46 + y; + auto outX = 46 + x; auto dx = static_cast(x); auto dy = static_cast(y); - ppm(y, x) = {dx, dy, (dx + dy) / 2.0, dx, dy, (dx + dy) / 2.0}; + ppm(outY, outX) = {dx, dy, (dx + dy) / 2.0, + dx, dy, (dx + dy) / 2.0}; + mask.at(outY, outX) = 255U; + cellMap.at(outY, outX) = y + 10; } } + ppm.setMask(mask); + ppm.setCellMap(cellMap); // Write the PPM std::string path{"vc_core_PerPixelMap_WriteRead.ppm"}; @@ -25,9 +35,17 @@ TEST(PerPixelMap, WriteRead) EXPECT_NO_THROW(result = PerPixelMap::ReadPPM(path)); // Test the values - for (auto y = 0; y < 10; ++y) { - for (auto x = 0; x < 10; ++x) { + for (auto y = 0; y < 100; ++y) { + for (auto x = 0; x < 100; ++x) { EXPECT_EQ(result(y, x), ppm(y, x)); } } + + // Test the mask + cv::Mat diff = ppm.mask() != result.mask(); + EXPECT_EQ(cv::countNonZero(diff), 0); + + // Test the cell map + diff = ppm.cellMap() != result.cellMap(); + EXPECT_EQ(cv::countNonZero(diff), 0); } \ No newline at end of file diff --git a/core/test/TIFFIOTest.cpp b/core/test/TIFFIOTest.cpp new file mode 100644 index 000000000..469a975f9 --- /dev/null +++ b/core/test/TIFFIOTest.cpp @@ -0,0 +1,533 @@ +#include + +#include +#include + +#include + +#include "vc/core/io/TIFFIO.hpp" + +using namespace volcart::tiffio; +namespace fs = volcart::filesystem; + +namespace +{ +template < + typename Tp, + int Cn = 1, + std::enable_if_t, bool> = true> +void FillRandom( + cv::Mat& mat, + Tp low = std::numeric_limits::min(), + Tp high = std::numeric_limits::max()) +{ + using PixelT = cv::Vec; + static std::random_device device; + static std::uniform_int_distribution dist(low, high); + static std::default_random_engine gen(device()); + + std::generate(mat.begin(), mat.end(), []() { + PixelT pixel; + for (int i = 0; i < Cn; i++) { + pixel[i] = dist(gen); + } + return pixel; + }); +} + +template < + typename Tp, + int Cn = 1, + std::enable_if_t, bool> = true> +void FillRandom(cv::Mat& mat, Tp low = 0, Tp high = 1) +{ + using PixelT = cv::Vec; + static std::random_device device; + static std::uniform_real_distribution dist(low, high); + static std::default_random_engine gen(device()); + + std::generate(mat.begin(), mat.end(), []() { + PixelT pixel; + for (int i = 0; i < Cn; i++) { + pixel[i] = dist(gen); + } + return pixel; + }); +} + +const cv::Size TEST_IMG_SIZE(10, 10); +} // namespace + +TEST(TIFFIO, WriteRead8UC1) +{ + using ElemT = std::uint8_t; + using PixelT = ElemT; + auto cvType = CV_8UC1; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_WriteRead_" + cv::typeToString(cvType) + ".tif"); + WriteTIFF(imgPath, img); + auto result = ReadTIFF(imgPath); + + EXPECT_EQ(result.size, img.size); + EXPECT_EQ(result.type(), img.type()); + + auto equal = std::equal( + result.begin(), result.end(), img.begin()); + EXPECT_TRUE(equal); +} + +TEST(TIFFIO, WriteRead8UC2) +{ + using ElemT = std::uint8_t; + using PixelT = cv::Vec; + auto cvType = CV_8UC2; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_WriteRead_" + cv::typeToString(cvType) + ".tif"); + WriteTIFF(imgPath, img); + auto result = ReadTIFF(imgPath); + + EXPECT_EQ(result.size, img.size); + EXPECT_EQ(result.type(), img.type()); + + auto equal = std::equal( + result.begin(), result.end(), img.begin()); + EXPECT_TRUE(equal); +} + +TEST(TIFFIO, WriteRead8UC3) +{ + using ElemT = std::uint8_t; + using PixelT = cv::Vec; + auto cvType = CV_8UC3; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_WriteRead_" + cv::typeToString(cvType) + ".tif"); + WriteTIFF(imgPath, img); + auto result = ReadTIFF(imgPath); + + EXPECT_EQ(result.size, img.size); + EXPECT_EQ(result.type(), img.type()); + + auto equal = std::equal( + result.begin(), result.end(), img.begin()); + EXPECT_TRUE(equal); +} + +TEST(TIFFIO, WriteRead8UC4) +{ + using ElemT = std::uint8_t; + using PixelT = cv::Vec; + auto cvType = CV_8UC4; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_WriteRead_" + cv::typeToString(cvType) + ".tif"); + WriteTIFF(imgPath, img); + auto result = ReadTIFF(imgPath); + + EXPECT_EQ(result.size, img.size); + EXPECT_EQ(result.type(), img.type()); + + auto equal = std::equal( + result.begin(), result.end(), img.begin()); + EXPECT_TRUE(equal); +} + +TEST(TIFFIO, WriteRead8SC1) +{ + using ElemT = std::int8_t; + using PixelT = ElemT; + auto cvType = CV_8SC1; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_WriteRead_" + cv::typeToString(cvType) + ".tif"); + WriteTIFF(imgPath, img); + auto result = ReadTIFF(imgPath); + + EXPECT_EQ(result.size, img.size); + EXPECT_EQ(result.type(), img.type()); + + auto equal = std::equal( + result.begin(), result.end(), img.begin()); + EXPECT_TRUE(equal); +} + +TEST(TIFFIO, WriteRead8SC2) +{ + using ElemT = std::int8_t; + using PixelT = cv::Vec; + auto cvType = CV_8SC2; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_WriteRead_" + cv::typeToString(cvType) + ".tif"); + WriteTIFF(imgPath, img); + auto result = ReadTIFF(imgPath); + + EXPECT_EQ(result.size, img.size); + EXPECT_EQ(result.type(), img.type()); + + auto equal = std::equal( + result.begin(), result.end(), img.begin()); + EXPECT_TRUE(equal); +} + +TEST(TIFFIO, Write8SC3) +{ + using ElemT = std::int8_t; + auto cvType = CV_8SC3; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_Write_" + cv::typeToString(cvType) + ".tif"); + EXPECT_THROW(WriteTIFF(imgPath, img), std::runtime_error); +} + +TEST(TIFFIO, Write8SC4) +{ + using ElemT = std::int8_t; + auto cvType = CV_8SC4; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_Write_" + cv::typeToString(cvType) + ".tif"); + EXPECT_THROW(WriteTIFF(imgPath, img), std::runtime_error); +} + +TEST(TIFFIO, WriteRead16UC1) +{ + using ElemT = std::uint16_t; + using PixelT = ElemT; + auto cvType = CV_16UC1; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_WriteRead_" + cv::typeToString(cvType) + ".tif"); + WriteTIFF(imgPath, img); + auto result = ReadTIFF(imgPath); + + EXPECT_EQ(result.size, img.size); + EXPECT_EQ(result.type(), img.type()); + + auto equal = std::equal( + result.begin(), result.end(), img.begin()); + EXPECT_TRUE(equal); +} + +TEST(TIFFIO, WriteRead16UC2) +{ + using ElemT = std::uint16_t; + using PixelT = cv::Vec; + auto cvType = CV_16UC2; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_WriteRead_" + cv::typeToString(cvType) + ".tif"); + WriteTIFF(imgPath, img); + auto result = ReadTIFF(imgPath); + + EXPECT_EQ(result.size, img.size); + EXPECT_EQ(result.type(), img.type()); + + auto equal = std::equal( + result.begin(), result.end(), img.begin()); + EXPECT_TRUE(equal); +} + +TEST(TIFFIO, WriteRead16UC3) +{ + using ElemT = std::uint16_t; + using PixelT = cv::Vec; + auto cvType = CV_16UC3; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_WriteRead_" + cv::typeToString(cvType) + ".tif"); + WriteTIFF(imgPath, img); + auto result = ReadTIFF(imgPath); + + EXPECT_EQ(result.size, img.size); + EXPECT_EQ(result.type(), img.type()); + + auto equal = std::equal( + result.begin(), result.end(), img.begin()); + EXPECT_TRUE(equal); +} + +TEST(TIFFIO, WriteRead16UC4) +{ + using ElemT = std::uint16_t; + using PixelT = cv::Vec; + auto cvType = CV_16UC4; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_WriteRead_" + cv::typeToString(cvType) + ".tif"); + WriteTIFF(imgPath, img); + auto result = ReadTIFF(imgPath); + + EXPECT_EQ(result.size, img.size); + EXPECT_EQ(result.type(), img.type()); + + auto equal = std::equal( + result.begin(), result.end(), img.begin()); + EXPECT_TRUE(equal); +} + +TEST(TIFFIO, WriteRead16SC1) +{ + using ElemT = std::int16_t; + using PixelT = ElemT; + auto cvType = CV_16SC1; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_WriteRead_" + cv::typeToString(cvType) + ".tif"); + WriteTIFF(imgPath, img); + auto result = ReadTIFF(imgPath); + + EXPECT_EQ(result.size, img.size); + EXPECT_EQ(result.type(), img.type()); + + auto equal = std::equal( + result.begin(), result.end(), img.begin()); + EXPECT_TRUE(equal); +} + +TEST(TIFFIO, WriteRead16SC2) +{ + using ElemT = std::int16_t; + using PixelT = cv::Vec; + auto cvType = CV_16SC2; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_WriteRead_" + cv::typeToString(cvType) + ".tif"); + WriteTIFF(imgPath, img); + auto result = ReadTIFF(imgPath); + + EXPECT_EQ(result.size, img.size); + EXPECT_EQ(result.type(), img.type()); + + auto equal = std::equal( + result.begin(), result.end(), img.begin()); + EXPECT_TRUE(equal); +} + +TEST(TIFFIO, Write16SC3) +{ + using ElemT = std::int16_t; + auto cvType = CV_16SC3; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_Write_" + cv::typeToString(cvType) + ".tif"); + EXPECT_THROW(WriteTIFF(imgPath, img), std::runtime_error); +} + +TEST(TIFFIO, Write16SC4) +{ + using ElemT = std::int16_t; + auto cvType = CV_16SC4; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_Write_" + cv::typeToString(cvType) + ".tif"); + EXPECT_THROW(WriteTIFF(imgPath, img), std::runtime_error); +} + +TEST(TIFFIO, WriteRead32SC1) +{ + using ElemT = std::int32_t; + using PixelT = ElemT; + auto cvType = CV_32SC1; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_WriteRead_" + cv::typeToString(cvType) + ".tif"); + WriteTIFF(imgPath, img); + auto result = ReadTIFF(imgPath); + + EXPECT_EQ(result.size, img.size); + EXPECT_EQ(result.type(), img.type()); + + auto equal = std::equal( + result.begin(), result.end(), img.begin()); + EXPECT_TRUE(equal); +} + +TEST(TIFFIO, WriteRead32SC2) +{ + using ElemT = std::int32_t; + using PixelT = cv::Vec; + auto cvType = CV_32SC2; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_WriteRead_" + cv::typeToString(cvType) + ".tif"); + WriteTIFF(imgPath, img); + auto result = ReadTIFF(imgPath); + + EXPECT_EQ(result.size, img.size); + EXPECT_EQ(result.type(), img.type()); + + auto equal = std::equal( + result.begin(), result.end(), img.begin()); + EXPECT_TRUE(equal); +} + +TEST(TIFFIO, Write32SC3) +{ + using ElemT = std::int32_t; + auto cvType = CV_32SC3; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_Write_" + cv::typeToString(cvType) + ".tif"); + EXPECT_THROW(WriteTIFF(imgPath, img), std::runtime_error); +} + +TEST(TIFFIO, Write32SC4) +{ + using ElemT = std::int32_t; + auto cvType = CV_32SC4; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_Write_" + cv::typeToString(cvType) + ".tif"); + EXPECT_THROW(WriteTIFF(imgPath, img), std::runtime_error); +} + +TEST(TIFFIO, WriteRead32FC1) +{ + using ElemT = std::float_t; + using PixelT = ElemT; + auto cvType = CV_32FC1; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_WriteRead_" + cv::typeToString(cvType) + ".tif"); + WriteTIFF(imgPath, img); + auto result = ReadTIFF(imgPath); + + EXPECT_EQ(result.size, img.size); + EXPECT_EQ(result.type(), img.type()); + + auto equal = std::equal( + result.begin(), result.end(), img.begin()); + EXPECT_TRUE(equal); +} + +TEST(TIFFIO, WriteRead32FC2) +{ + using ElemT = std::float_t; + using PixelT = cv::Vec; + auto cvType = CV_32FC2; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_WriteRead_" + cv::typeToString(cvType) + ".tif"); + WriteTIFF(imgPath, img); + auto result = ReadTIFF(imgPath); + + EXPECT_EQ(result.size, img.size); + EXPECT_EQ(result.type(), img.type()); + + auto equal = std::equal( + result.begin(), result.end(), img.begin()); + EXPECT_TRUE(equal); +} + +TEST(TIFFIO, WriteRead32FC3) +{ + using ElemT = std::float_t; + using PixelT = cv::Vec; + auto cvType = CV_32FC3; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_WriteRead_" + cv::typeToString(cvType) + ".tif"); + WriteTIFF(imgPath, img); + auto result = ReadTIFF(imgPath); + + EXPECT_EQ(result.size, img.size); + EXPECT_EQ(result.type(), img.type()); + + auto equal = std::equal( + result.begin(), result.end(), img.begin()); + EXPECT_TRUE(equal); +} + +TEST(TIFFIO, WriteRead32FC4) +{ + using ElemT = std::float_t; + using PixelT = cv::Vec; + auto cvType = CV_32FC4; + + cv::Mat img(::TEST_IMG_SIZE, cvType); + ::FillRandom(img); + + const fs::path imgPath( + "vc_core_TIFFIO_WriteRead_" + cv::typeToString(cvType) + ".tif"); + WriteTIFF(imgPath, img); + auto result = ReadTIFF(imgPath); + + EXPECT_EQ(result.size, img.size); + EXPECT_EQ(result.type(), img.type()); + + auto equal = std::equal( + result.begin(), result.end(), img.begin()); + EXPECT_TRUE(equal); +} \ No newline at end of file