4

I want to count the number of white points in a background image which is only black and white. I have a code like this:

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 ++ ;
            }
        }
    }

For some reason, the above code doesn't work, it just stops reacting. I checked, and the line" if ( Image.at(i,j) >= 150 ) " causes the problem. My "Image" is a "cv::Mat", with "CV_8UC3" type. Is there someone can help me? Thank you.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
E_learner
  • 3,512
  • 14
  • 57
  • 88
  • Do a cout of `Image.at(i,j)` – Neel Basu Aug 22 '12 at 09:34
  • 2
    Shouldn't it be `Image.at(j,i)`? – ekholm Aug 22 '12 at 09:39
  • Thank you so much for your reply. The cout partial result is something like this: 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... I changed it into "Image.at(j,i)", and the output of cout is like this:7745580006,-1869970562,1835821674,-1987475062,... Any ideas? – E_learner Aug 22 '12 at 09:48

6 Answers6

11

In addition to my comment to Robin's answer, your error is that you try to access an image of CV_8UC3 type as ints. If you want to check grey levels, do something like this (note the "unsigned char" instead of "int", as in Robin's answer).

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
  • 16,935
  • 4
  • 43
  • 66
  • Dear Etarion, Robin, and all others who answered my question, thank you so much for your help. Now it works fine. I used the code Etarion provided lastly. Best regards. – E_learner Aug 22 '12 at 11:24
  • Thanks for referencing me ^_^ – Robin Aug 22 '12 at 17:31
5

I believe this is much neater:

Mat result;
threshold(Image,result,150,255,THRESH_BINARY);
int white_count = countNonZero(result);
Kamyar Infinity
  • 2,711
  • 1
  • 21
  • 32
  • Yes, this is indeed much efficient. Thank you :) – E_learner Aug 22 '12 at 11:26
  • 1
    This is most probably the fastest way to do it - threshold and countNonZero are optimized better then the for-loop approach – Sam Aug 22 '12 at 11:30
  • @ederman You're welcome. I don't get it, if it helped, why down vote? (to whom it concerns!) – Kamyar Infinity Aug 23 '12 at 01:17
  • @Kamyar, I didn't down vote it, I selected Robin's answer for reference, then also voted your answer as useful. Did I miss something here? Please don't misunderstand anything. Thank you. – E_learner Aug 23 '12 at 07:26
  • @ederman, I knew! :) I was talking about the one who did it. I noted: to whom it concerns. Anyway, I'm glad if it helped anyone... – Kamyar Infinity Aug 23 '12 at 11:12
3

Write Image.at<unsigned char>(j,i) not Image.at<unsigned char>(i,j) if you are using i for cols and j for rows.

Adi Andon
  • 73
  • 1
  • 6
  • I changed it into (j,i) lastly, thank you for pointing this error out for me. Then I used "countNonZero" method that other friends provided here. Thank you for your answer. – E_learner Aug 22 '12 at 11:27
  • Sammy, da tu nu prea ai treaba pe acolo? :) Mai bine da-mi si mie un up! :) – Adi Andon Aug 22 '12 at 11:31
  • iti fac up, daca schimbi int cu uchar, ca asa nu merge ;) Si-apoi ma mai relaxez si eu, ca pe mine nu ma pandeste nimeni – Sam Aug 22 '12 at 11:31
2

I think you have to access the row before the column, meaning you should swap i and j.
Substitute if ( Image.at<int>(i,j) >= 150 ) with if ( Image.at<int>(j,i) >= 150 )

There are easier ways to access a Mat though.
OpenCV provides an STL-like iterator which is easy to use and if you want to access all the elements very easy to use. Example:

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

Last but not least you could also get a pointer to each row and access the data via the plain [] operator:

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
  • 1,322
  • 1
  • 13
  • 23
  • Thank you Robin for your answer. I tried iterator, but for the "if((*it) >=15)" line, it gives an error saying " no operator >= matches these operands". – E_learner Aug 22 '12 at 09:57
  • Even easier than your first way, `cv::Mat tmp; cv::threshold(image, tmp, 150, 1, cv::THRESH_BINARY); count = cv::countNonZero(tmp);` – etarion Aug 22 '12 at 10:11
1

you could access the CV_8UC3 Pixels with opencv bytes vectors (unsigned char pixels) ! In this case you can make the following (now you could also use some special color threshold)

int channel = 0;
Image.at<Vec3b>( row , col )[channel]
jamk
  • 836
  • 1
  • 10
  • 24
1

There are a lot of methods to access the cv::Mat image, if you want to directly access the color image(CV_8UC3), it could be implemented by following:

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++;
         }

    }
 }

but I recommend that if you only want to count white points, you can just convert image into grayscale (CV_8UC1), and do as following:

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++;
      }
   }
}

Finally, note that access cv::Mat image by img.ptr< Imagetype> will not check the accessed point is correct, so if you certainly know the range of image, the access image by ptr will be fine, otherwise, you can do by img.at< Imagetype>(), it will check every point is correct at every call,why access image by ptr is faster so if there are invalid accessed point, it will assert you!

Community
  • 1
  • 1
RyanLiu
  • 1,557
  • 2
  • 18
  • 27