第四章 图像的灰度变换
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、运行:
结束~