Search code examples
c++opencvtifflibtiff

I cannot get opencv's tiff compression to work


in opencv 4.10, we exposed all the compression parameters from libtiff:

cv::Mat1f image;
std::vector<int> compression_params = {
  cv::IMWRITE_TIFF_COMPRESSION, cv::IMWRITE_TIFF_COMPRESSION_LZMA
};
cv::imwrite(
    out_dir / str(boost::format{"%08d.tif"} % timestamp), 
    image,
    compression_params
);

the resulting file:

01160780.tif: TIFF image data, little-endian, direntries=11, height=76, bps=32, compression=none, PhotometricIntepretation=BlackIsZero, width=228

there is no compression at all.

But I can actually get PIL to compress it.

I also directly tried the libtiff in c++, which works fine too:

#include <iostream>
#include <vector>
#include <tiffio.h>
#include <cstdlib>
#include <ctime>

void generateRandomMatrix(float* matrix, int width, int height) {
    for (int i = 0; i < width * height; ++i) {
        matrix[i] = static_cast<float>(rand()) / RAND_MAX; // Random float between 0.0 and 1.0
    }
}

void generateGradientMatrix(float* matrix, int width, int height) {
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            matrix[y * width + x] = static_cast<float>(x) / (width - 1); // Gradient from 0.0 to 1.0
        }
    }
}

void saveTiff(
    const std::string& filename,
    const std::vector<float>& floatMatrix,
    const int width,
    const int height,
    const int compression
) {
    TIFF* tif = TIFFOpen(filename.c_str(), "w");
    if (!tif) {
        std::cerr << "Failed to open TIFF file for writing: " << filename << std::endl;
        exit(1);
    }

    // Set TIFF file parameters for 32-bit floating point
    TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);
    TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);
    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32);       // 32 bits per sample
    TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);      // 1 sample per pixel
    TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
    TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
    TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); // grayscale

    // Set the data type to floating point
    TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP);

    // Set compression type
    TIFFSetField(tif, TIFFTAG_COMPRESSION, compression);

    // Write the image data
    for (int row = 0; row < height; ++row) {
        TIFFWriteScanline(
            tif,
            const_cast<float*>(floatMatrix.data() + row * width),
            row,
            0
        );
    }

    // Close TIFF file
    TIFFClose(tif);
}

int main() {
    srand(static_cast<unsigned>(time(0))); // Seed for random number generation

    const int width = 256;
    const int height = 256;
    const int numPixels = width * height;

    // Allocate memory for the float matrix
    std::vector<float> floatMatrix(numPixels);
    generateGradientMatrix(floatMatrix.data(), width, height);

    // Save TIFF with no compression
    saveTiff("gradient_float32_no_compression.tif", floatMatrix, width, height, COMPRESSION_NONE);

    // Save TIFF with LZMA compression
    saveTiff("gradient_float32_lzma.tif", floatMatrix, width, height, COMPRESSION_LZMA);


    std::cout << "TIFF image saved successfully!" << std::endl;

    return 0;
}

here we can see the compressing: unknown 0xffff886d

file gradient_float32_lzma.tif 
gradient_float32_lzma.tif: TIFF image data, little-endian, direntries=11, height=256, bps=32, compression=(unknown 0xffff886d), PhotometricIntepretation=BlackIsZero, orientation=upper-left, width=256

any idea, what might go wrong here?


Solution

  • so for some reason opencv guys artificially imposed this limitation:

                case CV_32F:
                {
                    bitsPerChannel = 32;
                    page_compression = COMPRESSION_NONE;
                    sample_format = SAMPLEFORMAT_IEEEFP;
                    break;
                }
                case CV_64F:
                {
                    bitsPerChannel = 64;
                    page_compression = COMPRESSION_NONE;
                    sample_format = SAMPLEFORMAT_IEEEFP;
                    break;
                }
    

    https://github.com/opencv/opencv/blob/79faf857d988bcbfc993f0a17cd38835d9d243d9/modules/imgcodecs/src/grfmt_tiff.cpp#L1287

    will report to there, to see why they think this is good idea