MFC中實現簡單的位圖顯示、處理

 

最近在做圖像採集的工作,需要處理圖像數據,所以學習了一下位圖顯示,而且只看了設備相關位圖DDB。基本上實現了位圖的顯示、位圖數據的處理等功能。這裏就記錄一下我自己的理解,不一定全都對,僅供參考而已。

 

要顯示位圖,需要做如下工作:

 

CStatic* pStatic=(CStatic*)GetDlgItem(IDC_DISPLAY2);

CDC* pDC=pStatic->GetDC();

CBitmap bitmap;

bitmap.LoadBitmap(IDB_BITMAP2);

BITMAP bmp;

bitmap.GetBitmap(&bmp);

CDC dcCompatible;

dcCompatible.CreateCompatibleDC(pDC);

 

dcCompatible.SelectObject(&bitmap);

CRect rect;

pStatic->GetClientRect(&rect);

 

pDC->StretchBlt(0,0,rect.Width(),rect.Height(), &dcCompatible,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);

 

程序中,創建了一個位圖對象bitmap,bitmap.LoadBitmap(IDB_BITMAP2);將一幅位圖加載到了bitmap這個對象中,按我的理解,這個對象是存在在內存中的,所以我們對這個對象的操作並不會對原來那幅位圖有什麼影響。另外,還定義了一個BITMAP結構:bmp。之所以定義這個結構,是爲了獲得位圖的寬度、高度等圖像信息,如果我們知道加載的位圖的高度、寬度、一個像素佔多少字節等信息,那麼我們無需定義這個bmp結構,不過爲了方便,還是定義一個的好。要顯示位圖,還要創建與當前DC兼容的DC,當前DC怎麼獲得?就是下面這兩句:

 

CStatic* pStatic=(CStatic*)GetDlgItem(IDC_DISPLAY2);

CDC* pDC=pStatic->GetDC();

 

我要將位圖在對話框中的靜態文本控件中顯示,因此定義了一個指向靜態CStatic對象的指針,這樣,當前DC就需要使用pStatic->GetDC();來獲取。然後創建一個與當前DC兼容的DC: dcCompatible.CreateCompatibleDC(pDC);。這個DC創建了之後,就把內存中的位圖對象bitmap選入這個DC:dcCompatible.SelectObject(&bitmap);,從而確定這個兼容DC的顯示錶面的大小。這裏要搞清楚一個關係。做了以上各步驟之後,其實與那個位圖對象已經沒多少關係了,bitmap這個位圖對象的相關信息已經在dcCompatible這個保存着了,而要顯示的目標區域則由當前DC:pDC所指示出來,這也就是爲什麼要創建兼容DC,只有兩個DC兼容,才能順利的把圖像從dcCompatible複製到pDC中進行顯示。我們看到最後的那句

 

pDC->StretchBlt(0,0,rect.Width(),rect.Height(), &dcCompatible,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);

 

實際上就是起到將dcCompatible中的圖像在pDC的區域上顯示的作用。這句話中還有有一個參數rect,它是一個CRect對象,用來得到客戶區的大小,也就是我們所要顯示的區域的大小,在這裏也就是靜態文本框,一個矩形的區域,所以調用pStatic的成員函數GetClientRect()來獲取客戶區域。

 

這樣,一幅位圖就顯示了出來。上述這段程序是對話框中的一個按鈕的響應函數。我之前看到有人說,要把畫圖程序放在OnDraw或者OnPaint之類的函數中去。我覺得其實放在哪裏都無所謂,只要知道了要顯示位圖需要計算機做什麼工作,就能順利將位圖顯示出來。

 

 

學會顯示位圖之後,就得準備處理位圖。做圖像採集的時候,獲得的是一個指向內存中一塊區域的指針pData,這塊區域中存儲着圖像的灰度值。而如果要處理加載的位圖,也需要獲取其位圖數據的指針。因此兩者的處理方法是差不多的。這裏就以處理加載的位圖爲例進行說明。

 

要獲取位圖數據的指針,可以使用GetBitmapBits(),而要將處理後的數據COPY到位圖對象中,則使用SetBitmapBits(),具體的用法可以查閱MSDN。我加載的位圖是256色的,它的一個像素數據佔4個字節,前三個字節分別表示RGB,第四個字節爲保留字節。因此,如果這幅位圖寬度是1000個像素,那麼其字節寬度就是4000個字節。一般的圖像處理都是針對灰度圖像,也就是說一個像素的RGB值是相等的,從黑色(0,0,0)到白色(255,255,255)變化。因此,我處理圖像的時候,只需要處理它的像素數據的代表R的字節,然後代表G和B的字節就都等於R的值就可以了。下面是代碼:

 

BYTE *pmydata;    //定義一個指針用來指向位圖圖像數據在內存中的存儲區域

pmydata=new BYTE[bmp.bmWidthBytes*bmp.bmHeight];   //根據位圖的高度寬度初始化一下

bitmap.GetBitmapBits(bmp.bmWidthBytes*bmp.bmHeight,pmydata); //將位圖對象的數據COPY到pmydata指向的區域,bitmap是位圖對象,bmp是位圖結構,可參考上一篇文章的定義

for(int i=0;i<bmp.bmWidthBytes*bmp.bmHeight;i+=4) //這裏只是將RGB三個字節的值取平均值,再取個反

{                                                                    //因爲一個像素有4個字節,故i+=4

   BYTE temp=0;

   temp=(pmydata[i]+pmydata[i+1]+pmydata[i+2])/3;

   pmydata[i]=255-temp;//R值

   pmydata[i+1]=255-temp;//G值

   pmydata[i+2]=255-temp;//B值

}

 

這樣就實現了圖像數據的簡單處理,然後將處理的數據COPY回位圖對象:

 

bitmap.SetBitmapBits(bmp.bmWidthBytes*bmp.bmHeight,pmydata); //將處理後的數據COPY進位圖對象

 

隨後按照上一篇文章的步驟顯示位圖即可。

 

另外要說一下,對於圖像採集上來的灰度值數據,一個像素只有一個字節,而上面顯示的圖像的像素數據則是4個字節,這應該怎麼來轉換一下呢?我用的方法是定義一個COLORREF數組:

 

COLORREF* m_ColorData=new COLORREF[width*height];

 

這個COLORREF實際上就是DWORD,上面這句話定義了一個指向一塊存放DWORD類型的數據的指針,也就是說,m_ColorData[i]與m_ColorData[i+1]之間有4個字節的距離,這正好與256色圖像的像素數據存放方式相對應。將灰度值數據處理好之後,轉化成RGB值:

 

//將灰度值轉換爲RGB值進行顯示

   for(i=0;i<width*height;i++)  

    m_ColorData[i]=RGB( m_bytes[i], m_bytes[i], m_bytes[i]);

 

其中的m_bytes[i]就是灰度值,上面這句話的意思是將m_ColorData[i]的四個字節中的前三個字節賦值爲:m_bytes[i], m_bytes[i], m_bytes[i]。然後建立一個位圖對象bitmap,使用SetBitmapBits()函數設置它的圖像數據的值。

 

bitmap.SetBitmapBits(width*height*sizeof(COLORREF),m_ColorData);。然後按照前面說的步驟將這個位圖對象顯示處理即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章