OpenCV:读取矩阵值

4

我希望能够统计仅包含黑白颜色的背景图中,白点的数量。我有下面这样一段代码:

int count = 0; 
for ( int j = 0; j < Image.rows; j ++ )
    {
    for ( int i = 0; i < Image.cols; i ++ )
        {
            if ( Image.at<int>(i,j) >= 150 )
            {
                count ++ ;
            }
        }
    }

由于某些原因,上述代码无法正常工作,它只是停止响应。我检查了一下,发现"if ( Image.at(i,j) >= 150 ) "这一行引起了问题。我的"Image"是一个"cv::Mat",类型为"CV_8UC3"。有人可以帮我吗?谢谢。


输出 Image.at<int>(i,j) 的值。 - Neel Basu
2
应该是 Image.at<int>(j,i) 吧? - ekholm
非常感谢您的回复。cout的部分结果如下: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1109311264,11117845974,9668574984,0,0,0,0,0,0,0,0,0,0,0,0,2514654812,1101231542,121415421,0,0,0,0,0,0,0... 我将其改为“Image.at<int>(j,i)”,cout的输出结果如下:7745580006,-1869970562,1835821674,-1987475062,... 有什么想法吗? - E_learner
6个回答

11
除了对Robin答案的评论之外,您的错误在于尝试将CV_8UC3类型的图像作为整数进行访问。如果您想检查灰度级别,请像这样做(请注意,“unsigned char”而不是“int”,如Robin的回答中所示)。
cv::Mat greyscale;
cv::cvtColor(image,grayscale,CV_RGB2GRAY);
// either, most elegant:
int count = cv::countNonZero(greyscale >= 150);
// or, copied from Robin's answer:
int count = 0;
for(int i = 0; i < greyscale.rows; ++i) {
    const unsigned char* row = greyscale.ptr<unsigned char>(i);
    for(int j = 0; j < greyscale.cols; j++) {
        if (row[j] >= 150)
            ++count;
    }
}

亲爱的Etarion、Robin和所有回答我的问题的人,非常感谢你们的帮助。现在它已经正常工作了。我最后使用了Etarion提供的代码。最好的问候。 - E_learner
谢谢你提到了我 ^_^ - Robin

5
我认为这更加整洁:
Mat result;
threshold(Image,result,150,255,THRESH_BINARY);
int white_count = countNonZero(result);

是的,这确实更有效率。谢谢 :) - E_learner
1
这很可能是最快的方法 - 阈值和countNonZero比for循环更优化 - Sam
@ederman 不用谢。 我不明白,如果有帮助,为什么要踩?(针对相关人士!) - Kamyar Infinity
@Kamyar,我没有给它投反对票,我选择了Robin的答案作为参考,然后也投了你的答案作为有用的。我错过了什么吗?请不要误解任何事情。谢谢。 - E_learner
@ederman,我知道! :) 我说的是做这件事的人。我注意到了:关注的对象。无论如何,如果有帮助到任何人,我很高兴... - Kamyar Infinity

3

如果您使用i表示列,j表示行,请写成Image.at<unsigned char>(j,i)而不是Image.at<unsigned char>(i,j)


最后我把它改成了(j,i),感谢您指出这个错误。然后我使用了其他朋友在这里提供的“countNonZero”方法。谢谢您的答案。 - E_learner
Sammy,你在那里似乎没什么事情要做? :) 最好给我一个赞! :) - Adi Andon
如果你将int换成uchar,那么就会得到正确的结果。这样我就能放松了,毕竟没有人会找我麻烦。 - Sam

2

我认为您需要先访问行再访问列,也就是应该交换i和j的位置。
请将if (Image.at<int>(i,j) >= 150)替换为if(Image.at<int>(j,i) >= 150)

不过,有更简单的方法可以访问Mat。
OpenCV提供了STL类似的迭代器,使用起来很容易,并且如果您想访问所有元素,这种方法非常简单。例如:

int count = 0;
MatConstIterator_<int> it = Image.begin<int>(), it_end = Image.end<int>();
for(; it != it_end; ++it)
    if ((*it) >= 150)
        ++count;

最后但并非最不重要的,您还可以获取每一行的指针,并通过普通[]运算符访问数据:
int count = 0;
for(int i = 0; i < Image.rows; ++i) {
    const int* Ii = Image.ptr<int>(i);
    for(int j = 0; j < Image.cols; j++) {
        if (Ii[j] >= 150)
            ++count;
    }
}

谢谢Robin的回答。我尝试了迭代器,但是在"if((*it) >=15)"这一行,它报错说"没有匹配这些操作数的>=运算符"。 - E_learner
比你第一种方法更简单的是:cv::Mat tmp; cv::threshold(image, tmp, 150, 1, cv::THRESH_BINARY); count = cv::countNonZero(tmp); - etarion

1

有很多方法可以访问cv::Mat图像,如果您想直接访问彩色图像(CV_8UC3),可以按照以下方式实现:

int count = 0;
int threshold = 150;
for(int j = 0; j < img.rows; j++) {
   for(int i = 0; i < img.cols; i++) {
      //white point which means that the point in every channel(BGR)
      //are all higher than threshold!
      if(img.ptr<cv::Vec3b>(j)[i][0] > threshold && 
         img.ptr<cv::Vec3b>(j)[i][1] > threshold 
         img.ptr<cv::Vec3b>(j)[i][2] > threshold ) {
             count++;
         }

    }
 }

但是,如果您只想计算白点数,我建议您将图像转换为灰度图(CV_8UC1),然后按照以下步骤进行操作:

cv::Mat img;
cv::cvtColor(src,img,CV_BGR2RGB);
int count = 0;
int threshold = 150;
for(int j = 0; j < img.rows; j++) {
   for(int i = 0; i < img.cols; i++) {
      if(img.ptr<uchar>(j)[i] > threshold) {
            count++;
      }
   }
}

最后,注意通过img.ptr< Imagetype>访问cv :: Mat图像不会检查所访问的点是否正确,因此如果您确定知道图像的范围,则通过ptr访问图像将很好,否则,您可以通过img.at< Imagetype>()进行访问,它将在每次调用时检查每个点是否正确,为什么通过ptr访问图像更快 因此,如果有无效的访问点,它将向您断言!

1

你可以使用OpenCV字节向量(无符号字符像素)访问CV_8UC3像素!在这种情况下,您可以执行以下操作(现在您还可以使用一些特殊的颜色阈值)

int channel = 0;
Image.at<Vec3b>( row , col )[channel]

谢谢您的回答,现在我学会了如何使用颜色阈值 :) - E_learner

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