Reputation: 57
I tried to extract average pixel values (R, G, B) from the contoured image. However, my problem is when I applied the code below, something strange values were observed.
int main(){
cv::Mat star = imread("C:\\Users\\PC\\Desktop\\star\\starcircle.png");
cv::Mat mask = cv::Mat::zeros(star.rows, star.cols, CV_8UC1);
cv::Mat frame;
double b, g, r = 0.0;
cv::imshow("Original", star);
cv::cvtColor(star, frame, CV_BGR2HSV);
cv::inRange(frame, cv::Scalar(29, 220, 220), cv::Scalar(30, 255, 255), mask);
cv::imshow("mask", mask);
cv::Mat result = cv::Mat(star.rows, star.cols, CV_8UC1, star.type());
result.setTo(cv::Scalar(0, 0, 0));
star.copyTo(result, mask);
cv::Scalar temp = mean(mask);
cout << "avg_R: " << temp[2] << " \n"; // red value
cout << "avg_G: " << temp[1] << " \n"; // green value
cout << "avg_B: " << temp[0] << " \n\n"; // blue value
cv::imshow("result", result);
cv::waitKey(-1);
return 0;
}
And I got the correct images for the result like below.
I want to read pixel values only for yellow part, not for outside of mask.
And I have another code for read out pixel values in the yellow parts, but it showed the same result.
int main(){
cv::Mat star = imread("C:\\Users\\PC\\Desktop\\star\\starcircle.png");
cv::Mat mask = cv::Mat::zeros(star.rows, star.cols, CV_8UC1);
cv::Mat frame;
double b, g, r = 0.0;
cv::imshow("Original", star);
cv::cvtColor(star, frame, CV_BGR2HSV);
cv::inRange(frame, cv::Scalar(29, 220, 220), cv::Scalar(30, 255, 255), mask);
cv::imshow("mask", mask);
cv::Mat result = cv::Mat(star.rows, star.cols, CV_8UC1, star.type());
result.setTo(cv::Scalar(0, 0, 0));
star.copyTo(result, mask);
int hei = star.rows;
int wid = star.cols;
int corow = hei * wid;
double b, g, r = 0.0;
for (int x = 0; x < hei; x++) {
for (int y = 0; y < wid; y++) {
if (mask.at<unsigned char>(x, y) > 0) {
b += result.at<Vec3b>(x, y)[0];
g += result.at<Vec3b>(x, y)[1];
r += result.at<Vec3b>(x, y)[2];
}
else {
}
}
}
cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
cout << "avg_R: " << r / corow << " \n"; // red value
cout << "avg_G: " << g / corow << " \n"; // green value
cout << "avg_B: " << b / corow << " \n\n"; // blue value
}
Please help me to revise the error.
Thank you in advance.
Upvotes: 0
Views: 1522
Reputation: 41765
A few things:
Mat
types are at least confusing. Use proper names for the variables, and use Mat_<T>
whenever possible (I'd say always).cv::mean
cv::waitKey()
to actually see your cv::imshow
Check the code:
#include <opencv2\opencv.hpp>
int main()
{
cv::Mat3b star = cv::imread("path/to/image");
cv::imshow("Original", star);
cv::Mat3b hsv;
cv::cvtColor(star, hsv, cv::COLOR_BGR2HSV);
cv::Mat1b mask;
cv::inRange(hsv, cv::Scalar(29, 220, 220), cv::Scalar(30, 255, 255), mask);
cv::imshow("mask", mask);
// Change to 'false' to see how to use the 'cv::mask' approach
if (true)
{
double blue, green, red = 0.0;
int counter = 0;
for (int r = 0; r < star.rows; r++)
{
for (int c = 0; c < star.cols; c++)
{
if (mask(r, c) > 0)
{
++counter;
blue += star(r, c)[0];
green += star(r, c)[1];
red += star(r, c)[2];
}
}
}
// Avoid division by 0
if (counter > 0)
{
blue /= counter;
green /= counter;
red /= counter;
}
std::cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
std::cout << "avg_R: " << red << " \n";
std::cout << "avg_G: " << green << " \n";
std::cout << "avg_B: " << blue << " \n\n";
}
else
{
cv::Scalar mean_value = cv::mean(star, mask);
double blue = mean_value[0];
double green = mean_value[1];
double red = mean_value[2];
std::cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
std::cout << "avg_R: " << red << " \n"; // red value
std::cout << "avg_G: " << green << " \n"; // green value
std::cout << "avg_B: " << blue << " \n\n"; // blue value
}
cv::waitKey();
}
Upvotes: 2
Reputation: 11420
I see several errors in your code:
cv::Mat result = cv::Mat(star.rows, star.cols, CV_8UC1, star.type());
result.setTo(cv::Scalar(0, 0, 0));
star.copyTo(result, mask);
cv::Scalar temp = mean(mask);
If result is of type CV_8UC1 then you copyTo one channel? (The C1 from CV_8U means one channel). Then you use star.type()
where the value to be set should be... You do also the mean to a mask, which will give you a scalar with only one channel set, since it is a binary image of type CV_8UC1... for it to work, it should be:
cv::Mat result = cv::Mat(star.rows, star.cols, star.type(), cv::Scalar::all(0));
star.copyTo(result, mask);
cv::Scalar temp = mean(result);
For the second part, it is ok to add it like that, however if you have not fixed the previous error... I think it should give you segmentation error at some point or weird results if you are lucky. Finally the result part you have this:
cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
cout << "avg_R: " << r / corow << " \n"; // red value
cout << "avg_G: " << g / corow << " \n"; // green value
cout << "avg_B: " << b / corow << " \n\n"; // blue value
but corow should be the non zero points of the mask, so it should be:
corow = cv::countNonZero(mask);
cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
cout << "avg_R: " << r / corow << " \n"; // red value
cout << "avg_G: " << g / corow << " \n"; // green value
cout << "avg_B: " << b / corow << " \n\n"; // blue value
if not it will give you a smaller number, since it is divided with a a number that includes the black points which do not contribute.
As an extra note, you should use more OpenCV functions... in this case cv::mean
does the same thing, if not you can simplify it with sum and divide like:
cv::Scalar summed = cv::sum(result);
cv::Scalar mean = summed / static_cast<double>(cv::countNonZero(mask));
std::cout << "$$ Red(R), Green(G), Blue(B) $$" << std::endl << std::endl;
std::cout << "avg_R: " << mean[2] << std::endl; // red value
std::cout << "avg_G: " << mean[1] << std::endl; // green value
std::cout << "avg_B: " << mean[0] << std::endl << std::endl; // blue value
This is assuming you did the star.copyTo(result, mask);
line
Upvotes: 1
Reputation: 1927
Read about cv::Mat::at: first row, second col. Not (x, y)!
See you on cv::mean: it can work with mask.
Right initialization: double b = 0.0, g = 0.0, r = 0.0;
int corow = 0; And inside loop ++corow if mask > 0.
Upvotes: -1