Opencv影象在MFC中顯示的幾種方法
阿新 • • 發佈:2019-02-18
接觸opencv後,其強大的影象處理功能令人折服,但是當把處理過程做成軟體釋出的時候就需要將處理結果顯示給使用者看,而我常用的還是MFC。歸納了網上的幾種方式:
1:第一種方法比較優雅取巧。將opencv的視窗貼到MFC的圖片控制元件上去。
2:通過opencv已經丟掉的一個類CvvImage類來將Mat型別的資料顯示在DC上
3:通過手動轉換,將Mat資料轉換到CImage中,然後進行貼圖顯示
首先介紹第一種方法:
CRect rect; GetDlgItem(IDC_PIC)->GetClientRect(rect); cv::namedWindow("myshowWnd", WINDOW_NORMAL); cv::resizeWindow("myshowWnd",rect.Width(),rect.Height()); HWND hWnd = (HWND) cvGetWindowHandle("myshowWnd"); HWND hParent = ::GetParent(hWnd); ::SetParent(hWnd, GetDlgItem(IDC_PIC)->m_hWnd); ::ShowWindow(hParent, SW_HIDE);
看opencv原始碼,namedWindow函式建立了兩個視窗,一個就是用於顯示影象的視窗,另一個是它的父視窗(也就是框架視窗),那麼我要做的就是得到opencv建立的視窗控制代碼hWnd以及它的父視窗控制代碼hParent,然後將hWnd的父視窗設定為MFC中圖片控制元件,然後將hParent隱藏起來,當然貼上視窗後別忘記將視窗調整到和圖片控制元件一樣大哦···
剩下的就簡單啦,顯示影象直接呼叫opencv的imshow("myshowWnd",image);就好啦。
第二中方法:將CvvImage類的原始檔和標頭檔案加入到工程中,然後使用其中的DrawToHDC即可。具體如下:
void CPODtestDlg::ShowMat(cv::Mat& image,int IDC) { CDC* pDC=GetDlgItem(IDC)->GetDC(); //根據ID獲得視窗指標再獲取與該視窗關聯的上下文指標 HDC hDC=pDC->GetSafeHdc(); // 獲取裝置上下文控制代碼 CRect rect; GetDlgItem(IDC)->GetClientRect(rect); //獲取顯示區 CRect adjRect = Adjest(rect.Width(),rect.Height(),image.cols,image.rows); Mat cimage(adjRect.Width(),adjRect.Height(),CV_8UC3); cvResize(&(image.operator IplImage()),&(cimage.operator IplImage()),CV_INTER_LINEAR); IplImage* img=&cimage.operator IplImage(); //將影象轉換為IplImage格式,共用同一個記憶體(淺拷貝) CvvImage iimg; //建立一個CvvImage物件 iimg.CopyOf(img); iimg.DrawToHDC(hDC,&adjRect); ReleaseDC( pDC ); iimg.Destroy(); }
此函式實現了將Mat型別的影象資料顯示到指定ID的控制元件上。具體說明參看註釋即可。至於CvvImage類我也放在這裡供大家使用。
// CvvImage.h #pragma once #ifndef CVVIMAGE_CLASS_DEF #define CVVIMAGE_CLASS_DEF #include "opencv2/highgui/highgui.hpp" CV_INLINE RECT NormalizeRect( RECT r ); CV_INLINE CvRect RectToCvRect( RECT sr ); CV_INLINE RECT CvRectToRect( CvRect sr ); CV_INLINE IplROI RectToROI( RECT r ); class CvvImage{ public: CvvImage(); virtual ~CvvImage(); virtual bool Create( int width, int height, int bits_per_pixel, int image_origin = 0 ); virtual bool Load( const char* filename, int desired_color = 1 ); virtual bool LoadRect( const char* filename, int desired_color, CvRect r ); #if defined WIN32 || defined _WIN32 virtual bool LoadRect( const char* filename, int desired_color, RECT r ) { return LoadRect( filename, desired_color, cvRect( r.left, r.top, r.right - r.left, r.bottom - r.top )); } #endif virtual bool Save( const char* filename ); virtual void CopyOf( CvvImage& image, int desired_color = -1 ); virtual void CopyOf( IplImage* img, int desired_color = -1 ); IplImage* GetImage() { return m_img; }; virtual void Destroy(void); int Width() { return !m_img ? 0 : !m_img->roi ? m_img->width : m_img->roi->width; }; int Height() { return !m_img ? 0 : !m_img->roi ? m_img->height : m_img->roi->height;}; int Bpp() { return m_img ? (m_img->depth & 255)*m_img->nChannels : 0; }; virtual void Fill( int color ); virtual void Show( const char* window ); #if defined WIN32 || defined _WIN32 virtual void Show( HDC dc, int x, int y, int width, int height, int from_x = 0, int from_y = 0 ); virtual void DrawToHDC( HDC hDCDst, RECT* pDstRect ); #endif protected: IplImage* m_img; }; //typedef CvvImage CImage; namespace cv { typedef CvvImage CImage; } #endif
cpp檔案如下
//CvvImage.cpp
#include "StdAfx.h"
#include "CvvImage.h"
// Construction/Destruction
CV_INLINE RECT NormalizeRect( RECT r ) {
int t;
if( r.left > r.right ){
t = r.left;
r.left = r.right;
r.right = t;
}
if( r.top > r.bottom ) {
t = r.top;
r.top = r.bottom;
r.bottom = t;
}
return r;
}
CV_INLINE CvRect RectToCvRect( RECT sr ) {
sr = NormalizeRect( sr );
return cvRect( sr.left, sr.top, sr.right - sr.left, sr.bottom - sr.top );
}
CV_INLINE RECT CvRectToRect( CvRect sr ) {
RECT dr;
dr.left = sr.x;
dr.top = sr.y;
dr.right = sr.x + sr.width;
dr.bottom = sr.y + sr.height;
return dr;
}
CV_INLINE IplROI RectToROI( RECT r ) {
IplROI roi;
r = NormalizeRect( r );
roi.xOffset = r.left;
roi.yOffset = r.top;
roi.width = r.right - r.left;
roi.height = r.bottom - r.top;
roi.coi = 0;
return roi;
}
void 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;
}
}
}
CvvImage::CvvImage() { m_img = 0; }
void CvvImage::Destroy() { cvReleaseImage( &m_img ); }
CvvImage::~CvvImage() { Destroy(); }
bool CvvImage::Create( int w, int h, int bpp, int origin ) {
const unsigned max_img_size = 10000;
if( (bpp != 8 && bpp != 24 && bpp != 32) || (unsigned)w >= max_img_size || (unsigned)h >= max_img_size ||
(origin != IPL_ORIGIN_TL && origin != IPL_ORIGIN_BL)) {
assert(0); // most probably, it is a programming error
return false;
}
if( !m_img || Bpp() != bpp || m_img->width != w || m_img->height != h ) {
if( m_img && m_img->nSize == sizeof(IplImage))
Destroy();
m_img = cvCreateImage( cvSize( w, h ), IPL_DEPTH_8U, bpp/8 );
}
if( m_img )
m_img->origin = origin == 0 ? IPL_ORIGIN_TL : IPL_ORIGIN_BL;
return m_img != 0;
}
void CvvImage::CopyOf( CvvImage& image, int desired_color ) {
IplImage* img = image.GetImage();
if( img ) {
CopyOf( img, desired_color );
}
}
#define HG_IS_IMAGE(img) \
((img) != 0 && ((const IplImage*)(img))->nSize == sizeof(IplImage) && \
((IplImage*)img)->imageData != 0)
void CvvImage::CopyOf( IplImage* img, int desired_color ){
if( HG_IS_IMAGE(img) ) {
int color = desired_color;
CvSize size = cvGetSize( img );
if( color < 0 )
color = img->nChannels > 1;
if( Create( size.width, size.height, (!color ? 1 : img->nChannels > 1 ? img->nChannels : 3)*8, img->origin )) {
cvConvertImage( img, m_img, 0 );
}
}
}
bool CvvImage::Load( const char* filename, int desired_color ){
IplImage* img = cvLoadImage( filename, desired_color );
if( !img ) return false;
CopyOf( img, desired_color );
cvReleaseImage( &img );
return true;
}
bool CvvImage::LoadRect( const char* filename, int desired_color, CvRect r ){
if( r.width < 0 || r.height < 0 ) return false;
IplImage* img = cvLoadImage( filename, desired_color );
if( !img ) return false;
if( r.width == 0 || r.height == 0 ) {
r.width = img->width;
r.height = img->height;
r.x = r.y = 0;
}
if( r.x > img->width || r.y > img->height || r.x + r.width < 0 || r.y + r.height < 0 ) {
cvReleaseImage( &img );
return false;
}
if( r.x < 0 ) {
r.width += r.x;
r.x = 0;
}
if( r.y < 0 ) {
r.height += r.y;
r.y = 0;
}
if( r.x + r.width > img->width ) r.width = img->width - r.x;
if( r.y + r.height > img->height ) r.height = img->height - r.y;
cvSetImageROI( img, r );
CopyOf( img, desired_color );
cvReleaseImage( &img );
return true;
}
bool CvvImage::Save( const char* filename ){
if( !m_img ) return false;
cvSaveImage( filename, m_img );
return true;
}
void CvvImage::Show( const char* window ){
if( m_img ) cvShowImage( window, m_img );
}
void CvvImage::Show( HDC dc, int x, int y, int w, int h, int from_x, int from_y ){
if( m_img && m_img->depth == IPL_DEPTH_8U ) {
uchar buffer[sizeof(BITMAPINFOHEADER) + 1024];
BITMAPINFO* bmi = (BITMAPINFO*)buffer;
int bmp_w = m_img->width, bmp_h = m_img->height;
FillBitmapInfo( bmi, bmp_w, bmp_h, Bpp(), m_img->origin );
from_x = MIN( MAX( from_x, 0 ), bmp_w - 1 );
from_y = MIN( MAX( from_y, 0 ), bmp_h - 1 );
int sw = MAX( MIN( bmp_w - from_x, w ), 0 );
int sh = MAX( MIN( bmp_h - from_y, h ), 0 );
SetDIBitsToDevice(
dc, x, y, sw, sh, from_x, from_y, from_y, sh,
m_img->imageData + from_y*m_img->widthStep,
bmi, DIB_RGB_COLORS );
}
}
void CvvImage::DrawToHDC( HDC hDCDst, RECT* pDstRect ){
if( pDstRect && m_img && m_img->depth == IPL_DEPTH_8U && m_img->imageData ) {
uchar buffer[sizeof(BITMAPINFOHEADER) + 1024];
BITMAPINFO* bmi = (BITMAPINFO*)buffer;
int bmp_w = m_img->width, bmp_h = m_img->height;
CvRect roi = cvGetImageROI( m_img );
CvRect dst = RectToCvRect( *pDstRect );
if( roi.width == dst.width && roi.height == dst.height ) {
Show( hDCDst, dst.x, dst.y, dst.width, dst.height, roi.x, roi.y );
return;
}
if( roi.width > dst.width ) {
SetStretchBltMode( hDCDst, HALFTONE ); // handle to device context
}
else {
SetStretchBltMode(hDCDst, COLORONCOLOR );
}
FillBitmapInfo( bmi, bmp_w, bmp_h, Bpp(), m_img->origin );
::StretchDIBits(
hDCDst,
dst.x, dst.y, dst.width, dst.height,
roi.x, roi.y, roi.width, roi.height,
m_img->imageData, bmi, DIB_RGB_COLORS, SRCCOPY );
}
}
void CvvImage::Fill( int color ){
cvSet( m_img, cvScalar(color&255,(color>>8)&255,(color>>16)&255,(color>>24)&255) );
}
方法3:自己轉換Mat格式並顯示,利用到了CImage這個類,轉換過程是cv::Mat---->CImage---->CStatic
//首先需要定義幾個變數
cv::Mat m_matCVImage //要顯示的Mat資料
CStatic m_staticImage //用於顯示的靜態控制元件變數
cv::Size m_sizeShow //視窗的大小
CImage* m_pImg //用於轉換的CImage指標
//1、獲取靜態控制元件的視窗大小
RECT r;
m_staticImage.GetClientRect(&r);
m_sizeShow = cv::Size(r.right, r.bottom);
//2.將Mat型別的圖片縮放到與控制元件相同的比例
cv::Mat matImgTmp;
if (m_matCVImg.size() != m_sizeShow)
{
matImgTmp = cv::Mat(m_sizeShow, CV_8UC3);
cv::resize(m_matCVImg, matImgTmp, m_sizeShow, 0, 0, cv::INTER_AREA);
}
else {
matImgTmp = m_matCVImg.clone();
}
//3.垂直反轉影象
cv::flip(matImgTmp, matImgTmp, 0);
//4. 建立一個MFC的CImage物件
if (m_pImg)
{
m_pImg->ReleaseDC();
delete m_pImg;
m_pImg = NULL;
}
m_pImg = new CImage;
m_pImg->Create(m_sizeShow.width, m_sizeShow.height, 24);
//5. 還需要為CImage建立一個資訊頭,使用 BITMAPINFO 結構體
BITMAPINFO bitInfo;
bitInfo.bmiHeader.biBitCount = 24;
bitInfo.bmiHeader.biWidth = m_sizeShow.width;
bitInfo.bmiHeader.biHeight = m_sizeShow.height;
bitInfo.bmiHeader.biPlanes = 1;
bitInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitInfo.bmiHeader.biCompression = BI_RGB;
bitInfo.bmiHeader.biClrImportant =
bitInfo.bmiHeader.biClrUsed =
bitInfo.bmiHeader.biSizeImage =
bitInfo.bmiHeader.biXPelsPerMeter =
bitInfo.bmiHeader.biYPelsPerMeter = 0;
//6.給m_pImg增加頭並且將Mat資料關聯到該物件上
StretchDIBits(m_pImg->GetDC(), 0, 0,
m_sizeShow.width, m_sizeShow.height, 0, 0,
m_sizeShow.width, m_sizeShow.height,
matImgTmp.data, &bitInfo, DIB_RGB_COLORS, SRCCOPY);
//7. 在MFC視窗中顯示影象
m_pImg->BitBlt(::GetDC(m_staticImage.m_hWnd), 0, 0);
//8. 如果不在使用m_pImg,請及時釋放
if (m_pImg)
{
m_pImg->ReleaseDC();
delete m_pImg;
m_pImg = NULL;
}
//至此,影象影象顯示完成