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();`
Related
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.
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() );
}
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);
}
I am trying to track a custom circular marker in an image, and I need to check that a circle contains a minimum number of other circles/objects. My code for finding circles is below:
void findMarkerContours( int, void* )
{
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
vector<Point> approx;
cv::Mat dst = src.clone();
cv::Mat src_gray;
cv::cvtColor(src, src_gray, CV_BGR2GRAY);
//Reduce noise with a 3x3 kernel
blur( src_gray, src_gray, Size(3,3));
//Convert to binary using canny
cv::Mat bw;
cv::Canny(src_gray, bw, thresh, 3*thresh, 3);
imshow("bw", bw);
findContours(bw.clone(), contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
Mat drawing = Mat::zeros( bw.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) );
// contour
drawContours( drawing, contours, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
//Approximate the contour with accuracy proportional to contour perimeter
cv::approxPolyDP(cv::Mat(contours[i]), approx, cv::arcLength(cv::Mat(contours[i]), true) *0.02, true);
//Skip small or non-convex objects
if(fabs(cv::contourArea(contours[i])) < 100 || !cv::isContourConvex(approx))
continue;
if (approx.size() >= 8) //More than 6-8 vertices means its likely a circle
{
drawContours( dst, contours, i, Scalar(0,255,0), 2, 8);
}
imshow("Hopefully we should have circles! Yay!", dst);
}
namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
imshow( "Contours", drawing );
}
As you can see the code to detect circles works quite well:
But now I need to filter out markers that I do not want. My marker is the bottom one. So once I have found a contour that is a circle, I want to check if there are other circular contours that exist within the region of the first circle and finally check the color of the smallest circle.
What method can I take to say if (circle contains 3+ smaller circles || smallest circle is [color] ) -> do stuff?
Take a look at the documentation for
findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())
You'll see that there's an optional hierarchy output vector which should be handy for your problem.
hierarchy – Optional output vector, containing information about the image topology. It has as many elements as the number of contours.
For each i-th contour contours[i] , the elements hierarchy[i][0] ,
hiearchyi , hiearchyi , and hiearchyi are set to
0-based indices in contours of the next and previous contours at the
same hierarchical level, the first child contour and the parent
contour, respectively. If for the contour i there are no next,
previous, parent, or nested contours, the corresponding elements of
hierarchy[i] will be negative.
When calling findCountours using CV_RETR_TREE you'll be getting the full hierarchy of each contour that was found.
This doc explains the hierarchy format pretty well.
You are already searching for circles of a certain size
//Skip small or non-convex objects
if(fabs(cv::contourArea(contours[i])) < 100 || !cv::isContourConvex(approx))
continue;
So you can use that to look for smaller circles than the one youve got, instead of looking for < 100 look for contours.size
I imagine there is the same for color also...
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);