C++,使用OpenCV获取像素的负RGB值

3

我正在使用OpenCV遍历图像并找到每个像素的颜色,以下是我使用的部分代码:

IplImage* img = cvLoadImage("c:\\test.png");

int pixels = img->height * img->width;
int channels = img->nChannels;

for (int i = 0; i < pixels*channels; i+= channels)
{

    unsigned char red = img->imageData[i + 2];
    unsigned char green = img->imageData[i + 1];
    unsigned char blue = img->imageData[i];

    outputRGBValues(red, green, blue);
    if (red == REDCOLOUR && green == GREENCOLOUR && blue == BLUECOLOUR)
    {
        count++;
    }
}
cvReleaseImage(&img);

当我运行它时,输出RGBValues会输出负值。最常见的是R、G、B = -1,但偶尔会出现其他负数和少量正数。我听说过未初始化的内存内容以及像素没有被正确地分配到内存中。但我不太明白这些,并且肯定不知道该如何解决它。我错在哪里,该如何解决?
更新
在使用fschmitt所提供的更改修复代码后,我离成功更近了一步。这张图片是我使用的,如果有帮助的话。很难看清,但它只是一个由黑色像素组成的5*3的“V”形图案,底部有一个绿色像素。
对它运行代码,我得到了如下输出:
0 0 0
255 255 255
255 255 255
255 255 255
0 0 0
255 255 186
0 0 255
255 255 0
And it continues

前五行没有问题,正是应该的样子。这是图像的顶部行。接下来的一行,从第6行开始是错误的。应该是:

255 255 255
255 255 255
0 0 0

我不确定是什么原因导致这个问题。为什么第一行可以工作,而第二行却不能?是否存在某种不匹配,导致它取了比应该更左边一位的值?


如果红色、绿色和蓝色的取值范围应该是从 0 到 255,为什么要使用 int 呢?为什么不使用无符号字符型? - JoshD
4个回答

4

试试这个:

IplImage* img = cvLoadImage("c:\\test.png");

for (int i = 0; i < img->height; i++)
{
    for (int j = 0; j < img->width; j += img->nChannels)
    {
        unsigned char red = img->imageData[i * img->widthStep + j + 2];
        unsigned char green = img->imageData[i * img->widthStep + j + 1];
        unsigned char blue = img->imageData[i * img->widthStep + j];

        outputRGBValues(red, green, blue);
        if (red == REDCOLOUR && green == GREENCOLOUR && blue == BLUECOLOUR)
        {
            count++;
        }
    }
}
cvReleaseImage(&img);

请注意,widthstep是IplImage的成员,而不是给定代码中的实例变量。 - YeenFei
@YeenFei:感谢您的更正-现在已经进行了相应的编辑-不确定您在第二条评论中所说的字节对齐是什么意思? - Paul R
我误以为你正在通过nChannels增加指针偏移量,这是一个严重的错误。我删除了我的第二个回复。 - YeenFei

2
我不明白为什么你要盲目访问像素数据(通过1D索引),而你已经知道了图像的尺寸(通过使用和)。此外,OpenCV确实提供了访问像素值的函数()。你直接访问指针可能得到奇怪的值,因为你没有考虑OpenCV执行的字节填充,你可能会发现在8位图像中,(宽度*通道数<= widthStep)。
一份快速修改后的代码如下:
IplImage* img = cvLoadImage("c:\\test.png");
for(int iRow=0; iRow<img->height; ++iRow)
{
    for(int iCol=0; iCol<img->width; ++iCol)
    {
        CvScalar pixelVal = cvGet2D( img, iRow, iCol);
        unsigned char red = pixelVal.val[2];
        unsigned char green = pixelVal.val[1];
        unsigned char blue = pixelVal.val[0];

        outputRGBValues(red, green, blue);
        if (red == REDCOLOUR && green == GREENCOLOUR && blue == BLUECOLOUR)
        {
            count++;
        }
    }
}
cvReleaseImage(&img);

另外,请注意,通常OpenCV以BGR格式排列数据,可以在IplImage :: channelSeq中验证。 不要被IplImage :: colorModel所困惑,它只是告诉颜色模型。

我赞同Paul R的建议,在尝试使用它们之前,您应该先找到参考资料并了解库。


1
我从未使用过OpenCV,但我想象中的是,cvCreateImage不会初始化图像的内容,更不会初始化为任何有意义的内容。

1

这里有很多问题:

  • 您需要用有意义的数据填充图像
  • 根据图像的构造方式,您可能已经交换了红色和蓝色
  • 您应该使用unsigned char red
  • 您为每个像素声明了一个向量,但没有使用它

我原以为像素颜色在imageData中是以BGR而不是RGB的方式存储的?感谢你指出这些问题,我会进行修改。 - JBenson
这取决于您如何构建图像,但确实您是正确的,默认值为BGR,我已编辑答案。 - fschmitt
好的,我接近了。帖子已更新,第一行像素正确找到,但从第二行开始就不对了。 - JBenson
@JBenson:你获取像素数据的方法不正确,因为它没有考虑到“width”和“widthStep”之间的差异(每行末尾可能有填充,因此存在差异)。建议阅读O'Reilly出版的《学习OpenCV》一书,其中详细介绍了基础知识,如这个问题。 - Paul R

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