Related
// I used cvtColor, inRange , bitwise_and, GaussianBlur, Canny functions before.
vector<vector<Point>> contours; // to store contours
vector<Vec4i> hierarchy;
int mode = RETR_CCOMP;
//int module = CHAIN_APPROX_NONE;
//int module = CHAIN_APPROX_SIMPLE;
//int module = CHAIN_APPROX_TC89_L1;
int module = CHAIN_APPROX_TC89_KCOS;
findContours(img_edge, contours, hierarchy, mode, module);
sort(contours.begin(), contours.end(), compareContourAreas);
drawContours(image, contours, -1, Scalar(255, 0, 0), 1);
imshow("ContourImage", image);
vector<Point> contours_approx;
for (int i = 0; i < 5; i++)
{
double length = arcLength(contours[i], true);
approxPolyDP(contours[i], contours_approx, 0.1 * length, true);
if (contours_approx.size() == 4)
{
break;
}
contours_approx.clear();
}
for (Point i : contours_approx)
{
cout << i << endl; // to check coordinate of vertex
}
drawContours(contour2, vector<vector<Point>>(1, contours_approx), -1, Scalar(0, 0, 255), 1);
// to find vertex
circle(contour2, Point(x0, y0), 5, Scalar(255, 0, 0), -1, -1, 0); // blue
circle(contour2, Point(x1, y1), 5, Scalar(0, 255, 0), -1, -1, 0); // green
circle(contour2, Point(x2, y2), 5, Scalar(0, 0, 255), -1, -1, 0); // red
circle(contour2, Point(x3, y3), 5, Scalar(255, 255, 255), -1, -1, 0); // white
imshow("contour2", contour2);
In first, second picture, there is a result that I want. There are circles at vertex of rectangle.
But in third, fourth picture,I modified the angle of cuboid, and there is a result that I don't want. Circles were in very small rectangle above the large rectangle.
In first, third picture I guess that findContours function works well. And I guess that there is a problem in approxPolyDP function.
To locate circle in the largest rectangle, what should I have to do?
Thanks for your answer
I'm newbie OpenCV. I am using opencv for find position attach a stamp in image. The stamp can't overlap with other object in image.
Example binary image with white region is object on image, black region is none object
Example image
Result : Find rect in black region (in white circle) can attach stamp
Result imagge
Please help me find rect in black region same blue rect.
Thank you!
If you know about rectangles count then it can use kmeans for clusterization points.
First get only blue points and binarization they:
cv::Mat img = cv::imread("NQdmi.png", cv::IMREAD_COLOR);
std::vector<cv::Mat> chans;
cv::split(img, chans);
cv::Mat diff;
cv::absdiff(chans[2], chans[1], diff);
cv::threshold(diff, diff, 1, 255, cv::THRESH_BINARY);
cv::imshow("diff", diff);
Only blue points:
Clusterization points and find rotated rectangles:
std::vector<cv::Point2f> points;
for (int y = 0; y < diff.rows; ++y)
{
for (int x = 0; x < diff.cols; ++x)
{
if (diff.at<uchar>(y, x))
{
points.emplace_back(x, y);
}
}
}
cv::Mat pointsKmeans(points.size(), 1, CV_32FC2, &points[0]);
cv::Mat labels;
int clusterCount = 2;
cv::Mat centers;
cv::kmeans(pointsKmeans, clusterCount, labels,
cv::TermCriteria(cv::TermCriteria::EPS+cv::TermCriteria::COUNT, 100, 1.0),
3, cv::KMEANS_PP_CENTERS, centers);
std::vector<cv::Point2f> points1;
std::vector<cv::Point2f> points2;
cv::Mat draw = img.clone();
for (size_t i = 0; i < points.size(); ++i)
{
int clusterIdx = labels.at<int>(i);
if (clusterIdx > 0)
{
cv::circle(draw, points[i], 2, cv::Scalar(255, 0, 0), cv::FILLED, cv::LINE_AA);
points1.push_back(points[i]);
}
else
{
cv::circle(draw, points[i], 2, cv::Scalar(0, 0, 255), cv::FILLED, cv::LINE_AA);
points2.push_back(points[i]);
}
}
auto DrawRRect = [draw](const std::vector<cv::Point2f>& pp)
{
cv::RotatedRect rr = cv::minAreaRect(pp);
cv::Point2f corners[4];
rr.points(corners);
cv::line(draw, corners[0], corners[1], cv::Scalar(0, 255, 0), 2);
cv::line(draw, corners[1], corners[2], cv::Scalar(0, 255, 0), 2);
cv::line(draw, corners[2], corners[3], cv::Scalar(0, 255, 0), 2);
cv::line(draw, corners[3], corners[0], cv::Scalar(0, 255, 0), 2);
};
DrawRRect(points1);
DrawRRect(points2);
cv::imshow("draw", draw);
Result:
I use Hough transform method so I get 2 circles, how can I get just the zone of the big circle from the for loop?
vector<Vec3f> circles;
/// Apply the Hough Transform to find the circles;
HoughCircles(openImg, circles, CV_HOUGH_GRADIENT, 1,1,67, 17,35, 80);
/// Draw the circles detected
for (size_t i = 0; i < circles.size(); i++)
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
// circle center
circle(openImg, center, 1, Scalar(255, 255, 255), -1, 8, 0);
// circle outline
circle(openImg, center, radius, Scalar(255, 255, 255), 1, 4, 0);
}
/// Show your results
namedWindow("Hough Circle Transform Demo", CV_WINDOW_AUTOSIZE);
imshow("Hough Circle Transform Demo", openImg);
The documentation of opencv states:
circles – Output vector of found circles. Each vector is encoded as a 3-element floating-point vector (x, y, radius)
https://docs.opencv.org/3.4.1/d3/de5/tutorial_js_houghcircles.html
You are reading the radius (int radius = cvRound(circles[i][2]);). That is actually the size of the circle.
So, you need to loop through your array circles and pick the circle with the biggest radius:
// remember biggest radius
float radiusBiggest = 0;
// remember the index of the biggest radius / circle
int indexBiggest = -1;
// loop through all circles
for (size_t i = 0; i < circles.size(); i++)
{
// get the radius
float radius = circles[i][2];
// check if this radius is bigger than any previous
if (radius > radiusBiggest)
{
// this radius/circle is the biggest so far. remember it
radiusBiggest = radius;
indexBiggest = i;
}
}
// if we found a circle then draw it
if (indexBiggest != -1)
{
Point center(cvRound(circles[indexBiggest][0]), cvRound(circles[indexBiggest][1]));
int radius = cvRound(circles[indexBiggest][2]);
// circle center
circle(openImg, center, 1, Scalar(255, 255, 255), -1, 8, 0);
// circle outline
circle(openImg, center, radius, Scalar(255, 255, 255), 1, 4, 0);
}
Im new to programming and opencv and i try to detect a hdd using color segmentation. My code so far loads an image, creates 3 masks with different colors and draws an upright bounding box around the non zero points:
int main( int argc, char** argv )
{
//Load the image
Mat img = imread(argv[1], 1);
if (img.empty()){
cout << "No image found..." << endl;
return -1;
}
//Extracting colors - BGR
Mat silver, white, black;
//Silver
inRange(img, Scalar(180, 180, 180), Scalar(200, 200, 200), silver);
//White
inRange(img, Scalar(240, 240, 240), Scalar(255, 255, 255), white);
//Black
inRange(img, Scalar(0, 0, 0), Scalar(30, 30, 30), black);
// logical OR mask
Mat1b mask = silver | white | black;
// Find non zero pixels
vector<Point> pts;
findNonZero(mask, pts);
cout << "Non-Zero Locations = " << pts << endl << endl; // get non zero coordinates
// Compute bounding box
Rect box = boundingRect(pts);
// Show bounding box
rectangle(img, box, Scalar(0, 0, 255), 3);
namedWindow("box", CV_WINDOW_NORMAL);
imshow("box", img);
imshow("mask", mask);
waitKey(0);
destroyAllWindows;
return 0;}
Now I want to draw the smallest bounding box, so I tried to use
cv::RotatedRect box2 = cv::minAreaRect(pts);
instead. But it doesnt compile when I try to visualize that by replacing
Rect box = boundingRect(pts);
with
RotatedRect box2 = minAreaRect(pts);
Error Output:
error: no matching function for call to ‘rectangle(cv::Mat&, cv::RotatedRect&, cv::Scalar, int)’
rectangle(img, box2, Scalar(0, 0, 255), 3);
As per the cv::Rectangle Opencv Docs, the function has only 2 variants:
void rectangle(Mat& img, Point pt1, Point pt2, const Scalar& color, int thickness=1, int lineType=8, int shift=0)
void rectangle(Mat& img, Rect rec, const Scalar& color, int thickness=1, int lineType=8, int shift=0 )
So it is clear that it only accepts either cv::Rect or cv::Point. Hence there is no provision to directly input the cv::RotatedRect, due to which you are getting the above mentioned error.
To fix this issue, you can extract the 4 points of cv::RotatedRect using:
cv::Point2f points[4];
rotatedRect.points(points);
And then use cv::line() to draw the edges in pairs as:
cv::RotatedRect rotatedRect = cv::RotatedRect(cv::Point(70, 70), cv::Size(90, 90), 30);
cv::Mat canvas = cv::Mat(200, 200, CV_8UC3, cv::Scalar(255, 255, 255));
cv::Point2f points[4];
rotatedRect.points(points);
cv::line(canvas, points[0], points[1], cv::Scalar(0, 255, 0), 3);
cv::line(canvas, points[1], points[2], cv::Scalar(0, 255, 0), 3);
cv::line(canvas, points[2], points[3], cv::Scalar(0, 255, 0), 3);
cv::line(canvas, points[3], points[0], cv::Scalar(0, 255, 0), 3);
I'm trying to find the values for Height and Width to recover the Aspect Ration of the object using the contour of an image with the code below but not having success, since the code is creating many rectangles all over the image, when my intention is to create a single rectangle around the object.
I'm trying to create this rectangle because i don't know if there is another way to get the Height and Width (or even the Aspect Ratio) other than this one.
***RNG rng(12345); //Global Variable used for drawing rectangles and circles for the contours of images.
/*Load the image*/
Mat img_bgr = imread("img.jpg", 1);
if (img_bgr.empty()){
cout << "No image..." << endl;
return -1;
}
/*Display the image*/
namedWindow("Original Image", WINDOW_NORMAL);
imshow("Original Image", img_bgr);
/*Conversion to HSV*/
Mat img_hsv;
cvtColor(img_bgr, img_hsv, CV_BGR2HSV);
/*Extracting colors - HSV*/
Mat green, yellow, brown;
//Yellow
inRange(img_hsv, Scalar(25, 0, 0), Scalar(36, 255, 255), yellow); //until 33 - consider "yellow" - from there up to 36 - consider for chlorosis
imwrite("c:\\test\\results\\yellow.jpg", yellow);
//Green
inRange(img_hsv, Scalar(37, 0, 0), Scalar(70, 255, 255), green); //Consider lower as 37
imwrite("c:\\test\\results\\green.jpg", green);
//Brown
inRange(img_hsv, Scalar(10, 0, 0), Scalar(20, 255, 255), brown);
imwrite("c:\\test\\results\\brown.jpg", brown);
namedWindow("Yellow", WINDOW_NORMAL);
imshow("Yellow", yellow);
namedWindow("Green", WINDOW_NORMAL);
imshow("Green", green);
namedWindow("Brown", WINDOW_NORMAL);
imshow("Brown", brown);
/*Finding Contours of the Thresholded images*/
vector<std::vector<Point>>green_cnt;
vector<std::vector<Point>>yellow_cnt;
vector<std::vector<Point>>brown_cnt;
//Green Contour
findContours(green, green_cnt, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);
//Draw the Contours - Green
Mat green_cnt_draw(green.size(), CV_8UC3, Scalar(0, 0, 0));
Scalar green_cnt_colors[3];
green_cnt_colors[0] = Scalar(0, 255, 0);
green_cnt_colors[1] = Scalar(0, 255, 0);
green_cnt_colors[2] = Scalar(0, 255, 0);
for (size_t idx_green = 0; idx_green < green_cnt.size(); idx_green++){
drawContours(green_cnt_draw, green_cnt, idx_green, green_cnt_colors[idx_green % 3]);
}
namedWindow("Green - Contours", CV_WINDOW_NORMAL);
imshow("Green - Contours", green_cnt_draw);
//Yellow Contour
findContours(yellow, yellow_cnt, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);
//Draw the Contours - Yellow
Mat yellow_cnt_draw(yellow.size(), CV_8UC3, Scalar(0, 0, 0));
Scalar yellow_cnt_colors[3];
yellow_cnt_colors[0] = Scalar(0, 255, 255);
yellow_cnt_colors[1] = Scalar(0, 255, 255);
yellow_cnt_colors[2] = Scalar(0, 255, 255);
for (size_t idx_yellow = 0; idx_yellow < yellow_cnt.size(); idx_yellow++){
drawContours(yellow_cnt_draw, yellow_cnt, idx_yellow, yellow_cnt_colors[idx_yellow % 3]);
}
namedWindow("Yellow - Contours", CV_WINDOW_NORMAL);
imshow("Yellow - Contours", yellow_cnt_draw);
//Brown Contour
findContours(brown, brown_cnt, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);
//Draw the Contours - Brown
Mat brown_cnt_draw(brown.size(), CV_8UC3, Scalar(0, 0, 0));
Scalar brown_cnt_colors[3];
brown_cnt_colors[0] = Scalar(42, 42, 165);
brown_cnt_colors[1] = Scalar(42, 42, 165);
brown_cnt_colors[1] = Scalar(42, 42, 165);
for (size_t idx_brown = 0; idx_brown < brown_cnt.size(); idx_brown++){
drawContours(brown_cnt_draw, brown_cnt, idx_brown, brown_cnt_colors[idx_brown % 3]);
}
namedWindow("Brown - Contours", CV_WINDOW_NORMAL);
imshow("Brown - Contours", brown_cnt_draw);
/*Creating rectangles around the contours*/
//Green
vector<vector<Point>>green_contours_poly(green_cnt.size());
vector<Rect>green_boundRect(green_cnt.size());
vector<Point2f>green_center(green_cnt.size());
vector<float>green_radius(green_cnt.size());
for (int i = 0; i < green_cnt.size(); i++){
approxPolyDP(Mat(green_cnt[i]), green_contours_poly[i], 3, true);
green_boundRect[i] = boundingRect(Mat(green_cnt[i]));
minEnclosingCircle((Mat)green_contours_poly[i], green_center[i], green_radius[i]);
}
//Green - Draw polygonal contour AND bounding rects + circles
Mat green_drawRecAndCirc = Mat::zeros(green.size(), CV_8UC3);
for (int i = 0; i < green_cnt.size(); i++){
Scalar green_drawRecAndCircColor = Scalar(rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255));
rectangle(green_drawRecAndCirc, green_boundRect[i].tl(), green_boundRect[i].br(), green_drawRecAndCircColor, 2, 8, 0);
//circle(green_drawRecAndCirc, green_center[i], (int)green_radius[i], green_drawRecAndCircColor, 2, 8, 0);
}
imwrite("c:\\testeimagem\\theeye\\resultados\\green_rectangle_and_circle.jpg", green_drawRecAndCirc);
namedWindow("Green - Rectangle and Circle", CV_WINDOW_NORMAL);
imshow("Green - Rectangle and Circle", green_drawRecAndCirc);
/*Creating rectangles around the contours*/
//Yellow
vector<vector<Point>>yellow_contours_poly(yellow_cnt.size());
vector<Rect>yellow_boundRect(yellow_cnt.size());
vector<Point2f>yellow_center(yellow_cnt.size());
vector<float>yellow_radius(yellow_cnt.size());
for (int i = 0; i < yellow_cnt.size(); i++){
approxPolyDP(Mat(yellow_cnt[i]), yellow_contours_poly[i], 3, true);
yellow_boundRect[i] = boundingRect(Mat(yellow_cnt[i]));
minEnclosingCircle((Mat)yellow_contours_poly[i], yellow_center[i], yellow_radius[i]);
}
//Yellow - Draw polygonal contour AND bounding rects + circles
Mat yellow_drawRecAndCirc = Mat::zeros(yellow.size(), CV_8UC3);
for (int i = 0; i < yellow_cnt.size(); i++){
Scalar yellow_drawRecAndCircColor = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
rectangle(yellow_drawRecAndCirc, yellow_boundRect[i].tl(), yellow_boundRect[i].br(), yellow_drawRecAndCircColor, 2, 8, 0);
//circle(green_drawRecAndCirc, green_center[i], (int)green_radius[i], green_drawRecAndCircColor, 2, 8, 0);
}
waitKey(0);
destroyAllWindows;
return 0;
The original image is here:
And the example of the final result is here:
I tried the examples described in the following link (OpenCV Bounding Box) but I couldn't make it work either.
Edit 2:
Since i have to find some characteristics of the leaf that i cannot find with a rectangle (like, aspect ratio, mean diameter, radius ratio, roundness and mean feret) i had to change the approach of finding the leaf from a rectangle to an ellipse. The thing is, the ellipse is being drawn inside the leaf insteaf of contouring it.
Here is my code:
/*Load the image*/
Mat img_bgr = imread("image path", 1);
if (img_bgr.empty()){
cout << "No image found..." << endl;
return -1;
}
/*Conversion to HSV*/
Mat img_hsv;
cvtColor(img_bgr, img_hsv, CV_BGR2HSV);
/*Extracting colors - HSV*/
Mat yellow, green, brown;
//Yellow
inRange(img_hsv, Scalar(25, 80, 80), Scalar(36, 255, 255), yellow);
//Green
inRange(img_hsv, Scalar(37, 80, 80), Scalar(70, 255, 255), green);
//Brown
inRange(img_hsv, Scalar(10, 80, 80), Scalar(30, 200, 200), brown);
// logical OR mask
Mat1b mask = yellow | green | brown;
// Find non zero pixels
vector<Point> pts;
findNonZero(mask, pts);
// Compute ellipse
RotatedRect elipse = fitEllipse(pts);
//ELLIPSE - Heigth, Width and Center of Mass
cout << "ELLIPSE:" << endl;
cout << "\nHeight and Width: " << elipse.size; //Height and Width
cout << "\nCenter of Mass: " << elipse.center << endl; //Center of mass (probably given in X and Y coordinates)
// Show Ellipse
ellipse(img_bgr, elipse, Scalar(0, 0, 255), 3);
namedWindow("Ellipse", CV_WINDOW_NORMAL);
imshow("Ellipse", img_bgr);
waitKey(0);
destroyAllWindows;
return 0;
The result is shown below:
I can't understand what I'm doing wrong since i just changed the code the user Miki gave and that actually works perfectly.
Since your image is quite simple (you have a flat background) you can simplify a lot the task of finding the leaf. However, here I still use your approach based on thresholding the HSV values, which is likely to be more robust in general.
To find width and height of the leaf, you basically need to find it's bounding box. You don't need to find all contours of your color masks, nor to merge all bounding boxes. But you can:
1) compute the mask for the yellow, green and brown colors (I sligthly modified the ranges to more meaningful values)
Yellow:
Green:
Brown:
2) OR these mask toghether
3) find all non zero pixels
4) compute the bounding box
Code:
#include <opencv2/opencv.hpp>
#include <vector>
#include <string>
using namespace std;
using namespace cv;
int main()
{
// Load the image
Mat3b img_bgr = imread("path_to_image");
if (img_bgr.empty()){
cout << "No image..." << endl;
return -1;
}
// Convert to hsv
Mat3b img_hsv;
cvtColor(img_bgr, img_hsv, COLOR_BGR2HSV);
Mat1b yellow, green, brown;
//Yellow
inRange(img_hsv, Scalar(25, 80, 80), Scalar(36, 255, 255), yellow);
//Green
inRange(img_hsv, Scalar(37, 80, 80), Scalar(70, 255, 255), green);
//Brown
inRange(img_hsv, Scalar(10, 80, 80), Scalar(30, 200, 200), brown);
// logical OR mask
Mat1b mask = yellow | green | brown;
// Find non zero pixels
vector<Point> pts;
findNonZero(mask, pts);
// Compute bounding box
Rect box = boundingRect(pts);
cout << "Width: " << box.width;
cout << "Height: " << box.height << endl;
// Show box
rectangle(img_bgr, box, Scalar(0,0,255), 3);
imshow("Box", img_bgr);
return 0;
}