FindContour: Measure distance between contours - c++

I would be very thankful to know further on post: Finding Minimum Distance between Contours
I am using FindContours and as required getting multiple contours.
The problem is that I want to find min distance between each adjacent pair,
e.g. between yellow contour and dark green, between dark green and cyan, between cyan and purple.
I understood the general method from above post.
But can anyone please tell me how can i select them one after another(automaticaly)?
void thresh_function(int, void*)
{
Mat canny_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
/// Detect edges using canny
Canny( roiImg, canny_output, threshold_value, threshold_value*2, 3 );
/// Find contours
findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
/// Draw contours
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
for( int i = 0; i< contours.size(); i++ )
{
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
drawContours( drawing, contours, i, color, 2, 8, hierarchy, 0, Point() );//Scalar(255,255,255)
}
erode(drawing,drawing,erodeElement2);
erode(drawing,drawing,erodeElement1);
dilate(drawing,drawing,dilateElement);
/// Show in a window
//namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
resize(drawing, enlargeD0, Size(), 2, 2, CV_INTER_CUBIC);
done = 1;
imshow("Contours", enlargeD0);
}

vector<vector<Point> > all_contours;
...
findContours(... all_contours ... CV_CHAIN_APPROX_NONE ...);
...
// Remove small contours
int minSize = 20;
vector<vector<Point> > contours;
contours.reserve(all_contours.size());
for(int i=0; i<all_contours.size(); ++i)
{
if(all_contours[i].size() > minSize)
{
contours.push_back(all_contours[i]);
}
}
Mat1f dist(contours.size(), contours.size(), 0.f);
for(int i=0; i<contours.size()-1; ++i)
{
const vector<Point>& firstContour = contours[i];
for(int j=i+1; j<contours.size(); ++j)
{
const vector<Point>& secondContour = contours[j];
float d = compute_pairwise_distance(firstContour, secondContour); // You should implement this
dist(i,j) = d;
dist(j,i) = d; // distance from first to second is equal
// to distance from second to first
}
}
// Now dist contains the pairwise distances between i-th and j-th contours

Related

C++ Open CV draw one color for the same shape of contour

I have this code where I use Image Moments. I want to draw once a color per shape of the contour. Now if I have five triangles, all of it is drawing in different colors. All I want to do is a way to separate shapes each other, drawing them with the same color.
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(src, contours, hierarchy,
CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
vector<Moments> mu(contours.size());
vector<Point2f> mc(contours.size());
for (int i = 0; i < contours.size(); i++)
{
mu[i] = moments(Mat(contours[i]), false);
mc[i] = Point2f(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);
}
for (int i = 0; i < contours.size(); i++)
{
Scalar color(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(dst, contours, i, color, CV_16U, 8, hierarchy);
}
for (int i = 0; i < contours.size(); i++)
{
Scalar color(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(dst, contours, i, color, CV_16U, 8, hierarchy);
}
In the above code Scalar color(.....) defines the colour for each contour. Currently this is in the for loop, as such it creates a new colour for every contour.
Move the Scalar color(.....) out of the for loop and you will only have one colour assigned to the contours.
Scalar color(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
for (int i = 0; i < contours.size(); i++)
{
drawContours(dst, contours, i, color, CV_16U, 8, hierarchy);
}
I would suggest you create a scalar vector containing colors for each of the shape you want. total_shape corresponds to side of the shape you wish to color. For example if you would like to color shapes that includes octagon, then total_shape = 8 + 1. Store the colors to the vector shape_colors.
std::vector<Scalar> shape_colors;
for (int i = 0; i < total_shape; i++)
{
Scalar color(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
shape_colors.push_back(color);
}
Then when you want to color your contour, check out how many points each of your contour has. Based on the point number, select the color from the shape_color and voila, same color scheme for same shape.
However, depending on the angle of the shape, the contour result might return too many points. We need to simplify the contour to the simplest form as possible using approxPolyDP. Meaning we want a rectangle to contain only 4 points, a triangle 3 and a pentagon 5 points. The detailed explanation of this function is given by this link. By doing so, we will be able to determine the shape of contour by the total number of point it contains.
for (int i = 0; i < contours.size(); i++)
{
cv::Mat approx;
approxPolyDP(contours[i], approx, 30, true);
int n = approx.checkVector(2);
drawContours(dst, contours, i, shape_colors[n],25);
}
Here is the entire code:
void process()
{
cv::Mat src;
cv::Mat dst;
cv::RNG rng;
std::string image_path = "Picture1.png";
src = cv::imread(image_path,0);
dst = cv::imread(image_path);
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
cv::threshold(src, src, 120, 255, cv::THRESH_BINARY);
findContours(src, contours, hierarchy,
CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
int total_shape = 10;
std::vector<Scalar> shape_colors;
for (int i = 0; i < total_shape; i++)
{
Scalar color(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
shape_colors.push_back(color);
}
for (int i = 0; i < contours.size(); i++)
{
cv::Mat approx;
approxPolyDP(contours[i], approx, 30, true);
int n = approx.checkVector(2);
drawContours(dst, contours, i, shape_colors[n],25);
}
cv::imshow("dst", dst);
cv::waitKey(0);
}
And here is the result :

Draw Rect around result of canny edge

I want to draw Rect around detected canny edges. I have this image which is result of eye detection, morphological operations and canny edge.
I tried using contours to bound it by rect but result was not accurate.
How can I get some thing like this image?
I'm using this function to draw contours:
void find_contour(Mat image)
{
Mat src_mat, gray_mat, canny_mat;
Mat contour_mat;
Mat bounding_mat;
contour_mat = image.clone();
bounding_mat = image.clone();
cvtColor(image, gray_mat, CV_GRAY2BGR);
// apply canny edge detection
Canny(gray_mat, canny_mat, 30, 128, 3, false);
//3. Find & process the contours
//3.1 find contours on the edge image.
vector< vector< cv::Point> > contours;
findContours(canny_mat, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//3.2 draw contours & property value on the source image.
int largest_area = 0;
int largest_contour_index = 0;
Rect bounding_rect;
for (size_t i = 0; i< contours.size(); i++) // iterate through each contour.
{
double area = contourArea(contours[i]); // Find the area of contour
if (area > largest_area)
{
largest_area = area;
largest_contour_index = i; //Store the index of largest contour
bounding_rect = boundingRect(contours[i]); // Find the bounding rectangle for biggest contour
}
}
drawContours(image, contours, largest_contour_index, Scalar(0, 255, 0), 2);
imshow("Bounding ", image);
}
in your code you aren't drawing the bounding rectangle at all. Try this:
void find_contour(Mat image)
{
Mat src_mat, gray_mat, canny_mat;
Mat contour_mat;
Mat bounding_mat;
contour_mat = image.clone();
bounding_mat = image.clone();
cvtColor(image, gray_mat, CV_GRAY2BGR);
// apply canny edge detection
Canny(gray_mat, canny_mat, 30, 128, 3, false);
//3. Find & process the contours
//3.1 find contours on the edge image.
vector< vector< cv::Point> > contours;
findContours(canny_mat, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//3.2 draw contours & property value on the source image.
int largest_area = 0;
int largest_contour_index = 0;
Rect bounding_rect;
for (size_t i = 0; i< contours.size(); i++) // iterate through each contour.
{
// draw rectangle around the contour:
cv::Rect boundingBox = boundingRect(contours[i]);
cv::rectangle(image, boundingBox, cv::Scalar(255,0,255)); // if you want read and "image" is color image, use cv::Scalar(0,0,255) instead
// you aren't using the largest contour at all? no need to compute it...
/*
double area = contourArea(contours[i]); // Find the area of contour
if (area > largest_area)
{
largest_area = area;
largest_contour_index = i; //Store the index of largest contour
bounding_rect = boundingRect(contours[i]); // Find the bounding rectangle for biggest contour
}
*/
}
//drawContours(image, contours, largest_contour_index, Scalar(0, 255, 0), 2);
imshow("Bounding ", image);
}
You can do it like this also,
findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
vector<RotatedRect> minRect( contours.size() );
/// Draw contours
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
for( int i = 0; i< contours.size(); i++ )
{
Scalar color = Scalar(255, 255, 255);
cv::Rect boundingBox = cv::boundingRect(cv::Mat(contours[i]));
minRect[i] = minAreaRect(Mat(contours[i]));
drawContours( drawing, contours, i, color, 1, 8, hierarchy, 0, Point() );
}
for( int i = 0; i< contours.size(); i++ )
{
// rotated rectangle
Point2f rect_points[4]; minRect[i].points( rect_points );
for( int j = 0; j < 4; j++ )
line( drawing, rect_points[j], rect_points[(j+1)%4], Scalar(0,0,255), 1, 8 );
}

from std::vector<std::vector<cv::Point>> to cv::Mat [duplicate]

This question already has an answer here:
Convert Vector<Point> to Mat
(1 answer)
Closed 8 years ago.
I want to get every vector of points into a matrix like :
std::vector<std::vector<cv::Point>> vec;
......................
for (int i ; i < vec.size();i++){
imshow("stuff", cv::Mat(vec[i]); /// this crashes !!!
}
any idea how to do that?
thanks in advance
imshow looks for a complete picture.
by casting a contour to a Mat you won't have a picture.
what you can do is: (src is your picture)
src = imread( argv[1], 1 );
/// Convert image to gray and blur it
cvtColor( src, src_gray, CV_BGR2GRAY );
blur( src_gray, src_gray, Size(3,3) );
Mat canny_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
/// Detect edges using canny
Canny( src_gray, canny_output, thresh, thresh*2, 3 );
/// Find contours
findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
/// Draw contours
for( int i = 0; i< contours.size(); i++ )
{
Mat test_image = Mat::zeros( canny_output.size(), CV_8UC3 );
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
drawContours( test_image, contours, i, color, 2, 8, hierarchy, 0, Point() );
imshow("test",test_image);
waitKey();
}
You can do it like this:
void draw_contour(cv::Mat &dst_img, const std::vector<cv::Point> &contour, const cv::Scalar &color)
{
for (auto &point: contour)
{
dst_img.at<unsigned char>(point) = color;
}
}
Or if you are using approximation of contours:
void draw_contour(cv::Mat &dst_img, const std::vector<cv::Point> &contour, const cv::Scalar &color)
{
for (unsigned i = 0; i < contour.size(); ++i)
{
cv::line(dst_img, contour[i], contour[(i + 1) % contour.size(), color);
}
}

Calculating the area of Bounding Box

Hello StackOverflowers
I have created an application that Segments an image on the basis of a predefined color using inRange function. I then draw bounding box around the detected object.
My question here is how do I determine region properties such as: area, size, height and with, center point.
Here i placed a screen dump example.....
How should i approach to retrieve region properties of these bounding boxes or any other bounding boxes that get drown.......?
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(mBlur, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
/// Approximate contours to polygons + get bounding rects and circles
vector<vector<Point> > contours_poly( contours.size() );
vector<Rect> boundRect( contours.size() );
vector<Point2f>center( contours.size() );
vector<float>radius( contours.size() );
for( int i = 0; i < contours.size(); i++ )
{ approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
boundRect[i] = boundingRect( Mat(contours_poly[i]) );
}
/// Draw polygonal contour + bonding rects
Mat drawing = Mat::zeros( range_out.size(), CV_8UC3 );
for( int i = 0; i< contours.size(); i++ )
{
Scalar color = Scalar(255,0,255);
drawContours( drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0 );
}
Regards
You can get the area by using the built in OpenCV function. There are other functions there too to get everything you need.
Just iterate over the 2D coordinates of the segmented shape (the thin pink line in your pictures, you can found this just checking which pixels are not black and looking into its coordinates) and store maximum and minimum X and Y found. Then, width of is maxX - minX and height is maxY - minY
void visualizeSegments(Mat& img, Mat& dst)
{
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(img, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
dst=Mat::zeros(img.size(), CV_8UC3);
for(int i = 0; i < contours.size(); i++)
{
//Moments mu = moments(contours[i], true );
//Point2f centroid(mu.m10/mu.m00,mu.m01/mu.m00);
//double area = fabs(contourArea(Mat(contours[i])));
//vector<Point> contours_poly;
//approxPolyDP(Mat(contours[i]), contours_poly, 3, true);
//Rect boundRect = boundingRect(Mat(contours_poly));
drawContours(dst, contours, i, Scalar(255,0,0), -1, 8, hierarchy);
}
}
As stated befor there are a set of usefull functions in OpenCV
1. double contourArea(InputArray contour, bool oriented=false ) : to comute the area
2. double arcLength(InputArray curve, bool closed) : to compute the perimeter
3. Moments moments(InputArray array, bool binaryImage=false ) : to compute the center of gravity
4. void HuMoments(const Moments& m, OutputArray hu) : if you want additional properties that is usefull for classification

Contours / Connected components in OpenCV 2.4.4

I'm currently working on an image with a lot of detected contours.
My goal is to narrow down the number of contours to end up with only the one I'm looking for.
For that I conduct a bunch of tests based on area and bounding box.
For now I do after every step a drawContours for the contours that I want to keep followed by a findContours.
My problem is that I would like to do findContours only once and then just erase the contours I don't want, is this possible?
Current way :
Mat src;
Mat BW;
src = imread("img.bmp", 0);
if( src.channels() > 1)
{
cvtColor(src, src, CV_BGR2GRAY);
}
threshold(src, BW, 100, 255, CV_THRESH_OTSU);
imshow( "Tresh", BW );
Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC3);
Mat dstP = Mat::zeros(src.rows, src.cols, CV_8UC3);
Mat dst1 = Mat::zeros(src.rows, src.cols, CV_8UC3);
Mat dst2 = Mat::zeros(src.rows, src.cols, CV_8UC3);
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours( BW, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
for( int i = 0; i < contours.size(); i++ )
{
Scalar color( rand()&255, rand()&255, rand()&255 );
drawContours( dst, contours, i, color, 2/*CV_FILLED*/, 8, hierarchy );
}
/// Test on area ******************************************************************
for( int i = 0; i < contours.size(); i++ )
{
if ( contourArea(contours[i], false) > 100 && contourArea(contours[i], false) < 200000)
{
Scalar color( rand()&255, rand()&255, rand()&255 );
drawContours( dst1, contours, i, color, CV_FILLED, 8, hierarchy );
}
}
/// Next test **********************************************************************
cvtColor(dst1, dstP, CV_BGR2GRAY);
findContours( dstP, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
etc
Wanted way :
if ( contourArea(contours[i], false) < 100 && contourArea(contours[i], false) > 200000)
{
contours.erase(i); // Doesn't work
}
Does anyone now how to erase those contours?
PS : I don't care about inner contours, I want all of them to go trough my tests.
EDIT, the solution (pointed out by limonana) is : contours.erase(contours.begin()+i);