I am pretty new to programming. And I want to make program which is able to filtrate image from small objects and non-convex objects so only shapes such as rectangles, triangles, circles etc. stay.
What have I done so far?
I managed to obtain image in binary by two separate ways (color detection and canny function) Then I created contours with function findContours. So that is working flawlessly.
here's the code:
vector<Point> approxShape;
vector<vector<Point>> FiltredContours;
vector<vector<Point>> TRI;
vector<vector<Point>> RECT;
vector<vector<Point>> PENTA;
Mat WSO= Mat::zeros(im.size(), CV_8UC3); //Without Small Objects
for ( int j = 0; j < contours.size(); j++)
{
if ((fabs(contourArea(contours[j]))) >100)
drawContours(WSO, contours, j,Scalar(128,128,128),2,8,hiearchy,0, Point()); // to see how it looks before it goes further
{
approxPolyDP( Mat(contours[j]), approxShape, arcLength(Mat(contours[j]), true) * 0.02, true);
if (isContourConvex(approxShape))
{
FiltredContours.push_back(approxShape);
}
}
}
///--------Show image after filtring small obj. -----
imshow("WSO",WSO);
////--------Filtred-Image-Drawing---------------------
Mat approxmat = Mat::zeros(imHSV.size(),CV_8UC3);
drawContours(approxmat, FiltredContours, -1,barva,2,8,hiearchy,0, Point());//drawContours(approxkresba, FiltredContours, -1,Scalar(255, 0, 0),2,8,hiearchy,0, Point());
namedWindow("Filtred objects",CV_WINDOW_AUTOSIZE);
imshow("Filtred objects",approxmat);
I tried to change parameters in contourArea and in approxPollyDP as well. It still doesn't work the way I thought it would.
Related
I have a binary image, from which I need to consider only the white regions as contours but it also takes black region which is surrounded by white part as contour.
I don't want to use contour area, can we ignore the black regions while finding contours?
Here is the binary image and the orange color marked is also considered as contour, so do not want the black region surrounded with white to be considered as contour.
Contour image is:
My contouring code:
//contouring
vector<vector<Point> > contours;
findContours(img, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
vector<vector<Point> > contours_poly(contours.size());
vector<Rect> boundRect(contours.size());
vector<Point2f>centers(contours.size());
vector<float>radius(contours.size());
for (size_t i = 0; i < contours.size(); i++)
{
approxPolyDP(contours[i], contours_poly[i], 3, true);
boundRect[i] = boundingRect(contours_poly[i]);
minEnclosingCircle(contours_poly[i], centers[i], radius[i]);
}
Mat drawing = Mat::zeros(img.size(), CV_8UC3);
for (size_t i = 0; i < contours.size(); i++)
{
Scalar color = Scalar(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));
drawContours(drawing, contours_poly, (int)i, color);
}
Your code snippets do not compose a minimal reproducible example (https://stackoverflow.com/help/minimal-reproducible-example).
Therefore I could not run it for testing.
However - you might be able to achieve what you want by "playing" with the 2 last parameters of cv::findContours.
From opencv documentation of cv::findContours:
mode Contour retrieval mode, see [RetrievalModes][1]
method Contour approximation method, see [ContourApproximationModes][1]
Is there an OpenCV function that can determine if a contour is circular?
fitEllipse() seems to work for any contour (correct me if I am wrong, I did a quick test).
isContourConvex() would'nt even detect my best circles.
I am attempting to detect pizzas (camera is at an angle so the pizza shapes are ellipses). HoughCircles works for the pizza's general position but not for the pizza's elliptical shape (it either find circles too small or too large).
static void detectPizzas(const Mat& roi)
{
Mat blur, edges;
GaussianBlur(roi, blur, Size(3, 3), 0);
Canny(blur, edges, 200, 275);
dilate(edges, edges, getStructuringElement(CV_SHAPE_ELLIPSE, { 3, 3 }));
std::vector<std::vector<Point>> contours;
findContours(edges, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
// Following draws ellipses to all contours that aren't even circular.
// How can I discriminate between 'circular' contours and non-circular contours?
for (int i = 0; i < contours.size(); i++)
{
// Doesn't detect any ellipses
//if (!isContourConvex(contours[i]))
// continue;
RotatedRect r = fitEllipse(contours[i]);
ellipse(edges, r, { 125 }, 2);
}
imshow("edges", edges);
}
Sample images:
I have a photo where a person holds a sheet of paper. I'd like to detect the rectangle of that sheet of paper.
I have tried following different tutorials from OpenCV and various SO answers and sample code for detecting squares / rectangles, but the problem is that they all rely on contours of some kind.
If I follow the squares.cpp example, I get the following results from contours:
As you can see, the fingers are part of the contour, so the algorithm does not find the square.
I, also, tried using HoughLines() approach, but I get similar results to above:
I can detect the corners, reliably though:
There are other corners in the image, but I'm limiting total corners found to < 50 and the corners for the sheet of paper are always found.
Is there some algorithm for finding a rectangle from multiple corners in an image? I can't seem to find an existing approach.
You can apply a morphological filter to close the gaps in your edge image. Then if you find the contours, you can detect an inner closed contour as shown below. Then find the convexhull of this contour to get the rectangle.
Closed edges:
Contour:
Convexhull:
In the code below I've just used an arbitrary kernel size for morphological filter and filtered out the contour of interest using an area ratio threshold. You can use your own criteria instead of those.
Code
Mat im = imread("Sh1Vp.png", 0); // the edge image
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(11, 11));
Mat morph;
morphologyEx(im, morph, CV_MOP_CLOSE, kernel);
int rectIdx = 0;
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(morph, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
for (size_t idx = 0; idx < contours.size(); idx++)
{
RotatedRect rect = minAreaRect(contours[idx]);
double areaRatio = abs(contourArea(contours[idx])) / (rect.size.width * rect.size.height);
if (areaRatio > .95)
{
rectIdx = idx;
break;
}
}
// get the convexhull of the contour
vector<Point> hull;
convexHull(contours[rectIdx], hull, false, true);
// visualization
Mat rgb;
cvtColor(im, rgb, CV_GRAY2BGR);
drawContours(rgb, contours, rectIdx, Scalar(0, 0, 255), 2);
for(size_t i = 0; i < hull.size(); i++)
{
line(rgb, hull[i], hull[(i + 1)%hull.size()], Scalar(0, 255, 0), 2);
}
I am working in C++ and opencv
I am detecting the big contour in an image because I have a black area in it.
In this case, the area is only horizontally, but it can be in any place.
Mat resultGray;
cvtColor(result,resultGray, COLOR_BGR2GRAY);
medianBlur(resultGray,resultGray,3);
Mat resultTh;
Mat canny_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
Canny( resultGray, canny_output, 100, 100*2, 3 );
findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
Vector<Point> best= contours[0];
int max_area = -1;
for( int i = 0; i < contours.size(); i++ ) {
Scalar color = Scalar( 0, 0, 0 );
if(contourArea(contours[i])> max_area)
{
max_area=contourArea(contours[i]);
best=contours[i];
}
}
Mat approxCurve;
approxPolyDP(Mat(best),approxCurve,0.01*arcLength(Mat(best),true),true);
Wiht this, i have the big contour and it approximation (in approxCurve). Now, I want to obtain the corners of this approximation and get the image inside this contour, but I dont know how can I do it.
I am using this How to remove black part from the image?
But the last part I dont understad very well.
Anyone knows how can I obtain the corners? It is another way more simple that this?
Thanks for your time,
One much simpler way you could do that is to check the image pixels and find the minimum/maximum coordinates of non-black pixels.
Something like this:
int maxx,maxy,minx,miny;
maxx=maxy=-std::numeric_limits<int>::max();
minx=miny=std::numeric_limits<int>::min();
for(int y=0; y<img.rows; ++y)
{
for(int x=0; x<img.cols; ++x)
{
const cv::Vec3b &px = img.at<cv::Vec3b>(y,x);
if(px(0)==0 && px(1)==0 && px(2)==0)
continue;
if(x<minx) minx=x;
if(x>maxx) maxx=x;
if(y<miny) miny=y;
if(y>maxy) maxy=y;
}
}
cv::Mat subimg;
img(cv::Rect(cv::Point(minx,miny),cv::Point(maxx,maxy))).copyTo(subimg);
In my opinion, this approach is more reliable since you don't have to detect any contour, which could lead to false detections depending on the input image.
In a very efficient way, you can sample the original image until you find a pixel on, and from there move along a row and along a column to find the first (0,0,0) pixel. It will work, unless in the good part of the image you can have (0,0,0) pixels. If this is the case (e.g.: dead pixel), you can add a double check checking the neighbourhood of this (0,0,0) pixel (it should contain other (0,0,0) pixels.
I need to find the number of inner holes in the below image.i.e my ultimate requirement is to detect and find the area of round shape black holes alone using contour hierarchy in opencv.No need to use any other algorithms.
Based on this link Using hierarchy in findContours () in OpenCV? i tried but it won't worked.
is there any other method to find the no of holes in the image?
here with i have attached the sample image and code.Can anybody give idea to find the inner black holes alone using hierarchy.I don't have a much experience in contour hierarchy.Thanks in advance.
i used opencv c++ lib.
cv::Mat InputImage = imread("New Image.jpg");
int Err;
if(InputImage.empty() == 1)
{
InputImage.release();
cout<<"Error:Input Image Not Loaded"<<endl;
return 1;
}
cv::Mat greenTargetImage;
std::vector<cv::Mat> Planes;
cv::split(InputImage,Planes);
greenTargetImage = Planes[1];
cv::Mat thresholdImage = cv::Mat (greenTargetImage.size(),greenTargetImage.type());
cv::threshold(greenTargetImage,thresholdImage,128,255,THRESH_OTSU);
imwrite("thresholdImage.jpg",thresholdImage);
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(thresholdImage,contours,hierarchy,cv::RETR_CCOMP,cv::CHAIN_APPROX_SIMPLE,cv::Point(-1,-1));
cout<<contours.size()<<endl;
cout<<hierarchy.size()<<endl;
int count = 0;
if (!contours.empty() && !hierarchy.empty())
{
for (int i = 0;i<contours.size();i++ )
{
if ( hierarchy[i][3] != -1)
{
cv::drawContours(InputImage,contours,i,CV_RGB(0,255,0),3);
count = count+1;
}
}
}
cout<<count<<endl; //No of inner holes in same level
imwrite("ContourImage.jpg",InputImage);
After applying this code i got the output count value is 11.But my requirement is count value should be 10 and also i need to draw only inner black holes alone not all boundaries of outer contours.Sorry for my english.
Try this code works fine for me using hierarchy.
The idea is simple, just consider the contour which doesn’t have child.
That is
hierarchy[i][2]= -1
code:-
Mat tmp,thr;
Mat src=imread("img.jpg",1);
cvtColor(src,tmp,CV_BGR2GRAY);
threshold(tmp,thr,200,255,THRESH_BINARY_INV);
namedWindow("thr",0);
imshow("thr",thr);
vector< vector <Point> > contours; // Vector for storing contour
vector< Vec4i > hierarchy;
Mat dst(src.rows,src.cols,CV_8UC1,Scalar::all(0)); //create destination image
int count=0;
findContours( thr, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE ); // Find the contours in the image
for( int i = 0; i< contours.size(); i=hierarchy[i][0] ) // iterate through each contour.
{
Rect r= boundingRect(contours[i]);
if(hierarchy[i][2]<0){
rectangle(src,Point(r.x,r.y), Point(r.x+r.width,r.y+r.height), Scalar(0,0,255),3,8,0);
count++;
}
}
cout<<"Numeber of contour = "<<count<<endl;
imshow("src",src);
imshow("contour",dst);
waitKey();
Result:-