Make mask from edge detection on image? - python-2.7

I am trying to pull all the pixels from the image on the right that are part of the shoe. I decided to take the edge detection of the image, but now I need to make a mask so that I can grab all the pixels bounded by the outer outline of the shoe. Is there a way in opencv to do this? I looked at the findContours function, but that only gave me a bunch of contours with no way to then make a mask?

If floodfill does not provide you with a sufficient mask, another way could be to take the edge image from figure 1 and apply a dilation operator and then a closing operator. The mask will be slightly larger than the original due to the dilation although the dilation helps in closing black spots when applying the closing operator.
This is the result I obtained (I do not have a high enough rep to post the image in the answer. Here is the link):
http://tinypic.com/view.php?pic=33jmpao&s=8#.U_cHm_mSz9s
The link below may also be useful to you.
http://docs.opencv.org/trunk/doc/py_tutorials/py_imgproc/py_morphological_ops/py_morphological_ops.html
The code I used:
// Dilation
Mat se = getStructuringElement(CV_SHAPE_ELLIPSE, Size(9, 9));
dilate(edge_image, dst, se, Point(-1,-1), 1);
// Closing
Mat closed;
Mat element = getStructuringElement(MORPH_ELLIPSE, Size(19, 19));
morphologyEx(dst, closed, MORPH_CLOSE, element, Point(-1,-1), 3);
This is my first answer on stackoverflow. I hope it helps and good luck! :)

Related

OpenCV : Reversing the negative areas of an image

i'm using OpenCV 3.4.6 in a c++/Objective C project and given an image with negative rectangular areas, like this one:
I should detect those negative areas, reverse them and finally get the original image.
I tried to use findContours, enhancing the contrast of the original image or adding a threshold but the rectangles are not detected.
Here one of the test i've tried:
Mat contrasted = [self enhanceContrastTo: matOriginal];
Mat thresholded;
threshold(contrasted, thresholded, 125, 241, THRESH_BINARY);
std::vector<std::vector<cv::Point> > contours;
std::vector<Vec4i> hierarchy;
findContours( thresholded, contours, hierarchy, CV_RETR_EXTERNAL, CV_RETR_TREE );
/* contrast method */
+(Mat)enhanceContrastTo:(Mat)image {
cv::Mat lab_image;
cv::cvtColor(image, lab_image, CV_BGR2Lab);
// Extract the L channel
std::vector<cv::Mat> lab_planes(3);
cv::split(lab_image, lab_planes); // now we have the L image in lab_planes[0]
// apply the CLAHE algorithm to the L channel
cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE();
// clahe->setClipLimit(4);
clahe->setClipLimit(3);
cv::Mat dst;
clahe->apply(lab_planes[0], dst);
// Merge the the color planes back into an Lab image
dst.copyTo(lab_planes[0]);
cv::merge(lab_planes, lab_image);
// convert back to RGB
cv::Mat image_clahe;
cv::cvtColor(lab_image, image_clahe, CV_Lab2BGR);
return image_clahe;
}
The rectangles are clearly visible to the naked eye, I hope that opencv can also identify them but I don't know how.
Any idea?
Thanks
This particular question isn't too complicated but even minor variants can get complex. I can advise you on a couple of simple ideas that should suffice to solve the problem.
1) Instead of contour you can check whether neighboring points are close to reverse - this should filter out most irrelevant edges. But just checking for near-reverse is not sufficient as monotone grey area (127) fits the criteria too. Require also minimal threshold difference.
2) Since rectangles are parallel to axes - you can simply go along each row and column and count the number of pixels that are potentially edges of the reversed rectangles. It is better not to just count the number - but to check whether you have continuous large sequences of such pixels and record where exactly these segments are.
3) Use the found segments (or just indexes of rows and columns) of reversed edge-pixels to make candidates for reversed rectangles and then make final verifications.
This is but an algo draft - it will surely require refining. I am not sure why you wanted to use the contour function, tho.

segmentation of the source image in opencv based on canny edge outline attained from processing the said source image

I have a source image. I need a particular portion to be segmented from it and save it as another image. I have the canny outline of the portion I need to be segmented out,but how do I use it to cut the portion from the source image? I have attached both the source image and the canny edge outline. Please help me and suggest me a solution.
EDIT-1: Alexander Kondratskiy,Is this what you meant by filling the boundary?
EDIT-2 : according to Kannat, I have done this
Now how do I separate the regions that are outside and inside of the contour into two separate images?
Edit 3- I thought of 'And-ing'the mask and the contour lined source image.Since I am using C, I am having a little difficulty.
this is the code I use to and:-
hsv_gray = cvCreateImage( cvSize(seg->width, seg->height), IPL_DEPTH_8U, 1 );
cvCvtColor( seg, hsv_gray, CV_BGR2GRAY );
hsv_mask=cvCloneImage(hsv_gray);
IplImage* contourImg =cvCreateImage( cvSize(hsv_mask->width, hsv_mask->height), IPL_DEPTH_8U, 3 );
IplImage* newImg=cvCreateImage( cvSize(hsv_mask->width, hsv_mask->height), IPL_DEPTH_8U, 3 );
cvAnd(contourImg, hsv_mask,newImg,NULL);
I always get an error of mismatch size or Type. I adjusted the size but I can't seem to adjust the type,since one(hsv_mask) is 1 channel and the others are 3 channels.
#kanat- I also tried your boundingrect but could not seem to get in right in C format.
Use cv::findContours on your second image to find the contour of the segment. Then use cv::boundingRect to find bounding box for this segment. After that you can create matrix and save in it cropped bounding box from your second image (as I see it is a binary image). To crop needed region use this:
cv::getRectSubPix(your_image, BB_size, cv::Point(BB.x + BB.width/2,
BB.y + BB.height/2), new_image).
Then you can save new_image using cv::imwrite. That's it.
EDIT:
If you found only one contour then use this (else you will iterate through elements of found contours). The following code shows the steps but sorry I can't test it now:
std::vector<std::vector<cv::Point>> contours;
// cv::findContours(..., contours, ...);
cv::Rect BB = cv::boundingRect(cv::Mat(contours[0]));
cv::Mat new_image;
cv::getRectSubPix(your_image, BB.size(), cv::Point(BB.x + BB.width/2,
BB.y + BB.height/2), new_image);
cv::imwrite("new_image_name.jpg", new_image);
You could fill the boundary created by the canny-edge detector, and use that as an alpha mask on the original image.

Segment depth image with low contrast

I am trying to segment the hand from this depth image:
I tried watershed, region growing, grabcut, but all of them failed mainly because there is not a clear edge. I tried to sharp the image as well, but it didn't give me good results either.
This might not be the answer you were hoping for, but it might help you step forward a bit. Since I only provide algorithmic hints I will use Matlab rather than opencv.
Since this is not an ordinary intensity image, but rather depth image, you should use the implied geometry of the scene. The key assumption that can help you here is that the hand is resting on a surface. If you can estimate the surface equation, you can detect the hand much easier.
[y x] = ndgrid( linspace(-1,1,size(img,1)), linspace(-1,1,size(img,2)) );
X = [reshape(x(101:140,141:180),[],1), reshape(y(101:140,141:180),[],1), ones(1600,1)];
srf=(X\reshape(img(101:140,141:180),[],1)); %// solving least-squares for the 40x40 central patch
aimg = img - x*srf(1) - y*srf(2) - srf(3); %// subtracting the recovered surface
Using median filter to "clean" it a bit, and applying a simple thereshold
medfilt2(aimg,[3 3]) < -1.5
Yields
Not exactly what you were hoping for, but I think it's a step forward ;)
PS,
You might find the work of Alpert, Galun, Nadler and Basri Detecting faint curved edges in noisy images (ECCV2010) relevant to your problem.
Please check my code implementation.
void applyClahe(const cv::Mat& src, cv::Mat& dst)
{
cv::Mat lab;
cv::cvtColor(src, lab, cv::COLOR_BGR2Lab);
vector<cv::Mat> labChannels;
cv::split(lab, labChannels);
auto clahe = cv::createCLAHE();
cv::Mat cl; clahe->apply(labChannels[0], cl);
cl.copyTo(labChannels[0]);
cv::merge(labChannels, dst);
cv::cvtColor(dst, dst, cv::COLOR_Lab2BGR);
}
This function will give you the following result and good starting point.

Extract one object from bunch of objects and detect edges

For my college project I need to identify a species of a plant from plant leaf shape by detecting edges of a leaf. (I use OpenCV 2.4.9 and C++), but the source image has taken in the real environment of the plant and has more than one leaf. See the below example image. So here I need to extract the edge pattern of just one leaf to process further.
Using Canny Edge Detector I can identify edges of the whole image.
But I don't know how to proceed from here to extract edge pattern of just one leaf, may be more clear and complete leaf. I don't know even if this is possible also. Can anyone please tell me if this is possible how to extract edges of one leaf I just want to know the image peocessing steps that I need to apply to the image. I don't want any code samples. I'm new to image processing and OpenCV and learning by doing experiments.
Thanks in advance.
Edit
As Luis said said I have done Morphological close to the image after doing edge detection using Canny edge detection, but it seems still it is difficult me to find the largest contour from the image.
Here are the steps I have taken to process the image
Apply Bilateral Filter to reduce noise
bilateralFilter(img_src, img_blur, 31, 31 * 2, 31 / 2);
Adjust contrast by histogram equaliztion
cvtColor(img_blur,img_equalized,CV_BGR2GRAY);
Apply Canny edge detector
Canny(img_equalized, img_edge_detected, 20, 60, 3);
Threshold binary image to remove some background data
threshold(img_edge_detected, img_threshold, 1, 255,THRESH_BINARY_INV);
Morphological close of the image
morphologyEx(img_threshold, img_closed, MORPH_CLOSE, getStructuringElement(MORPH_ELLIPSE, Size(2, 2)));
Following are the resulting images I'm getting.
This result I'm getting for the above original image
Source image and result for second image
Source :
Result :
Is there any way to detect the largest contour and extract it from the image ?
Note that my final target is to create a plant identification system using real environmental image, but here I cannot use template matching or masking kind of things because the user has to take an image and upload it so the system doesn't have any prior idea about the leaf.
Here is the full code
#include <opencv\cv.h>
#include <opencv\highgui.h>
using namespace cv;
int main()
{
Mat img_src, img_blur,img_gray,img_equalized,img_edge_detected,img_threshold,img_closed;
//Load original image
img_src = imread("E:\\IMAG0196.jpg");
//Apply Bilateral Filter to reduce noise
bilateralFilter(img_src, img_blur, 31, 31 * 2, 31 / 2);
//Adjust contrast by histogram equaliztion
cvtColor(img_blur,img_equalized,CV_BGR2GRAY);
//Apply Canny edge detector
Canny(img_equalized, img_edge_detected, 20, 60, 3);
//Threshold binary image to remove some background data
threshold(img_edge_detected, img_threshold, 15, 255,THRESH_BINARY_INV);
//Morphological close of the image
morphologyEx(img_threshold, img_closed, MORPH_CLOSE, getStructuringElement(MORPH_ELLIPSE, Size(2, 2)));
imshow("Result", img_closed);
waitKey(0);
return 0;
}
Thank you.
Well there is a similar question that was asked here:
opencv matching edge images
It seems that edge information is not a good descriptor for the image, still if you want to try it I'll do the following steps:
Load image and convert it to grayscale
Detect edges - Canny, Sobel try them and find what it suits you best
Set threshold to a given value that eliminates most background - Binarize image
Close the image - Morphological close dont close the window!
Count and identify objects in the image (Blobs, Watershed)
Check each object for a shape (assuming you have described shapes of the leaf you could find before or a standard shape like an ellipse) features like:
http://docs.opencv.org/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html
http://www.math.uci.edu/icamp/summer/research_11/park/shape_descriptors_survey.pdf
If a given object has a given shape that you described as a leaf then you detected the leaf!.
I believe that given images are taken in the real world these algorithm will perform poorly but it's a start. Well hope it helps :).
-- POST EDIT 06/07
Well since you have no prior information about the leaf, I think the best we could do is the following:
Load image
Bilateral filter
Canny
Extract contours
Assume: that the contour with the largest perimeter is the leaf
Convex hull the 3 or 2 largest contours (the blue line is the convex hull done)
Use this convex hull to do a graph cut on the image and segmentate it
If you do those steps, you'll end up with images like these:
I won't post the code here, but you can check it out in my messy github. I hope you don't mind it was made in python.
Leaf - Github
Still, I have a couple of things to finish that could improve the result.. Roadmap would be:
Define the mask in the graphcut (like its described in the doc)
Apply region grow may give a better convex hull
Remove all edges that touch the border of the image can help to identify larger edges
Well, again, I hope it helps

Contours opencv : How to eliminate small contours in a binary image

I am currently working on image processing project. I am using Opencv2.3.1 with VC++.
I have written the code such that, the input image is filtered to only blue color and converted to a binary image. The binary image has some small objects which I don't want. I wanted to eliminate those small objects, so i used openCV's cvFindContours() method to detect contours in Binary image. but the problem is I cant eliminate the small objects in the image output. I used cvContourArea() function , but didn't work properly.. , erode function also didn't work properly.
So please someone help me with this problem..
The binary image which I obtained :
The result/output image which I want to obtain :
Ok, I believe your problem could be solved with the bounding box demo recently introduced by OpenCV.
As you have probably noticed, the object you are interested at should be inside the largest rectangle draw in the picture. Luckily, this code is not very complex and I'm sure you can figure it all out by investigating and experimenting with it.
Here is my solution to eliminate small contours.
The basic idea is check the length/area for each contour, then delete the smaller one from vector container.
normally you will get contours like this
Mat canny_output; //example from OpenCV Tutorial
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
Canny(src_img, canny_output, thresh, thresh*2, 3);//with or without, explained later.
findContours(canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0,0));
With Canny() pre-processing, you will get contour segments, however each segment is stored with boundary pixels as a closed ring. In this case, you can check the length and delete the small one like
for (vector<vector<Point> >::iterator it = contours.begin(); it!=contours.end(); )
{
if (it->size()<contour_length_threshold)
it=contours.erase(it);
else
++it;
}
Without Canny() preprocessing, you will get contours of objects.
Similarity, you can also use area to define a threshold to eliminate small objects, as OpenCV tutorial shown
vector<Point> contour = contours[i];
double area0 = contourArea(contour);
this contourArea() is the number of non-zero pixels
Are you sure filtering by small contour area didn't work? It's always worked for me. Can we see your code?
Also, as sue-ling mentioned, it's a good idea to use both erode and dilate to approximately preserve area. To remove small noisy bits, use erode first, and to fill in holes, use dilate first.
And another aside, you may want to check out the new C++ versions of the cv* functions if you weren't aware of them already (documentation for findContours). They're much easier to use, in my opinion.
Judging by the before and after images, you need to determine the area of all the white areas or blobs, then apply a threshold area value. This would eliminate all areas less than the value and leave only the large white region which is seen in the 2nd image. After using the cvFindContours function, try using 0 order moments. This would return the area of the blobs in the image. This link might be helpful in implementing what I've just described.
http://www.aishack.in/2010/07/tracking-colored-objects-in-opencv/
I believe you can use morphological operators like erode and dilate (read more here)
You need to perform erosion with a kernel size near to the radius of the circle on the right (the one you want to eliminate).
followed by dilation using the same kernel to fill the gaps created by the erosion step.
FYI erosion followed by dilation using the same kernel is called opening.
the code will be something like this
int erosion_size = 30; // adjust with you application
Mat erode_element = getStructuringElement( MORPH_ELLIPSE,
Size( 2*erosion_size + 1, 2*erosion_size+1 ),
Point( erosion_size, erosion_size ) );
erode( binary_img, binary_img, erode_element );
dilate( binary_img, binary_img, erode_element );
It is not a fast way but may be usefull in some cases.
There is a new function in OpencCV 3.0 - connectedComponentsWithStats. With it we can get area of connected components and eliminate unnecessary. So we can easy remove circle with holes, with the same bounding box as solid circle.