20

I tried to get horizontal projection using countNonZero() function as below.

Mat src = imread(INPUT_FILE, CV_LOAD_IMAGE_COLOR);
Mat binaryImage = src.clone();
cvtColor(src, src, CV_BGR2GRAY);

Mat horizontal = Mat::zeros(1,binaryImage.cols, CV_8UC1);

for (int i = 0; i<binaryImage.cols; i++)
{
    Mat roi = binaryImage(Rect(0, 0, 1, binaryImage.rows));

    horizontal.at<int>(0,i) = countNonZero(roi);
    cout << "Col no:" << i << " >>" << horizontal.at<int>(0, i);
}

But an error is occured in the line of calling countonZero() function. Error is as follows.

    OpenCV Error: Assertion failed (src.channels() == 1 && func != 0) in cv::countNo
    nZero, file C:\builds\2_4_PackSlave-win32-vc12-shared\opencv\modules\core\src\st
    at.cpp, line 549

Can somebody please point out the mistake?

Samitha Chathuranga
  • 1,689
  • 5
  • 30
  • 57
  • 4
    binaryImage is a copy of src which is 3 channel color image. try cvtColor(src, binaryImage, CV_BGR2GRAY); – Micka Jul 05 '15 at 15:03
  • 2
    there's another error: switch horizontal.at(0,i) to horizontal.at(0,i) since you created 8 bit data type. – Micka Jul 05 '15 at 15:19
  • I did the changes and errors are solved. Thanks for that. But now I see that the value returned by countNonZero(roi) function are always zero. I also confirmed that binaryImage is not a completely a black image. (it has both black and white pixels everywhere) – Samitha Chathuranga Jul 05 '15 at 15:46
  • did you change to cout << "Col no:" << i << " >>" << horizontal.at(0, i); too? – Micka Jul 05 '15 at 15:50
  • @Mika I just printed the values of countNonZero(roi), as for (int i = 0; i – Samitha Chathuranga Jul 05 '15 at 16:08
  • can you try to countNonZero on this sample matrix? Mat testMat = Mat::ones(1,256, CV_8UC1); – Micka Jul 05 '15 at 16:54
  • 1
    is the first column of your input matrix completely zero? in each iteration you read the same column! try Mat roi = binaryImage(Rect(i, 0, 1, binaryImage.rows)); – Micka Jul 05 '15 at 16:56
  • @Mikhail Sorry for the delay.. but was expecting to focus back soon. I needed to check Miki's solution – Samitha Chathuranga Jul 13 '15 at 02:28
  • @Micka Oh.. that was even a horrible mistake. I solved the problem using above comments. Thanks Mikhail and Micka. – Samitha Chathuranga Jul 13 '15 at 02:39

3 Answers3

26

Assertion src.channels() == 1 means that image should have 1 channel, i.e. it has to be gray, not colored. You are calling countNonZero on roi, which is a subimage of binaryImage, which is a clone of src, which is originally colored.

I suppose you wanted to write cvtColor(binaryImage, binaryImage, CV_BGR2GRAY);. In this case it makes sense. However, I do not see you using src anywhere again, so perhaps you do not need this intermediate image. In case you do, do not call "binary", since "binary" in computer vision usually stands for black-or-white image, only two colors. Your image is "gray", since it has all shades of black and white.

Concerning your original task, Miki is right, you should use cv::reduce for it. He already gave you an example on how to use it.

Mikhail
  • 20,685
  • 7
  • 70
  • 146
  • Thanks for the answer. Other than above things I had to change,Mat roi = binaryImage(Rect(0, 0, 1, binaryImage.rows)); as this, Mat roi = binaryImage(Rect(i, 0, 1, binaryImage.rows)); I was just iterating the same column. – Samitha Chathuranga Jul 13 '15 at 02:45
0

BTW, you can compute horizontal projection using reduce giving as argument CV_REDUCE_SUM.

A minimal example:

Mat1b mat(4, 4, uchar(0));
mat(0,0) = uchar(1);
mat(0,1) = uchar(1);
mat(1,1) = uchar(1);

// mat is: 
//
// 1100
// 0100
// 0000
// 0000

// Horizontal projection, result would be a column matrix
Mat1i reducedHor;
cv::reduce(mat, reducedHor, 1, CV_REDUCE_SUM);

// reducedHor is:
//
// 2
// 1
// 0
// 0

// Vertical projection, result would be a row matrix
Mat1i reducedVer;
cv::reduce(mat, reducedVer, 0, CV_REDUCE_SUM);

// reducedVer is:
//
// 1200


// Summary
//
// 1100 > 2
// 0100 > 1
// 0000 > 0
// 0000 > 0
// 
// vvvv
// 1200

You can use this with your images like this:

// RGB image
Mat3b img = imread("path_to_image");

// Gray image, contains values in [0,255]
Mat1b gray;
cvtColor(img, gray, CV_BGR2GRAY);

// Binary image, contains only 0,1 values
// The sum of pixel values will equal the count of non-zero pixels
Mat1b binary;
threshold(gray, binary, 1, 1, THRESH_BINARY);

// Horizontal projection
Mat1i reducedHor;
cv::reduce(binary, reducedHor, 1, CV_REDUCE_SUM);

// Vertical projection
Mat1i reducedVer;
cv::reduce(binary, reducedVer, 0, CV_REDUCE_SUM);
Miki
  • 40,887
  • 13
  • 123
  • 202
0

Convert Image into Gray scale and after that perform countNonZero(), It will work

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
area = cv.countNonZero(gray)