Writing 16 bit uncompressed image using OpenCV

13,332

Solution 1

After 2 years OpenCV can save 16-bit TIFFs (2.4.x versions) with compression (LZW). But it also supports undocumented parameter TIFFTAG_COMPRESSION in function

bool imwrite(const string& filename, InputArray img, const vector<int>& params=vector<int>() )

It can be set to COMPRESSION_LZMA, for example (and working correctly).

You can use COMPRESSION_NONE as a value for this parameter (also you have to set TIFFTAG_ROWSPERSTRIP parameter to image height).

But OpenCV still generates incorrect TIFF in that case (trying to set TIFFTAG_COMPRESSION to COMPRESSION_NONE) even in 2.4.9 version.

After looking at OpenCV sources I found 2 ways to overcome this restriction:

  1. Recompile OpenCV with changes in "opencv/modules/imgcodecs/src/grfmt_tiff.cpp" - guys from OpenCV added parameter for compression, but forgot about the fact that libtiff can't set predictor in TIFF handle if we don't want compression. You should find this code

    if ( !TIFFSetField(pTiffHandle, TIFFTAG_IMAGEWIDTH, width) || !TIFFSetField(pTiffHandle, TIFFTAG_IMAGELENGTH, height) || !TIFFSetField(pTiffHandle, TIFFTAG_BITSPERSAMPLE, bitsPerChannel) || !TIFFSetField(pTiffHandle, TIFFTAG_COMPRESSION, compression) || !TIFFSetField(pTiffHandle, TIFFTAG_PHOTOMETRIC, colorspace) || !TIFFSetField(pTiffHandle, TIFFTAG_SAMPLESPERPIXEL, channels) || !TIFFSetField(pTiffHandle, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG) || !TIFFSetField(pTiffHandle, TIFFTAG_ROWSPERSTRIP, rowsPerStrip) || !TIFFSetField(pTiffHandle, TIFFTAG_PREDICTOR, predictor) ) { TIFFClose(pTiffHandle); return false; }

    and change it to

    if ( !TIFFSetField(pTiffHandle, TIFFTAG_IMAGEWIDTH, width) || !TIFFSetField(pTiffHandle, TIFFTAG_IMAGELENGTH, height) || !TIFFSetField(pTiffHandle, TIFFTAG_BITSPERSAMPLE, bitsPerChannel) || !TIFFSetField(pTiffHandle, TIFFTAG_COMPRESSION, compression) || !TIFFSetField(pTiffHandle, TIFFTAG_PHOTOMETRIC, colorspace) || !TIFFSetField(pTiffHandle, TIFFTAG_SAMPLESPERPIXEL, channels) || !TIFFSetField(pTiffHandle, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG) || !TIFFSetField(pTiffHandle, TIFFTAG_ROWSPERSTRIP, rowsPerStrip) ) { TIFFClose(pTiffHandle); return false; } if (compression != COMPRESSION_NONE && !TIFFSetField(pTiffHandle, TIFFTAG_PREDICTOR, predictor) ) { TIFFClose(pTiffHandle); return false; }

    after this OpenCV can save compressed and uncompressed TIFFs (depends on parameters to imwrite).

  2. The other way is direct using libtiff in your application for saving TIFFs. Of course you need implement writing function in such case. For windows you should compile libtiff, but it is rather simple task (libtiff contains makefiles for many compilers and perfectly compiles in MinGW and MSVC, 32 and 64 bit).

Solution 2

According to the OpenCV documentation, cvSaveImage only supports 8bit single channel or BGR images. See 2.0 docs.

You should use libpng, libMagick++ or some other library to save in other bit depths and formats.

The libpng documentation contains a tutorial on how to write PNG images. To specify RGB with 16bit per channel data, use bit_depth = 16 and color_type = PNG_COLOR_TYPE_RGB.

To make your life (probably) a little easier, there is also png++.

Edit: OpenCV seems to support writing 16bpc images in newer versions. See the answer from p.streef. Here's the link to the relevant documentation again.

Solution 3

At least from 2.1 (haven't used older versions) cvSaveImage saves a single channel 16b depth image without problems. I usualy use .png for this.

This would get you a 16b image with "somedata":

IplImage* image = cvCreateImage(cvSize(100,100),16,1);
memset(image->imageData,someData,image->width*image->height*2);
cvSaveImage("image.png",image);

Solution 4

In case anyone else runs across this avtomaton's solution seems to have been added via pull request https://github.com/opencv/opencv/pull/3744,

allowing you to do something like (works with 3.2):

cv::Mat shorts(1024*cv::Mat::ones(100, 100, CV_16UC1));
vector<int> tags = {TIFFTAG_COMPRESSION, COMPRESSION_NONE};

bool success = cv::imwrite("uncompressed.tif", shorts, tags);
Share:
13,332
EyalG
Author by

EyalG

Updated on June 21, 2022

Comments

  • EyalG
    EyalG almost 2 years

    How can I save a 16 bit image (PNG or TIFF) without any compression? What is the syntax?