OpenCV學習心得(3) -- 在MFC上顯示圖像

原创 2012年03月21日 19:05:33

轉載請注明出處和作者

在MFC上顯示圖像最重要的部份就是將數據轉換為BitMap格式.一開始我一直在找如果將cv::Mat中的數據直接顯示在MFC上.結果找了很久也沒找到.最后發現如果行的字節數不是4的倍數,顯示的圖像就一定會是傾斜并失真的...所以如果行的字節數不是4的倍數就一定會發生數據拷貝(因為要準備一個行是4的倍數的容器來裝這些數據)...看來想省事是不可能了..

所以在MFC上顯示圖像的要點就是將行的字節數調整為4的倍數.

還有一點需要注意的就是cv::Mat是以左上角為頂點,而BITMAP是以左下角為頂點.所以顯示的時候需要將數據進行翻轉.

我這裡採用了一個省事的辦法,

對於4 channel的圖,因為是4個channel,所以一定是4的倍數,直接翻轉并顯示.

對於2,3 channel的圖,可能不是4的倍數,將其轉換為4channel,然後翻轉并顯示.

對於1channel的圖,先申請一個行的字節數是4的倍數的空間,進行像素點拷貝,最後翻轉并顯示.這個方法會在圖像的最右邊產生一個邊界.如果不想要邊界的話其實也可以使用轉換為4channel的方式.但是這個時候就會產生一個問題,那就是這1個channel的圖像要顯示成灰度圖呢還是紅/綠/藍單色的通道圖呢?如果是灰度圖的話就要將這個單channel的數據拷貝3分,分別放到0~2這三個channel上. 如果是紅/綠/藍單色的通道圖就只需要將這個channel的數據拷貝到對應的channel中即可,至於怎麼拷貝,可以參看cv::mixChannels函數.

順便說明一下,默認情況下4channel的顏色順序是BGRA(B=藍色,G=綠色,R=紅色,A=透明度)


至於在MFC上顯示就是先準備BITMAP信息頭,然後調用StretchDIBits函數將之前準備好的4channel數據進行繪圖.

public: //HDC Draw
	//!在HDC上畫圖.如果m_src有更改,則先準備m_draw,再進行畫圖,否則直接畫m_draw中的圖像.
	//!這個函數會將src_r範圍內的圖像經過伸縮后顯示為dest_r大小的圖像.
	//!src_r為源圖像中要顯示的範圍, dest_r為顯示圖像的畫布的大小(也就是HDC的rect),filltype為調色板填充類型(只有為單通道(256色圖像)的時候才起作用)
	void Draw2HDC(HDC dc,const cv::Rect& src_r,const cv::Rect& dest_r,RGBQUAD_FILLTYPE filltype=RGBQUAD_FILLTYPE_GRAY);
	//!將整個圖片縮放到dest_r大小后顯示在HDC上.
	void Draw2HDC(HDC dc,const cv::Rect& dest_r);
	//!將原始圖像m_src轉換為Windows可以顯示的數據并保存到m_draw中,以便接下來的顯示動作
	//!只有當m_modifyed_for_draw為true的時候才進行轉換,轉換完成后將m_modifyed_for_draw設為false.
	bool PrepareDrawData();

bool COpenCVImage::PrepareDrawData()
{
	if(m_modifyed_for_draw)
	{
		
		m_modifyed_for_draw=false;
		if(m_src.empty()) return false;

		cv::Mat m_src_tmp;
		if(m_src.depth()!=CV_8U)
		{
			m_src.convertTo(m_src_tmp,CV_8U);
		}
		else
		{
			m_src_tmp=m_src;
		}
		switch(m_src_tmp.channels())
		{
			case 4: //4channel直接翻轉.
			{
				//m_draw=m_src_tmp;
				cv::flip(m_src_tmp,m_src_tmp,0);
				m_draw=m_src_tmp;
				break;
			}
			case 1:	//1channel,
			{
				cv::Mat tmp(m_src.rows,(m_src_tmp.cols+3)/4*4,CV_8UC1);//申請4byte字節倍數的空間.
				for(int i=0;i<m_src_tmp.cols;++i)//數據拷貝.
					for(int j=0;j<m_src_tmp.rows;++j)
					{
						tmp.at<uchar>(j,i)= m_src_tmp.at<uchar>(j,i);
					}
				cv::flip(tmp,tmp,0);
				m_draw=tmp;
				break;
			}

			case 3:	//3channel
			{
				cv::Mat tmp;
				cv::cvtColor(m_src,tmp,CV_BGR2BGRA);
				cv::flip(tmp,tmp,0);
				m_draw=tmp;
				break;
			}
			default://channels=2,3
			{
				cv::Mat tmp(m_src.rows,m_src.cols,CV_8UC4);
				int *fromto=new int[m_src_tmp.channels()*2];
				for(int i=0;i<m_src_tmp.channels();++i)
				{
					fromto[i*2]=fromto[i*2+1]=i;
				}
				cv::mixChannels(&m_src_tmp,1,&tmp,1,fromto,m_src_tmp.channels());
				cv::flip(tmp,tmp,0);
				m_draw=tmp;
				if(fromto) delete [] fromto;
				
			}
			
		}
		
	}
	return true;
}
void COpenCVImage::Draw2HDC(HDC dc,const cv::Rect& src_r,const cv::Rect& dest_r,RGBQUAD_FILLTYPE filltype)
{
	if(!PrepareDrawData()) return;
	if(m_draw.depth()==CV_8U && (!m_draw.empty()))
	{
		uchar *buffer=NULL;
		
		if(m_draw.channels()>1)
		{
			buffer=new uchar[sizeof(BITMAPINFOHEADER)];
		}
		else
		{
			buffer=new uchar[sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD)];
			Fill_RGBQUAD((BITMAPINFO*)buffer,filltype);
		}
		Fill_BITMAPINFOHEADER((BITMAPINFOHEADER*)buffer,m_draw.cols,m_draw.rows,m_draw.elemSize1()* 8*m_draw.channels());
		SetStretchBltMode(dc, COLORONCOLOR ); 
		::StretchDIBits(  dc,  
         dest_r.x, dest_r.y, dest_r.width, dest_r.height,  
		 src_r.x, src_r.y, src_r.width, src_r.height,  
		 m_draw.data, (BITMAPINFO*)buffer, DIB_RGB_COLORS, SRCCOPY ); 
		if(buffer) delete [] buffer;
	}
}
void COpenCVImage::Draw2HDC(HDC dc,const cv::Rect& dest_r)
{
	Draw2HDC(dc,cv::Rect(0,0,m_src.cols,m_src.rows),dest_r,m_rgbQuad_filltype);
}

這裡需要說明一下:

cv::flip(m_src_tmp,m_src_tmp,0); 翻轉圖像.
這個函數的第三個參數是翻轉方式,

等於0: 延x軸上下翻轉, 

大於0為延y軸左右翻轉,

小於0為同時延x,y軸翻轉(效果等同于旋轉180度)

此函數可以進行In-Place操作.


在處理3channel圖像的時候使用了一個技巧,就是cv::cvtColor函數,

cv::cvtColor(m_src,tmp,CV_BGR2BGRA); 這個函數可以進行不同色彩空間的轉換.

第三個參數是轉換類型,使用預定義的宏,格式類似于CV_源空間格式2新空間格式.

第四個參數是轉換后的channel數.默認情況下是0,即根據源格式和新格式自動確定.

這裡使用這個函數直接將cv::Mat中的BGR 3channel格式轉換為BGRA的4channel格式.


default中的處理方式也可以適用於2,3,4channel的情況.其中主要使用的函數就是cv::mixChannels

void mixChannels(const Mat* src, int nsrc, Mat* dst, int ndst, const int* fromTo, size_t npairs)

這個函數可以將源Mat中的某些channel的數據按指定的方式拷貝到目的Mat中.

src參數為源Mat數組,

nsrc參數為源Mat數組中有多少個Mat.

dst為目的Mat數組,

ndst參數為目的Mat數組中有多少個Mat.

fromTo參數是一個int型指針,也就是一個int型的數組.它表明yuanMat中的channel要拷貝到目的Mat中的那些channel中.這個參數是2個一組的,即fromTo[0]和fromTo[1]為一組,表明從源Mat中的第fromTo[0]個channel拷貝到目的Mat中的第fromTo[1]個channel中. fromTo[2]和fromTo[3]為一組..依次類推.

npairs參數表明fromTo數組中一共有多少對(注意:不是多少個)數據.

src與dst都是Mat數組..那么當src與dst中不止一個Mat的時候channel的index是怎麼計算的呢?很簡單,就是向後延續,例如src中有3個mat, Mat1有2個channel,Mat2有3個channel,Mat3有2個channel,則Mat1的channel index分別為 0,1 ,Mat2的channel index分別為 2,3,4,Mat3的channel index分別為 5,6



相关文章推荐

OpenCV學習心得(6) -- 圖像的縮放

轉載請著名出處與作者 OpenCV提供了一個實現圖像縮放的函數:cv::resize void resize(InputArray src, OutputArray dst, Size dsize...

OpenCV學習心得(5) -- 圖像通道分離

轉載請注明出處與作者 OpenCV提供了一個函數可以進行通道的分離,就是 cv::split. void split(const Mat& mtx, vector& mv); void sp...

opencv学习心得(四)——opencv提取截获图像

首先确定选区区域,这里利用鼠标选取区域具体操作看opencv学习心得(一)。 下面就是从感兴趣区域选取图像。 第一:创建图像空间,大小与rect相同。 第二:利用setroi选取区域。 第三:...

OpenCV學習心得(8) -- 直方圖的顯示

轉載請注明出處與作者 這裡只討論1維的直方圖 void calcHist(const Mat* arrays, int narrays, const int* channels, InputArr...

opencv学习心得(一)鼠标响应事件绘制轮廓

//#include "gaosi.h" #include #include #include using namespace cv; using namespace std; //声明全局变...

OpenCV學習筆記(9)利用MFC的Picture控件顯示圖像+播放視頻和捕獲攝像頭畫面

http://blog.csdn.net/chenyusiyuan/article/details/4744097 原始版本下載頁面(僅顯示圖像):  http://download.cs...
  • rocky69
  • rocky69
  • 2012年07月08日 15:19
  • 3698

opencv学习——在MFC中读取和显示图像

在MFC中,使用opencv
  • shawpan
  • shawpan
  • 2014年05月01日 16:07
  • 2027

OpenCV学习 利用MFC的Picture控件显示图像+播放视频和捕获摄像头画面

原始版本下载页面(仅显示图像):  http://download.csdn.net/source/1779188   P.S.:后面又添加了播放视频和捕获摄像头画面的功能,其中播放视频...

c++和opencv学习心得

1.表达式a^2中,a必须为int型的,而pow(a,2)中a可以是float型的;2.sqrt(b),b在c++下为float或double long型,在c下一般为double型;(msdn:C+...
  • ycx2006
  • ycx2006
  • 2011年03月10日 21:44
  • 733

OpenCV學習心得(11) -- Mat類型與Arraya

轉載請注明出處與作者 下面對Mat矩陣進行一些說明: 1. cv::Mat中的數據不需要釋放.cv::Mat會自動釋放. 例如,cv::Mat m=cv::imread("123.jpg")...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:OpenCV學習心得(3) -- 在MFC上顯示圖像
举报原因:
原因补充:

(最多只允许输入30个字)