【數字影象處理】三.MFC實現影象灰度、取樣和量化功能詳解
個人認為對初學者VC++6.0可能還是很值得學習的工具,所以採用它來講解,而不是VS或C#。同時文章比較詳細基礎,希望該篇文章對你有所幫助~
【數字影象處理】一.MFC詳解顯示BMP格式圖片
【數字影象處理】二.MFC單文件分割視窗顯示圖片
免費資源下載地址:
http://download.csdn.net/detail/eastmount/8748403
一. 單文件顯示BMP圖片
第一步:新建專案"MFC AppWizard(exe)",專案名為ImageProcessing,在應用程式型別中選擇"單個文件",點選"確定"。在左欄的"資源檢視"中,點選"Menu->IDR_MAINFRAM"可以檢視並修改選單檢視。

// Implementation
public:
//新增成員函式
void ShowBitmap(CDC* pDC,CString BmpName); //顯示點陣圖函式
//新增成員變數
CString EntName; //影象副檔名
CString BmpName; //影象檔名稱
CBitmap m_bitmap; //建立點陣圖物件
同時採用類檢視新增後,會自動在XXXView.h中新增函式定義,在XXXView.cpp中新增函式實現程式碼。

//****************顯示BMP格式圖片****************//
void CImageProcessingView::ShowBitmap(CDC *pDC, CString BmpName)
{
//定義bitmap指標 呼叫函式LoadImage裝載點陣圖
HBITMAP m_hBitmap;
m_hBitmap = (HBITMAP) LoadImage(NULL,BmpName,IMAGE_BITMAP,0,0,
LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);
/*************************************************************************/
/* 1.要裝載OEM影象,則設此引數值為0 OBM_ OEM點陣圖 OIC_OEM圖示 OCR_OEM游標
/* 2.BmpName要裝載圖片的檔名
/* 3.裝載影象型別:
/* IMAGE_BITMAP-裝載點陣圖 IMAGE_CURSOR-裝載游標 IMAGE_ICON-裝載圖示
/* 4.指定圖示或游標的畫素寬度和長度 以畫素為單位
/* 5.載入選項:
/* IR_LOADFROMFILE-指明由lpszName指定檔案中載入影象
/* IR_DEFAULTSIZE-指明使用影象預設大小
/* LR_CREATEDIBSECTION-當uType引數為IMAGE_BITMAP時,建立一個DIB項
/**************************************************************************/
if( m_bitmap.m_hObject )
{
m_bitmap.Detach(); //切斷CWnd和視窗聯絡
}
m_bitmap.Attach(m_hBitmap); //將控制程式碼HBITMAP m_hBitmap與CBitmap m_bitmap關聯
//邊界
CRect rect;
GetClientRect(&rect);
//圖片顯示(x,y)起始座標
int m_showX=0;
int m_showY=0;
int m_nWindowWidth = rect.right - rect.left; //計算客戶區寬度
int m_nWindowHeight = rect.bottom - rect.top; //計算客戶區高度
//定義並建立一個記憶體裝置環境DC
CDC dcBmp;
if( !dcBmp.CreateCompatibleDC(pDC) ) //建立相容性的DC
return;
BITMAP m_bmp; //臨時bmp圖片變數
m_bitmap.GetBitmap(&m_bmp); //將圖片載入點陣圖中
CBitmap *pbmpOld = NULL;
dcBmp.SelectObject(&m_bitmap); //將點陣圖選入臨時記憶體裝置環境
//圖片顯示呼叫函式stretchBlt
pDC->StretchBlt(0,0,m_bmp.bmWidth,m_bmp.bmHeight,&dcBmp,0,0,
m_bmp.bmWidth,m_bmp.bmHeight,SRCCOPY);
/*******************************************************************************/
/* BOOL StretchBlt(int x,int y,int nWidth,int nHeight,CDC* pSrcDC,
/* int xSrc,int ySrc,int nSrcWidth,int nSrcHeight,DWORD dwRop );
/* 1.引數x、y點陣圖目標矩形左上角x、y的座標值
/* 2.nWidth、nHeigth點陣圖目標矩形的邏輯寬度和高度
/* 3.pSrcDC表示源裝置CDC指標
/* 4.xSrc、ySrc表示點陣圖源矩形的左上角的x、y邏輯座標值
/* 5.dwRop表示顯示點陣圖的光柵操作方式 SRCCOPY用於直接將點陣圖複製到目標環境中
/*******************************************************************************/
dcBmp.SelectObject(pbmpOld); //恢復臨時DC的點陣圖
DeleteObject(&m_bitmap); //刪除記憶體中的點陣圖
dcBmp.DeleteDC(); //刪除CreateCompatibleDC得到的圖片DC
/**
* 面程式碼為後面顯示第二張圖片
*/
}
第四步:設定開啟BMP圖片函式。"檢視"->"建立類嚮導"(Ctrl+W)->選擇"類名"CImageProcessing->在命令物件ID中雙擊"ID_FILE_OPEN"->自動生成預設成員函式OnFileOpen,訊息為COMMAND。雙擊成員函式(Member
Functions)進入函式編輯。
//****************開啟檔案****************//
void CImageProcessingView::OnFileOpen()
{
//兩種格式的檔案:bmp gif
CString filter;
filter="所有檔案(*.bmp,*.jpg,*.gif)|*.bmp;*.jpg| BMP(*.bmp)|*.bmp| JPG(*.jpg)|*.jpg||";
CFileDialog dlg(TRUE,NULL,NULL,OFN_HIDEREADONLY,filter,NULL);
//按下確定按鈕 dlg.DoModal() 函式顯示對話方塊
if( dlg.DoModal() == IDOK )
{
BmpName = dlg.GetPathName(); //獲取檔案路徑名 如D:\pic\abc.bmp
EntName = dlg.GetFileExt(); //獲取副檔名
EntName.MakeLower(); //將副檔名轉換為一個小寫字元
Invalidate(); //呼叫該函式就會呼叫OnDraw重繪畫圖
}
}
第五步:在ImageProcessingView.cpp中找到OnDraw()函式,通過OnDraw()函式呼叫ShowBitmap()函式顯示圖片。程式碼如下:void CImageProcessingView::OnDraw(CDC* pDC)
{
CImageProcessingDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
if (!pDoc) return;
if( EntName.Compare(_T("bmp")) == 0 ) //bmp格式
{
ShowBitmap(pDC,BmpName); //顯示圖片
}
}
第六步:此時點選執行,同時點選檔案-開啟,即可顯示圖片如下圖所示:
二. 讀取BMP圖片和儲存圖片

第一步:在XXXView.h中新增BMP格式影象相關的成員變數和成員函式,其中成員函式通過類檢視右鍵新增,成員變數可以在XXXView.h中直接複製。
// Implementation
public:
//新增成員函式
void ShowBitmap(CDC* pDC,CString BmpName); //顯示點陣圖函式
bool ReadBmp(); //用來讀取bmp個手機圖片
bool SaveBmp(LPCSTR lpFileName); //用來儲存bmp格式圖片
//新增成員變數
CString EntName; //影象副檔名
CString BmpName; //影象檔名稱
CBitmap m_bitmap; //建立點陣圖物件
int m_nWidth; //影象實際寬度
int m_nHeight; //影象實際高度
int m_nDrawWidth; //影象顯示寬度
int m_nDrawHeight; //影象顯示高度
DWORD m_nImage; //影象資料的位元組數 只含點陣圖
DWORD m_nSize; //影象檔案大小
int m_nLineByte; //影象一行所佔位元組數
int m_nBitCount; //影象每個畫素所佔位數
int m_nPalette; //點陣圖實際使用的顏色表中的顏色數
BYTE *m_pImage; //讀入圖片資料後的指標
BITMAPFILEHEADER bfh; //全域性變數檔案頭
BITMAPINFOHEADER bih; //全域性變數資訊頭
RGBQUAD m_pPal; //顏色表指標
第二步:在ImageProcessingView.cpp中實現ReadBmp函式和SaveBmp函式。//***************讀取圖片資料*************//
bool CImageProcessingView::ReadBmp()
{
//圖片讀出儲存其中的東西
FILE *fp = fopen(BmpName,"rb");
if(fp==0)
{
AfxMessageBox("無法開啟檔案!",MB_OK,0);
return 0;
}
//讀取檔案頭 解決BMP格式倒置的方法
fread(&bfh.bfType,sizeof(WORD),1,fp);
fread(&bfh.bfSize,sizeof(DWORD),1,fp);
fread(&bfh.bfReserved1,sizeof(WORD),1,fp);
fread(&bfh.bfReserved2,sizeof(WORD),1,fp);
fread(&bfh.bfOffBits,sizeof(DWORD),1,fp);
//影象檔案的總位元組數
m_nSize = bfh.bfSize;
//判斷是否是bmp格式圖片
if(bfh.bfType!=0x4d42) //'BM'
{
AfxMessageBox("不是BMP格式圖片!",MB_OK,0);
return 0;
}
//讀取資訊頭
fread(&bih.biSize,sizeof(DWORD),1,fp);
fread(&bih.biWidth,sizeof(LONG),1,fp);
fread(&bih.biHeight,sizeof(LONG),1,fp);
fread(&bih.biPlanes,sizeof(WORD),1,fp);
fread(&bih.biBitCount,sizeof(WORD),1,fp);
fread(&bih.biCompression,sizeof(DWORD),1,fp);
fread(&bih.biSizeImage,sizeof(DWORD),1,fp);
fread(&bih.biXPelsPerMeter,sizeof(LONG),1,fp);
fread(&bih.biYPelsPerMeter,sizeof(LONG),1,fp);
fread(&bih.biClrUsed,sizeof(DWORD),1,fp);
fread(&bih.biClrImportant,sizeof(DWORD),1,fp);
if(bih.biSize!=sizeof(bih))
{
AfxMessageBox("本結構所佔用位元組數出現錯誤");
return 0;
}
//點陣圖壓縮型別,必須是 0(不壓縮) 1(BI_RLE8壓縮型別)或2(BI_RLE壓縮型別)之一
if(bih.biCompression == BI_RLE8 || bih.biCompression == BI_RLE4)
{
AfxMessageBox("點陣圖被壓縮!");
return 0;
}
//獲取影象高寬和每個畫素所佔位數
m_nHeight = bih.biHeight;
m_nWidth = bih.biWidth;
m_nDrawHeight = bih.biHeight;
m_nDrawWidth = bih.biWidth;
m_nBitCount = bih.biBitCount; //每個畫素所佔位數
//計算影象每行畫素所佔的位元組數(必須是32的倍數)
m_nLineByte = (m_nWidth*m_nBitCount+31)/32*4;
//圖片大小 呼叫系統自帶的檔案頭 BITMAPFILEHEADER bfh; BITMAPINFOHEADER bih;
//否則用 BITMAPFILEHEADER_ bfh; BITMAPINFOHEADER_ bih;要 m_nImage = m_nLineByte * m_nHeight - 2;
m_nImage = m_nLineByte * m_nHeight;
//點陣圖實際使用的顏色表中的顏色數 biClrUsed
m_nPalette = 0; //初始化
if(bih.biClrUsed)
m_nPalette = bih.biClrUsed;
//申請點陣圖空間 大小為點陣圖大小 m_nImage
//malloc只能申請4位元組的空間 (未知)
m_pImage=(BYTE*)malloc(m_nImage);
fread(m_pImage,m_nImage,1,fp);
fclose(fp);
return true;
}
其中SaveBmp()函式程式碼如下://****************儲存檔案****************//
bool CImageProcessingView::SaveBmp(LPCSTR lpFileName) //lpFileName為點陣圖檔名
{
//儲存bmp格式圖片 寫圖片過程 只處理24畫素的圖片 該圖片無調色盤
FILE *fpo = fopen(BmpName,"rb");
FILE *fpw = fopen(lpFileName,"wb");
fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);
fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);
fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);
fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);
//malloc只能申請4位元組的空間 (未知)
m_pImage=(BYTE*)malloc(m_nImage);
fread(m_pImage,m_nImage,1,fpo);
fwrite(m_pImage,m_nImage,1,fpw);
fclose(fpo);
fclose(fpw);
return true;
}
第三步:新增儲存menu控制元件和函式。點選”檢視-建立類嚮導“,在ID列表中找到ID_FILE_SAVE,點選COMMAND(Message列表),雙擊新增預設成員函式OnFileSave,同時在Member
Functions(成員函式)中雙擊該函式進入函式並編輯。新增如下程式碼://******************檔案儲存*****************//
void CImageProcessingView::OnFileSave()
{
// TODO: Add your command handler code here
CString filter;
filter="所有檔案(*.bmp,*.jpg)|*.bmp;*.jpg| BMP(*.bmp)|*.bmp| JPG(*.jpg)|*.jpg||";
//重點: 1-檔案開啟 0-檔案儲存
CFileDialog dlg(0,NULL,NULL,OFN_HIDEREADONLY,filter,NULL);
//按下確定按鈕
if( dlg.DoModal() == IDOK ) {
CString str;
CString strName;
CString filename;
str = dlg.GetPathName(); //獲取檔案的路徑
filename = dlg.GetFileTitle(); //獲取檔名
int nFilterIndex=dlg.m_ofn.nFilterIndex;
if( nFilterIndex == 2 ) //當使用者選擇檔案過濾器為".BMP"時
{
str = str + ".bmp"; //自動加副檔名.bmp
SaveBmp(str); //儲存bmp圖片 就是一個寫出圖片的過程
AfxMessageBox("圖片儲存成功",MB_OK,0);
}
}
}
第四步:在XXXView.cpp中OnDraw()函式中呼叫讀取圖片函式。if( EntName.Compare(_T("bmp")) == 0 ) //bmp格式
{
ReadBmp();
ShowBitmap(pDC,BmpName); //顯示圖片
}
執行程式,開啟圖片點選儲存即可實現。重點是ReadBmp獲取一些重要引數。

三. 影象灰度處理
1.灰度影象概念
什麼叫灰度圖?任何顏色都有紅、綠、藍三原色組成,假如原來某點的顏色為RGB(R,G,B),那麼我們可以通過下面幾種方法,將其轉換為灰度:
浮點演算法:Gray=R*0.3+G*0.59+B*0.11
整數方法:Gray=(R*30+G*59+B*11)/100
移位方法:Gray=(R*28+G*151+B*77)>>8;
平均值法:Gray=(R+G+B)/3;(此程式採用演算法)
僅取綠色:Gray=G;
通過上述任一種方法求得Gray後,將原來的RGB(R,G,B)中的R,G,B統一用Gray替換,形成新的顏色RGB(Gray,Gray,Gray),用它替換原來的RGB(R,G,B)就是灰度圖了。
改變象素矩陣的RGB值,來達到彩色圖轉變為灰度圖
加權平均值演算法:根據光的亮度特性,其實正確的灰度公式應當是:
R=G=B=R*0.299+G*0.587+B0.144
為了提高速度我們做一個完全可以接受的近似,公式變形如下:R=G=B=(R*3+G*6+B)/10
真正的24位真彩圖與8位的灰度圖的區別就在於,真彩圖檔案中沒有調色盤,灰度圖有調色盤,真彩圖中的象素矩陣是RGB值,灰度圖中的象素矩陣是調色盤索引值。原始碼只簡單的改變象素矩陣的RGB值,來達到彩色圖轉為灰度圖,並沒有新增調色盤;該程式未實現新增了調色盤。
2.灰度處理原始碼
// Implementation
public:
//新增成員函式
void ShowBitmap(CDC* pDC,CString BmpName); //顯示點陣圖函式
bool ReadBmp(); //用來讀取bmp個手機圖片
bool SaveBmp(LPCSTR lpFileName); //用來儲存bmp格式圖片
//新增成員變數
CString EntName; //影象副檔名
CString BmpName; //影象檔名稱
CBitmap m_bitmap; //建立點陣圖物件
CBitmap m_bitmaplin; //建立臨時點陣圖物件進行處理
CString BmpNameLin; //儲存影象副本檔案
第二步:在ImageProcessingView.cpp中ShowBitmap()函式前新增變數numPicture和level。/*************************************************************/
/* numPicture變數顯示圖片數量
/* 0-提示錯誤或未開啟圖片 1-顯示一張圖片 2-顯示兩張圖片和處理
/*************************************************************/
int numPicture = 0;
/*************************************************************/
/* level變數顯示具體的處理操作,每個處理函式中賦值該變數
/* 0-顯示2張圖片 1-顯示灰度圖片 3-顯示圖片取樣
/* 2 4 8 16 32 64-不同量化等級量化圖片
/*************************************************************/
int level = 0;
//****************顯示BMP格式圖片****************//
void CImageProcessingView::ShowBitmap(CDC *pDC, CString BmpName)
{
....
}
第三步:修改ImageProcessingView.cpp中OnFileOpen()函式,新增臨時變數名和顯示一張圖片標誌變數。程式碼如下://****************開啟檔案****************//
void CImageProcessingView::OnFileOpen()
{
CString filter;
filter="所有檔案(*.bmp,*.jpg,*.gif)|*.bmp;*.jpg| BMP(*.bmp)|*.bmp| JPG(*.jpg)|*.jpg||";
CFileDialog dlg(TRUE,NULL,NULL,OFN_HIDEREADONLY,filter,NULL);
if( dlg.DoModal() == IDOK )
{
BmpName = dlg.GetPathName();
BmpNameLin = "picture.bmp"; //臨時變數名
numPicture=1; //顯示一張圖片
EntName = dlg.GetFileExt();
EntName.MakeLower();
Invalidate();
}
}
第四步:將檢視切換到ResourceView介面,選中Menu->在IDR_MAINFRAME中新增選單”顯示“,雙擊它在選單屬性中選擇”彈出“。在”顯示“的子選單中新增:雙圖顯示--ID_SHOW_TWO(ID)--預設屬性
灰度圖片--ID_SHOW_HD(ID)--預設屬性


//****************顯示兩張圖片****************//
void CImageProcessingView::OnShowTwo()
{
//如果沒有匯入圖片直接點選雙顯 提示錯誤
if(numPicture==0)
{
AfxMessageBox("載入圖片後才能顯示2張圖片!");
return;
}
AfxMessageBox("顯示兩張圖片!",MB_OK,0);
numPicture = 2; //全域性變數 顯示兩圖
level =0; //level=0雙顯
Invalidate(); //呼叫Invalidate 每秒呼叫一次OnDraw畫圖
}
第六步:同上面相同的方法,"檢視"->”建立類嚮導“->ID_SHOW_HD(ID)->COMMAND(Message),預設成員函式名。在XXXView.cpp新增程式碼如下:/********************************************************************************************/
/* 祥見http://blog.csdn.net/xiakq/article/details/2956902有詳細的灰度演算法
/* 其中24位的圖片灰度時,採用如下演算法:
/* 1.平均值演算法 R=G=B=(R+G+B)/3
/* 2.快速演算法 R=G=B=(R+G+B+128)/4>>2
/* 3.加權平均值演算法 根據光的亮度特性,其實正確的灰度公式應當是R=G=B=R*0.299+G*0.587+B0.144
/* 為了提高速度我們做一個完全可以接受的近似,公式變形如下 R=G=B=(R*3+G*6+B)/10
/* 4.精確加權平均值演算法 R=G=B=R*0.299+G*0.587+B0.144
/********************************************************************************************/
//**灰度影象就是 R=G=B且為三者的1/3 level=1時灰度影象**//
void CImageProcessingView::OnShowHd()
{
if(numPicture==0)
{
AfxMessageBox("載入圖片後才能灰度圖片!",MB_OK,0);
return;
}
AfxMessageBox("灰度影象!",MB_OK,0);
//開啟臨時的圖片
FILE *fpo = fopen(BmpName,"rb");
FILE *fpw = fopen(BmpNameLin,"wb+");
//讀取檔案
fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);
fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);
fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);
fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);
//灰度影象
unsigned char color;
unsigned char red,green,blue;
/********************************************************************/
/* 注意:原來下面所有操作都是for( i=0; i<m_nWidth*m_nHeight; i++ )
/* 後發現如果圖片最後一行沒有完整的一行資料,會出現影象變多或變少
/* 但影象的總畫素為m_nImage,如果是m_nImage/3就可以保證所有畫素都有
/********************************************************************/
for(int i=0; i < m_nImage/3; i++ )
{
fread(&red,sizeof(char),1,fpo);
fread(&green,sizeof(char),1,fpo);
fread(&blue,sizeof(char),1,fpo);
color=(red+green+blue)/3;
red=color;
green=color;
blue=color;
fwrite(&red,sizeof(char),1,fpw);
fwrite(&green,sizeof(char),1,fpw);
fwrite(&blue,sizeof(char),1,fpw);
}
fclose(fpo);
fclose(fpw);
numPicture = 2;
level=1;
Invalidate();
}
第七步:修改ShowBitmap()函式中雙顯部分,新增如下程式碼://****************顯示BMP格式圖片****************//
void CImageProcessingView::ShowBitmap(CDC *pDC, CString BmpName)
{
....
/**
* 面程式碼為後面顯示第二張圖片
*/
if(numPicture==2) {
//顯示圖片函式LoadImage
HBITMAP m_hBitmapChange;
if(level==0) //顯示2張圖 BmpNameLin原圖
{
m_hBitmapChange = (HBITMAP) LoadImage(NULL,BmpName,IMAGE_BITMAP,0,0,
LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);
}
else
if(level==1) //灰度圖片 BmpNameLin臨時圖片
{
m_hBitmapChange = (HBITMAP) LoadImage(NULL,BmpNameLin,IMAGE_BITMAP,0,0,
LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);
}
if( m_bitmap.m_hObject ) {
m_bitmap.Detach(); //m_bitmap為建立的點陣圖物件
}
m_bitmap.Attach(m_hBitmapChange);
//定義並建立一個記憶體裝置環境
CDC dcBmp;
if( !dcBmp.CreateCompatibleDC(pDC) ) //建立相容性的DC
return;
BITMAP m_bmp; //臨時bmp圖片變數
m_bitmap.GetBitmap(&m_bmp); //將圖片載入點陣圖中
CBitmap *pbmpOld = NULL;
dcBmp.SelectObject(&m_bitmap); //將點陣圖選入臨時記憶體裝置環境
//如果圖片太大顯示大小為固定640*640 否則顯示原圖大小
if(m_nDrawWidth<650 && m_nDrawHeight<650)
pDC->StretchBlt(m_nWindowWidth-m_nDrawWidth,0,
m_nDrawWidth,m_nDrawHeight,&dcBmp,0,0,m_bmp.bmWidth,m_bmp.bmHeight,SRCCOPY);
else
pDC->StretchBlt(m_nWindowWidth-640,0,640,640,&dcBmp,0,0,
m_bmp.bmWidth,m_bmp.bmHeight,SRCCOPY);
//恢復臨時DC的點陣圖
dcBmp.SelectObject(pbmpOld);
}
}
雙顯和灰度執行效果如下圖所示:
四. 圖片量化處理
(參考我的文庫:http://wenku.baidu.com/view/80b18961f5335a8102d220a0)
1.量化基本概念
影象數字化包括量化和取樣兩個過程,其中:
量化:幅值f(x,y)的離散化,f(x,y)表示靜止灰度影象的空間座標
取樣:對空間連續座標(x,y)的離散化
一幅行數為M、列數為N的影象大小為M×N的矩陣形式為:(其中矩陣中每個元素代表一個畫素)


如圖量化級不同產生的灰度也不同,量化是使連續訊號的幅度用有限級的數碼錶示的過程。
量化等級=2:使用2種灰度級(0~255)表示圖片,小於128的取0,大於等於128的取128。把點陣圖資料塊所有資料在臨時圖片中取值,在顯示即可。
量化等級=4:使用4種灰度級顯示圖片,就會發現圖片分層為4種顏色。同時,0-64區間取0,64-128區間取64,128-192區間取128,192-255區間取192。
量化的取值各不相同,我採用的是最簡單的取值。其它方法可自己去查閱資料。


2.量化處理原始碼
第一步:設定選單欄。將試圖切換到ResourceView介面--選中Menu--在IDR_MAINFRAME中新增選單“量化”--雙擊它在選單屬性中選擇“彈出”。在“顯示”的子選單中新增:屬性為預設屬性。
量化 Level 2--ID_LH_2 量化 Level 4--ID_LH_4
量化 Level 8--ID_LH_8 量化 Level 16--ID_LH_16
量化 Level 32--ID_LH_32 量化 Level 64--ID_LH_64


核心流程是開啟兩張圖片原圖(BmpName)和臨時圖片(BmpNameLin),然後讀取原圖資訊頭賦值給臨時處理圖片,在讀取原圖m_nImage整個畫素矩陣,量化處理每個畫素(即分等級量化),最後檔案寫量化後的畫素矩陣給BmpNameLin,在賦值全域性變數level\numPicture和呼叫Invalidate()重繪影象即可。
//****************量化 量化等級為2****************//
void CImageProcessingView::OnLh2()
{
if(numPicture==0) {
AfxMessageBox("載入圖片後才能量化!",MB_OK,0);
return;
}
AfxMessageBox("量化等級Level=2!",MB_OK,0);
//開啟臨時的圖片
FILE *fpo = fopen(BmpName,"rb");
FILE *fpw = fopen(BmpNameLin,"wb+");
//讀取檔案
fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);
fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);
fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);
fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);
//malloc只能申請4位元組的空間
m_pImage=(BYTE*)malloc(m_nImage);
fread(m_pImage,m_nImage,1,fpo);
//等級2量化
for(int i=0; i<m_nImage; i++ ) {
//24位的為調色盤為真彩圖 Red Green Blue 為3位元組
//量化等級為2取中間值為 64 和 192
if(m_pImage[i]<128) {
m_pImage[i]=0;
}
else if(m_pImage[i]>=128) {
m_pImage[i]=128;
}
}
fwrite(m_pImage,m_nImage,1,fpw);
fclose(fpo);
fclose(fpw);
numPicture = 2;
level=2;
Invalidate();
}
//****************量化 量化等級為4****************//
void CImageProcessingView::OnLh4()
{
if(numPicture==0) {
AfxMessageBox("載入圖片後才能量化!",MB_OK,0);
return;
}
AfxMessageBox("量化等級Level=4!",MB_OK,0);
//開啟臨時的圖片
FILE *fpo = fopen(BmpName,"rb");
FILE *fpw = fopen(BmpNameLin,"wb+");
fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);
fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);
fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);
fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);
m_pImage=(BYTE*)malloc(m_nImage);
fread(m_pImage,m_nImage,1,fpo);
//等級4量化
for(int i=0; i<m_nImage; i++ ) {
if(m_pImage[i]<64) {
m_pImage[i]=0;
}
else if( (m_pImage[i]>=64) && (m_pImage[i]<128) ) {
m_pImage[i]=64;
}
else if( (m_pImage[i]>=128) && (m_pImage[i]<192) ) {
m_pImage[i]=128;
}
else if(m_pImage[i]>=192) {
m_pImage[i]=192;
}
}
fwrite(m_pImage,m_nImage,1,fpw);
fclose(fpo);
fclose(fpw);
numPicture = 2;
level=4;
Invalidate();
}
//****************量化 量化等級為8****************//
void CImageProcessingView::OnLh8()
{
if(numPicture==0) {
AfxMessageBox("載入圖片後才能量化!",MB_OK,0);
return;
}
AfxMessageBox("量化等級Level=8!",MB_OK,0);
//開啟臨時的圖片 讀取檔案
FILE *fpo = fopen(BmpName,"rb");
FILE *fpw = fopen(BmpNameLin,"wb+");
fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);
fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);
fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);
fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);
//malloc只能申請4位元組的空間 (未知)
m_pImage=(BYTE*)malloc(m_nImage);
fread(m_pImage,m_nImage,1,fpo);
//等級8量化
for(int i=0; i<m_nImage; i++ ) {
if(m_pImage[i]<32) {
m_pImage[i]=0;
}
else if( (m_pImage[i]>=32) && (m_pImage[i]<64) ) {
m_pImage[i]=32;
}
else if( (m_pImage[i]>=64) && (m_pImage[i]<96) ) {
m_pImage[i]=64;
}
else if( (m_pImage[i]>=96) && (m_pImage[i]<128) ) {
m_pImage[i]=96;
}
else if( (m_pImage[i]>=128) && (m_pImage[i]<160) ) {
m_pImage[i]=128;
}
else if( (m_pImage[i]>=160) && (m_pImage[i]<192) ) {
m_pImage[i]=160;
}
else if( (m_pImage[i]>=192) && (m_pImage[i]<224) ) {
m_pImage[i]=192;
}
else if(m_pImage[i]>=224) {
m_pImage[i]=224;
}
}
fwrite(m_pImage,m_nImage,1,fpw);
fclose(fpo);
fclose(fpw);
numPicture = 2;
level=8;
Invalidate();
}
//****************量化 量化等級為16****************//
void CImageProcessingView::OnLh16()
{
if(numPicture==0) {
AfxMessageBox("載入圖片後才能量化!",MB_OK,0);
return;
}
AfxMessageBox("量化等級Level=16!",MB_OK,0);
int i,j;
//開啟臨時的圖片
FILE *fpo = fopen(BmpName,"rb");
FILE *fpw = fopen(BmpNameLin,"wb+");
fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);
fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);
fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);
fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);
m_pImage=(BYTE*)malloc(m_nImage);
fread(m_pImage,m_nImage,1,fpo);
for( i=0; i<m_nImage; i++ ) {
j=16;
while(j<=256)
{
if(m_pImage[i]<j)
{
if(m_pImage[i]<16)
m_pImage[i]=0;
else
m_pImage[i]=j-16;
break;
}
else j+=16;
}
}
fwrite(m_pImage,m_nImage,1,fpw);
fclose(fpo);
fclose(fpw);
numPicture = 2;
level=16;
Invalidate();
}
//****************量化 量化等級為32****************//
void CImageProcessingView::OnLh32()
{
if(numPicture==0) {
AfxMessageBox("載入圖片後才能量化!",MB_OK,0);
return;
}
AfxMessageBox("量化等級Level=32!",MB_OK,0);
int i,j;
//開啟臨時的圖片
FILE *fpo = fopen(BmpName,"rb");
FILE *fpw = fopen(BmpNameLin,"wb+");
//讀取檔案
fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);
fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);
fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);
fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);
m_pImage=(BYTE*)malloc(m_nImage);
fread(m_pImage,m_nImage,1,fpo);
//等級32化
for( i=0; i<m_nImage; i++ )
{
j=8;
while(j<=256)
{
if(m_pImage[i]<j)
{
if(m_pImage[i]<8)
m_pImage[i]=0;
else
m_pImage[i]=j-8;
break;
}
else j+=8;
}
}
fwrite(m_pImage,m_nImage,1,fpw);
fclose(fpo);
fclose(fpw);
numPicture = 2;
level=32;
Invalidate();
}
//****************量化 量化等級為64****************//
void CImageProcessingView::OnLh64()
{
if(numPicture==0) {
AfxMessageBox("載入圖片後才能量化!",MB_OK,0);
return;
}
AfxMessageBox("量化等級Level=64!",MB_OK,0);
int i,j;
//開啟臨時的圖片
FILE *fpo = fopen(BmpName,"rb");
FILE *fpw = fopen(BmpNameLin,"wb+");
//讀取檔案
fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);
fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);
fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);
fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);
m_pImage=(BYTE*)malloc(m_nImage);
fread(m_pImage,m_nImage,1,fpo);
//等級64量化
for( i=0; i<m_nImage; i++ )
{
j=4;
while(j<=256)
{
if(m_pImage[i]<j)
{
if(m_pImage[i]<16)
m_pImage[i]=0;
else
m_pImage[i]=j-4;
break;
}
else j+=4;
}
}
fwrite(m_pImage,m_nImage,1,fpw);
fclose(fpo);
fclose(fpw);
numPicture = 2;
level=64;
Invalidate();
}
第四步:修改ShowBitmap()函式,顯示量化處理。新增如下程式碼:if(level==0) //顯示2張圖 BmpNameLin原圖
{
m_hBitmapChange = (HBITMAP) LoadImage(NULL,BmpName,IMAGE_BITMAP,0,0,
LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);
}
else
if(level==1) //灰度圖片 BmpNameLin臨時圖片
{
m_hBitmapChange = (HBITMAP) LoadImage(NULL,BmpNameLin,IMAGE_BITMAP,0,0,
LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);
}
else //量化2
if(level==2)
{
m_hBitmapChange = (HBITMAP) LoadImage(NULL,BmpNameLin,IMAGE_BITMAP,0,0,
LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);
}
else //量化4
if(level==4)
{
m_hBitmapChange = (HBITMAP) LoadImage(NULL,BmpNameLin,IMAGE_BITMAP,0,0,
LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);
}
else //量化8
if(level==8)
{
m_hBitmapChange = (HBITMAP) LoadImage(NULL,BmpNameLin,IMAGE_BITMAP,0,0,
LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);
}
else //量化16
if(level==16)
{
m_hBitmapChange = (HBITMAP) LoadImage(NULL,BmpNameLin,IMAGE_BITMAP,0,0,
LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);
}
else //量化32
if(level==32)
{
m_hBitmapChange = (HBITMAP) LoadImage(NULL,BmpNameLin,IMAGE_BITMAP,0,0,
LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);
}
else //量化64
if(level==64)
{
m_hBitmapChange = (HBITMAP) LoadImage(NULL,BmpNameLin,IMAGE_BITMAP,0,0,
LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);
}
執行效果如下圖,當量化Level=2時很明顯的兩種灰度顏色,Level=4有4種顏色。
五. 影象取樣功能
(參考我的文庫:http://wenku.baidu.com/view/b3ef4e1f964bcf84b9d57baf)
1.影象取樣概念
該工程所有的處理都基於24位的bmp格式圖片的處理,24為表示biBitCount=24,1個畫素佔3個位元組(red、green、blue)。如圖一張512*512的原圖,保持灰度級256不變後的各種取樣。輸入取樣座標:如16*16,它的含義是原圖512*512畫素,現在組成一個新的圖片為16*16畫素,(512/16=32,512/16=32)則每32*32組成一個新的區域。共有這種區域16*16個,取樣的方法有2種:
a.把這個32*32區域全部賦值成左上角那個畫素,這樣圖片的大小不變,困難在於賦值要4層迴圈。(專案中採用的就是這種方法)
b.把這個32*32區域的左上角取出來,組成一個新的圖片,共有16*16個畫素,這張圖片的大小要變小,只有16*16個畫素。但難點在於同時要把bmp檔案頭中的圖片大小、資訊頭中的長寬畫素改變、偏移量等資訊更新。

原圖8*8的矩陣要處理成3*3的矩陣,則迴圈先處理第一二行,①②④⑤為3*3處理,去左上角的RGB,③⑥為2*3的處理;重點是原圖讀取一維陣列需要轉成二維陣列賦值處理;最後再處理最後一行資料。取樣中公式為:
//獲取填充顏色 相當於一次讀取一個畫素的RGB值再乘3跳3個位元組
red=m_pImage[(X+Y*m_nWidth)*3];
green=m_pImage[(X+Y*m_nWidth)*3+1];
blue=m_pImage[(X+Y*m_nWidth)*3+2];
//填出影象迴圈 小區域中的長寬迴圈
//(X+Y*m_nWidth)*3跳到該小區域 再賦值3*3小區域的RGB 同一區域RGB相同
m_pImage[(X+Y*m_nWidth)*3+m+n*m_nWidth*3]=red; m++;
m_pImage[(X+Y*m_nWidth)*3+m+n*m_nWidth*3]=green; m++;
m_pImage[(X+Y*m_nWidth)*3+m+n*m_nWidth*3]=blue; m++;

2.影象取樣程式碼
a.將檢視切換到ResourceView介面--選中Menu--在IDR_MAINFRAME中新增選單“取樣”--雙擊它在選單屬性中選擇“彈出”;
b.在“取樣”的子選單中新增:屬性為預設屬性。ID_CY--圖片取樣。
c.建立類導向:檢視--建立類導向(Ctrl+W)--CImageProcessingView(類名)--ID_CY--COMMAND(Messages)--預設成員函式名。生成void CImageProcessingView::OnCy()取樣函式。
第二步:設定取樣對話方塊
a.將試圖切換到ResourceView介面--選中Dialog,右鍵滑鼠新建一個Dialog,並新建一個名為IDD_DIALOG_CY。編輯框(X)IDC_EDIT_CYX 和 (Y)IDC_EDIT_CYY,確定為預設按鈕。設定成下圖對話方塊:


IDC_EDIT_CYY--int--m_yPlace
d.在View.cpp中新增取樣的標頭檔案#include "ImageCYDlg.h"

第三步:在ImageProcessingView.cpp中新增程式碼
//****************圖片取樣****************//
void CImageProcessingView::OnCy()
{
if(numPicture==0) {
AfxMessageBox("載入圖片後才能取樣!",MB_OK,0);
return;
}
CImageCYDlg dlg; //定義取樣對話方塊
//顯示對話方塊
if( dlg.DoModal()==IDOK ) {
//取樣座標最初為圖片的自身畫素
if( dlg.m_xPlace==0 || dlg.m_yPlace==0 ) {
AfxMessageBox("輸入圖片畫素不能為0!",MB_OK,0);
return;
}
if( dlg.m_xPlace>m_nWidth || dlg.m_yPlace>m_nHeight ) {
AfxMessageBox("圖片畫素不能為超過原圖長寬!",MB_OK,0);
return;
}
AfxMessageBox("圖片取樣!",MB_OK,0);
//開啟臨時的圖片 讀取檔案
FILE *fpo = fopen(BmpName,"rb");
FILE *fpw = fopen(BmpNameLin,"wb+");
fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);
fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);
fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);
fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);
fread(m_pImage,m_nImage,1,fpo);
/*圖片取樣*/
int numWidth,numHeight; //圖片此區間取相同的畫素點
int numSYWidth,numSYHeight; //剩餘期間區域
/*********************************************************/
/* 表示numWidth*numHeight為一個區域 該區域顏色相同
/* 如 512/512=1 512/512=1 1*1為一個區域
/* dlg.m_xPlace*dlg.m_yPlace 表示新的(x,y)座標
/* numSYWidth表示剩餘空間 該區域統一為一個顏色
/*********************************************************/
numWidth=m_nWidth/dlg.m_xPlace;
numHeight=m_nHeight/dlg.m_yPlace;
numSYWidth=m_nWidth%dlg.m_xPlace;
numSYHeight=m_nHeight%dlg.m_yPlace;
int Y,X;
int i,j,m,n;
unsigned char red,green,blue; //儲存三種顏色
/* 有((m_xPlace * m_yPlace)+ 剩餘區域 )個小區域 */
for( i=0; i<dlg.m_yPlace; i++ ) //高度
{
Y=numHeight*i; //獲取Y座標
for( j=0; j<dlg.m_yPlace; j++ ) //寬度
{
X=numWidth*j; //獲取X座標
/*獲取填充顏色*/
red=m_pImage[(X+Y*m_nWidth)*3];
green=m_pImage[(X+Y*m_nWidth)*3+1];
blue=m_pImage[(X+Y*m_nWidth)*3+2];
/*填出影象迴圈 小區域中的長寬迴圈*/
for( n=0; n<numHeight; n++ )
{
for( m=0; m<numWidth*3; )
{
m_pImage[(X+Y*m_nWidth)*3+m+n*m_nWidth*3]=red;
m++;
m_pImage[(X+Y*m_nWidth)*3+m+n*m_nWidth*3]=green;
m++;
m_pImage[(X+Y*m_nWidth)*3+m+n*m_nWidth*3]=blue;
m++;
}
}
}
}
fwrite(m_pImage,m_nImage,1,fpw);
fclose(fpo);
fclose(fpw);
numPicture = 2;
level=3;
Invalidate();
}
}
執行效果如下圖所示,其中彩色圖片應該先灰度處理再進行其他操作。



總結:後悔當初還沒有寫部落格,通過回憶幾年前的程式碼,很多當時的體會和思想都已不復存在了!可能你在百度文庫中看到類似的文章,因為那些都是我在2012年上傳的,最初是通過它進行分享程式設計知識的,後來發現了更好的CSDN而取代之。這篇文章感覺太詳細,有時候一直懷疑是不是失去了演算法的本質,不應該寫這麼詳細的文章,而更加精簡一點,但可能和從小記筆記有關,很難改過來了,慢慢改吧!
最後還是希望文章對你有所幫助,如果文章有不足或錯誤之處,請海涵~
(By:Eastmount 2015-5-28 下午點 http://blog.csdn.net/eastmount/)
相關文章
- 數字影像處理-取樣量化(Matlab)Matlab
- 數字影象處理DIP
- [Python影像處理] 三十.影像量化及取樣處理萬字詳細總結(推薦)Python
- 數字影象處理-第一節
- [Python影象處理] 三.獲取影象屬性、興趣ROI區域及通道處理Python
- 【筆記】基於Python的數字影象處理筆記Python
- 【模型推理】量化實現分享三:詳解 ACIQ 對稱量化演算法實現模型演算法
- 數字貨幣量化交易系統開發功能詳解丨量化交易開發原始碼模式原始碼模式
- 影象處理之影象增強
- 數字影像處理(一)之灰度轉換和卷積python實現卷積Python
- OpenCV計算機視覺學習(12)——影像量化處理&影像取樣處理(K-Means聚類量化,區域性馬賽克處理)OpenCV計算機視覺聚類
- Nginx實現高速併發處理的原理詳解Nginx
- 實戰 | 用Python做影象處理(一)Python
- [Python影象處理] 八.影象腐蝕與影象膨脹Python
- MFC——SkinMagic使用詳解
- [Python影象處理] 六.影象縮放、影象旋轉、影象翻轉與影象平移Python
- 量化現貨交易系統開發(功能詳解)| 量化現貨交易系統(原始碼demo示例)原始碼
- 高手如何處理快取:SpringBoot整合Redis實現快取處理(AOP技術)!快取Spring BootRedis
- [Python影象處理] 七.影象閾值化處理及演算法對比Python演算法
- 海報分享功能實現詳解
- [Python影象處理] 一.影象處理基礎知識及OpenCV入門函式PythonOpenCV函式
- Java入門教程五(數字和日期處理)Java
- 影象中的畫素處理
- 這才是數字孿生汙水處理廠該有的樣子
- 數字訊號處理實驗(四):數字濾波器結構
- 數字影像處理實驗(四)影像銳化
- php 處理 浮點數 精度運算 數字處理等PHP
- GoWeb開發_Iris框架講解(三):路由功能處理方式GoWeb框架路由
- [Python影象處理] 二.OpenCV+Numpy庫讀取與修改畫素PythonOpenCV
- [Python影象處理] 五.影象融合、加法運算及影象型別轉換Python型別
- Stable diffusion取樣器詳解
- 架構設計 | 非同步處理流程,多種實現模式詳解架構非同步模式
- 影象處理庫GPUImage簡單使用GPUUI
- 數字影像處理實驗之對比度拉伸
- [Python影象處理] 十.形態學之影象頂帽運算和黑帽運算Python
- 數字影像處理讀書筆記(三)直方圖匹配筆記直方圖
- VirtualView iOS 模板載入功能實現詳解ViewiOS
- SpringBoot實現檔案上傳功能詳解Spring Boot
- 標籤實現預載入功能詳解