C++ OpenCV equivalent to Matlab imfill? - c++

I have an original image
if I use imfill in Matlab, the result is
But I use the following code in C++ as an attempt to mimic the imfill from Matlab:
cv::Mat BW = cv::imread("smallHoles.bmp", cv::IMREAD_GRAYSCALE);
cv::Mat edgesNeg = BW.clone();
cv::floodFill(edgesNeg, cv::Point(0, 0), cv::Scalar(255));
bitwise_not(edgesNeg, edgesNeg);
cv::Mat filledEdgesOut = (edgesNeg | BW);
cv::imwrite("C:/Users/me/circleCVfill.bmp", filledEdgesOut);
the result is that all the black objects, even the black vertical object, gets filled with white pixels, which doesn't match the Matlab result:
What is the correct C++ OpenCV code to use?
EDIT:
The Matlab code I use:
BW=imread('smallHoles.bmp');
BWfill=imfill(BW,'holes');
EDIT2:
I also tried the following code
cv::Mat circle = cv::imread("smallHoles.bmp", cv::IMREAD_GRAYSCALE);
std::vector<std::vector<cv::Point> > contours;
cv::Mat contourOutput = circle.clone();
cv::findContours(contourOutput, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
cv::Mat contourImage(circle.size(), CV_8UC1, cv::Scalar(255));
cv::drawContours(contourImage, contours, -1, cv::Scalar(255));
cv::imwrite("C:/Users/me/output.bmp", contourImage);
but it also gave just the same result of an all-white image just like the previous C++ code I tried
EDIT3:
I tried
std::vector<std::vector<cv::Point> > contours;
cv::Mat contourOutput = circle.clone();
cv::findContours(contourOutput, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
cv::drawContours(contourOutput, contours, -1, cv::Scalar(255));
cv::imwrite("C:/me/output.bmp", contourOutput);
It returns as output the original image with the 4 small black holes that should be filled

In trying your EDIT3:
cv::Mat circle = cv::imread(argv[1], cv::IMREAD_GRAYSCALE);
std::vector<std::vector<cv::Point> > contours;
cv::Mat contourOutput = circle.clone();
cv::findContours(contourOutput, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
cv::drawContours(contourOutput, contours, -1, cv::Scalar(255));
cv::imwrite("output.bmp", contourOutput);
I got the following:
https://i.stack.imgur.com/T5YBR.png
This is expected. If you check the documentation on the function drawContours, it says
thickness – Thickness of lines the contours are drawn with. If it is negative (for example, thickness=CV_FILLED ), the contour interiors are drawn.
So we can see the desired shapes are taken but the default for drawing is a thickness of one, but you want a negative value (or CV_FILLED) as the next parameter and you should get your desired solution.
I haven't used the floodFill function though, so can't help you there though.

Related

How to straighten curved line using OpenCV?

I have image with curved line like this :
I couldn't find a technique to straighten curved line using OpenCV. It is similar to this post Straightening a curved contour, but my question is specific to coding using opencv (in C++ is better).
So far, I'm only able to find the contour of the curved line.
int main()
{
Mat src; Mat src_gray;
src = imread("D:/2.jpg");
cvtColor(src, src_gray, COLOR_BGR2GRAY);
cv::blur(src_gray, src_gray, Size(1, 15));
Canny(src_gray, src_gray, 100, 200, 3);
/// Find contours
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
RNG rng(12345);
findContours(src_gray, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
/// Draw contours
Mat drawing = Mat::zeros(src_gray.size(), CV_8UC3);
for (int i = 0; i < contours.size(); i++)
{
drawContours(drawing, contours, i, (255), 1, 8, hierarchy, 0, Point());
}
imshow("Result window", drawing);
imwrite("D:/C_Backup_Folder/Ivan_codes/VideoStitcher/result/2_res.jpg", drawing);
cv::waitKey();
return 0;
}
But I have no idea how to determine which line is curved and not, and how to straighten it. Is it possible? Any help would be appreciated.
Here is my suggestion:
Before everything, resize your image into a much bigger image (for example 5 times bigger). Then do what you did before, and get the contours. Find the right-most pixel of each contour, and then survey all pixel of that contour and count the horizontal distance of each pixels to the right-most pixel and make a shift for that row (entire row). This method makes a right shift to some rows and left shift to the others.
If you have multiple contours, calculate this shift value for every one of them in every single row and compute their "mean" value, and do the shift according to that mean value for each row.
At the end resize back your image. This is the simplest and fastest thing I could think of.

Get the location of a blob in OpenCV using C++

enter image description here
The image shown is the difference between two images. All I want to do is get the location of the white part. I want to do this because I want to be able to highlight the place where the difference is on the original image.
I am thinking about using clustering or blob detection or maybe just locating the brightest or whitest pixel in the image.
What method do you think would be the easiest? Is there another method I haven't though of?
Use the findContour method to find the closed contour in the image.
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours( BinaryImage, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
And you can draw that contours using drawContours .
And the variable contours contains the coordinates making that particular contour. You can display it by the following command
for(double i=0; i<contours.size(); i++)
{
cout << contours[i];
drawContours( OutputImage, contours, i, Scalar(0,255,0), 2, 8, hierarchy, 0, Point() );
}

opencv findContours() crashes the program

I am new to image processing, and I'm working on a real time tracking
But I am stuck with the findCountours function.
cvtColor(*pImg, *pImg, CV_RGBA2GRAY); //convert to gray image
Mask = pImg->clone(); //clone the source
Mask.convertTo(Mask,CV_8UC1); //convert to 8UC1
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours( Mask, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
contours.clear();
hierarchy.clear();
and when I run the program it crashes, If I comment the findCountours function it will be fine.
I have checked some documents but there is no clue what happened.
Mask = pImg->clone(); //clone the source
Mask.convertTo(Mask,CV_8UC1); //convert to 8UC1
replace
pImg.convertTo(Mask, CV_8U, arg);
arg may be different for different types of the input image. 255 for float/double.
Try using "Canny edge detector or Threshold operator" before finding contours.
Choose your operator based on the task you need to perform.
`cvtColor(*pImg, *pImg, CV_RGBA2GRAY); //convert to gray image
Mask = pImg->clone(); //clone the source
Mask.convertTo(Mask,CV_8UC1); //convert to 8UC1
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
/*based on your need use any of these operator */
threshold( input, output, threshold_value, max_BINARY_value,threshold_type );
Canny( input, canny_output, thresh, thresh*2, 3 );
findContours( output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
contours.clear();
hierarchy.clear();`

How to find contours from a webcam frame using opencv and c++?

My goal is to find contours by capturing frame from a webcam. I was able to do it with static images but then I tried to use the same concept in a webcam frame and its giving me this error:
"OpenCV Error: Assertion failed (mtype == type0 || (CV_MAT_CN(mtype) == CV_MAT_CN
(type0) && ((1 << type0) & fixedDepthMask) != 0)) in cv::_OutputArray::create, f
ile C:\builds\2_4_PackSlave-win64-vc11-shared\opencv\modules\core\src\matrix.cpp
, line 1486"
This is the code that I used to find the contours in my program;
Rng rng(12345);
Mat captureframe,con,threshold_output;
vector<vector<Point> > contours; //
vector<Vec4i> hierarchy;
while(true)
{
capturedevice>>captureframe;
con = captureframe.clone();
cvtColor(captureframe,con,CV_BGR2GRAY);
threshold( con, threshold_output, thresh, 255, THRESH_BINARY );
findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
for( int i = 0; i< contours.size(); i++ )
{
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
drawContours( drawing, contours, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
}
imshow("contour drawing",drawing);
}
I think that the problem is in the following two lines:
con = captureframe.clone();
cvtColor(captureframe,con,CV_BGR2GRAY);
In the first line, you are making con as a clone of captureFrame which means that con is a 3 channel image and in the Second line you are trying to make con a grayScale image which is 1 channel therefore you are getting the fault related to the image type.
You should try to do the following (i am not sure whether your code would run after this or not but you should not get the current error after this):
con.create(captureframe.rows , captureframe.cols, CV_8UC1);
cvtColor(captureframe,con,CV_BGR2GRAY);
Guys thank you so much for your help. I finally figured out my mistake and there was a problem in my declaration. I was looking online for some references and then i stumbled upon this code for object detection. The guy actually declared "contour" like this - "std::vector < std::vector < cv::Point > >contours; " and my declaration was "vector contours". My declaration worked for static images but it gave me this error while finding contours from webcam. Can anyone explain me the difference between the above two declarations? Also, as suggested by skm i converted my frame capture to a 1 channel depth image by using the con.create(frame.rows,frame.cols,cv_8uc1) and then converting it to grayscale image. This step is really crucial. So, here is my complete working code!! Thanks
VideoCapture capturedevice;
capturedevice.open(0);
Mat frame,con;
Mat grayframe;
std::vector < std::vector < cv::Point > >contours; //this is very important decalartion
while(true)
{
capturedevice>>frame;
con.create(frame.rows,frame.cols,CV_8UC1);
cvtColor(frame,con,CV_BGR2GRAY);
cv::findContours (con, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
cv::drawContours (frame, contours, -1, cv::Scalar (0, 0, 255), 2);
imshow("frame", frame);
waitKey(33);
}

Drawing contours retrieved from the binary image

I want use findContours with the binary image, but the callback function causes an error:
Invalid address specified to RtlFreeHeap
when returning.
When i want to use clear() to free the vector<vector<Point> > value, it causes the same exception and the code crashed in free.c at the line:
if (retval == 0) errno = _get_errno_from_oserr(GetLastError());
For example:
void onChangeContourMode(int, void *)
{
Mat m_frB = imread("3.jpg", 0);
vector<vector<Point>> contours
vector<Vec4i> hierarchy;
findContours(m_frB, contours, hierarchy, g_contour_mode, CV_CHAIN_APPROX_SIMPLE);
for( int idx = 0 ; idx >= 0; idx = hierarchy[idx][0] )
drawContours( m_frB, contours, idx, Scalar(255,255,255),
CV_FILLED, 8, hierarchy );
imshow( "Contours", m_frB );
}
Can anyone help me? Thank you very much!
Mat m_frB = imread("3.jpg", CV_LOAD_IMAGE_GRAYSCALE);
loads 3.jpg as a 8bpp grayscale image, so it's not binary image. It is specific for findContours function that "non-zero pixels are treated as 1’s. Zero pixels remain 0’s, so the image is treated as binary". Also note that this "function modifies the image while extracting the contours".
The actual problem here is that although the destination image is 8bpp, you should make sure that it has 3 channels by using CV_8UC3 before you draw RGB contours into it. Try this:
// find contours:
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(m_frB, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
// draw contours:
Mat imgWithContours = Mat::zeros(m_frB.rows, m_frB.cols, CV_8UC3);
RNG rng(12345);
for (int i = 0; i < contours.size(); i++)
{
Scalar color = Scalar(rng.uniform(50, 255), rng.uniform(50,255), rng.uniform(50,255));
drawContours(imgWithContours, contours, i, color, 1, 8, hierarchy, 0);
}
imshow("Contours", imgWithContours);