Can findContour in OpenCV work like bwlabel in Matlab? - c++

Some people in this Q & A site suggested I use findContour to imitate what bwlabel in Matlab. But I am not sure because I think a contour is closed shape of detected edges and element from bwlabel is a connected shape. I guess they might be logically the same. What about them in practice? Are they really same?

Use either of these two library....cvBlobslib or cvblob...you will get many features about the connected components such as size and contour and ellipticity and bounding box...you can filter blobs and add togethar 2 or more blobs...try it..under the hood algo of bwlabel is a two scan connected component where as cvblob or cvBlobslib is a one scan algo...

bwlabel will give you the image connected components, i.e. different label for different connected objects in a background.
Probably what you mean is the combination of im2bw and imcontours provides, i.e. a combination of binarizing the image and trivially finding the single contour (boundaries) per retained object on the output.
Consider the following example:
I = imread('coins.png'); % grayscale
level = graythresh(I); % find thershold
BW = im2bw(I, level); % threshold image
imcontour(BW, 1); % plot single contour
For a grayscale image you can increase the number of requested contours, though findContours operates on binary images.

I found an exact article about this. Quick answer is "Yeah, their eventual output will be the same." So I might go with findContour after all considering cvBlob still using old C-style API and having its own implementation of finding contours.

Related

Depth/Disparity Map from a moving camera in OpenCV

Is that possible to get the depth/disparity map from a moving camera? Let say I capture an image at x location, after I travelled let say 5cm and I capture another picture, and from there I calculate the depth map of the image.
I have tried using BlockMatching in opencv but the result is not good.The first and second image are as following:
first image,second image,
disparity map (colour),disparity map
My code is as following:
GpuMat leftGPU;
GpuMat rightGPU;
leftGPU.upload(left);rightGPU.upload(right);
GpuMat disparityGPU;
GpuMat disparityGPU2;
Mat disparity;Mat disparity1,disparity2;
Ptr<cuda::StereoBM> stereo = createStereoBM(256,3);
stereo->setMinDisparity(-39);
stereo->setPreFilterCap(61);
stereo->setPreFilterSize(3);
stereo->setSpeckleRange(1);
stereo->setUniquenessRatio(0);
stereo->compute(leftGPU,rightGPU,disparityGPU);
drawColorDisp(disparityGPU, disparityGPU2,256);
disparityGPU.download(disparity);
disparityGPU2.download(disparity2);
imshow("display img",disparityGPU);
how can I improve upon this? From the colour disparity map, there are quite a lot error (ie. the tall circle is red in colour and it is the same as some of the part of the table.). Also,from the disparity map, there are small noise (all the black dots in the picture), how can I pad those black dots with nearby disparities?
It is possible if the object is static.
To properly do stereo matching, you first need to rectify your images! If you don't have calibrated cameras, you can do this from detected feature points. Also note that for cuda::StereoBM the minimum default disparity is 0. (I have never used cuda, but I don't think your setMinDisparity is doing anything, see this anser.)
Now, in your example images corresponding points are only about 1 row apart, therefore your disparity map actually doesn't look too bad. Maybe having a larger blockSize would already do in this special case.
Finally, your objects have very low texture, therefore the block matching algorithm can't detect much.

Search contours on the image

I'm trying solve the recognition problem with a help OpenCV library for C++.
I have a some text(below) and i want to separate each symbol in this text using by cvFindContours(...) function. After, I want to send each separated symbol on the input of neural network for recognition it. It's all ok. I will can get all contours in my image and i can drawn it on my image with a help cvDrawContours(...) function(below). But cvFindContours(...) returns unordered sequence(pointer on the first contour in this sequence) where contains all the found contours. For my task order is very important.
CVAPI(int) cvFindContours( CvArr* image, CvMemStorage* storage, CvSeq** first_contour,
int header_size CV_DEFAULT(sizeof(CvContour)),
int mode CV_DEFAULT(CV_RETR_LIST),
int method CV_DEFAULT(CV_CHAIN_APPROX_SIMPLE),
CvPoint offset CV_DEFAULT(cvPoint(0,0)));
-image- source image
-storage- for storing where contains contours
-first_contour- pointer to the first contour in the storage
-mode- mode of search (I use the CV_RETR_EXTERNAL for search external contours)
-method- method of approximation (I'm using the CV_CHAIN_APPROX_SIMPLE by default)
How can I make the cvFindContours(...) function that returns the contours in the order in which they in the picture? Is it possible?
Thanks!
You can't directly force findContours to yield contours in a certain order (I mean there is no parameter to tune this in the function call).
To sort your contours in a "read text" order, you could do a loop which goes through all your contours and retrieves for each contour the top-leftest point, either by directly going through all points in each contour object, or by using a boundingbox (see minAreaRect for example).
Once you have all these points, sort them from left to right and bottom to top (some adjustments will probably have to be made, like detecting all contours starting within a range of heights to be all part of the same text line)
You have found bounding rectangles for all the contours present in your image. Instead of going about with the left-most point approach, you can sort your contours based on the centroid of each contour, which is more robust since your approach is being for text.
THIS ANSWER from the OpenCV community might help provide a start

Hough Circle on binary images

I'm trying to create a generic function which always finds my 3 color balls. (Red, yellow and white). I spend a lot of time to search a solution, and it's pretty hard...
For the moment, first, I use the Canny filter (I use the Otsu method to determine the lower and highter parameter) and I call the Hough Circle method by incrementing param2 until I find 3 circles.
while (!findCircles){
Imgproc.HoughCircles(hough, circles, Imgproc.CV_HOUGH_GRADIENT, 1, 100, 200, low, 20, 100); //find3Circles = true;
if (circles.cols() == 3){
findCircles = true;
}
low++;
}
It doesn't work very well...
If someone vote up for my question, i could post images (i have no enough points...) Please, if someone found the solution, it would be nice to tell me.
I think that you should base you method on finding colors, not shapes or at least you should stary with finding colors and then find shapes. Here there is good(it uses old OpenCV API, but everything else is fine) article describing how to perform color based object tracking in OpenCV. The general idea is simple - convert image to HSV color space, use inRange function to find pixels which might be your objects and then track them (most likely you will have to filter the pixels - find biggest contour or contour which shape is close to circle). Note that you will need to call inRange function 3 times (one for each ball).

OpenCV HSV weird converted

I am working on project what detect hematoma from skin. I am having issue with color after convertion from RGB to HSV. My algorithm detect hematoma by its color.
With some images I have good results like here:
Original img: http://imgur.com/WHiOWdj
Result img: http://imgur.com/PujbnHa
But with some images i have bad result like this:
Original img: http://imgur.com/OshB99r
Result img: http://imgur.com/CuNzAId
The same original image after convertion to HSV: http://imgur.com/lkVwtCs
Do you have any ideas how to fix it?
Thanks
Looking at your result image I think that you are only using the H channel of the original image in your algorithm. The false positive detection can inherit from that the some part of the healty skin has quite the same H value than the hematoma has. You can see on the qrey-scale image of H channel that both parts have similar values:
The difference between the two parts is the saturation value. On the following image you can see the S channel of the original image and it shows perfectly that at the hematoma the saturation is much higher than at other the part of the arm:
This was expected because the hematoma has much stronger color than the healty skin has.
So, I suggest you to use both H and S channel in your algorithm that is you have to take into account only that parts of H image where the S image contains high saturation values. A possible and simple solution to do that is that you binarize both H and S images and with an AND operation you can execute this filtering:
H image after binarisation:
S image after binarisation:
Image after H&S operation:
You can see that on the result image only the hematoma part is white (except some noise but you can eliminate easily, for example by size or by morphological filtering).
EDIT
Important to note that binarization is one of most important (and sometimes also very complicated) step in the object detection algorithms namely binarization is the first highlight of the objects to detect.
If the the external conditions (lighting, color of objects etc.) do not change significantly from image to image you can use fix binaraziation thresholds. If this constant environment can not be issured you have to use more complicated methods. There are a lot of possibilies you can use, here you can read some examples:
Wikipedia - Thresholding
Wikipedia - Balanced histogram thresholding
Several solutions are based on the histogram analysis: on the histograms with objects there are always more local maximums which positions can vary depend on the environment and if you find them you can adapt the binarization threshold easily.
For example the histogram of the H channel of the original image is the following:
The first maximum belongs to the background, the second to the skin and the last to the hematome. It can be supposed that these 3 thresholds can be found in each image only their positions vary depend on the lighting or on other conditions. To put a threshold between the 2nd and the 3rd local maximum it can be a good choice to highlight the hematome.
Finally I offer you the read the following articel about thresholding in OpenCV:
OpenCV - Thresholding

OpenCV, how to use arrays of points for smoothing and sampling contours?

I have a problem to get my head around smoothing and sampling contours in OpenCV (C++ API).
Lets say I have got sequence of points retrieved from cv::findContours (for instance applied on this this image:
Ultimately, I want
To smooth a sequence of points using different kernels.
To resize the sequence using different types of interpolations.
After smoothing, I hope to have a result like :
I also considered drawing my contour in a cv::Mat, filtering the Mat (using blur or morphological operations) and re-finding the contours, but is slow and suboptimal. So, ideally, I could do the job using exclusively the point sequence.
I read a few posts on it and naively thought that I could simply convert a std::vector(of cv::Point) to a cv::Mat and then OpenCV functions like blur/resize would do the job for me... but they did not.
Here is what I tried:
int main( int argc, char** argv ){
cv::Mat conv,ori;
ori=cv::imread(argv[1]);
ori.copyTo(conv);
cv::cvtColor(ori,ori,CV_BGR2GRAY);
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i > hierarchy;
cv::findContours(ori, contours,hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
for(int k=0;k<100;k += 2){
cv::Mat smoothCont;
smoothCont = cv::Mat(contours[0]);
std::cout<<smoothCont.rows<<"\t"<<smoothCont.cols<<std::endl;
/* Try smoothing: no modification of the array*/
// cv::GaussianBlur(smoothCont, smoothCont, cv::Size(k+1,1),k);
/* Try sampling: "Assertion failed (func != 0) in resize"*/
// cv::resize(smoothCont,smoothCont,cv::Size(0,0),1,1);
std::vector<std::vector<cv::Point> > v(1);
smoothCont.copyTo(v[0]);
cv::drawContours(conv,v,0,cv::Scalar(255,0,0),2,CV_AA);
std::cout<<k<<std::endl;
cv::imshow("conv", conv);
cv::waitKey();
}
return 1;
}
Could anyone explain how to do this ?
In addition, since I am likely to work with much smaller contours, I was wondering how this approach would deal with border effect (e.g. when smoothing, since contours are circular, the last elements of a sequence must be used to calculate the new value of the first elements...)
Thank you very much for your advices,
Edit:
I also tried cv::approxPolyDP() but, as you can see, it tends to preserve extremal points (which I want to remove):
Epsilon=0
Epsilon=6
Epsilon=12
Epsilon=24
Edit 2:
As suggested by Ben, it seems that cv::GaussianBlur() is not supported but cv::blur() is. It looks very much closer to my expectation. Here are my results using it:
k=13
k=53
k=103
To get around the border effect, I did:
cv::copyMakeBorder(smoothCont,smoothCont, (k-1)/2,(k-1)/2 ,0, 0, cv::BORDER_WRAP);
cv::blur(smoothCont, result, cv::Size(1,k),cv::Point(-1,-1));
result.rowRange(cv::Range((k-1)/2,1+result.rows-(k-1)/2)).copyTo(v[0]);
I am still looking for solutions to interpolate/sample my contour.
Your Gaussian blurring doesn't work because you're blurring in column direction, but there is only one column. Using GaussianBlur() leads to a "feature not implemented" error in OpenCV when trying to copy the vector back to a cv::Mat (that's probably why you have this strange resize() in your code), but everything works fine using cv::blur(), no need to resize(). Try Size(0,41) for example. Using cv::BORDER_WRAP for the border issue doesn't seem to work either, but here is another thread of someone who found a workaround for that.
Oh... one more thing: you said that your contours are likely to be much smaller. Smoothing your contour that way will shrink it. The extreme case is k = size_of_contour, which results in a single point. So don't choose your k too big.
Another possibility is to use the algorithm openFrameworks uses:
https://github.com/openframeworks/openFrameworks/blob/master/libs/openFrameworks/graphics/ofPolyline.cpp#L416-459
It traverses the contour and essentially applies a low-pass filter using the points around it. Should do exactly what you want with low overhead and (there's no reason to do a big filter on an image that's essentially just a contour).
How about approxPolyDP()?
It uses this algorithm to 'smooth' a contour (basically gettig rid of most of the contour's points and leave the ones that represent a good approximation of your contour)
From 2.1 OpenCV doc section Basic Structures:
template<typename T>
explicit Mat::Mat(const vector<T>& vec, bool copyData=false)
You probably want to set 2nd param to true in:
smoothCont = cv::Mat(contours[0]);
and try again (this way cv::GaussianBlur should be able to modify the data).
I know this was written a long time ago, but did you tried a big erode followed by a big dilate (opening), and then find the countours? It looks like a simple and fast solution, but I think it could work, at least to some degree.
Basically the sudden changes in contour corresponds to high frequency content. An easy way to smooth your contour would be to find the fourier coefficients assuming the coordinates form a complex plane x + iy and then by eliminating the high frequency coefficients.
My take ... many years later ...!
Maybe two easy ways to do it:
loop a few times with dilate,blur,erode. And find the contours on that updated shape. I found 6-7 times gives good results.
create a bounding box of the contour, and draw an ellipse inside the bounded rectangle.
Adding the visual results below:
This applies to me. The edges are smoother than before:
medianBlur(mat, mat, 7)
morphologyEx(mat, mat, MORPH_OPEN, getStructuringElement(MORPH_RECT, Size(12.0, 12.0)))
val contours = getContours(mat)
This is opencv4android code.