OpenCV findContours - 2.4.5 Heap Corruption - c++

I have a heap corruption happening with 100% certainty on the findContours function. When I do not use it, everything works fine.
unsigned char* UCFromMatUC(cv::Mat& input)
{
int size = input.size.p[0] * input.size.p[1];
unsigned char* result = new unsigned char[size];
memcpy(result, input.data, size);
return result;
}
unsigned char* CannyEdgeCV(unsigned char* input, int width, int height)
{
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
cv::RNG rng(12345);
cv::Mat inp(cv::Size(width, height), CV_8UC1, input);
cv::Mat canny_output;
cv::Mat outp;
cv::blur(inp, outp, cv::Size(3,3));
cv::Canny(outp, canny_output, 4.0, 8.0);
if(canny_output.type()!=CV_8UC1){
return NULL;
}
cv::findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) );
cv::Mat drawing = cv::Mat::zeros( canny_output.size(), CV_8UC3 );
for( int i = 0; i< contours.size(); i++ )
{
cv::Scalar color = cv::Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
drawContours( drawing, contours, i, color, 2, 8, hierarchy, 0, cv::Point() );
}
cv::imwrite( "contours.jpg", drawing );
unsigned char* result = UCFromMatUC(canny_output);
return result;
}
Originally I was only using the canny edge map, but later on I wanted to test the results of the contour functionality.
The Canny Edge works fine, and I get an image as expected, but the findContours (both the one in code and the commented version) fail with a heap corruption error. What causes this?
The entry point for this is the CannyEdgeCV(), and is called with a 640x480 8bit grey image.
Edit: updated code.
Edit2: when I tried to create a minimal example to reproduce this, my code failed at even imread("imagename.bmp"); which was really odd, so I began investigating what might cause this. Now someone else wrote in a corresponding SO question that you cannot mix debug / release mode libraries, so if you are in debug, you have to use debug DLLs, and that worked for me now, I get the expected results.

The main culprit is your use of cv::Mat* and new. This is a Bad Idea. It is unnecessary and often problematic (as you have discovered) to dynamically allocate cv::Mat objects. A better solution is to pass them by value or const reference, since the underlying image data is refcounted, and cv::Mat are shallow copied.
The first specific problem is that you manually assign the data member in CVMatFromUC():
resultMat->data = input
You should not do this. cv::Mat has other members which also reference the data location, and you are asking for trouble. If you need to create a cv::Mat header for external data, you should create a cv::Mat like so:
cv::Mat inp(cv::Size(width, height), CV_8UC1, input); //Create cv::Mat header, no memory copied
Also, your type check for canny_output is incorrect. !canny_output.type() is evaluated first, and implicitly converts to true, as does CV_8UC1. So the expression is always true. The condition you want is: canny_output.type() != CV_8UC1
Given this, it turns out that the CVMatFromUC() function is unnecessary. An improved version of your function follows:
uchar* CannyEdgeCV(uchar* input, int width, int height)
{
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
cv::Mat inp(cv::Size(width, height), CV_8UC1, input); //Create cv::Mat header, no memory copied
cv::Mat canny_output;
cv::Mat outp;
cv::blur(inp, outp, cv::Size(3,3));
cv::Canny(outp, canny_output, 10.0, 15.0);
if(canny_output.type()!=CV_8UC1){
return NULL;
}
cv::findContours(canny_output, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE );
//cv::findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) );
unsigned char* result = UCFromMatUC(&inp);
return result;
}
I should note that it appears that CannyEdgeCV returns the same data that it receives, so it may be possible to remove the call to UCFromMatUC() and the associated data copy entirely. However, I tried and got memory errors, so there might be other problems lurking elsewhere.

-Change findContours as follows
findContours(gray, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
If it doesn't work again, your problem is probabely uninitialized memory. You can try to create new Mat outP2 and clone the original one into this.
Then use outP2 from the following step:
cv::Canny(*outP2, canny_output, 10.0, 15.0);
...
...

Related

Opencv Findcontours cause heap error

I am using opencv 2.49.
But I am stuck by find contours function for hours.
When I run the program in debug mode and the error box returns
Debug assertion failed
Program: ...
File f:\dd\vctools\crt_bld\self_x86\crt\src\dbgheap.c
Line: 1322
Exception:_ CrtIsValidHeapPoionter(pUserData)
And here is my function
HRESULT OpenCVHelper::DrawHand(Mat* pImg)
{
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
cvtColor(*pImg, *pImg, CV_RGBA2GRAY);
//Canny(*pImg, *pImg, 30,50);
threshold( *pImg, *pImg, 50, 255,THRESH_BINARY);
if(pImg->type() == CV_8UC1)
{
findContours( *pImg, contours, hierarchy, CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
}
for( int i = 0; i< contours.size(); i++ )
{
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
Scalar color( rand()&255, rand()&255, rand()&255 );
drawContours( *pImg, contours, i, color, 2, 8, hierarchy, 0, Point() );
}
//contours.clear();
//hierarchy.clear();
cvtColor(*pImg, *pImg, CV_GRAY2RGBA);
return S_OK;
}
When I remove findcontour function there is no error.
When I use findcontour it pops the error box showed above.
When I add "contours.clear(); hierarchy.clear();" these two lines, there is no error message but the program still crashes.
Anyone can help?
EDIT 1. I find out the allocator for causing heap corruption, which is vector > contours; but I still don't know how to fix it.
Finally I found problem...
My Dev Environment
window10 x64, Intel i5 ( X64 ),
MS Visual Studio 2010 SP1
OpenCV 2.4.9 ( Test 2.4.13 get the same error)
Some people solve this problem : up to opencv version. 2.4.9.
, Hear is my code.
Mat Img = imread( src_image );
if (Img.rows == 0 || Img.cols == 0)
return -1;
Mat ImgGray;
cvtColor( Img, ImgGray, CV_BGR2GRAY );
Mat threshold_output;
vector<vector<Point>> contours; // <<
vector<Vec4i> hierarchy;
int blkSize = 5;
int nKernelSz = 3;
double dFactor = 2.0f;
adaptiveThreshold(ImgGray,threshold_output,
100,CV_ADAPTIVE_THRESH_MEAN_C,CV_THRESH_BINARY_INV,blkSize, 5);
findContours(threshold_output, contours, hierarchy, CV_RETR_TREE,
CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
"vector < vector < Point > > "
Causes an error on runtime library /MTd
Solve : runtime library change to /MD
This is the site referenced below.
http://opencv-users.1802565.n2.nabble.com/c-interface-heap-mem-problem-findcontours-td7020857.html
The code you have should work. Most likely point of failure is having an empty image when you get to findContours. I am not talking about uninitialized image but the one that was initialized with Size(0,0). Change your 'if-statement' to be:
if ((pImg->type() == CV_8UC1) && (pImg->rows>0))
If this won't help, than next step is to verify that the function that crushes is indeed findContours and not drawContours. The crash may be in drawContours because it is not used when you remove findContours.
Also it is VERY not recommended to use pointer to Mat. Mat is a smart pointer by itself, and it has reference counter. This will create all kinds of nasty bugs.

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);
}

Program crashes when using cv::findContours with Qt Gui

I have been stuck with this issue for days;
I created a Qt console project, connected it with OpenCV and everything was working just fine;
I created a Qt Gui project, added a button and copied the same code from the previous project in the button's slot, I got a windows segFault and program exited with code -1073741819.
So I used the debugger to detect the problem and it turned out to be at the use of function cv::threshold.
I changed it and instead used cv::Canny but then I got the same problem with cv::findContours !
The strange thing is that when I called the button's 'MainWindow::on_pushButton_clicked()'
in the windows' constructor it worked!!!
here's debugger output:
0 cv::thresh_8u(cv::Mat const&, cv::Mat&, unsigned char, unsigned char, int) C:\OpenCV2.4\OpenMinGw\install\bin\libopencv_imgproc240.dll 0 0x62b2c624
1 cv::_InputArray::getMat(int) const C:\OpenCV2.4\OpenMinGw\install\bin\libopencv_core240.dll 0 0x65c1a838
2 ?? 0 0x00000000
and here's the function where I get the error (which I got from OpenCV tutorials):
void MainWindow::on_pushButton_clicked(){
Mat src; Mat src_gray;
int thresh = 100;
RNG rng(12345);
Mat canny_output;
vector<vector<Point> > contours;
/// Load source image and convert it to gray
src = imread( "hat-10.bmp", 1 );
cvtColor( src, src_gray, CV_BGR2GRAY );
blur( src_gray, src_gray, Size(3,3) );
/// Detect edges using canny
Canny( src_gray, canny_output, thresh, thresh*2, 3 );
qDebug("Ok 1");
/// Find contours
if(cv::sum(src_gray).val[0] > 0.0){
qDebug("Ok 2");
cv::findContours( src_gray, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE );
/// Draw contours
Mat drawing = Mat::zeros( src_gray.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, 2);//, 8, hierarchy, 0, Point() );
}
/// Show in a window
imshow( "Contours", drawing);
}
Using:
Windows 7 x64
OpenCV 2.4.0 compiled using mingw 4.1.0
Qt Creator 2.0.0 Based on Qt 4.7.0 (32 bit)
Edit:
here's a shorter version of my code :
void MainWindow::on_toolButton_clicked(){
std::vector<std::vector<cv::Point> > contours;
/// Load source image and convert it to gray
Mat src = imread( "C:/Users/poste/L3 ISIL/PFE Licence/new bmp/hat-10.bmp", 1);
// my image is already a binary one
Mat canny_output(src.size(), src.type());
Canny(src,canny_output,100,200,3);
imshow("Source", canny_output); // here image is displayed before crash
waitKey(500);
/// Find contours
findContours(canny_output, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE );
}
in console mode, there's no problem. when called from GUI app constructor there's also no problem.
It only crashes when actually clicking on the button.
edit:
I took a screenshot ![here]http://i.stack.imgur.com/1LyeF.png
canny_output was displayed which means image was loaded.
Uploaded project here
First make sure that the image that you want to threshold is really a gray scaled image. Show it in another window after Thresholding.
Do that before cv::FindContours:
cvThreshold(originalImage,resultingImage,100,100,CV_THRESH_BINARY)
also change that:
vector<vector<Point> > contours;
to:
vector<vector<cv::Point2f> > contours;
Try this:
/// Load source image and convert it to gray
Mat src = imread( "hat-10.bmp", 1 );
Mat src_gray(src.size(), CV_8U);
cvtColor( src, src_gray, CV_BGR2GRAY );
blur( src_gray, src_gray, Size(3,3) );
//Apply threshold
cv::Mat thres_output(src_gray.size(), src_gray.type());
cv::threshold(src_gray, thres_output, 100, 255, cv::THRESH_BINARY);
qDebug("Ok 1");
OpenCV docs have a full demo on Basic Thresholding Operations.
EDIT:
After carefully reviewing your code and comments, I think I know what's going on: these problems might be happening because imread() can't access the specified file. This makes the function return an empty Mat. To check if this is the case, simply do:
Mat src = imread( "hat-10.bmp", 1 );
if (src.empty())
{
std::cout << "!!! imread failed to open image\n";
return;
}
The reason why it happens is because Qt Creator builds the .exe of the project in a separate folder, so when the application runs, it tries to load the image from the directory where the .exe was launched, and it fails because the image isn't there.
When calling imread() remember to pass the FULL PATH to the file. See if that fixes the issue.
EDIT:
Remember to convert the image to binary before feeding it to findContours():
// Convert from 32F to 8U
cv::Mat binary_img;
canny_output.convertTo(binary_img, CV_8U);
std::vector<std::vector<cv::Point> > contours;
cv::findContours(binary_img, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
I used function Qtconcurrent::run() and everything started working.
Even though this isn't a permanent (nor a good) solution; this is all I could come up with.
I'm still open to other answers though.

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);

OpenCV pointer to bitmap processing

I've created a shared library for contour detection that is loaded from a Delphi/Lazarus application. The main app, passes a pointer to a bitmap to be processed by a function inside the library.
Here's the function inside the library. The parameter "img" is the pointer to my bitmap.
extern "C" {
void detect_contour(int imgWidth, int imgHeight, unsigned char * img, int &x, int &y, int &w, int &h)
{
Mat threshold_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
Mat src_gray;
int thresh = 100;
int max_thresh = 255;
RNG rng(12345);
/// Load source image and convert it to gray
Mat src(imgHeight, imgWidth, CV_8UC4);
int idx;
src.data = img;
/// Convert image to gray and blur it
cvtColor( src, src_gray, CV_BGRA2GRAY );
blur( src_gray, src_gray, Size(10,10) );
/// Detect edges using Threshold
threshold( src_gray, threshold_output, thresh, 255, THRESH_BINARY );
/// Find contours
findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
/// Approximate contours to polygons + get bounding rects and circles
vector<vector<Point> > contours_poly( contours.size() );
vector<Rect> boundRect( contours.size() );
vector<Point2f>center( contours.size() );
vector<float>radius( contours.size() );
int lArea = 0;
int lBigger = -1;
for( int i = 0; i < contours.size(); i++ )
{
approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
boundRect[i] = boundingRect( Mat(contours_poly[i]) );
if(lArea < boundRect[i].width * boundRect[i].height)
{
lArea = boundRect[i].width * boundRect[i].height;
lBigger = i;
}
}
if(lBigger > -1)
{
x = boundRect[lBigger].x;
y = boundRect[lBigger].y;
w = boundRect[lBigger].width;
h = boundRect[lBigger].height;
}
}
}
From the Delphi side, I'm passing a pointer to an array of this structure:
TBGRAPixel = packed record
blue, green, red, alpha: byte;
end;
I need to process the bitmap in-memory, that's why I'm not loading the file from inside the library.
The question is: Is this the right way to assign a bitmap to a cv::Mat ?
I ask this because the code works without problems in Linux, but fails on Windows compiled with Mingw.
Note: it fails with a SIGSEGV on this line:
blur( src_gray, src_gray, Size(10,10) );
EDIT: The SIGSEGV is raised only if I compile OpenCV in Release mode, in Debug mode it works ok.
Thanks in advance,
Leonardo.
So you are creating an image this way:
Mat src(imgHeight, imgWidth, CV_8UC4);
int idx;
src.data = img;
The first declaration and instantiation
Mat src(imgHeight, imgWidth, CV_8UC4) will allocate memory for a new image and a reference counter that automatically keeps track of the number of references to the allocated memory.
Then you mutate an instance variable through
src.data = img;
When the the instance src goes out of scope, the destructor is called and most likely tries to deallocate the memory at src.data, which you assigned and this might cause a segmentation fault. The right way to do it is to not change instance variable of an object, but to simply use the right constructor when you instantiate src:
Mat src(imgHeight, imgWidth, CV_8UC4, img);
This way, you just create a matrix header and no reference counter or deallocation will be performed by the destructor of src.
Good luck!
EDIT: I am not sure that the segfault is actually caused by an attempt to deallocate memory incorrectly, but it is a good practice not to break data abstraction by assigning directly to instance variables.