使用指向图像数据的指针访问 R、G 和 B 像素值

Posted

技术标签:

【中文标题】使用指向图像数据的指针访问 R、G 和 B 像素值【英文标题】:Access R,G and B pixel values using a pointer to image data 【发布时间】:2015-06-29 13:45:23 【问题描述】:

我已阅读Mat 格式的图像。

Mat image = imread("image.png", 1);

我使用

声明指向其数据的指针
unsigned char *ptr_source = image.data

现在,我想访问for loop 中每个像素的 R、G 和 B 值。 我已经知道用img.at<Veb3b>(i,j) 或类似的东西来做这件事的方法,但现在,我必须用unsigned char 类型的指针来做。

uchar R_value = ptr_source[ i*?? + ??? ];
uchar G_value = ptr_source[ i*?? + ??? ];
uchar B_value = ptr_source[ i*?? + ??? ];

重要提示:有些人here 提到使用以下内容:

unsigned char *input = (unsigned char*)(img.data);
for(int j = 0;j < img.rows;j++)
    for(int i = 0;i < img.cols;i++)
        unsigned char b = input[img.step * j + i ] ;
        unsigned char g = input[img.step * j + i + 1];
        unsigned char r = input[img.step * j + i + 2];
    

根据openCV docs,这对我来说很有意义,但不幸的是它不适用于我的情况。另一个method posted at SO 说要使用以下内容:

uchar b = frame.data[frame.channels()*(frame.cols*y + x) + 0];    
uchar g = frame.data[frame.channels()*(frame.cols*y + x) + 1];
uchar r = frame.data[frame.channels()*(frame.cols*y + x) + 2];

基本问题:虽然,它似乎工作,但我不明白它的逻辑。 为什么我们需要将(frame.cols*y + x)frame.channels() 相乘??

【问题讨论】:

你真的必须使用data吗?无论如何.. 我认为 OpenCV 以 BGR 顺序存储像素,并且大小可能会根据加载的图像类型而变化。在这个solution(第二个答案)......他使用image.channels() 来了解步长。我希望他的回答能对你有所帮助。 @wendelbsilva:是的,我正在寻找类似的东西,但我不确定这是否适合我。另外,B频道的frame.channels()*(frame.cols*y + x) + 0我也看不懂。 数据按顺序存储。所以frame.channels()*(frame.cols*y + x) + 0 表示他正在尝试访问像素(x, y)。由于数据是连续的,因此数组中的位置将是y 乘以矩阵的宽度 (frame.cols) 加上x。他在最后添加了0,只是为了帮助阅读算法。 @wendelbsilva:我能够理解那部分。我的困惑是,为什么我们需要乘以 frame.channels() 以及它如何帮助我们获得 R、G 和 B 像素值? 您可能有灰度图像。在这种情况下,字节数和它们的存储方式将与您拥有 RGB 或 RGBA 图像时不同。如果你知道你的图像是 RGB,你可以继续使用你想要的大小。但是如果你不知道它使用了多少字节,或者你想使用函数让算法更容易阅读,你可以使用.channels() 【参考方案1】:

cv::Mat::channels() 方法返回图像中的通道数。 在8UC3 三通道彩色图像中,channels() 返回 3,像素存储在连续的字节三元组中:BGRBGRBGRBGRBGR...

要在给定unsigned char* ptr_source 指针的情况下访问像素(x,y),您需要计算像素偏移量。图像宽度为frame.cols。每个像素channels() == 3字节,所以像素的unsiged char*偏移量将是ptr_source + frame.channels()*(frame.cols*y + x)。这个unsigned char* 通常是蓝色通道,后面的 2 个chars 是绿色和红色。

例如,给定一个 3x4 的图像,内存中的像素将如下所示(空格仅用于清楚起见):

r\c  0   1   2   
 0  BGR BGR BGR
 1  BGR BGR BGR
 2  BGR>BGR<BGR
 3  BGR BGR BGR

因此,如果您计算字节数,您会看到像素(1,2) 的蓝色通道字节正好在字节偏移3*(2*3+1) = 21

实际上建议使用img.step 而不是原始计算,因为某些图像在每个像素行的末尾都有填充,因此img.step[0] == img.channels()*img.cols 并不总是正确的。 在这种情况下,您应该使用ptr_source[img.step[0]*y + img.channels()*x]

此外,您的问题假设像素深度为8U,这可能不适用于所有图像。如果不是,您还需要将所有内容乘以深度(每像素字节数)。 这基本上就是cv::Mat:at&lt;&gt; 所做的......

【讨论】:

非常感谢。答案确实相当详尽和令人满意。我对img.step 的用法仍然有些困惑。我应该用img.step 而不是ptr_source + frame.channels()*(frame.cols*y + x) 写什么?? 我更新了答案。我建议你像我一样在一张数学纸上手工做一个例子,你会发现你自己会得出同样的计算。 是的,我正在尝试在纸上做这件事,我可以用第一种方法理解。但是与img.step 相关的方法给出了错误的结果,我可以看到原因是对通道或其他东西的错误解释。我用unsigned char b_val = ptr_source[ step_size + numChannels*x + 0 ]; 等等r_valg_val 其实我觉得你应该使用img.step[0]作为合适的宽度步长值。 不,它没有给出预期的结果。它生成带条纹的图像。我们不应该在您的答案中提到的公式ptr_source[img.step[0] + img.channels()*x] 中将img.step[0]y 相乘吗?

以上是关于使用指向图像数据的指针访问 R、G 和 B 像素值的主要内容,如果未能解决你的问题,请参考以下文章

RGB模式的图像中每个像素的颜色值都有R、G、B三个数值来决定,每个数值的范围是0到255。当R、G、B 数值相

如何找到图像中的所有像素都是灰度或每个像素的 R、G、B 值相等

labview怎么提取一幅图像rgb值,最后生成三个矩阵,每个矩阵里分别存放R,G,B的值

图片灰度化

Opencv利用Mat访问像素值

android图像处理浮雕效果