将RGB IplImage转换为3个数组

3

我需要一些关于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是浮点数数组吗? - csl
是的,所有数据都是浮点数。所有图像都是EXR格式(32位浮点数,不是HALF)。我只是使用cvLoadImage()代码作为一个快速示例。 - rem7
2个回答

5
首先,如果您熟悉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();

现在您拥有每个通道的副本在向量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)=通道k中列j的第i行中的一个元素
另外,OpenCV会填充其图像..因此不要忘记使用widthStep跳过行,这可以解决这些填充间隙。与csl said的建议相似,在外部循环中增加行指针(使用widthStep),并递增该指针以访问行中的元素。
注意:

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


谢谢,前几天我下载了openCV 2.0,但还没来得及看。我们使用的是64位系统,所以我需要研究一下如何将2.0重新编译成64位系统,所以一直在回避它 :p - rem7
我认为这是值得努力的。使用makefile(CMake)非常容易,并且使用Mat和所有新运算符进行编码使我的代码更加清晰简洁。 - Jacob
Jacob,谢谢,我刚刚编译了2.0并按照你的建议去做了。非常酷。我得看看这里有什么新东西......有C++相关的文档吗?还是说我只能浏览头文件来寻找信息? - rem7
没问题,看看 doc\opencv.pdf - 他们对新接口进行了很好的文档记录。另外,随意接受我的答案 :) - Jacob

1

这里有很多地方需要改进。非常之多,以至于你应该了解一下人们如何访问位图。

首先,尽可能增加内存的局部性。这将增加缓存命中率和性能。也就是说,不要为每个颜色通道使用三个单独的数组。把它们存放在一起,因为你很可能主要在像素上工作。

其次,不要在每个像素上都进行那个 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。即使它适用,许多图像算法也是独立操作通道的,当它们不必跳过三元组的未使用部分时,它们会获得更好的局部性能。 - Mark Ransom
OP想知道如何将img分离成通道rgb - Jacob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接