【问题标题】:OpenCV N-Channel ImageOpenCV N 通道图像
【发布时间】:2020-10-27 16:38:29
【问题描述】:

我想使用 C++ API 创建一个 8 通道的 openCV 图像:

 cv::Mat lookupTable = cv::Mat(height, width, CV_32FC(8), cv::Scalar::all(-1.f));

我得到: external/opencv/modules/core/src/array.cpp:3229: error: (-215:Assertion failed) cn <= 4 in function 'scalarToRawData'

我很确定 OpenCV 支持多通道 >4 图像。我错了吗?


更新 1: 显然它与初始化值cv::Scalar::all(-1.f)有关。仅限于cv::Vec<T,4>。然而,

cv::Vec<float, 8> a = {-1.f, -1.f, -1.f, -1.f, 1.f, -1.f, -1.f, -1.f};
cv::Mat lookupTable = cv::Mat(height, width, CV_32FC(8), a);

也不支持,并且失败并出现同样的错误。


更新 2:

cv::Mat lookupTable   = cv::Mat(height, width, CV_32FC(8));
lookupTable.setTo(-1);

编译并运行,但我还没有验证 all 值是否真的初始化为 -1。 但是,下一个问题是 cv::imwrite() 无法保存该图像。

哦,伙计..真是一团糟!


更新 3:好的,我可以确认 lookupTable.setTo(-1) 有效,并且所有值都已相应初始化。此外,保存该图像的方法是通过cv::FileStorage

cv::FileStorage fs("lookup.ext", cv::FileStorage::WRITE_BASE64);
fs.write("LUT",lookupTable);

但是,这是以文本格式保存的,似乎无法以二进制格式存储:-(


更新 4: 好的,将.gz 附加到扩展名即可压缩文件。

cv::FileStorage fs("lookup.ext.gz", cv::FileStorage::WRITE_BASE64);

【问题讨论】:

  • 你好。您可以在 n 个通道中拆分图像并将它们保存在不同的文件中。然后你必须在读取通道时重建图像。
  • 嗨让-马克!是的,我知道这个解决方案。我可以这样做,但是,这不是我打算在这里做的。我看到我可以将通用 cv::Mats 保存为 JSON/XML。想知道是否还有将这些存储为二进制文件的选项。
  • TIFF 可能是保存多通道图像的最佳格式,并且通常是多/高光谱卫星图像的首选格式。
  • 谢谢马克!我希望直接用 OpenCV 做到这一点。我知道tiff 格式支持多通道图像,但我相信不是通过 OpenCV API。

标签: c++ opencv


【解决方案1】:

好的,我拼凑了一些没有错误的东西,并且似乎可以被 libtiff 工具接受,但除了tiff_dump 工具之外,我不知道有任何应用程序可以读取创建的文件。

我实现了@haraldK 对this answer 的评论。

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

typedef Vec<float,8> Vec8f;

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;
   int channels       = image.channels();
   int bytespersample = image.elemSize1();

   // Check what we've got
   std::cout << "width: " << width << std::endl;
   std::cout << "height: " << height << std::endl;
   std::cout << "channels: " << channels << std::endl;
   std::cout << "bytespersample: " << bytespersample << std::endl;

   const char *ink_names = "Chan0\0Chan1\0Chan2\0Chan3\0Chan4\0Chan5\0Chan6\0Chan7\0";
   const int ink_names_size = 48;

   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);
   TIFFSetField(tif, TIFFTAG_INKSET, 2);
   TIFFSetField(tif, TIFFTAG_INKNAMES, ink_names_size, ink_names);

   // 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
      Vec8f* ptr = image.ptr<Vec8f>(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 an 8-channel image of floats
    cv::Mat multichannel(800, 600, CV_32FC(8));

    // Check number of channels
    std::cout << "Channels: " << multichannel.channels() << std::endl;

    // Save as TIFF
    tiffwrite("result.tif",multichannel);
}

tiff dump 实用程序似乎对文件感到满意:

tiffdump result.tif 

result.tif:
Magic: 0x4949 <little-endian> Version: 0x2a <ClassicTIFF>
Directory 0: offset 66408 (0x10368) next 0 (0)
ImageWidth (256) SHORT (3) 1<600>
ImageLength (257) SHORT (3) 1<800>
BitsPerSample (258) SHORT (3) 8<32 32 32 32 32 32 32 32>
Compression (259) SHORT (3) 1<5>
Photometric (262) SHORT (3) 1<5>
StripOffsets (273) LONG (4) 100<8 672 1336 2000 2664 3328 3992 4656 5320 5984 6648 7312 7976 8640 9304 9968 10632 11296 11960 12624 13288 13952 14616 15280 ...>
SamplesPerPixel (277) SHORT (3) 1<8>
RowsPerStrip (278) SHORT (3) 1<8>
StripByteCounts (279) LONG (4) 100<664 664 664 664 664 664 664 664 664 664 664 664 664 664 664 664 664 664 664 664 664 664 664 664 ...>
PlanarConfig (284) SHORT (3) 1<1>
InkSet (332) SHORT (3) 1<2>
InkNames (333) ASCII (2) 48<Chan0\0Chan1\0Chan2\0Chan3\0 ...>

【讨论】:

  • 哇,感谢您提供的漂亮代码 sn-p!我试试看。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-07-26
  • 1970-01-01
  • 2020-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多