Effective way to redraw object using contours - opencv

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.

Related

OpenCV iOS Drawing Bounding Box

I'm trying to draw a box around a foot in a photo. I can do it with other objects, including ones that aren't fully in the image (I have pasted my code below). However, when I try to use it with a foot, it draws the contour perfectly but the rectangle is always the around the full image. I have attached pictures below of the result. Is there any way to change my code to fix this, or is there any other method possible of drawing a box around the foot?
Original Image
Contour and Bounding Box
RNG rng(12345);
Mat threshold_output;
vector<vector<cv::Point> > contours;
vector<Vec4i> hierarchy;
Mat srcImage;
UIImageToMat(image, srcImage);
cvtColor(srcImage, srcImage, CV_BGR2GRAY);
blur(srcImage, srcImage, cv::Size(3,3));
threshold(srcImage, threshold_output, 100, 255, THRESH_BINARY );
findContours(threshold_output, 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( threshold_output.size(), CV_8UC3 );
for( int i = 0; i< contours.size(); i++ )
{
if (contourArea(contours[i]) < 1000) {
continue;
}
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
drawContours(drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, cv::Point());
rectangle(drawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0 );
cout << "Height:" << boundRect[i].height << " Width: " << boundRect[i].width << endl;
}
return MatToUIImage(drawing);
Since the bottom part of the foot reaches the image boundary, the contour seems to wrap around the perimeter of the image and thereby create a large bounding box that includes the entire image.
A quick and dirty solution will be to set the border pixels of the image to zero before applying findContours(). For a better solution, you have to think about the actual problem that you are trying to solve.

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 - 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;
}

How to draw a rectangle around the contours?

I am just starting out with opencv and I am trying to make a program that puts squares around a picture of rocks on some sand. The documentation for the function here includes an example of how to use it.
findContours( src, contours, hierarchy,
CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
The prototype of findContours is
void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point()) ;
I have two questions.
1. The third argument in the example hierarchy is a vector<Vec4i> does not match the type findContours expects. Why is that?
2. How does one use the data stored in contours to find where the contours are to create a bounding box?
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours( mask, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_TC89_KCOS);
for ( size_t i=0; i<contours.size(); ++i )
{
cv::drawContours( img, contours, i, Scalar(200,0,0), 1, 8, hierarchy, 0, Point() );
cv::Rect brect = cv::boundingRect(contours[i]);
cv::rectangle(img, brect, Scalar(255,0,0));
}

Resources