接触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;
}
//至此,图像图像显示完成