I' am actually working with a cv::Mat with B&W pixels.
I'am searching for a way to get a list of my black point in this Mat.
Does someone know how to do such thing ?
I want to do that because I want to detect the bounding rect of this points.
(The best is to get them back in a vector)
somekind of :
cv::Mat blackAndWhite;
std::vector<cv::Point> blackPixels = MAGIC_FUNCTION(blackAndWhite);
Thanks for your help.
Edit: I want to precise that I want the best practices, the more Opencv compliant as possible.
You can traverse the cv::Mat to check the pixels that are 0, and get the x and y coordinates from the linear index if the matrix is continuous in memory:
// assuming your matrix is CV_8U, and black is 0
std::vector<cv::Point> blackPixels;
unsigned int const *p = blackAndWhite.ptr<unsigned char>();
for(int i = 0; i < blackAndWhite.rows * blackAndWhite.cols; ++i, ++p)
{
if(*p == 0)
{
int x = i % blackAndWhite.cols;
int y = i / blackAndWhite.cols;
blackPixels.push_back(cv::Point(x, y));
}
}
This example from OpenCV shows how to do exactly what you want: Creating Bounding boxes and circles for contours. Basically, it this:
// ...
/// Find contours
findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, 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]) );
minEnclosingCircle( (Mat)contours_poly[i], center[i], radius[i] );
}
Related
I have the following output from red-only filtration done by the following algorithm:
cv::Mat findColor(const cv::Mat & inputBGRimage, int rng=20)
{
// Make sure that your input image uses the channel order B, G, R (check not implemented).
cv::Mat mt1, mt2;
cv::Mat input = inputBGRimage.clone();
cv::Mat imageHSV; //(input.rows, input.cols, CV_8UC3);
cv::Mat imgThreshold, imgThreshold0, imgThreshold1; //(input.rows, input.cols, CV_8UC1);
assert( ! input.empty() );
// blur image
cv::blur( input, input, Size(11, 11) );
// convert input-image to HSV-image
cv::cvtColor( input, imageHSV, cv::COLOR_BGR2HSV );
// In the HSV-color space the color 'red' is located around the H-value 0 and also around the
// H-value 180. That is why you need to threshold your image twice and the combine the results.
cv::inRange( imageHSV, cv::Scalar( H_MIN, S_MIN, V_MIN ), cv::Scalar( H_MAX, S_MAX, V_MAX ), imgThreshold0 );
if ( rng > 0 )
{
// cv::inRange(imageHSV, cv::Scalar(180-rng, 53, 185, 0), cv::Scalar(180, 255, 255, 0), imgThreshold1);
// cv::bitwise_or( imgThreshold0, imgThreshold1, imgThreshold );
}
else
{
imgThreshold = imgThreshold0;
}
// cv::dilate( imgThreshold0, mt1, Mat() );
// cv::erode( mt1, mt2, Mat() );
return imgThreshold0;
}
And here is the output:
And I want to detect the four coordinates of the rectangle. As you can see, the output is not perfect, I used cv::findContours in conjunction with cv::approxPolyDP before, but it's not working good anymore.
Is there any filter that I can apply for input image (except blur, dilate, erode) to make image better for processing?
Any suggestions?
Updated:
When I am using findContours like this:
findContours( src, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE );
double largest_area = 0;
for( int i = 0; i < contours.size(); i++) { // get the largest contour
area = fabs( contourArea( contours[i] ) );
if( area >= largest_area ) {
largest_area = area;
largestContours.clear();
largestContours.push_back( contours[i] );
}
}
if( largest_area > 5000 ) {
cv::approxPolyDP( cv::Mat(largestContours[0]), approx, 100, true );
cout << approx.size() << endl; /* ALWAYS RETURN 2 ?!? */
}
The approxPolyDP is not working as expected.
I think your result is quite good, maybe if you select the contour with greatest area using Image Moments and then finding the minimal rotated rectangle of the bigger contour.
vector<cv::RotatedRect> cv::minRect( contours.size() );
for( size_t = 0; i < contours.size(); i++ )
{
minRect[i] = minAreaRect( cv::Mat(contours[i]) );
}
Rotated Rect class already has a vector of Point2f to store the points.
RotatedRect rRect = RotatedRect(Point2f(100,100), Size2f(100,50), 30);
Point2f vertices[4];
rRect.points(vertices);
for(int i = 0; i < 4; i++){
std::cout << vertices[i] << " ";
}
I am eroding an image with text blocks on it then using findContours() to find all of the text blocks, then drawing their bounding rect. However sometimes there are very small rects created by noise in the image which either are in a bigger rect or in a place where there is no text.
I am using this code to find the contours and draw them.
double element_size = 20;
RNG rng(12345);
Mat element = getStructuringElement( cv::MORPH_ELLIPSE,cv::Size( 2*element_size + 1, 2*element_size+1 ),cv::Point( element_size, element_size ) );
erode(quad, quad, element);
vector<vector<cv::Point> > contours;
vector<Vec4i> hierarchy;
quad.convertTo(quad, CV_8UC1);
findContours( quad, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) );
vector<vector<cv::Point> > contours_poly( contours.size() );
vector<cv::Rect> boundRect( 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]) );
}
Mat drawing = Mat::zeros( quad.size(), CV_8UC3 );
for( int i = 0; i< contours.size(); i++ )
{
Scalar color = Scalar(0,255, 0 );
rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0 );
}
After a sample run here is what I get back:
How can I modify my code so that I can remove any rects that are not greater than n so I can only keep complete text blocks, I also need to remove the largest contour which surrounds the entire card.
Yo could use contourArea to eliminate contour, use below code before finding bounding rect, this will remove all the contour with area less than a threshold.
double min_area=100; // area threshold
for( int i = 0; i< contours.size(); i++ ) // iterate through each contour.
{
double area=contourArea( contours[i],false); // Find the area of contour
if(area<min_area)
contours.erase(contours.begin() + i);
}
Edit:-
For any one who is going to use the above code, please see below comment.
For a safer method of removing cv::Rect elements from your vector, you could use the erase-remove idiom to remove the elements which are below a certain area threshold. This method is much safer than deleting elements one-by-one by their index, as in Haris' answer, since you do not risk running off the end of the vector.
boundRect.erase(std::remove_if(boundRect.begin(), boundRect.end(),
[] (cv::Rect r)
{
const int min_area = 100;
return r.area() < min_area;
}), boundRect.end());
Here I use a C++11 lambda for the comparison. If you don't have C++11, it's easy enough to create a functor class instead.
As for removing the largest contour, you can use another function from the standard library, max_element (Again using a lambda for comparison):
boundRect.erase(std::max_element(boundRect.begin(), boundRect.end(),
[] (cv::Rect left, cv::Rect right)
{
return left.area() < right.area();
}));
i m new in this topic.i'm trying to use some openCV library to make a project but i have some problems with findContour,drawContours.After i read an input image and make a tresholding , i use findContours as in the code
cv::Mat cont; // i create a matrix
result2.copyTo(cont); // this is the copy of the input image tresholded
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(cont,contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cv::Point(0,0 ));
eiid::imshow("Im_Find", cont); // I watch the points
for( int s = 0; s< contours.size(); s++ )
{
printf(" * Contour[%d]= Area OpenCV: %.2f \n", s,cv::contourArea(contours[s])) ;
drawContours( cont, contours, s, cv::Scalar(255,255), 10, 1, hierarchy, 0, cv::Point() );
}
eiid::imshow ( "FINE" , cont);
I have 7 elements connected, ( so 7 areaContours)
This is my objective: knowing the areas of my all elemets, I want to draw, to paint, only the biggest area (and so exclude the other 6 areas )
I can't do it , someone can help me please?
(I tried to store my 7 areas in double array but i can't go on :( )
Thanks to answer me man, but i still have a problem. Your code works but i don't know why some pixels are still in the image... When i watch the final image i see my biggest contour but also some white pixels :( .. The problem is that i have to use this algoritm for more pictures, so my code has to be standard for all pictures..Maybe i use uncorrectly the functions??
My image is black and white but Color(255,255) i think is good in my case. Howerver i understood my problem is that findContours put pixels in my image,so i copyed in other image and now i don t have problems. HOwever i still have a problem :( Now i have the biggest contour,but i need to apply it on my original image to exclude some text from it..The problem is that the inside of the biggest contour is black , but in my original image , the inside is white...so i can 't get a operation like " less - " (interseption) ....:( how can i do man?
Just find the biggest one first, then draw.
int idx = -1;
double maxArea = 0.0;
for( int s = 0; s< contours.size(); s++ )
{
double area = cv::contourArea(contours[s]);
if( area > maxArea){
maxArea = area;
idx = s;
}
}
cv::drawContours( cont, contours, idx , cv::Scalar(255,255), 10, 1, hierarchy, 0, cv::Point() );
There we go.
(FYI: try not to be lazy and figure out what happens in my function below.
cv::Mat findBiggestBlob(cv::Mat & matImage){
int largest_area=0;
int largest_contour_index=0;
vector< vector<Point> > contours; // Vector for storing contour
vector<Vec4i> hierarchy;
findContours( matImage, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE ); // Find the contours in the image
for( int i = 0; i< contours.size(); i++ ) {// iterate through each contour.
double a=contourArea( contours[i],false); // Find the area of contour
if(a>largest_area){
largest_area=a;
largest_contour_index=i; //Store the index of largest contour
//bounding_rect=boundingRect(contours[i]); // Find the bounding rectangle for biggest contour
}
}
drawContours( matImage, contours, largest_contour_index, Scalar(255), CV_FILLED, 8, hierarchy ); // Draw the largest contour using previously stored index.
return matImage;
}
I am developing a OpenCV project.
I am currently working on detecting contours of particular ROI (Regoin Of Interest). What I want to achieve is to eliminate all the smaller contours in other words I do not want these smaller contours to be drown at all.
So Far if I have coded this algorithm to do this job:
CODE:
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(mBlur, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
//----------------------------------------------------------------------------->
//Contours Vectors
vector<vector<Point> > contours_poly(contours.size());
vector<Rect> boundRect (contours.size());
vector<Point2f> ContArea(contours.size());
Mat drawing = Mat::zeros( range_out.size(), CV_8UC3 );
//----------------------------------------------------------------------------->
//Detecting Contours
for( int i = 0; i < contours.size(); i++ )
{
ContArea.clear();
ContArea.push_back(Point2f(boundRect[i].x, boundRect[i].y));
ContArea.push_back(Point2f(boundRect[i].x + boundRect[i].width, boundRect[i].y));
ContArea.push_back(Point2f(boundRect[i].x + boundRect[i].width, boundRect[i].y + boundRect[i].height));
ContArea.push_back(Point2f(boundRect[i].x, boundRect[i].y + boundRect[i].height));
double area = contourArea(ContArea);
if(area > 2000)
{
approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
boundRect[i] = boundingRect( Mat(contours_poly[i]));
cout<<"The area of Contour: "<<i<< " is: " <<area<<endl;
}
}
/// Draw polygonal contour + bonding rects
//////////////////////////////////////////////////////////////////////////////////
for( int i = 0; i< contours.size(); i++ )
{
Scalar color = Scalar(255,255,255);
drawContours( drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
fillPoly(drawing, contours, Scalar(255,0,0));
}
The problem here is that it looks like the if(area > 2000) statement is not executed even dough i know some of areas present in image are way bigger than this.
I have been trying a lot of different solutions but this looks to be the most appropriate one to me.
THE KEY QUESTIONS:
Is it Possible to achieve what I want with the given code....?
If so can anyone see where I am going wrong with this
Else Could someone suggest some kind of solution or a good online source....?
If you want to remove based on the size of ROIs, you can do as follow (based on the example of bounding box of opencv):
vector< vector< Point > > contours_poly( contours.size() );
vector< Rect > boundRect (contours.size() );
vector< Point2f > centeres ( contours.size() );
vector< float > radiuses ( contours.size() );
// finding the approximate rectangle and circle of contours
for( int i = 0; i < contours.size(); i++ )
{
approxPolyDP( Mat ( contours[i] ), contours_poly[i], 3, true );
boundRect[i] = boundingRect( Mat ( contours_poly[i] ) );
minEnclosingCircle( ( Mat ) contours_poly[i], centeres[i], radiuses[i] );
}
The above code snippet gives you the approximate rectangles of contours (boundRect), and approximate circles (centers, radiuses). After this you must be able to call contourArea(); and if it's smaller than certain threshold, you can eliminate it.
If you just want to remove contours that are smaller certain length, you can calculate it's length, look at the answer to similar question, and it seems you can use arcLength() function. I think something like this: double perimeter = arcLength ( Mat ( contours[i] ), true );.
I am doing a OpenCV software that detects boxing gloves therefore i want to detect and draw only 2 largest contours (one for each boxing glove).
My software draws contours for everything and some things are noise only which ofcourse i dont want
My code for drawing contours:
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(mBlur, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
//----------------------------------------------------------------------------->
vector<vector<Point> > contours_poly(contours.size());
vector<Rect> boundRect (contours.size());
vector<Point2f> boundingBoxArea(boundRect.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(0,0,255);
drawContours( drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
fillPoly(drawing, contours, Scalar(255,0,0));
}
Here is an Image Example:
My program already segments the gloves by color, The problelem is that at times small contours are drawn in random locations due to noise. Now of course the gloves contours are by far dominant and this is why i want to keep only the contours of these. Hope this makes my question clearer
Could someone suggest a solution please
I am coding in C++ environment
regards
The easiest way to look at the two largest contours is to simply look at the contours size. Something like this should do the trick:
int largestIndex = 0;
int largestContour = 0;
int secondLargestIndex = 0;
int secondLargestContour = 0;
for( int i = 0; i< contours.size(); i++ )
{
if(contours[i].size() > largestContour){
secondLargestContour = largestContour;
secondLargestIndex = largestIndex;
largestContour = contours[i].size();
largestIndex = i;
}else if(contours[i].size() > secondLargestContour){
secondLargestContour = contours[i].size();
secondLargestIndex = i;
}
}
Scalar color = Scalar(0,0,255);
drawContours( drawing, contours, largestIndex, color, CV_FILLED, 8);
drawContours( drawing, contours, secondLargestIndex, color, CV_FILLED, 8);
It seems that vector<vector<Point> > contours stores all your contours. What you need to do is iterate on this vector and do a little arithmetics with it elements, to be able to detect the 2 largest contours in the vector.
On this answer I shared code that detects the largest contour in a vector<vector<Point> >, so you are half way there.