【问题标题】:Convert RGB IplImage to 3 arrays将 RGB IplImage 转换为 3 个数组
【发布时间】:2010-12-06 19:18:51
【问题描述】:

我需要一些 C++/指针帮助。当我创建 RGB IplImage 并想访问 i,j 时,我使用以下 C++ 类,取自:http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html

template<class T> class Image
{
private:
    IplImage* imgp;

public:
    Image(IplImage* img=0) {imgp=img;}
    ~Image(){imgp=0;}
    void operator=(IplImage* img) {imgp=img;}
    inline T* operator[](const int rowIndx) {
        return ((T *)(imgp->imageData + rowIndx*imgp->widthStep));}
};

typedef struct{
  unsigned char b,g,r;
} RgbPixel;

typedef struct{
  float b,g,r;
} RgbPixelFloat;

typedef Image<RgbPixel>       RgbImage;
typedef Image<RgbPixelFloat>  RgbImageFloat;
typedef Image<unsigned char>  BwImage;
typedef Image<float>          BwImageFloat;

我一直在使用 CUDA,所以有时我必须将所有数据放入一个数组中,我喜欢将每个通道保留在自己的数组中,这样处理数据似乎更容易。所以我通常会这样做:

IplImage *image = cvLoadImage("whatever.tif");
RgbImageFloat img(image);
for(int i = 0; i < exrIn->height; i++)
{
    for(int j = 0; j < exrIn->width; j++)
    {
        hostr[j*data->height+i] = img[i][j].r;
        hostg[j*data->height+i] = img[i][j].g;
        hostb[j*data->height+i] = img[i][j].b;
    }
}

然后我会将我的数据复制到设备,用它做一些事情,将它返回到主机,然后再次循环,通过将数据分配回 IplImage 的数组并保存我的结果。

似乎我循环了很多,必须有一种更快的方法来使用指针来执行此操作,但我迷路了,必须有一种更有效的方法来做到这一点。有没有办法可以简单地为每个频道使用一个指针?我尝试做这样的事情,但没有奏效:

float *hostr = &img[0][0].r
float *hostg = &img[0][0].b
float *hostb = &img[0][0].g

有什么建议吗?谢谢!

编辑: 谢谢大家的回答。也许我对我的问题不是很清楚。我熟悉如何访问渠道及其数据。我感兴趣的是提高将数据完全从 IplImage 复制到标准数组的性能和效率,更符合 csl 到目前为止所说的内容。我看到的问题是 IplImage 中数据的排列方式是“rgbrgbrgbrgb”。

【问题讨论】:

  • hostr、hostg 和 hostb 是浮点数组吗?
  • 是的,所有数据都是浮点数。所有图像都是 EXR(32 位浮点数,而不是半数)我只是使用 cvLoadImage() 代码作为一个快速示例。

标签: c++ performance arrays opencv iplimage


【解决方案1】:

首先,如果您熟悉 C++,您应该考虑使用 OpenCV 2.0,它消除了图像和矩阵的不同数据类型(IplImage*CvMat*)并使用一种结构(Mat)处理两者。除了自动内存管理和大量有用的例程来处理通道等之外,还有一些 MATLAB 风格的例程,使用起来真的很有趣。

对于您的具体问题,您可以使用Mat 访问IplImage* 的频道,如下所示:

 IplImage *image = cvLoadImage("lena.bmp");
 Mat Lena(image);
 vector<Mat> Channels;
 split(Lena,Channels);
 namedWindow("LR",CV_WINDOW_AUTOSIZE);
 imshow("LR",Channels[0]);
 waitKey();

现在您在vector Channels 中拥有每个频道的副本。

如果您不想想使用OpenCV2.0 并提取频道,请注意以下几点。 OpenCV 按以下方式对多通道图像进行排序:

x(1,1,1) x(1,1,2) x(1,1,3) x(1,2,1) x(1,2,2) x(1,2,3) ...

在哪里x(i,j,k) = an element in row i of column j in channel k

此外,OpenCV pads 它是图像 .. 所以不要忘记使用 widthStep 跳转行,这会导致这些填充间隙。按照csl said 的思路,增加外循环中的行指针(使用widthStep)并增加此指针以访问一行中的元素。

注意:

由于您现在使用的是 2.0,因此您可以使用 Mat Lena = imread("Lena.bmp"); 绕过 IplImage*

【讨论】:

  • 谢谢,前几天我下载了openCV 2.0,还没来得及看,我们使用的是64位,所以我得考虑重新编译2.0 64位所以我一直在避免它:p
  • 我认为这是值得的。 makefile 非常易于使用 (CMake),并且使用 Mat 和所有新运算符进行编码使我的代码更加干净和简单。
  • Jacob,谢谢,我刚刚编译了 2.0 并按照您的建议进行了操作。很酷。我得看看这里有什么新东西……有没有关于 C++ 东西的文档,还是我只需要通过头文件来寻找东西?
  • 没办法,看看doc\opencv.pdf - 他们已经很好地记录了新界面。另外,请随时接受我的回答:)
【解决方案2】:

这里有很多的改进空间。这么多,你应该阅读人们如何访问位图。

首先,尽可能增加内存局部性。这将增加缓存命中率和性能。即,不要为每个颜色通道使用三个单独的数组。将它们存储在一起,因为您可能主要处理像素。

其次,不要对每个像素进行 y*width 计算。在内部循环中完成时,会消耗很多循环。

最后,如果您只想要图像的完整副本,那么您可以简单地执行 memcpy(),这非常快。如果您从浮点数转换为整数,我无法推断,但如果不是,则将 memcpy() 用于非重叠区域。

如果您想知道如何使用指针来做到这一点(一种伪代码,也未经测试):

float *dst = &hostg[0][0];
RgbPixelFloat *src = &img[0][0];
RgbPixelFloat *end = &img[HEIGHT][WIDTH] + 1;

// copy green channel of whole image
while ( src != end )  {
    *dst = src->g;
    ++dst;
    ++src;
}

【讨论】:

  • 我认为缓存不适用于 CUDA。即便如此,许多图像算法在通道上独立运行,并且当它们不必跳过三元组中未使用的部分时,它们会获得更好的局部性。
  • OP 想知道如何将img 分离到频道rgb
猜你喜欢
  • 2014-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多