Ask Your Question
0

Strange memory leak using CvMat structure

asked 2014-06-22 22:39:47 -0500

huongnhat gravatar image

updated 2014-06-22 22:41:13 -0500

Dear all,

I've tried to modify the cvShowImageHWND in opencv/modules/highgui/src/window_w32.cpp in order to show image by a HWND window specified instead of using window name. My modified one worked well until I relized it's memory leak. I found that the line cvConvertImage() causes the leak and try to prevent it from leaking memory. Can anyone help me? Actually I tried to use smart pointer cv::Ptr<cvmat> to wrap it but it raises error whenever program goes out of cvShowImageHWND scope, I guess that the data is retained for displaying after the function cvShowImageHWND done, but if I didn't free the memory, my program memory keeps increasing roughly 4MB each loop. Please show me a hint or tell me what should I do

void OpenCV::cvShowImageHWND(HWND w_hWnd, const cv::Mat arr0){
        CV_FUNCNAME("cvShowImage");
        __BEGIN__;
        IplImage* arr=cvCloneImage(&(IplImage)arr0);
        SIZE size = { 0, 0 };
        int channels = 0;
        void* dst_ptr = 0;
        const int channels0 = 3;
        int origin = 0;
        CvMat stub, dst;
        cv::Ptr<CvMat> image;
        bool changed_size = false;
        BITMAPINFO* binfo;
        HDC hdc = NULL;

        if( !arr ) EXIT;
        if( !w_hWnd )
            EXIT;

        hdc = ::GetDC(w_hWnd);


        if( CV_IS_IMAGE_HDR( arr ) )
            origin = ((IplImage*)arr)->origin;

        CV_CALL( image = cv::Ptr<CvMat>(cvGetMat( arr, &stub )) );

        //image = cvGetMat( arr, &stub );
        if ( hdc )
        {

                //GetBitmapData
                BITMAP bmp;
                GdiFlush();
                HGDIOBJ h = GetCurrentObject( hdc, OBJ_BITMAP );

                if (h == NULL) EXIT;
                if (GetObject(h, sizeof(bmp), &bmp) == 0) //GetObject(): returns size of object, 0 if error
                EXIT;

                channels = bmp.bmBitsPixel/8;
                dst_ptr = bmp.bmBits;
         }


        if( size.cx != image->width || size.cy != image->height || channels != channels0 )
        {

            changed_size = true;

            uchar buffer[sizeof(BITMAPINFO) + 255*sizeof(RGBQUAD)];
            binfo = (BITMAPINFO*)buffer;

            BOOL bDeleteObj = DeleteObject(GetCurrentObject(hdc, OBJ_BITMAP));
                    CV_Assert( FALSE != bDeleteObj );

            size.cx = image->width;
            size.cy = image->height;
            channels = channels0;

            OpenCV::FillBitmapInfo( binfo, size.cx, size.cy, channels*8, channels );

            SelectObject( hdc, CreateDIBSection( hdc, binfo, DIB_RGB_COLORS, &dst_ptr, 0, 0));
        }


        cvInitMatHeader( &dst, size.cy, size.cx, CV_8UC3, dst_ptr, (size.cx * channels + 3) & -4 );
        //TODO QUYETNM This line causes memory leak!!!
        cvConvertImage( image, &dst, origin == 0 ? CV_CVTIMG_FLIP : 0 );

        // Image stretching to fit the window
        RECT rect;
        ::GetClientRect(w_hWnd, &rect);
        StretchDIBits( hdc, 0, 0, rect.right, rect.bottom, 0, 0, image->width, image->height, dst_ptr, binfo, DIB_RGB_COLORS, SRCCOPY );
        system("pause");
        // only resize window if needed
        ::InvalidateRect(w_hWnd, 0, 0);
        //Release resource
        cvReleaseImage(&arr);

        __END__;
    }

    void OpenCV::FillBitmapInfo( BITMAPINFO* bmi, int width, int height, int bpp, int origin )
    {
        assert( bmi && width >= 0 && height >= 0 && (bpp == 8 || bpp == 24 || bpp == 32));

        BITMAPINFOHEADER* bmih = &(bmi->bmiHeader);

        memset( bmih, 0, sizeof(*bmih));
        bmih->biSize = sizeof(BITMAPINFOHEADER);
        bmih->biWidth = width;
        bmih->biHeight = origin ? abs(height) : -abs(height);
        bmih->biPlanes = 1;
        bmih->biBitCount = (unsigned short)bpp;
        bmih->biCompression = BI_RGB;

        if( bpp == 8 )
        {
            RGBQUAD* palette = bmi->bmiColors;
            int i;
            for( i = 0; i < 256; i++ )
            {
                palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed = (BYTE)i;
                palette[i].rgbReserved = 0;
            }
        }
    }
edit retag flag offensive close merge delete

2 answers

Sort by ยป oldest newest most voted
0

answered 2014-06-23 00:43:11 -0500

Michael Burdinov gravatar image

Both CvMat and IplImage were obsolete and deprecated a years ago. You shouldn't use them. They will be excluded from next version of OpenCV. Use Mat instead. One of good properties of Mat is that it is a smart pointer, i.e. it has its own reference counter and it releases the memory it allocates.

edit flag offensive delete link more

Comments

I tried to modify the display function so as to be able to display opencv processed image in the MFC/Win32 windows. I know CvMat and IplImage were deprecated but I can't find other solution. The problem here is the data pointed by the CvMat *image var is somewhat retained outside the scope of the function (because when I tried to release the data, the program will raise error). Pls correct me if I were wrong or tell me how to deal with the problem :(

huongnhat gravatar imagehuongnhat ( 2014-06-23 01:29:57 -0500 )edit

I can't put a finger on specific problem with the code. There seems to be too many conversions and casts between various types of images and their headers. Each of them can be the reason for the leak. That was another reason to use Mat - it makes the code much more simple and readable. Can you be more specific about why can't you use Mat, and what do you mean by using 'image' outside of the scope?

Michael Burdinov gravatar imageMichael Burdinov ( 2014-06-23 02:51:16 -0500 )edit

I think that the program still used data of the dst / dst_ptr / image when the call to functioni cvShowImageHWND() is done because when I tried to release their data, the program crash. Am I correct?

huongnhat gravatar imagehuongnhat ( 2014-06-23 04:30:40 -0500 )edit

My bad. I thought that you wrote this by yourself. I just noticed that most of your code is based on existing code of OpenCV. Please ignore my recommendation about using Mat. As for the question itself, I suspect that HWND have different behavior in terms of who and how release memory that was provided to it. This may cause the leak. This is just my speculation, I know very little about this.

Michael Burdinov gravatar imageMichael Burdinov ( 2014-06-23 06:59:34 -0500 )edit

OK I fixed it. Btw, does opencv pass Mat as ref or value? Function such as cv::resize(src, dst, size) will creates a local copy or does operation on src?

huongnhat gravatar imagehuongnhat ( 2014-06-23 23:49:03 -0500 )edit

You can think of Mat as a pointer. So 'resize' will work on src itself. A rule of thumb: OpenCV will not perform deep copy of data unless you explicitly say so (with functions like 'clone' or 'copyTo'). This include copy constructor and assign operator of Mat.

Michael Burdinov gravatar imageMichael Burdinov ( 2014-06-24 00:27:45 -0500 )edit

And what was the source of the leak?

Michael Burdinov gravatar imageMichael Burdinov ( 2014-06-24 00:28:41 -0500 )edit

Thanks, got it. I fixed by remove all of the CvMat var, and assign dst_ptr = arr0.data The source of leak is the cvConvertImage, it fill data to dst var but never free it

huongnhat gravatar imagehuongnhat ( 2014-06-24 00:52:18 -0500 )edit

Function, GetObject(h, sizeof(bmp), &bmp), allways returns 0. Anybody test?

Compvis gravatar imageCompvis ( 2014-10-06 03:53:37 -0500 )edit
0

answered 2014-11-22 03:02:23 -0500

gudanr gravatar image

i am not sure but i think memory leak is not causing by "cvInitMatHeader" or "cvConvertImage". i encountered same issue with you and i check the upper two function and nothing seems to make leak. so, i check the others, and i found that memory leak causing by under following code.

SelectObject( hdc, CreateDIBSection( hdc, binfo, DIB_RGB_COLORS, &dst_ptr, 0, 0));

because, created bitmap handle by 'CreateDIBSection' must be deleted after use. but there wasn't.

so, i changed like :

HBITMAP hbitmap = CreateDIBSection(hdc, binfo, DIB_RGB_COLORS, &dst_ptr, 0, 0); h = SelectObject(hdc, hbitmap);

......

and call delete and release function, end of this function.

DeleteObject(hbitmap); ::ReleaseDC(hwnd, hdc); // if not call this will causes few bytes leak every call.

after this everything is fine to me. DIB will be changing by StretchDIBits to DDB and that will be stored to display memory. therefore, after call StretchDIBits the hbitmap can be deleted.

edit flag offensive delete link more
Login/Signup to Answer

Question Tools

Stats

Asked: 2014-06-22 22:39:47 -0500

Seen: 862 times

Last updated: Nov 22 '14