Find contours within a specific area - opencv

I have detected all the contours withing a 640*480 image using openCV contour detection method. Among all of these contours, I want to find the contours that reside in a specific area I'll define. Can you give me any suggestions to do it?
Do I have to find the centro-ids of all the contours and check that they are residing in the given area ?

With C++ as reference, let us suppose that you've saved the contours in:
vector < vector < Point > >contours;
then you can enclose the individual contours in Rects as shown:
vector<Rect> boundRect( contours.size() );
vector<Point> center( contours.size() )//center of each Rect
for( int i = 0; i < contours.size(); i++ )
{
boundRect[i] = boundingRect( contours[i] );
center[i]=Point(boundRect[i].x+boundRect[i].width/2,boundRect[i].y+boundRect[i].height/2)
}
You now have the approximate Center of Mass of each contour. You can now check whether your center lies in the required ROI or not.

Related

OpenCV - How to determine whether a given point is within a contour?

Given a random contour, how can I say if a given input point lies within the contour or not? I am sorry if it has a simple solution, but I am unable to figure it out.
One idea I had was to use equation of lines, connect points and see if it is greater or smaller, etc. But that doesn't get me anywhere, as it depends on the position of line.
You can find a full solution to this problem using OpenCV here
/// Get the contours
vector<vector<Point> > contours; vector<Vec4i> hierarchy;
Mat src_copy = src.clone();
findContours( src_copy, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
/// Calculate the distances to the contour
Mat raw_dist( src.size(), CV_32FC1 );
for( int j = 0; j < src.rows; j++ )
{ for( int i = 0; i < src.cols; i++ )
{ raw_dist.at<float>(j,i) = pointPolygonTest( contours[0], Point2f(i,j), true ); }
}

opencv find concave hull

I have a set of discrete points shown in an image, like the following
I want to reconstruct or up sampling (I'm not sure what's the correct way to describe it) the image, so that the result image would be like the following. It doesn't need to be exactly the same as the example image, but the main idea is to fill up the original one.
I have an initial idea about how to do it. But I don't know how to do it after the first step. My idea is to first separate image using kmeans and find out the different objects. And I have successfully done it. The resulting images after kmeans are: .
After kmeans, I want to use find contour or something like concave to get the outline of these shapes and fill the shape using functions like fill holes. However, I found "find contour" does not work, it will consider each single pixel as a contour.
The other way I'm thinking is to use interpolation. But I'm not sure whether it is possible with so sparse points. Does anyone have any ideas about how to do this? I'm not sure whether I'm on the right way and I'm open to any solutions.
Thanks a lot!
Take a look at the morphological transformations. I would start with a dilation operation using a large kernel, say the MORPH_ELLIPSE with a size(15,15). Afterwards, thin the blobs back down using the erosion operation with the same size kernel. Take a look at the docs here. Note that OpenCV offers chained, or sequenced, morphological operations, too. See here. You'll then see that my suggestion is a "closing" operation.
Update:
I experimented with simple dilation and contouring to yield the results shown in the image. The results appear to satisfy the general requirements of the problem.
Likewise, what "realtime" means for the application isn't specified, but this set of operations may be quickly executed and could easily be applied to a 30fps application.
Code snippet below:
// Convert image to grayscale
cvtColor(src, gray, CV_BGR2GRAY);
threshold(gray, gray, 128.0, 128.0, THRESH_BINARY);
// Dilate to fill holes
dilate(gray, dest, getStructuringElement(MORPH_ELLIPSE, Size(13,13)));
// Find contours
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(dest, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0,0));
// Prune contours
float maxArea = 0.0f;
for (size_t i = 0; i< contours.size(); i++)
{
if (contourArea(contours[i]) >= maxArea)
{
maxArea = contourArea(contours[i]);
}
}
float minArea = 0.20f * maxArea;
vector<vector<Point> > prunedContours;
for (size_t i = 0; i< contours.size(); i++)
{
if (contourArea(contours[i]) >= minArea)
{
prunedContours.push_back(contours[i]);
}
}
// Smooth the contours
vector<vector<Point> > smoothedContours;
smoothedContours.resize(prunedContours.size());
for (size_t i=0;i<prunedContours.size();i++)
{
vector<float> x;
vector<float> y;
const size_t n = prunedContours[i].size();
for (size_t j=0;j<n;j++)
{
x.push_back(prunedContours[i][j].x);
y.push_back(prunedContours[i][j].y);
}
Mat G;
transpose(getGaussianKernel(11,4.0,CV_32FC1),G);
vector<float> xSmooth;
vector<float> ySmooth;
filter2D(x,xSmooth, CV_32FC1, G);
filter2D(y,ySmooth, CV_32FC1, G);
for (size_t j=0;j<n;j++)
{
smoothedContours[i].push_back(Point2f(xSmooth[j],ySmooth[j]));
}
}

OpenCV Multiple marker detection?

I've been working on detecting fiducial markers in scenes. An example of my fiducial marker is here:
http://tinypic.com/view.php?pic=4r6k3q&s=8#.VNgsgzVVK1F
I have been able to detect a single fiducial marker in a scene very well. What is the methodology for detecting multiple fiducial markers in a scene? Doing feature detection, extraction, and then matching is great for finding a single match, but it seems to be the wrong method for detecting multiple matches since it would be difficult to determine which features belong to which marker?
The fiducial markers would be the same, and would not be in a known location in the scene.
Update:
Below is some sample code. I was trying to match the first fiducial marker with x number of keypoints, and then use the remaining keypoints to match the second marker. However, this is not robust at all. Does anybody have any suggestions?
OrbFeatureDetector detector;
vector<KeyPoint> keypoints1, keypoints2;
detector.detect(im1, keypoints1);
detector.detect(im2, keypoints2);
Mat display_im1, display_im2;
drawKeypoints(im1, keypoints1, display_im1, Scalar(0,0,255));
drawKeypoints(im2, keypoints2, display_im2, Scalar(0,0,255));
SiftDescriptorExtractor extractor;
Mat descriptors1, descriptors2;
extractor.compute( im1, keypoints1, descriptors1 );
extractor.compute( im2, keypoints2, descriptors2 );
BFMatcher matcher;
vector< DMatch > matches1, matches2;
matcher.match( descriptors1, descriptors2, matches1 );
sort (matches1.begin(), matches1.end());
matches2 = matches;
int numElementsToSave = 50;
matches1.erase(matches1.begin()+numElementsToSave,matches1.end());
matches2.erase(matches2.begin(),matches2.begin()+numElementsToSave);
Mat match_im1, match_im2;
drawMatches( im1, keypoints1, im2, keypoints2,
matches1, match_im1, Scalar::all(-1), Scalar::all(-1),
vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );
drawMatches( im1, keypoints1, im2, keypoints2,
matches2, match_im2, Scalar::all(-1), Scalar::all(-1),
vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );
I have never tried it before, but here you have a good explanation about the detection of multiple occurrences:
This tutorial shows how enabling multiple detection of the same
object. To enable multiple detection, the parameter
General->multiDetection should be checked. The approach is as
following:
As usual, we match all features between the objects and the scene.
For an object which is in the scene two times (or more), it should have twice of matched features. We apply a RANSAC algorithm to find a
homography. The inliers should belong to only one occurrence of the
object, all others considered as outliers. We redo the homography
process on the outliers, then find another homography… we do this
process until no homography can be computed.
It may happens that a homography can be found superposed on a previous one using the outliers. You could set
Homography->ransacReprojThr (in pixels) higher to accept more
inliers in the homographies computed, which would decrease the chance
of superposed detections. Another way is to ignore superposed
homographies on a specified radius with the parameter General->multiDetectionRadius (in pixels).
For more information see the page below:
https://code.google.com/p/find-object/wiki/MultiDetection
I developed a semi-automatic algorithm to detect multiple markers (interest points) from image using findContours method on a binary image (my markers are white on a green surface then I limit my search to area constraint as I know how big is each marker in each frame. of course this got some false positives but it was good enough. I couldn't see the picture in your post as tinypic is blocked here for some reason. But you can use the matchShape opencv function to eliminate the bad contours.
here is the part of code I made for this.
Mat tempFrame;
cvtColor(BallFrame, tempFrame, COLOR_BGR2GRAY);
GaussianBlur(tempFrame, tempFrame, Size(15, 15), 2, 2); // remove noise
Mat imBw;
threshold(tempFrame, imBw, 220, 255, THRESH_BINARY); // High threshold to get better results
std::vector<std::vector<Point> > contours;
std::vector<Vec4i> hierarchy;
findContours(imBw, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
Point2f center;
float radius = 0.0;
for (int i = 0; i < contours.size(); i++)
{
double area = contourArea(contours[i]);
if (area > 1 && area < 4000) {
minEnclosingCircle(contours[i], center, radius);
if (radius < 50) // eliminate wide narrow contours
{
// You can use `matchShape` here to match your marker shape
}
}
}
I hope this will help

OpenCV - find bounding box of largest blob in binary image

What is the most efficient way to find the bounding box of the largest blob in a binary image using OpenCV? Unfortunately, OpenCV does not have specific functionality for blob detection. Should I just use findContours() and search for the largest in the list?
Here. It. Is.
(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;
}
If you want to use OpenCV libs, check out OpenCVs SimpleBlobDetector. Here's another stack overflow showing a small tutorial of it: How to use OpenCV SimpleBlobDetector
This only gives you key points though. You could use this as an initial search to find the blob you want, and then possibly use the findContours algorithm around the most likely blobs.
Also the more information you know about your blob, you can provide parameters to filter out the blobs you don't want. You might want to test out the area parameters of the SimpleBlobDetector. Possibly could could compute the area based on the size of the area of the image and then iteratively allow for a smaller blob if the algorithm does not detect any blobs.
Here is the link to the main OpenCV documentation: http://docs.opencv.org/modules/features2d/doc/common_interfaces_of_feature_detectors.html#simpleblobdetector
To find the bounding box of the largest blob, I used findContours, followed by the following code:
double maxArea = 0;
for (MatOfPoint contour : contours) {
double area = Imgproc.contourArea(contour);
if (area > maxArea) {
maxArea = area;
largestContour = contour;
}
}
Rect boundingRect = Imgproc.boundingRect(largestContour);
Since no one has posted a complete OpenCV solution, here's a simple approach using thresholding + contour area filtering
Input image
Largest blob/contour highlighted in green
import cv2
# Load image, grayscale, Gaussian blur, and Otsu's threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Find contours and sort using contour area
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
for c in cnts:
# Highlight largest contour
cv2.drawContours(image, [c], -1, (36,255,12), 3)
break
cv2.imshow('thresh', thresh)
cv2.imshow('image', image)
cv2.waitKey()
TimZaman, your code has a bug but I can't comment so I start a new and correct answer.
Here is my solution based on 1"'s and TimZaman's ideas:
Mat measure::findBiggestBlob(cv::Mat &src){
int largest_area=0;
int largest_contour_index=0;
Mat temp(src.rows,src.cols,CV_8UC1);
Mat dst(src.rows,src.cols,CV_8UC1,Scalar::all(0));
src.copyTo(temp);
vector<vector<Point>> contours; // storing contour
vector<Vec4i> hierarchy;
findContours( temp, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
for( int i = 0; i< contours.size(); i++ ) // iterate
{
double a=contourArea( contours[i],false); //Find the largest area of contour
if(a>largest_area)
{
largest_area=a;
largest_contour_index=i;
}
}
drawContours( dst, contours,largest_contour_index, Scalar(255), CV_FILLED, 8, hierarchy );
// Draw the largest contour
return dst;
}

Effective way to redraw object using contours

Problems
Having an image with many blobs. I required to remove blobs that do not meet the requirement. However, the blobs that meet the requirement do have a holes inside. I need to redraw back the successful blobs. Here are some of the code that I used. Hopefully someone can point out how to deal with it.
/// Find contours
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
cv::findContours( srcImg, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE, Point(0,0) );
more info (http://docs.opencv.org/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?highlight=moments#findcontours)
/// Start to iterate to each contour found
vector<vector<Point> >::iterator itc = contours.begin();
vector<Rect> rects;
Mat dstImg = Mat::zeros( srcImg.size(), CV_8UC1 );
//Remove patch that are no inside limits.
while( itc != contours.end() ) {
/// eliminating blobs here
}
/// To redraw the contours. Error here since some blobs already been removed
int idx = 0;
for( ; idx >= 0; idx = hierarchy[idx][0] )
{
Scalar color( 255, 255, 255 );
drawContours( dstImg, contours, idx, color, CV_FILLED, 8, hierarchy );
}
/// To redraw the contours but the holes also filled
for(unsigned int i = 0; i < rects.size(); i++) {
Scalar color = Scalar(255,255,255);
drawContours( dstImg, contours, i, color, CV_FILLED, 8, noArray(), 0, Point() );
}
Do I have to use findContours again?
I think maybe there are two problems here. First, you want to remove the contours found within contours? To do this, use CV_RETR_EXTERNAL instead of CV_RETR_CCOMP. Second, you want to only draw the contours that weren't removed? This depends more on how you removed the other contours. A simple and quick way to get around this would be to create a new vector > and push_back contours that don't get thrown away in your while loop.

Resources