使用 OpenCV 写入 16 位未压缩图像

Posted

技术标签:

【中文标题】使用 OpenCV 写入 16 位未压缩图像【英文标题】:Writing 16 bit uncompressed image using OpenCV 【发布时间】:2012-08-22 12:34:33 【问题描述】:

如何在不进行任何压缩的情况下保存 16 位图像(PNG 或 TIFF)?语法是什么?

【问题讨论】:

为什么人们不赞成这个问题? @onitake 我没有,但我只能假设,因为 OP 似乎在问一个基本问题而没有做功课。根据常见问题解答中的"How to Ask" 条目,用户必须在此处提问之前彻底研究问题并在问题中分享他们的结果。不这样做可能会导致该问题收到的反对票和接近票。我不能说我完全不同意他们。 您应该检查 OpenCV 源代码。许多此类高级功能可用,但未记录在案。也许一些额外的 imwrite 参数会让你到达那里 也许 OP 没有搜索,但这不是一个微不足道的问题。 【参考方案1】:

2 年后,OpenCV 可以通过压缩 (LZW) 保存 16 位 TIFF(2.4.x 版本)。但它也支持函数中未记录的参数 TIFFTAG_COMPRESSION

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

例如,它可以设置为 COMPRESSION_LZMA(并且工作正常)。

您可以使用 COMPRESSION_NONE 作为此参数的值(还必须将 TIFFTAG_ROWSPERSTRIP 参数设置为图像高度)。

但即使在 2.4.9 版本中,OpenCV 在这种情况下仍然会生成不正确的 TIFF(尝试将 TIFFTAG_COMPRESSION 设置为 COMPRESSION_NONE)。

查看 OpenCV 源代码后,我发现了两种方法来克服这个限制:

    使用“opencv/modules/imgcodecs/src/grfmt_tiff.cpp”中的更改重新编译 OpenCV——来自 OpenCV 的人添加了压缩参数,但忘记了如果我们不这样做,libtiff 无法在 TIFF 句柄中设置预测器这一事实想要压缩。你应该找到这个代码

    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;

    并将其更改为

    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;

    在此之后 OpenCV 可以保存压缩和未压缩的 TIFF(取决于 imwrite 的参数)。

    另一种方法是在应用程序中直接使用 libtiff 来保存 TIFF。当然在这种情况下你需要实现写功能。对于 Windows,您应该编译 libtiff,但这是一个相当简单的任务(libtiff 包含许多编译器的 makefile,并且可以在 32 位和 64 位的 MinGW 和 MSVC 中完美编译)。

【讨论】:

【参考方案2】:

至少从 2.1(未使用旧版本)开始,cvSaveImage 可以毫无问题地保存单通道 16b 深度图像。我通常为此使用 .png。

这将为您提供带有“somedata”的 16b 图像:

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

【讨论】:

很好的发现!谷歌似乎忘记了更新的文档? Here's the relevant docs page【参考方案3】:

根据 OpenCV 文档,cvSaveImage 仅支持 8bit 单通道或 BGR 图像。 See 2.0 docs.

您应该使用 libpng、libMagick++ 或其他库以其他位深度和格式保存。

libpng 文档包含关于how to write PNG images 的教程。要指定每通道 16 位数据的 RGB,请使用 bit_depth = 16 和 color_type = PNG_COLOR_TYPE_RGB。

为了让你的生活(可能)更轻松一点,还有png++。

编辑: OpenCV 似乎支持在较新版本中写入 16bpc 图像。请参阅 p.streef 的答案。这里是the link to the relevant documentation again.

【讨论】:

【参考方案4】:

如果其他人遇到这个 avtomaton 的解决方案,似乎是通过拉取请求添加的 https://github.com/opencv/opencv/pull/3744,

允许您执行类似的操作(适用于 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);

【讨论】:

【参考方案5】:

我的经验是 OpenCV 将图像保存为 16 位(没有尝试使用 3 通道)。 pgm 格式确实有效,它是一种真正的原始格式(没有运行长度压缩)。

【讨论】:

以上是关于使用 OpenCV 写入 16 位未压缩图像的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV - arm/android 手机上的 JPEG 压缩不会产生正确的图像

在 Python 中为 OpenCV 视频对象指定压缩质量

估计图像 Opencv 的亮度

OpenCV图像金字塔

OpenCV教程(11)-- 图像压缩操作

在 OpenCV 中保存原始图像