Display a image in a MFC/C++ application using OpenCV
I would like to display in a MFC application, frames that I capture from an avi file with OpenCV (cvCaptureFromAVI
function).
I'm new to MFC but feel like I'm close to making it work. But instead of the frames being displayed in the picture box they are displayed in a new window.
cvGetWindowName
returns always a null value.
There is my code:
CWnd* hPic = 0;
hPic = GetDlgItem(IDC_STATICPIC1);
const char* szWindName = cvGe开发者_JS百科tWindowName(hPic->GetSafeHwnd());
cvShowImage(szWindName, frame_copy);
So I found something to make it work after long researches.
The solution is to create the window and then insert it inside the picture box. I'm not sure it's good practice but I haven't found anything better for now.
cvNamedWindow("IDC_STATIC_OUTPUT", 0);
cvResizeWindow("IDC_STATIC_OUTPUT", 420, 240);
HWND hWnd = (HWND) cvGetWindowHandle("IDC_STATIC_OUTPUT");
HWND hParent = ::GetParent(hWnd);
::SetParent(hWnd, GetDlgItem(IDC_PIC1)->m_hWnd);
::ShowWindow(hParent, SW_HIDE);
cvShowImage("IDC_STATIC_OUTPUT", frame_copy);
In this case the picture box is called IDC_PIC1 and frame_copy is a OpenCV IplImage.
Hope this helps somebody.
Using the following code you can convert Mat to CImage and then display CImage everywhere you want:
int Mat2CImage(Mat *mat, CImage &img){
if(!mat || mat->empty())
return -1;
int nBPP = mat->channels()*8;
img.Create(mat->cols, mat->rows, nBPP);
if(nBPP == 8)
{
static RGBQUAD pRGB[256];
for (int i = 0; i < 256; i++)
pRGB[i].rgbBlue = pRGB[i].rgbGreen = pRGB[i].rgbRed = i;
img.SetColorTable(0, 256, pRGB);
}
uchar* psrc = mat->data;
uchar* pdst = (uchar*) img.GetBits();
int imgPitch = img.GetPitch();
for(int y = 0; y < mat->rows; y++)
{
memcpy(pdst, psrc, mat->cols*mat->channels());//mat->step is incorrect for those images created by roi (sub-images!)
psrc += mat->step;
pdst += imgPitch;
}
return 0;
}
NOTE: If you use the StretchDIBits() method with the BITMAPINFO approach, you MUST be aware that StretchDIBits() expects the raw OpenCV Mat::data pointer to have row lengths in even multiples of 4 bytes! If not, you'll get freaky shearing when you try to copy the data to the DC via StretchDIBits() - where the image is not only sheered along an angle, but the colors are all be trashed as well.
Here is my completely working edition of the code, which also supports maintaining image aspect ratio in the target control's rectangle. It can probably be made a bit faster, but it works for now:
void AdjustAspectImageSize( const Size& imageSize,
const Size& destSize,
Size& newSize )
{
double destAspectRatio = float( destSize.width ) / float( destSize.height );
double imageAspectRatio = float( imageSize.width ) / float( imageSize.height );
if ( imageAspectRatio > destAspectRatio )
{
// Margins on top/bottom
newSize.width = destSize.width;
newSize.height = int( imageSize.height *
( double( destSize.width ) / double( imageSize.width ) ) );
}
else
{
// Margins on left/right
newSize.height = destSize.height;
newSize.width = int( imageSize.width *
( double( destSize.height ) / double( imageSize.height ) ) );
}
}
void DrawPicToHDC( Mat cvImg,
UINT nDlgID,
bool bMaintainAspectRatio /* =true*/ )
{
// Get the HDC handle information from the ID passed
CDC* pDC = GetDlgItem(nDlgID)->GetDC();
HDC hDC = pDC->GetSafeHdc();
CRect rect;
GetDlgItem(nDlgID)->GetClientRect(rect);
Size winSize( rect.right, rect.bottom );
// Calculate the size of the image that
// will fit in the control rectangle.
Size origImageSize( cvImg.cols, cvImg.rows );
Size imageSize;
int offsetX;
int offsetY;
if ( ! bMaintainAspectRatio )
{
// Image should be the same size as the control's rectangle
imageSize = winSize;
}
else
{
Size newSize;
_AdjustAspectImageSize( origImageSize,
winSize,
imageSize );
}
offsetX = ( winSize.width - imageSize.width ) / 2;
offsetY = ( winSize.height - imageSize.height ) / 2;
// Resize the source to the size of the destination image if necessary
Mat cvImgTmp;
resize( cvImg,
cvImgTmp,
imageSize,
0,
0,
INTER_AREA );
// To handle our Mat object of this width, the source rows must
// be even multiples of a DWORD in length to be compatible with
// SetDIBits(). Calculate what the correct byte width of the
// row should be to be compatible with SetDIBits() below.
int stride = ( ( ( ( imageSize.width * 24 ) + 31 ) & ~31 ) >> 3 );
// Allocate a buffer for our DIB bits
uchar* pcDibBits = (uchar*) malloc( imageSize.height * stride );
if ( pcDibBits != NULL )
{
// Copy the raw pixel data over to our dibBits buffer.
// NOTE: Can setup cvImgTmp to add the padding to skip this.
for ( int row = 0; row < cvImgTmp.rows; ++row )
{
// Get pointers to the beginning of the row on both buffers
uchar* pcSrcPixel = cvImgTmp.ptr<uchar>(row);
uchar* pcDstPixel = pcDibBits + ( row * stride );
// We can just use memcpy
memcpy( pcDstPixel,
pcSrcPixel,
stride );
}
// Initialize the BITMAPINFO structure
BITMAPINFO bitInfo;
bitInfo.bmiHeader.biBitCount = 24;
bitInfo.bmiHeader.biWidth = cvImgTmp.cols;
bitInfo.bmiHeader.biHeight = -cvImgTmp.rows;
bitInfo.bmiHeader.biPlanes = 1;
bitInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitInfo.bmiHeader.biCompression = BI_RGB;
bitInfo.bmiHeader.biClrImportant = 0;
bitInfo.bmiHeader.biClrUsed = 0;
bitInfo.bmiHeader.biSizeImage = 0; //winSize.height * winSize.width * * 3;
bitInfo.bmiHeader.biXPelsPerMeter = 0;
bitInfo.bmiHeader.biYPelsPerMeter = 0;
// Add header and OPENCV image's data to the HDC
StretchDIBits( hDC,
offsetX,
offsetY,
cvImgTmp.cols,
cvImgTmp.rows,
0,
0,
cvImgTmp.cols,
cvImgTmp.rows,
pcDibBits,
& bitInfo,
DIB_RGB_COLORS,
SRCCOPY );
free(pcDibBits);
}
ReleaseDC(pDC);
}
int DrawImageToHDC(IplImage* img, HDC hdc, int xDest, int yDest, UINT iUsage, DWORD rop)
char m_chBmpBuf[2048];
BITMAPINFO *m_pBmpInfo = 0;
m_pBmpInfo = (BITMAPINFO*)m_chBmpBuf;
m_pBmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
m_pBmpInfo->bmiHeader.biWidth = img->width;
m_pBmpInfo->bmiHeader.biHeight = -img->height;
m_pBmpInfo->bmiHeader.biBitCount = 24;
m_pBmpInfo->bmiHeader.biPlanes = 1;
m_pBmpInfo->bmiHeader.biCompression = BI_RGB;
m_pBmpInfo->bmiHeader.biSizeImage = 0;
m_pBmpInfo->bmiHeader.biXPelsPerMeter = 0;
m_pBmpInfo->bmiHeader.biYPelsPerMeter = 0;
m_pBmpInfo->bmiHeader.biClrUsed = 0;
m_pBmpInfo->bmiHeader.biClrImportant = 0;
return StretchDIBits(hdc, xDest, yDest, img->width, img->height, 0, 0,
img->width, img->height, img->imageData, m_pBmpInfo, DIB_RGB_COLORS, SRCCOPY);
Usage: DrawImageToHDC(img, pDC->m_hDC, Area.left, Area.top, DIB_RGB_COLORS, SRCCOPY);
精彩评论