如何在 C++ 中创建 CMYK 模式图像?

Posted

技术标签:

【中文标题】如何在 C++ 中创建 CMYK 模式图像?【英文标题】:How to create CMYK mode image in c++? 【发布时间】:2019-10-29 11:33:50 【问题描述】:

我最近正在研究 opencv。我需要创建图像垫并将其写入 .tif 图像文件。 .tif 文件在 photoshop 中打开打印时应为 CMYK 模式。

但是当我尝试将imread CMYK 图像文件与UNCHANGED 类型,然后将imwrite mat 转换为.tif 文件时,它在photoshop 中仍然是RGB 模式。

图像是否仅通过opencv保存为RGB(或BGR)模式? (我找到了一些答案,python 有可以选择 CMYK 模式保存的库。但我想知道有没有办法在 C++ 中解决这个问题?)


感谢@Mark。 当我尝试在 windows10 中编译 libtiff 库时遇到了一些麻烦。

我下载libtiff 版本为tiff-4.0.10.zip。并使用nmake /f makefile.vc在vs2017中编译。 (nmake.opt 没有任何变化)。

然后获取一些 .dll .lib 文件。将 libtiff_i.lib 链接到库文件夹和听者文件以将文件夹包含在项目中。

尝试使用@Mark 代码进行测试。然后,我在 vs 中遇到错误(LNK2019),看起来像链接问题。

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)

我不确定是链接问题还是编译文件(libtiff.dll,libtiff_i.lib)问题?

(这里是vs2017中的链接设置:linker lib and include


我通过使用cmake编译libtiff来解决这个问题。

关注Reference 可以在 windows(64 位)上获得适用于 vs2017 的正确库。

【问题讨论】:

"主要的 OpenCV 模式是 BGR/灰色。BGRA 是可选的。一般不支持其他模式。" - 在similar GitHub issue 下评论了 OpenCV 维护者之一 alalek。 @HansHirse 感谢您的回答。我正在尝试以其他方式解决这个问题。 好的,看起来编译成功了,所以你的include 目录大概是正确的,所以是的,我同意它看起来像一个链接器问题。我将四处挖掘,看看我能看到什么。也许在您的问题中添加tiffvisual-studio 标签以吸引合适的人。 你知道为什么是libtiff-i.lib,特别是-i部分吗?可能包括由nmake 命令生成的文件的目录列表... 其实我也不知道为什么是libtiff-i.lib。我访问了一些问题,有人说libtiff-i.liblibtiff.lib 之间的区别是静态库与否。但是这两个库都足以使用libtiff。我想知道也许我应该在编译之前更改nmake.opt 中的一些设置。 【参考方案1】:

我认为正确的答案是使用libtiff 对其进行编码。我不是libtiff 的专家,但这似乎适用于我尝试过的案例:

#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);


我还提出了一些您可能想尝试的技巧。

第一个也是最简单的 hack,像往常一样使用 OpenCV 保存为 JPEG、TIFF 或 PNG,然后使用 system() 函数将壳输出到 ImageMagick 并转换为来自那里的 CMYK TIFF。

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

system("...");

在里面你放了相当于:

convert intermediate.jpg -colorspace CMYK result.tif

或者如果使用 ImageMagick

的 v7
magick intermediate.jpg -colorspace CMYK result.tif

第二个技巧,如果您不想安装并依赖 ImageMagick... 有一个名为 raw2tiff 的工具随 libtiff 一起提供。您可以将原始文件(无标题)传递给它,它会为您将其转换为 TIFF。因此,您基本上会使用 OpenCV 将原始 C 字节、原始 M 字节、原始 Y 字节和原始 K 字节写入单个文件,并使用 system() 调用该实用程序。我使用了这个命令行:

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

所以我的图像宽 1024 像素,高 768 像素,由文件 CMYK 中的 4 个波段组成,我想要一个名为 result.tif 的 LZW 压缩 TIFF。在 Photoshop 中打开后看起来像这样:

我用 ImageMagick 合成了 C、M、Y 和 K 通道,如下所示:

#!/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

这是 4 张图片,并排附加,左侧是 C,然后是 M,然后是 Y,最后是右侧的 K。

在您的情况下,您将获得指向您的 Matdata 指针,并且只需将每个通道的 height*width 字节写入二进制文件,而不是使用 ImageMagick。您可以在我的答案顶部的 libtiff 示例中看到如何做到这一点。

关键字:TIFF、TIF、图像处理、libtiff、CMYK、分色、分色、Photoshop。

看图像,我想我应该先否定(反转)图像,但这并不太难。

【讨论】:

非常感谢您的回答。但是当我尝试编译libtiff 时遇到了一些麻烦。我从download.osgeo.org/libtiff 下载libtiff,版本为tiff-4.0.10.zip。我使用nmake /f makefile.vc在vs2017中编译。将 libtiff.dll、libtiff_i.lib 链接到库和 tif_config.h、tif_dir.h、tiff.h、tiffconf.h、tiffio.h、tiffiop.h、tiffvers.h、uvcode.h 以包含在项目中。尝试您的代码以进行测试。然后,我遇到了一些看起来像链接问题的错误(LNK2019)。我不确定是链接问题还是编译文件(libtiff.dll,libtiff_i.lib)问题? 很抱歉,我没有使用 Windows 或 VS2017。也许如果您屏幕抓取您的 VS 编译器和链接器设置页面并将其与确切的错误消息一起编辑到您的问题中,我或其他人可能会发现一些东西。请不要将其放在评论中(太难读了)-在您的问题下单击edit。谢谢。

以上是关于如何在 C++ 中创建 CMYK 模式图像?的主要内容,如果未能解决你的问题,请参考以下文章

在PS里如何将RGB模式改成CMYK,还不失去太多颜色啊?或者怎样调整,能与RGB颜色接近!

怎样在AI里将RGB模式转为CMYK模式?

如何防止在 C++ 中创建对象

如何使用 C++ 在 %appdata% 中创建文件 [重复]

如何设法在 C++ 中创建矢量类?

如何在 Xcode 中创建一个新的 C++ 项目?