使用指向图像数据的指针访问 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 个char
s 是绿色和红色。
例如,给定一个 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<>
所做的......
【讨论】:
非常感谢。答案确实相当详尽和令人满意。我对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_val
和g_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 值相等