OpenCV example code for find contours: vector deallocation issue
You're building your application in debug mode and linking against the Multithreaded Debug DLL CRT. Do you know which CRT the OpenCV DLL(s) are linked against? If it's linked against a static CRT it'll fill up the vector with data allocated from a separate heap, which causes an assertion in the Debug CRT you're using.
If you build your application in Release mode you should no longer see the assert, but you might end up leaking memory. The best thing would be to ensure that both your application and the OpenCV DLL(s) are linked against the same Multithreaded DLL CRT.
EDIT: If you can't rebuild OpenCV to use the same CRT as your application you could try telling the linker to use the same CRT version as OpenCV for your application by modifying the application manifest. See How to Enforce C++ compiler to use specific CRT version? for more information on how to do that.
ShdNx
Updated on February 08, 2020Comments
-
ShdNx over 4 years
I'm trying to get started with contour detection in OpenCV 2.4.2. To this end, I set up a project for OpenCV and copied the whole example code found in the documentation. For future reference, here is the code:
#include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include <iostream> #include <stdio.h> #include <stdlib.h> using namespace cv; using namespace std; Mat src; Mat src_gray; int thresh = 100; int max_thresh = 255; RNG rng(12345); /// Function header void thresh_callback(int, void* ); /** @function main */ int main( int argc, char** argv ) { /// Load source image and convert it to gray src = imread( argv[1], 1 ); /// Convert image to gray and blur it cvtColor( src, src_gray, CV_BGR2GRAY ); blur( src_gray, src_gray, Size(3,3) ); /// Create Window char* source_window = "Source"; namedWindow( source_window, CV_WINDOW_AUTOSIZE ); imshow( source_window, src ); createTrackbar( " Canny thresh:", "Source", &thresh, max_thresh, thresh_callback ); thresh_callback( 0, 0 ); waitKey(0); return(0); } /** @function thresh_callback */ void thresh_callback(int, void* ) { Mat canny_output; vector<vector<Point> > contours; vector<Vec4i> hierarchy; /// Detect edges using canny Canny( src_gray, canny_output, thresh, thresh*2, 3 ); /// Find contours findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) ); /// Draw contours Mat drawing = Mat::zeros( canny_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, 2, 8, hierarchy, 0, Point() ); } /// Show in a window namedWindow( "Contours", CV_WINDOW_AUTOSIZE ); imshow( "Contours", drawing ); contours.clear(); // Error!! }
It compiles fine in Visual Studio 11 RC (Windows 7 SP1), but I get an error at the end of the
thresh_callback
function. Here is the stacktrace:msvcr110d.dll!_CrtIsValidHeapPointer(const void * pUserData) Line 2036 msvcr110d.dll!_free_dbg_nolock(void * pUserData, int nBlockUse) Line 1322 msvcr110d.dll!_free_dbg(void * pUserData, int nBlockUse) Line 1265 msvcr110d.dll!operator delete(void * pUserData) Line 54 std::allocator<cv::Point_<int> >::deallocate(cv::Point_<int> * _Ptr, unsigned int __formal) Line 586 std::_Wrap_alloc<std::allocator<cv::Point_<int> > >::deallocate(cv::Point_<int> * _Ptr, unsigned int _Count) Line 888 std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > >::_Tidy() Line 1542 std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > >::~vector<cv::Point_<int>,std::allocator<cv::Point_<int> > >() Line 901 std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > >::`scalar deleting destructor'(unsigned int) std::allocator<std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > > >::destroy<std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > > >(std::<cv::Point_<int>,std::allocator<cv::Point_<int> > > * _Ptr) Line 624 std::allocator_traits<std::allocator<std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > > > >::destroy<std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > > >(std::allocator<std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > > > & _Al, std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > > * _Ptr)758 std::_Wrap_alloc<std::allocator<std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > > > >::destroy<std::vector<cv::Point_<int>,std::allocator<cv::Poin> > > >(std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > > * _Ptr) Line 909 std::_Destroy_range<std::_Wrap_alloc<std::allocator<std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > > > > >(std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > > * _First, std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > > * _Last, std::_Wrap_alloc<std::allocator<std::vector<cv::Point_<int>,std::allocator<cv::<int> > > > > & _Al, std::_Nonscalar_ptr_iterator_tag __formal) Line 89 std::_Destroy_range<std::_Wrap_alloc<std::allocator<std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > > > > >(std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > > * _First, std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > > * _Last, std::_Wrap_alloc<std::allocator<std::vector<cv::Point_<int>,std::allocator<cv::<int> > > > > & _Al) Line 80 std::vector<std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > >,std::allocator<std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> >::_Destroy(std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > > * _First, std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > > * _Last) Line 1480 std::vector<std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > >,std::allocator<std::vector<cv::Point_<int>,std::allocator<cv::Point_<int> > > > >::c Line 1416 thresh_callback(int __formal, void * __formal) Line 143 opencv_highgui242d.dll!icvUpdateTrackbar(CvTrackbar * trackbar, int pos) Line 1938 opencv_highgui242d.dll!HGToolbarProc(HWND__ * hwnd, unsigned int uMsg, unsigned int wParam, long lParam) Line 1982
(Note that actually I have a slightly modified version of the example code, added a few printf-s, and a
contours.clear()
call, which is at line 143, and which triggers the vector deallocation [that would happen automatically at the end of the function] which seems to be the source of the problem. The very same issue occurs with the exact example code as well.)The problem seems to be with the deallocation of the
contours
vector. The same occurs if I try to callcontours.clear()
. I have examined the variable's contents in the VS debugger, but there's nothing unusual as far as I can tell.I tried to clear the vector as soon as it was no longer needed (after the
for
loop), but it didn't help. I also tried to switch the Platform Toolset to Visual Studio 10, which in turn won't even compile with error messages that are beyond me:error C1083: Cannot open include file: 'SDKDDKVer.h': No such file or directory (C:\<project path>\targetver.h) (Line 8, Column 1) IntelliSense: cannot open source file "SDKDDKVer.h" (C:\<project path>\targetver.h) (Line 8, Column 1) IntelliSense: cannot open source file "windows.h" (C:\<opencv path>\build\include\opencv2\core\operations.hpp (Line 83, Column 3)
Any help would be greatly appreciated. Please note that I'm not a C++ programmer: I have very little knowledge, and even less experience with C++ programming and native programming in general.
Edit: it turns out that the debugger displayed the wrong line as the source of error. I should have seen in the call stack that the problem was with vector >. So the problematic vector is
contours
, nothierarchy
!Edit #2: I also tried to reproduce the issue with a minimal code that creates a
vector<vector<cv::Point> >
, puts some items in it, then clears it, but I couldn't reproduce the problem. -
ShdNx almost 12 yearsI downloaded the OpenCV source code, and built it on my machine. I don't know what it's linked against - I didn't change the preset setting. Where can I see this information? I tried to compile and run my application in Release mode, but the problem persists.
-
Michael almost 12 yearsDependency Walker (dependencywalker.com) can show you for a given DLL/EXE which DLLs it has dependencies to. If anything named something like MSVC* pops up in the dependency tree you can compare that against what your application is using. If no such dependency is found, the DLL is probably linked statically against the CRT.
-
Michael almost 12 yearsAlso, since you mention that you're building OpenCV yourself (also using visual studio?), you could go into the project properties when building OpenCV and make sure you're using the same CRT as for your application (Code Generation -> Runtime Library).
-
ShdNx almost 12 yearsI checked the dependencies for my exe and the OpenCV dlls, and it turns out that while OpenCV uses MSVCP100D.DLL, my exe uses MSVCP110D.DLL. I guess this comes from OpenCV being built for VS 10 (it uses CMake), while my exe is built in VS 11. I checked the Code Generation -> Runtime Library option, but there's no option to use earlier runtime library, I can just decide between Multi-threaded (Debug)? (DLL)?
-
ShdNx almost 12 yearsThank you very much for your help. I fixed the problem by building my application with VS 2010, so that it uses MSVCP100D.DLL by default.
-
memyself over 11 years@Michael thanks for your post - I was having the same problem and couldn't figure it out! You say
you might end up leaking memory.
- how can I check whether this might be the case? -
Michael over 11 years@memyself: See e.g. msdn.microsoft.com/en-us/library/x98tx3cf.aspx or valgrind.org
-
cwadding over 11 yearsI was having this problem with a project using gtest and gmock with opencv. Both gtest and gmock used /MT by default when I built it with cmake and opencv used /MD by default for the runtime library. My console app for my test project was setup to use /MT which was conflicting with a dll project compiled using /MD. To resolve the issue I recompiled gtest and gmock to use /MD and changed my console test app so it is also /MD. Everything works great now. At first I tried recompiling opencv to use /MT but this proved to be more painful but in theory that should work as well.