Improving Grabcut in OpenCV - c++

I just implemented the GrapCV-algorithm (OpenCV, C++, Visual Studio 12):
grabCut( *image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_MASK
);
I used it to segment to following picture:
My result is this:
My question is: How i am able now to improve the algorithm, so that only the both players can be seen? My first idea was a change concerning the colors depth, but i don't know how to send it to "grabCut". Or are there other ways?

In the folder opencv2.4.6/samples/cpp, there are a lot of files that demonstrate the use of some classical algorithms. There is a file called grabcut.cpp which tells you how to apply this algorithm to your application in detail. So what you need to do is just embed this file into your program.
If you have any other questions, just debug with the grabcut.cpp and you would see how it works in detail. And if you wanna implement it by yourself. you need to change the function with new parameter GC_EVAL or GC_INIT_WITH_MASK.
grabCut(image, // input image
mask, // segmentation mask
rect,// rectangle containing foreground
bgModel,fgModel, // models
2, // number of iterations
GC_INIT_WITH_RECT); // use rectangle

Related

How to use stock-objects with GDI+

I was using the following pattern to record an enhanced meta-file for a later playback:
POINT pts[] = {
//.....
};
::SelectObject(hEnhDC, ::GetStockObject(LTGRAY_BRUSH));
::Polygon(hEnhDC, pts, _countof(pts));
Now I'm forced to use GDI+ to provide anti-aliasing, so I'm trying to convert that code sample:
Gdiplus::Point pts[] = {
//...
};
Gdiplus::Graphics grx(hEnhDC);
Gdiplus::Pen pen(Gdiplus::Color(255, GetRValue(clrPen), GetGValue(clrPen), GetBValue(clrPen)), PEN_THICKNESS);
grx.FillPolygon(&brush, pts, _countof(pts));
grx.DrawPolygon(&pen, pts, _countof(pts));
The issue is how do I convert a stock-object HBRUSH from ::GetStockObject(LTGRAY_BRUSH) to GDI+ Brush object?
EDIT: Guys, thank you for all your suggestions. And I apologize for not providing more details. This question is not about getting the RGB color triplet from the stock brush. I can do all that with the GetSysColor function, or with the LOGBRUSH like you showed below.
The trick lies in the first sentence above. I am recording an enhanced metafile that may be played on a separate computer, so I cannot hard-code colors into it.
Let me explain. Say, the first GDI example (let's simplify it down to a triangle with a gray fill):
POINT pts[] = {
{100, 100,},
{100, 120,},
{120, 100,},
};
::SelectObject(hEnhDC, ::GetStockObject(LTGRAY_BRUSH));
::Polygon(hEnhDC, pts, _countof(pts));
If I then call GetEnhMetaFileBits on that meta-file, I'll get the following data:
So as you see the EMR_SELECTOBJECT object in that recorded meta-file specifies the LTGRAY_BRUSH = 0x80000001, which will be properly substituted for the color when that meta-file is played on the target system.
And that's what I'm trying to achieve here with GDI+. For some reason it only seems to support hard-coded color triplets in its Brush class. That's why I asked.
Otherwise, one solution is to parse the enhanced meta-file's raw data. (For GDI+ it is a much more complex structure though, that also involves parsing EMR_GDICOMMENT objects.) And then substitute the needed color on the target system before the GDI+ meta-file is played. But it involves writing a lot of code, which I was trying to avoid at this stage ...
I’m afraid you can’t easily convert.
A simple workaround is create GDI+ solid brush with the same color.
See this spec for color values of GDI stock objects, that particular brush has color #C0C0C0

Execute HoughCircles after Inrange

I'm trying to detect shapes with a specific color, using openCV.
The first thing i'm trying to do, is to detect circles in an image.
I'm able to detect the circles, using houghCircles, with this function call:
HoughCircles(gray, c, CV_HOUGH_GRADIENT, 1.5, gray.rows / 10, 200, 100, 0, 0);
Now i'm trying to detect the circles with the specific color.
I'm doing this using the inRange function, which returns a 8-bit, single-channel image.
This function is able to filter only the given color out of the image. I.e. yellow.
But when i pass the returned image in to the houghCircles function, it's returning no circles.
There are no compilation errors.
I already tried to change some of the parameters of houghCircles, but i'm not able to detect the circle.
This is an example of the image that inRange is returning:
Grayscale image
What parameters do i need to use, to detect the circle in that image?
Thanks in advance,
Peter
By doing some playing around with your image, I have found a set of parameters that work.
HoughCircles(shapes, circles, CV_HOUGH_GRADIENT, 1, shapes.rows / 4, 400, 20, 0, 0);
I doubt these are ideal parameters, and I strongly advise you to go and build your own app for changing function parameters - there are plenty of examples of simple slider-based apps in the OpenCV docs which you can use to play around with parameters until you hit upon something that works. This one, for example, is a demo for Hough Circles.
Additionally, you may not be using the best tool for the job. By using contours, you should be able to detect and classify a whole bunch of different shapes, not just circles. This tutorial is very close to what you're trying to do and probably worth a read.

OpenCV border mode issue with blur filter

I've been stuck on this for a few days now, maybe someone will be able to help me here.
I'm using OpenCV C++ API to perform some basic image processing. I have a step where I want to blur my image and specify BORDER_WRAPas my border type :
cv::blur(img, img, cv::Size(3, 3), cv::Point(-1, -1), cv::BORDER_WRAP);
But when executing my code, I get the following error:
OpenCV Error: Assertion failed (columnBorderType != BORDER_WRAP)
However, everything works fine when I use other border types, (BORDER_REFLECT for example), but I need BORDER_WRAP
Things seem to work if I use copyMakeBorder(img, img, 1, 1, 1, 1, cv::BORDER_WRAP) first on my image, blur this new image and then crop it back to the size of the original one, but still I can't figure out why my first try doesn't work.
Anyone knows how I can solve this ?
You can't do this. BORDER_WRAP is not accepted by all functions - it's valid just for a few of them and, as the assertion failure confirms, cv::blur is not one of them..
But as you've already found out yourself, you can first use cv::copyMakeBorder, blur this new image and crop it back to the size of the original.

Taking a screenshot of a particular area

Looking for a way for taking a screenshot of a particular area on the screen in C++. (So not the whole screen) Then it should save it as .png .jpg whatever to use it with another function afterwards.
Also, I am going to use it, somehow, with openCV. Thought i'd mention that, maybe it's a helpful detail.
OpenCV cannot take screenshots from your computer directly. You will need a different framework/method to do this. #Ben is correct, this link would be worth investigating.
Once you have read this image in, you will need to store it into a cv:Mat so that you are able to perform OpenCV operations on it.
In order to crop an image in OpenCV the following code snippet would help.
CVMat * imagesource;
// Transform it into the C++ cv::Mat format
cv::Mat image(imagesource);
// Setup a rectangle to define your region of interest
cv::Rect myROI(10, 10, 100, 100);
// Crop the full image to that image contained by the rectangle myROI
// Note that this doesn't copy the data
cv::Mat croppedImage = image(myROI);

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.