'C Win32 API display Opencv Image cv::Mat + Resizing, results in distorted, wrong colored images

So I've been looking for some simple code, on how I can display opencv images in a windows window, without using extra libraries. Most solutions that I have found use the StretchDIBits() method of the Windows API. This works fine, until I resize the window, although I call my display function after WM_SIZE and resize my image with opencv before I display it with StretchDIBits(). Sometimes the image is displayed fine, but most of the time it's distorted or even in greyscale, even though it is a RGB image.

The image resizing with opencv works fine. When I display the resized image with cv::imshow("test", imgResized); the image is displayed as expected in the same size as the resized window.

This is an image at application launch (works fine)

This is an image at application launch (works fine)

This is an image after resizing the window (distorted and greyscaled)

This is an image after resizing the window (distorted and greyscaled)

My Code

VOID ImgviewShowImg(IMGVIEW* imgView)
{
    if(imgView->img == NULLPTR)
    {
        return;
    }

    cv::Mat imgResized; 
    POSZ inner  = GetInnerPosSize(imgView);
    INT width  = inner.w;
    INT height = inner.h;

    cv::resize(*imgView->img, imgResized, cv::Size(width, height), 0, 0, cv::INTER_CUBIC);
    
    INT bytesPerPixel = imgResized.elemSize();

    BITMAPINFO bmpInfo;
    memset(&bmpInfo, 0, sizeof(bmpInfo));
    bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmpInfo.bmiHeader.biWidth  = width;
    bmpInfo.bmiHeader.biHeight = height;
    bmpInfo.bmiHeader.biPlanes   = 1;
    bmpInfo.bmiHeader.biBitCount = (bytesPerPixel * 8);
    bmpInfo.bmiHeader.biCompression = BI_RGB;
    bmpInfo.bmiHeader.biXPelsPerMeter = 100;
    bmpInfo.bmiHeader.biYPelsPerMeter = 100;

    PAINTSTRUCT paintStruct;
    memset(&paintStruct, 0, sizeof(paintStruct));

    HDC hdc = BeginPaint(imgView->hWnd, &paintStruct);

    INT result = StretchDIBits(hdc, // HDC              hdc,
        0,                          // int              xDest,
        0,                          // int              yDest,
        width,                      // int              DestWidth,
        height,                     // int              DestHeight,
        0,                          // int              xSrc,
        0,                          // int              ySrc,
        width,                      // int              SrcWidth,
        height,                     // int              SrcHeight,
        imgResized.data,            // const            VOID       *lpBits,
        &bmpInfo,                   // const            BITMAPINFO *lpbmi,
        DIB_RGB_COLORS,             // UINT             iUsage,
        SRCCOPY                     // DWORD            rop
    );

    EndPaint(imgView->hWnd, &paintStruct);
}


Solution 1:[1]

Thanks to the anwser of @IInspectable and a quick google search where I found this https://gist.github.com/AhiyaHiya/6e455a3a6c846766f1017044131bfab7 with the simple solution resize(frame, frame, Size(frame.cols / 4 * 4, frame.rows));

I finally got my code to work and it now looks like this:

VOID ImgviewShowImg(IMGVIEW* imgView)
{
    if(imgView->img == NULLPTR)
    {
        return;
    }

    RECT clientRect;
    GetClientRect(imgView->hWnd, &clientRect);

    INT wndWidth  = clientRect.right - clientRect.left;
    INT wndHeight = clientRect.bottom - clientRect.top;
    FLOAT32 aspectRatio = (FLOAT32) wndHeight / (FLOAT32) wndWidth;

    INT imgWidth  = (INT) ((wndWidth / 4) * 4);
    INT imgHeight = (INT) (imgWidth * aspectRatio);

    cv::Mat imgResized; 
    cv::resize(*imgView->img, imgResized, cv::Size(imgWidth, imgHeight), 0, 0, cv::INTER_CUBIC);
    
    INT bytesPerPixel = imgResized.elemSize();

    BITMAPINFO bmpInfo;
    memset(&bmpInfo, 0, sizeof(bmpInfo));
    bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmpInfo.bmiHeader.biWidth  = imgWidth;
    bmpInfo.bmiHeader.biHeight = imgHeight;
    bmpInfo.bmiHeader.biPlanes   = 1;
    bmpInfo.bmiHeader.biBitCount = (bytesPerPixel * 8);
    bmpInfo.bmiHeader.biCompression = BI_RGB;
    bmpInfo.bmiHeader.biXPelsPerMeter = 100;
    bmpInfo.bmiHeader.biYPelsPerMeter = 100;

    PAINTSTRUCT paintStruct;
    memset(&paintStruct, 0, sizeof(paintStruct));

    HDC hdc = BeginPaint(imgView->hWnd, &paintStruct);

    INT result = StretchDIBits(hdc, // HDC              hdc,
        0,                          // int              xDest,
        0,                          // int              yDest,
        wndWidth,                      // int              DestWidth,
        wndHeight,                     // int              DestHeight,
        0,                          // int              xSrc,
        0,                          // int              ySrc,
        imgWidth,                      // int              SrcWidth,
        imgHeight,                     // int              SrcHeight,
        imgResized.data,            // const            VOID       *lpBits,
        &bmpInfo,                   // const            BITMAPINFO *lpbmi,
        DIB_RGB_COLORS,             // UINT             iUsage,
        SRCCOPY                     // DWORD            rop
    );

    EndPaint(imgView->hWnd, &paintStruct);
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ambadrant