1

I am working on opencv recently. And I need to create image mat and write it to .tif image file. The .tif file should be CMYK mode when it is opened in photoshop for printing.

But when I tried to imread CMYK image file with UNCHANGED type, and then imwrite the mat to .tif file, it’s still RGB mode in photoshop.

Is the image only saved as RGB(or BGR) mode by opencv? (I found some answers that python have library which can choose the CMYK mode to save. But I want to know is there any way to solve this in C++?)


Thanks @Mark. I meet some troubles when I am trying to compile libtiff library in windows10.

I download libtiff with version tiff-4.0.10.zip. And use nmake /f makefile.vc to compile in vs2017. (without any change in nmake.opt).

Then get some .dll .lib file. Link the libtiff_i.lib to library folder and hearder files to include folder in project.

Try @Mark code for testing. Then, I meet error(LNK2019) in vs which looks like link problem.

LNK2019 unresolved external symbol TIFFClose referenced in function "bool __cdecl tiffwrite(char const *,class cv::Mat)" (?tiffwrite@@YA_NPEBDVMat@cv@@@Z)  
LNK2019 unresolved external symbol TIFFSetField referenced in function "bool __cdecl tiffwrite(char const *,class cv::Mat)" (?tiffwrite@@YA_NPEBDVMat@cv@@@Z)       
LNK2019 unresolved external symbol TIFFWriteScanline referenced in function "bool __cdecl tiffwrite(char const *,class cv::Mat)" (?tiffwrite@@YA_NPEBDVMat@cv@@@Z)  
LNK2019 unresolved external symbol TIFFOpen referenced in function "bool __cdecl tiffwrite(char const *,class cv::Mat)" (?tiffwrite@@YA_NPEBDVMat@cv@@@Z)

I am not sure is it the link problem or compiled file (libtiff.dll,libtiff_i.lib) problem?

(Here are link setting in vs2017 :linker lib and include


I fix this problem by using cmake to compile libtiff.

Follow this Reference can get the right library for vs2017 on windows(64-bit).

  • "Primary OpenCV modes are BGR/gray. BGRA is optional. Other modes are not supported in general." - commented alalek, one of the OpenCV maintainers, under a [similar GitHub issue](https://github.com/opencv/opencv/issues/10132#issuecomment-346284835). – HansHirse Oct 29 '19 at 11:47
  • @HansHirse Thanks for your answer. I am trying to figure out this problem in other way. – Sylviaqqqqq Oct 30 '19 at 09:22
  • Ok, it looks like the compilation worked, so your `include` directories are presumably correct, so yes, I agree it looks like a linker problem. I shall dig around and see what I can see. Maybe add `tiff` and `visual-studio` tags to your question to attract the right people. – Mark Setchell Oct 30 '19 at 10:13
  • Do you know why it is `libtiff-i.lib`, specifically the `-i` part? Maybe include a directory listing of the files produced by the `nmake` command... – Mark Setchell Oct 30 '19 at 10:51
  • Actually I don't know why it is `libtiff-i.lib`. I visit some questions and someone says the difference between `libtiff-i.lib` and `libtiff.lib` is static library or not. But both library are enough for use `libtiff`. I am wondering maybe I should change some settings in `nmake.opt` before I compile it. – Sylviaqqqqq Oct 30 '19 at 11:12

1 Answers1

2

I think the proper answer is to code it up using libtiff. I am no expert on libtiff but this seems to work for the cases I have tried:

#include <iostream>
#include <opencv2/opencv.hpp>

extern "C" {
#define uint64 uint64_hack_
#define int64 int64_hack_
#include "tiffio.h"
#undef uint64
#undef uint64
}

using namespace cv;

bool tiffwrite(const char* name, Mat image){

   TIFF* tif = TIFFOpen(name, "w");
   if(tif==NULL){
      std::cerr << "ERROR: Unable to open output file";
      return false;
   }

   int width  = image.cols;
   int height = image.rows;

   // We expect 4 channels
   int channels = image.channels();
   assert(channels==4);

   // We expect 8-bit data
   int bytespersample = image.elemSize1();
   assert(bytespersample == 1);

   TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);
   TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);
   TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
   TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, channels);
   TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW);
   TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8*bytespersample);
   TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, 8);
   TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED);

   // Write to file, 1 scanline at a time
   for (int row = 0; row < height; row++) {
      // std::cerr << "DEBUG: Writing row " << row << std::endl;

      // Get pointer to row
      cv::Vec4b* ptr = image.ptr<cv::Vec4b>(row);

      int ret = TIFFWriteScanline(tif, (unsigned char*)ptr, row, 0);
      if (ret == -1) {
         TIFFClose(tif);
         return false;
      }
   }
   TIFFClose(tif);
   return true;
}

int
main(int argc,char*argv[])
{
    // Create a 4-channel CMYK yellow image
    Mat4b image(480, 640, Vec4b(0,0,255,0));

    // Write as CMYK TIFF
    tiffwrite("result.tif",image);
}

I have also come up with a couple of hacks you may feel like trying.

First and simplest hack, save with OpenCV as usual as a JPEG, TIFF or PNG, then use system() function to shell out to ImageMagick and convert to a CMYK TIFF from there.

cv2::imwrite("intermediate.jpg",image);

system("...");

And inside there you put the equivalent of:

convert intermediate.jpg -colorspace CMYK result.tif

Or if using v7 of ImageMagick

magick intermediate.jpg -colorspace CMYK result.tif

Second hack, if you don't want to install and depend on ImageMagick... there is a tool called raw2tiff that ships with libtiff. You can pass it a raw file (no headers) and it will convert it to a TIFF for you. So you would basically use OpenCV to write the raw C bytes, then the raw M bytes, then the raw Y bytes and the raw K bytes into a single file and use system() to call that utility. I used this command line:

raw2tiff -w 1024 -l 768 -b 4 -i band -c lzw -p cmyk CMYK result.tif

So my image was 1024 pixels wide, 768 pixels tall, consisting of 4 bands in file CMYK and I wanted an LZW-compressed TIFF called result.tif. That opens to look like this in Photoshop:

enter image description here

I made synthetic C, M, Y and K channels with ImageMagick like this:

#!/bin/bash
# Make raw and viewable C channel
convert -size 1024x768 gradient: -depth 8 -write gray:C C.png
# Make raw and viewable M channel
convert -size 1024x768 xc:black  -depth 8 -write gray:M M.png
# Make raw and viewable Y channel
convert -size 768x1024 gradient: -rotate 90 -depth 8 -write gray:Y Y.png
# Make raw and viewable K channel
convert -colorspace RGB -size 400x400 gradient: -gravity center -background black -extent 1024x768 -depth 8 -write gray:K K.png

# Stack side-by-side, just for viewing
convert C.png M.png Y.png K.png  +append CMYK.png

# Concatenate them all together into single file "CMYK" as input file for "raw2tiff"
cat C M Y K > CMYK

Here are the 4 images, appended side-by-side with C on the left, then M, then Y and finally with K on the right.

enter image description here

In your case, you would get the data pointer to your Mat and simply write height*width bytes for each channel to a binary file, rather than use ImageMagick. You can see how to do that in the libtiff example at the top of my answer.

Keywords: TIFF, TIF, image processing, libtiff, CMYK, colour separation, color separation, Photoshop.

Looking at the image, I guess I should have negated (inverted) the image first, but that is not too hard.

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • Thank you so much for your answer. But I meet some troubles when I am trying to compile `libtiff`. I download `libtiff` from http://download.osgeo.org/libtiff/ with version tiff-4.0.10.zip. And I use `nmake /f makefile.vc` to compile in vs2017. Link the libtiff.dll,libtiff_i.lib to library and tif_config.h, tif_dir.h, tiff.h, tiffconf.h, tiffio.h, tiffiop.h, tiffvers.h, uvcode.h to include in project. Try your code in answer for testing. Then, I meet some error(LNK2019) which looks like link problem. I am not sure is it the link problem or compiled file(libtiff.dll,libtiff_i.lib) problem? – Sylviaqqqqq Oct 30 '19 at 09:09
  • I'm really sorry but I don't use Windows or VS2017. Maybe if you screen-grab your VS compiler and linker settings pages and edit it into your question along with the exact error message, I or someone else, may spot something. Don't put it in a comment please (too hard to read) - click `edit` under your question. Thanks. – Mark Setchell Oct 30 '19 at 09:16