VC++圖像處理程序設計(第1版) 楊淑瑩 編著 邊奠英 主審
第四章 圖像的灰度變換
Joanna-In-Hdu&Hust 手工打,印象更深刻
使用工具 VS2010 mfc
整本書的代碼文件、測試圖片和程序運行exe請在這里下載:https://github.com/CaptainLYN/VCPictureProcessing
1、此章的灰度代碼的頭文件:HuiDuDib.h:
1 #pragma once
2 class HuiDuDib:public CObject 3 { 4 protected: 5 CDib* dib; 6 public: 7 void Fei0(); 8 void GetDib(CDib *d); 9 void GuDing(int YuZhi); 10 void ShuangYu(int low,int high,int mode); 11 void FanSe(); 12 void ChuangKou(BYTE low,BYTE high); 13 void ZheXian(BYTE X1,BYTE y1,BYTE X2,BYTE Y2);//優點是根據用戶需要,拉伸感興趣的物體細節,相對抑制不感興趣的灰度級
14 float* ZhiFangTu(bool i);//相比原書,改變了接口,為true返回小數的,為false返回整數的
15 void FenBuJunHengHua();//減少像素個數少的灰度級,展寬灰度級個數多的,從而達到清晰圖像的目的
16 void PiPeiBianHuan(BYTE jishu,int* huidu,float *shuju);//jishu表示要匹配的灰度圖有多少級,huidu中依次記錄了從小到大每一個灰度值,shuju記錄了每一個灰度值的gailv;對原圖和目標灰度直方圖進行灰度直方圖均衡化,然后對於原圖的每一個灰度級找到在目標灰度圖中的灰度概率最相近的灰度,進行單映射變換,然后對原圖進行灰度替換
17 int PingJunHuiDu();//返回圖像的平均灰度
18 };
2、HuiDuDib.cpp:
1 #include"stdafx.h"
2 #include"CViewImage.h"
3 #include"CDib.h"
4 #include<WindowsX.h>
5 #include"ZhiFangDlg.h"
6 #include"HuiDuDib.h"
7
8 void HuiDuDib::Fei0()//Luna的圖片全是白色
9 { 10 LPBYTE p_data=dib->GetData(); 11 LPBYTE t; 12 int width=dib->GetWidth(); 13 int height=dib->GetHeight(); 14 int linebytes=dib->GetDibWidthBytes();//其實早已經寫好了每行字節的獲取方法,一直沒用
15 for(int j=0;j<height;j++) 16 for(int i=0;i<width;i++) 17 { 18 t=p_data+j*linebytes+i;//書上的寫的比較辣雞
19 if(*t>0) 20 *t=255; 21 } 22 } 23 void HuiDuDib::GetDib(CDib *d) 24 { 25 dib=d; 26 } 27 void HuiDuDib::GuDing(int YuZhi) 28 { 29 LPBYTE p_data,p; 30 p_data=dib->GetData(); 31 int width=dib->GetWidth(); 32 int height=dib->GetHeight(); 33 int linebytes=dib->GetDibWidthBytes(); 34 for(int j=0;j<height;j++) 35 for(int i=0;i<width;i++) 36 { 37 p=p_data+j*linebytes+i; 38 if(*p<YuZhi) 39 *p=0; 40 else
41 *p=255; 42 } 43 } 44 void HuiDuDib::ShuangYu(int low,int high,int mode) 45 { 46 LPBYTE p_data=dib->GetData(); 47 LPBYTE t; 48 int width=dib->GetWidth(); 49 int height=dib->GetHeight(); 50 int linebytes=dib->GetDibWidthBytes(); 51 switch(mode) 52 { 53 case 0://0-255-0
54 for(int j=0;j<height;j++) 55 for(int i=0;i<width;i++) 56 { 57 t=p_data+j*linebytes+i; 58 if(*t<=low||*t>=high) 59 *t=0; 60 else
61 *t=255; 62 } 63 break; 64 case 1://255-0-255
65 for(int j=0;j<height;j++) 66 for(int i=0;i<width;i++) 67 { 68 t=p_data+j*linebytes+i; 69 if(*t<=low||*t>=high) 70 *t=255; 71 else
72 *t=0; 73 } 74 break; 75 } 76
77 } 78 void HuiDuDib::FanSe() 79 { 80 LPBYTE p_data=dib->GetData(); 81 LPBYTE t; 82 int width=dib->GetWidth(); 83 int height=dib->GetHeight(); 84 int linebytes=dib->GetDibWidthBytes(); 85 for(int j=0;j<height;j++) 86 for(int i=0;i<width;i++) 87 { 88 t=p_data+linebytes*j+i; 89 *t=255-*t; 90 } 91 } 92 void HuiDuDib::ChuangKou(BYTE low,BYTE high) 93 { 94 LPBYTE p_data=dib->GetData(); 95 LPBYTE t; 96 int width=dib->GetWidth(); 97 int height=dib->GetHeight(); 98 int linebytes=dib->GetDibWidthBytes(); 99 for(int j=0;j<height;j++) 100 for(int i=0;i<width;i++) 101 { 102 t=p_data+j*linebytes+i; 103 if(*t<low) 104 *t=0; 105 else if(*t>high) 106 *t=255; 107 } 108 } 109 void HuiDuDib::ZheXian(BYTE X1,BYTE Y1,BYTE X2,BYTE Y2) 110 { 111 LPBYTE p_data=dib->GetData(); 112 LPBYTE t; 113 int width=dib->GetWidth(); 114 int height=dib->GetHeight(); 115 int linebytes=dib->GetDibWidthBytes(); 116 for(int j=0;j<height;j++) 117 for(int i=0;i<width;i++) 118 { 119 t=p_data+linebytes*j+i; 120 if(*t>0&&*t<=X1) 121 { 122 *t=*t*(Y1/X1); 123 } 124 else if(*t<X1&&*t<X2) 125 { 126 *t=Y1+(Y2-Y1)/(X2-X1)*(*t-X1); 127 } 128 else if(X2!=255) 129 { 130 *t=Y2+(255-Y2)/(255-X2)*(*t-X2); 131 } 132 } 133 } 134 float* HuiDuDib::ZhiFangTu(bool moshi) 135 { 136 float *tongji=new float[256];//要記得銷毀
137
138 memset(tongji,0,sizeof(float)*256); 139 BYTE* p_data=dib->GetData(); 140 BYTE *temp; 141 int width=dib->GetWidth(); 142 int height=dib->GetHeight(); 143 int linebytes=dib->GetDibWidthBytes(); 144 int i,j; 145 for(j=0;j<height;j++) 146 { 147 for(i=0;i<width;i++) 148 { 149 temp=p_data+j*linebytes+i; 150 tongji[*temp]++; 151 } 152 } 153 if(moshi==true) 154 { 155 for(i=0;i<256;i++) 156 tongji[i]=tongji[i]/(height*width); 157 } 158 return tongji; 159 } 160 void HuiDuDib::FenBuJunHengHua() 161 { 162 int i,j; 163 //灰度分布密度
164 float *fPs_R; 165 //中間變量
166 float temp_r[256];//就是S[]累計函數
167 int nNs_R[256];//就是L[],r對應的新像素值 168 //初始化
169 memset(temp_r,0,sizeof(temp_r)); 170 LPBYTE p_data=dib->GetData(); 171 fPs_R=ZhiFangTu(true);//這里是跟書處理不一樣的 172 //進行均衡化處理
173 for(i=0;i<256;i++) 174 { 175 if(i==0) 176 temp_r[0]=fPs_R[0]; 177 else
178 temp_r[i]=temp_r[i-1]+fPs_R[i]; 179 nNs_R[i]=(int)(255.0f*temp_r[i]+0.5f);//+0.5f為了減少精度的缺失,滿足四舍五入
180 } 181 int width=dib->GetWidth(); 182 int height=dib->GetHeight(); 183 unsigned char temp; 184 int linebytes=dib->GetDibWidthBytes(); 185 //對各像素進行灰度轉換
186 for(j=0;j<height;j++) 187 for(i=0;i<width;i++) 188 { 189 temp=*((unsigned char*)p_data+linebytes*j+i);//8位數據
190 *((unsigned char*)p_data+linebytes*j+i)=nNs_R[temp]; 191 } 192 delete []fPs_R; 193 } 194 void HuiDuDib::PiPeiBianHuan(BYTE jishu,int* huidu,float *shuju) 195 { 196 long i,j; 197 int daiti[256];//記錄每個像素被代替為哪一個像素
198 float *gailv;//灰度分布概率
199 float temp[256]; 200 LPBYTE p_data=dib->GetData(); 201 long width=dib->GetWidth(); 202 long height=dib->GetHeight(); 203 gailv=ZhiFangTu(true); 204 //計算原始累計直方圖
205 for(i=0;i<256;i++) 206 { 207 if(i==0) 208 temp[0]=gailv[0]; 209 else
210 temp[i]=temp[i-1]+gailv[i]; 211 gailv[i]=temp[i]; 212 } 213 //計算規定的累積直方圖
214 for(i=0;i<256;i++) 215 { 216 if(i==0) 217 temp[0]=shuju[0]; 218 else
219 temp[i]=temp[i-1]+shuju[i]; 220 shuju[i]=temp[i]; 221 } 222 for(i=0;i<256;i++) 223 { 224 //最接近的規定直方圖灰度等級,用規定的直方圖等級代替概率差不多的等級 225 //讓原來的直方圖的形狀大體接近規定的直方圖
226 int m_r=0; 227 //最小差值
228 float min_value_r=1; 229 for(j=0;j<jishu;j++) 230 { 231 //當前差值
232 float now_value=0; 233 //計算差值
234 if(gailv[i]-shuju[j]>=0) 235 now_value=gailv[i]-shuju[j]; 236 else
237 now_value=shuju[j]-gailv[i]; 238 //尋找最接近的規定直方圖灰度級
239 if(now_value<min_value_r) 240 { 241 m_r=j; 242 min_value_r=now_value; 243 } 244 } 245 //建立灰度映射表,即用規定直方圖的哪一個值代替當前灰度值
246 daiti[i]=huidu[m_r]; 247 } 248 //對各像素進行處理
249 int linebytes=dib->GetDibWidthBytes(); 250 unsigned char t; 251 for(j=0;j<height;j++) 252 { 253 for(i=0;i<width;i++) 254 { 255 t=*((unsigned char*)p_data+linebytes*j+i); 256 *((unsigned char*)p_data+linebytes*j+i)=daiti[t]; 257 } 258 } 259 } 260 int HuiDuDib::PingJunHuiDu() 261 { 262 float* data=ZhiFangTu(true); 263 float shu=0; 264 for(int i=0;i<256;i++) 265 shu+=data[i]*i; 266 return (int)(shu+0.5); 267 }
3、其中用於顯示直方圖的文件:
頭文件ZhiFangDlg.h:
1 #pragma once
2
3
4 // ZhiFangDlg dialog
5
6 class ZhiFangDlg : public CDialogEx 7 { 8 DECLARE_DYNAMIC(ZhiFangDlg) 9
10 public: 11 ZhiFangDlg(CWnd* pParent = NULL); // standard constructor
12 virtual ~ZhiFangDlg(); 13 void OnPaint(); 14 void SetData(float* data); 15
16 // Dialog Data
17 enum { IDD = 139 };//這里總是出問題,所以直接寫成數字了
18
19 protected: 20 virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
21 float *data; 22 DECLARE_MESSAGE_MAP() 23 public: 24 afx_msg void OnBnClickedButton1(); 25 };
ZhiFangDlg.cpp:
1 // ZhiFangDlg.cpp : implementation file 2 // 3
4 #include "stdafx.h"
5 #include "MfcPictureProcessing.h"
6 #include "ZhiFangDlg.h"
7 #include "afxdialogex.h"
8
9
10 // ZhiFangDlg dialog
11
12 IMPLEMENT_DYNAMIC(ZhiFangDlg, CDialogEx) 13
14 ZhiFangDlg::ZhiFangDlg(CWnd* pParent /*=NULL*/) 15 : CDialogEx(ZhiFangDlg::IDD, pParent) 16 { 17 data=NULL; 18 } 19
20 ZhiFangDlg::~ZhiFangDlg() 21 { 22 } 23
24 void ZhiFangDlg::DoDataExchange(CDataExchange* pDX) 25 { 26 CDialogEx::DoDataExchange(pDX); 27 } 28
29
30 BEGIN_MESSAGE_MAP(ZhiFangDlg, CDialogEx) 31 ON_BN_CLICKED(IDC_BUTTON1, &ZhiFangDlg::OnBnClickedButton1) 32 END_MESSAGE_MAP() 33
34
35 // ZhiFangDlg message handlers
36 void ZhiFangDlg::SetData(float *d) 37 { 38 data=d; 39 } 40 void ZhiFangDlg::OnPaint() 41 { 42 //
43 //CPaintDC dc(this);//用這個畫不出來
44 CDC *dc=GetDC(); 45 CPen* pPen=new CPen;//創建畫筆
46 pPen->CreatePen(PS_SOLID,1,RGB(0,0,0));//創建一支黑筆
47 CGdiObject *pOldPen=dc->SelectObject(pPen);//選中新畫筆,保存舊畫筆
48 int i=0; 49 CString str; 50 CPoint OPos(14,188),NowPos;//繪制坐標系 51 //繪制X坐標軸
52 dc->MoveTo(OPos); 53 NowPos.x=284; 54 NowPos.y=188; 55 dc->LineTo(NowPos); 56 //繪制箭頭
57 dc->LineTo(279,183); 58 dc->MoveTo(NowPos); 59 dc->LineTo(279,193); 60 //繪制x軸坐標系數 61 //下面單獨挑出來是為了好看
62 i=0; 63 dc->MoveTo(OPos.x+i,OPos.y); 64 dc->LineTo(CPoint(OPos.x+i,OPos.y+5)); 65 str.Format(_T("%d"),i); 66 dc->TextOutW(OPos.x+i-5,OPos.y+7,str); 67 for(i=10;i<256;i+=10)//這里有修改
68 { 69 if(i%10==0)//繪制豎杠
70 { 71 dc->MoveTo(OPos.x+i,OPos.y); 72 dc->LineTo(CPoint(OPos.x+i,OPos.y+5)); 73 } 74 if(i%40==0)//繪制字
75 { 76 str.Format(_T("%d"),i); 77 dc->TextOutW(OPos.x+i-10,OPos.y+7,str); 78 } 79 } 80 //繪制y軸坐標系數
81 dc->MoveTo(OPos); 82 NowPos.x=OPos.x; 83 NowPos.y=4; 84 dc->LineTo(NowPos); 85 //繪制箭頭
86 dc->LineTo(NowPos.x-5,NowPos.y+5);//畫筆最后停在哪里,繪制的中心就在哪里
87 dc->MoveTo(NowPos); 88 dc->LineTo(NowPos.x+5,NowPos.y+5); 89 //尋找數組最大的數據
90 float max=0; 91 for(i=0;i<256;i++) 92 { 93 if(max<data[i]) 94 max=data[i]; 95 } 96 //y軸坐標系數的數據步長 97 //float Tstep=max/10; 98 //y軸坐標系數的刻度步長
99 float Ystep=174/20; 100 //顯示y坐標的刻度和數據
101 i=20; 102 dc->MoveTo(OPos.x,OPos.y-Ystep*i); 103 dc->LineTo(OPos.x+5,OPos.y-Ystep*i); 104 str.Format(_T("%f"),max);//這里修改了
105 dc->TextOutW(20,OPos.y-Ystep*i-20,str); 106
107 //繪制灰度直方圖
108 for(i=0;i<256;i++) 109 { 110 NowPos.x=OPos.x+i; 111 NowPos.y=OPos.y; 112 dc->MoveTo(NowPos); 113 NowPos.y=187-20*Ystep*data[i]/max;//計算比例,用20*Ystep,不能用175,因為中間精度缺失了太多
114 dc->LineTo(NowPos); 115 } 116 dc->SelectObject(pOldPen); 117
118 delete pPen; 119 //Invalidate();
120 } 121
122 void ZhiFangDlg::OnBnClickedButton1() 123 { 124 if(data!=NULL) 125 OnPaint(); 126 }
4、用於調用上面文件的運行函數:
1 void CMfcPictureProcessingDlg::On32797()//灰度非零取一
2 { 3 if(filePath.Compare(_T(""))!=0) 4 { 5 CDib dib; 6 dib.LoadFile(filePath); 7 if(dib.m_valid) 8 { 9 HuiDuDib d; 10 d.GetDib(&dib); 11 d.Fei0(); 12 CViewImage i; 13 i.GetDib(&dib); 14 CDC *pDC=GetDC(); 15 i.OnDraw2(pDC,dib.GetWidth()+5,0); 16 } 17 } 18 else{ 19 MessageBox(_T("請先選擇文件!"),_T("提示"),MB_OK); 20 } 21 } 22
23
24 void CMfcPictureProcessingDlg::On32798()//灰度固定閾值
25 { 26 if(filePath.Compare(_T(""))!=0) 27 { 28 CDib dib; 29 dib.LoadFile(filePath); 30 if(dib.m_valid) 31 { 32 HuiDuDib hui; 33 hui.GetDib(&dib); 34 GuDingDlg gd; 35 gd.DoModal(); 36 if(gd.ifok) 37 { 38 hui.GuDing(gd.GetYuZhi()); 39 CViewImage i; 40 i.GetDib(&dib); 41 CDC *pDC=GetDC(); 42 i.OnDraw2(pDC,dib.GetWidth()+5,0); 43 } 44 } 45 } 46 else{ 47 MessageBox(_T("請先選擇文件!"),_T("提示"),MB_OK); 48 } 49 } 50
51 void CMfcPictureProcessingDlg::On32800()//灰度雙閾值
52 { 53 if(filePath.Compare(_T(""))!=0) 54 { 55 CDib dib; 56 dib.LoadFile(filePath); 57 if(dib.m_valid) 58 { 59 HuiDuDib hd; 60 hd.GetDib(&dib); 61 ShuangYuZhiDlg s; 62 s.DoModal(); 63 if(s.ifok) 64 { 65 hd.ShuangYu(s.GetLow(),s.GetHigh(),s.GetMode()); 66 CViewImage imageview; 67 imageview.GetDib(&dib); 68 CDC *pDC=GetDC(); 69 imageview.OnDraw2(pDC,dib.GetWidth()+5,0); 70 } 71 } 72 } 73 else{ 74 MessageBox(_T("請先選擇文件!"),_T("提示"),MB_OK); 75 } 76 } 77
78
79 void CMfcPictureProcessingDlg::On32801()//灰度反色變換
80 { 81 if(filePath.Compare(_T(""))!=0) 82 { 83 CDib dib; 84 dib.LoadFile(filePath); 85 if(dib.m_valid) 86 { 87 CDC *pDC=GetDC(); 88 HuiDuDib h; 89 h.GetDib(&dib); 90 h.FanSe(); 91 CViewImage imageview; 92 imageview.GetDib(&dib); 93 imageview.OnDraw2(pDC,dib.GetWidth()+5,0); 94 } 95 } 96 else{ 97 MessageBox(_T("請先選擇文件!"),_T("提示"),MB_OK); 98 } 99 } 100
101
102 void CMfcPictureProcessingDlg::On32802()//灰度窗口變換
103 { 104 if(filePath.Compare(_T(""))!=0) 105 { 106 CDib dib; 107 dib.LoadFile(filePath); 108 if(dib.m_valid) 109 { 110 ChuangkouDlg k; 111 k.DoModal(); 112 if(k.ifok) 113 { 114 HuiDuDib h; 115 h.GetDib(&dib); 116 h.ChuangKou(k.GetLow(),k.GetHigh()); 117 CDC *pDC=GetDC(); 118 CViewImage imageview; 119 imageview.GetDib(&dib); 120 imageview.OnDraw2(pDC,dib.GetWidth()+5,0); 121 } 122 } 123 } 124 else{ 125 MessageBox(_T("請先選擇文件!"),_T("提示"),MB_OK); 126 } 127 } 128
129
130 void CMfcPictureProcessingDlg::On32803()//折線變換
131 { 132 if(filePath.Compare(_T(""))!=0) 133 { 134 CDib dib; 135 dib.LoadFile(filePath); 136 if(dib.m_valid) 137 { 138 ZheXianDlg z; 139 z.DoModal(); 140 if(z.ifok) 141 { 142 CDC *pDC=GetDC(); 143 HuiDuDib hdib; 144 hdib.GetDib(&dib); 145 hdib.ZheXian(z.GetX1(),z.GetY1(),z.GetX2(),z.GetY2()); 146 CViewImage imageview; 147 imageview.GetDib(&dib); 148 imageview.OnDraw2(pDC,dib.GetWidth()+5,0); 149 } 150 } 151 } 152 else{ 153 MessageBox(_T("請先選擇文件!"),_T("提示"),MB_OK); 154 } 155 } 156
157
158 void CMfcPictureProcessingDlg::On32804()//灰度直方圖
159 { 160 if(filePath.Compare(_T(""))!=0) 161 { 162 CDib dib; 163 dib.LoadFile(filePath);//這里為了練習效果,只顯示原圖的
164 if(dib.m_valid) 165 { 166 HuiDuDib hdib; 167 hdib.GetDib(&dib); 168 float *p=hdib.ZhiFangTu(true); 169 //開始畫直方圖
170 ZhiFangDlg d; 171 d.SetData(p); 172 d.DoModal(); 173 delete []p; 174 } 175 } 176 else{ 177 MessageBox(_T("請先選擇文件!"),_T("提示"),MB_OK); 178 } 179 } 180
181
182 void CMfcPictureProcessingDlg::On32805()//灰度分布均衡化
183 { 184 if(filePath.Compare(_T(""))!=0) 185 { 186 CDib dib; 187 dib.LoadFile(filePath); 188 if(dib.m_valid) 189 { 190 HuiDuDib hdib; 191 hdib.GetDib(&dib); 192 hdib.FenBuJunHengHua(); 193 CViewImage imageview; 194 imageview.GetDib(&dib); 195 CDC* pDC=GetDC(); 196 imageview.OnDraw2(pDC,dib.GetWidth()+5,0); 197 float* p=hdib.ZhiFangTu(true); 198 ZhiFangDlg d; 199 d.SetData(p); 200 d.DoModal(); 201 delete []p; 202 } 203 } 204 else{ 205 MessageBox(_T("請先選擇文件!"),_T("提示"),MB_OK); 206 } 207 }
5、運行:
結束~