Reputation: 35
Friends, I am trying to detect the coin first, since I already know its size is 1.011cm2. And then measure the leaves in the image. I am using findContours, but I am not always able to distinguish the currency first, I have also tried to use hougCircles but it is not working in my case. Would anyone have any ideas?
OpenCv 4.5.0 C++
My code
//variables for segmentation image
cv::Mat imagem_original, imagem_gray, imagem_binaria, imagem_inRange, imagem_threshold, dst, src;
vector<Vec3f> circles;
cv::Scalar min_color = Scalar(50, 50, 50);
cv::Scalar max_color = Scalar(90, 120, 180);
imagem_original = load_image("IMG_1845.jpg");
//imshow("Imagem Original", imagem_original);
cv::cvtColor(imagem_original, imagem_gray, COLOR_BGR2GRAY);
//imshow("imagem_gray", imagem_gray);
//cv::inRange(imagem_gray, min_color, max_color, imagem_inRange);
cv::threshold(imagem_gray, imagem_threshold, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
imshow(" Threshold", imagem_threshold);
// find outer-contours in the image these should be the circles!
cv::Mat conts = imagem_threshold.clone();
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(conts, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
int total_IAF = 0;
cout << "\n\n";
cout << contours.size() << "\n\n";
for (int i = 0; i < contours.size(); i++) {
int area = contourArea(contours[i]);
if (area <= 10) {
cv::drawContours(imagem_original, contours, i, Scalar(0, 0, 255));
}
else {
cout << area << "\n";
cv::drawContours(imagem_original, contours, i, Scalar(255, 0, 0));
}
if (area > 5000) {
total_IAF += contourArea(contours[i]);
}
}
imshow(" ORIGINAL ", imagem_original);
double iAF_cm2 = total_IAF / 4658;
cout << "\n\n TOTAL AREA IAF: " << total_IAF;
cout << "\n IAF em cm2: " << iAF_cm2 << " cm2\n\n";
Upvotes: 0
Views: 341
Reputation: 18895
If your setup has constant white-ish/gray-ish background and green leaves, I'd use the HSV color space to detect all objects using the S
channel (the green leaves and the golden part of the coin will have significantly more saturation than the background) and then distinguish between the coin and the leaves using the H
channel (the green leaves will have hue values around 45
). The remainder is to determine the image areas of all contours, and set the coin's image area as some kind of reference area to calculate the object areas w.r.t. the coin's object area of 1.011
.
That's the saturation channel of the given image:
The saturation channel thresholded at 64
:
That's the hue channel of the image:
Here's some code executing the above idea:
int main()
{
// Read image
cv::Mat img = cv::imread("Wcj1R.jpg", cv::IMREAD_COLOR);
// Convert image to HSV color space, and split H, S, V channels
cv::Mat img_hsv;
cv::cvtColor(img, img_hsv, cv::COLOR_BGR2HSV);
std::vector<cv::Mat> hsv;
cv::split(img_hsv, hsv);
// Binary threshold S channel at fixed threshold
cv::Mat img_thr;
cv::threshold(hsv[1], img_thr, 64, 255, cv::THRESH_BINARY);
// Find most outer contours only
std::vector<std::vector<cv::Point>> cnts;
std::vector<cv::Vec4i> hier;
cv::findContours(img_thr.clone(), cnts, hier, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
// Iterate found contours
std::vector<cv::Point> cnt_centers;
std::vector<double> cnt_areas;
double ref_area = -1;
for (int i = 0; i < cnts.size(); i++)
{
// Current contour
std::vector<cv::Point> cnt = cnts[i];
// If contour is too small, discard
if (cnt.size() < 100)
continue;
// Calculate and store center (just for visualization) and area of contour
cv::Moments m = cv::moments(cnt);
cnt_centers.push_back(cv::Point(m.m10 / m.m00 - 30, m.m01 / m.m00));
cnt_areas.push_back(cv::contourArea(cnt));
// Check H channel, whether the contour's image parts are mostly green
cv::Mat mask = hsv[0].clone().setTo(cv::Scalar(0));
cv::drawContours(mask, cnts, i, cv::Scalar(255), cv::FILLED);
double h_mean = cv::mean(hsv[0], mask)[0];
// If it's not mostly green, that's the coin, thus the reference area
if (h_mean < 40 || h_mean > 50)
ref_area = cv::contourArea(cnt);
}
// Iterate all contours again
for (int i = 0; i < cnt_centers.size(); i++)
{
// Calculate actual object area
double area = cnt_areas[i] / ref_area * 1.011;
// Put area on image w.r.t. the contour's center
cv::putText(img, std::to_string(area), cnt_centers[i], cv::FONT_HERSHEY_COMPLEX_SMALL, 1, cv::Scalar(255, 255, 255));
}
return 0;
}
And, that'd be the output:
Upvotes: 3
Reputation: 43
Your code finds all contours in a image and shows them. So I'm confused about the meaning of "detect the coin first".
If you want to draw the contour of the coin first, sort contours vector by size. The coin is the smallest object so it would be the first element of the vector after sorting.(Of course, some unwanted contours should removed before sorting.)
Upvotes: 0