under some scenario, probably one wants to combine the OpenCV's ability to manipulate pixel values, and the Windows ability to display the resulting image in a native way (for example, utilizing the GDI system). Such a requirement demands some deep understanding of image representation between these two systems, and probably a non-trivial task than just taking for granted.
I came across a same situation as just mentioned above, and after some try-and-error iteration, finally everything seems have been put onto track. since it hasn't undergone thorough test, I am not sure the tracks are all right tracks.
would like to share here for polishing or beneficial for others facing the same situation.
The header file is as follows:
#pragma once
#include <opencv2/core.hpp>
class COpenCVImage
{
public:
COpenCVImage(LPCTSTR lpszFileName);
~COpenCVImage(void);
BOOL GetSize(LPRECT lpRect);
BOOL DrawBitmap(HDC hdc, LPRECT lpRect);
BOOL Sharpen(HWND hWnd);
BOOL Flip(HWND hWnd);
private:
bool createBitmap(HDC hdc, cv::Mat mat);
private:
cv::Mat m_matImage;
HBITMAP m_hBitmap;
bool m_bInit;
};
the implementation file is as follows:
#include "stdafx.h"
#include "OpenCVImage.h"
#include <Shlwapi.h>
// The following header file will be excluded, since we will draw the picture
// to the window all by ourselves
// #include <opencv2/highgui.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
COpenCVImage::COpenCVImage(LPCTSTR lpszFileName) : m_hBitmap(NULL), m_bInit(false)
{
if(!PathFileExists(lpszFileName))
{
MessageBox(NULL, _T("Cannot open the image file"), _T("Error"), MB_OK);
return ;
}
char* pFileName;
UINT nLen;
nLen = WideCharToMultiByte(CP_UTF8, 0, lpszFileName, sizeof(TCHAR) * (_tcslen(lpszFileName) + 1), NULL, 0, NULL, NULL);
pFileName = new char[nLen];
WideCharToMultiByte(CP_UTF8, 0, lpszFileName, sizeof(TCHAR) * (_tcslen(lpszFileName) + 1), pFileName, nLen, NULL, NULL);
m_matImage = cv::imread(pFileName, cv::IMREAD_COLOR);
delete [] pFileName;
if (m_matImage.empty())
{
MessageBox(NULL, _T("Cannot read in the image file"), _T("Error"), MB_OK);
return;
}
m_bInit = true;
}
COpenCVImage::~COpenCVImage(void)
{
if (m_hBitmap)
DeleteObject(m_hBitmap);
}
BOOL COpenCVImage::DrawBitmap(HDC hdc, LPRECT lpRect)
{
if (!m_bInit)
return FALSE;
HDC hDCMem = CreateCompatibleDC(hdc);
if (!m_hBitmap)
{
createBitmap(hDCMem, m_matImage);
}
SelectObject(hDCMem, m_hBitmap);
BitBlt(hdc, lpRect->left, lpRect->top, lpRect->right - lpRect->left,
lpRect->bottom - lpRect->top, hDCMem, 0, 0, SRCCOPY);
DeleteDC(hDCMem);
return TRUE;
}
BOOL COpenCVImage::GetSize(LPRECT lpRect)
{
if (!m_bInit)
{
memset(lpRect, 0, sizeof(RECT));
return FALSE;
}
lpRect->left = 0;
lpRect->top = 0;
lpRect->right = m_matImage.cols;
lpRect->bottom = m_matImage.rows;
return TRUE;
}
BOOL COpenCVImage::Sharpen(HWND hWnd)
{
if (!m_bInit)
return FALSE;
cv::Mat matFiltImage;
cv::Matx33f matKernel;
matKernel(1, 1) = 5.0;
matKernel(0, 1) = - 1.0;
matKernel(1, 0) = - 1.0;
matKernel(1, 2) = - 1.0;
matKernel(2, 1) = - 1.0;
cv::filter2D(m_matImage, matFiltImage, m_matImage.depth(), matKernel);
HDC hDCMem = CreateCompatibleDC(GetDC(hWnd));
createBitmap(hDCMem, matFiltImage);
DeleteObject(hDCMem);
InvalidateRect(hWnd, NULL, TRUE);
return TRUE;
}
BOOL COpenCVImage::Flip(HWND hWnd)
{
if (!m_bInit)
return FALSE;
cv::Mat matFlipImage;
cv::flip(m_matImage, matFlipImage, 1);
HDC hDCMem = CreateCompatibleDC(GetDC(hWnd));
createBitmap(hDCMem, matFlipImage);
DeleteObject(hDCMem);
InvalidateRect(hWnd, NULL, TRUE);
return TRUE;
}
bool COpenCVImage::createBitmap(HDC hdc, cv::Mat mat)
{
unsigned char* lpBitmapBits;
if (m_hBitmap)
DeleteObject(m_hBitmap);
BITMAPINFO bi;
ZeroMemory(&bi, sizeof(BITMAPINFO));
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = m_matImage.cols;
bi.bmiHeader.biHeight = - m_matImage.rows;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = (m_matImage.elemSize() << 3);
bi.bmiHeader.biCompression = BI_RGB;
m_hBitmap = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, (VOID**)&lpBitmapBits, NULL, 0);
#define ALIGN(x,a) __ALIGN_MASK(x, a-1)
#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask))
int width = m_matImage.cols * m_matImage.elemSize();
int pitch = ALIGN(width, 4);
#undef __ALIGN_MASK
#undef ALIGN
for(int i = 0; i < m_matImage.rows; i ++)
{
unsigned char* data = mat.ptr<unsigned char>(i);
memcpy(lpBitmapBits, data, width);
lpBitmapBits += pitch;
}
return true;
}
a typical usage probably be:
1. instantiate the instance, for example:
COpenCVImage image(_T("lenna.jpg"));
2. respond to the WM_PAINT message:
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
RECT rect;
image.GetSize(&rect);
image.DrawBitmap(hdc, &rect);
EndPaint(hWnd, &ps);
break;
the final result is as follows:
appreciate any suggestion to enhance the code