第三章 图像的几何变换

时间:2023-02-10 19:02:15
VC++图像处理程序设计(第1版)    杨淑莹 编著     边奠英 主审 第二章 图像的几何变换 Joanna-In-Hdu&Hust 手工打,印象更深刻 使用工具 VS2010 mfc

声明:这一章开始的代码都是本人实际演示过的,按照本人的操作可以运行。程序并没有很强的健壮性,只能对正确的操作做出符合预期的响应。

整本书的代码文件、测试图片和程序运行exe请在这里下载https://github.com/CaptainLYN/VCPictureProcessing

这一章中如之前一样,会有很对的错误,比如循环里面的边界有时候会少一多一等,大部分修改的部分都在代码后面说明了,喜欢的同学可以直接拷下来运行试试效果,或者直接前往上面的链接下载源文件。代码都是以类的形式建立的。

 

一、建立JHBHDib.h头文件,建立类,声明好我们需要实现的函数:

第三章 图像的几何变换第三章 图像的几何变换
 1 #pragma once
2 class CDib;
3 class JHBHDib:public CObject
4 {
5 protected:
6 CDib *dib;
7 public:
8 void GetDib(CDib* cdib);
9 JHBHDib();
10 ~JHBHDib();
11 void PingYi(int m_Xmove,int m_Ymove);
12 void JingXiang(bool fangshi);
13
14 //内存一定要在主程序申请,否则程序结束就收回了
15 LPBYTE FangDa(LPBYTE temp,int xzoom,int yzoom,long width,long height,int newwidth,int newheight);//横和纵的放大倍数,这里降低一下要求,是int
16 void SuoXiao(float xzoom,float yzoom);
17 LPBYTE ZhuanZhi();
18 LPBYTE XuanZhuan(double RotateAngle);
19 double RADIAN(double RotateAngle);
20
21 };
View Code

 

二、建立JHBHDib.cpp,用于实现类中的方法,也就是书中的所有功能函数,有一些函数我可能根据现实需要把参数修改了,嘿嘿嘿,不太记得了哈。另外,我觉得我写代码的一个有点就是注释丰富,同时,不得不说,这也是我的一个缺点,但是没有办法,我不会的地方实在太多了,另一方面,更是为了以后不要看不懂啊(。^▽^):

第三章 图像的几何变换第三章 图像的几何变换
  1 #include"stdafx.h"
2 #include"JHBHDib.h"
3 #include"CDib.h"
4 #include<WindowsX.h>
5 #define _USE_MATH_DEFINES
6 #include<cmath>
7 JHBHDib::JHBHDib()
8 {}
9 JHBHDib::~JHBHDib()
10 {}
11 void JHBHDib::GetDib(CDib *cdib)
12 {
13 dib=cdib;
14 }
15 void JHBHDib::PingYi(int m_Xmove,int m_Ymove)//图像平移
16 {
17 LPBYTE lpSrc;//指向原图像像素的指针
18 LPBYTE p_data;//指向原图像数据
19
20 LPBYTE lpDst;//指向要像素的指针
21 LPBYTE temp;//指向复制图像数据的指针
22 LONG i;//循环变量
23 LONG wide,height;//图像的高和宽
24 LONG lLineBytes;//图像每行的字节数
25 p_data=dib->GetData();
26 wide=dib->GetWidth();
27 height=dib->GetHeight();
28 lLineBytes=((wide*8)+31)/32*4;//计算图像每行的字节数:!!!!4字节对齐,不足补零
29 temp=new BYTE[lLineBytes*height];//暂时分配内存,以保存新图像
30 lpDst=(LPBYTE)temp;
31 memset(lpDst,(BYTE)0,lLineBytes*height);//初始化新分配的内存,初始值为255
32 for(i=0;i<wide;i++)//一竖列一竖列的复制
33 {
34 for(int j=0;j<height;j++)
35 {
36 lpSrc=(LPBYTE)p_data+lLineBytes*(height-1-j)+i;//计算该像素在原dib中的坐标
37 int i0,j0;
38 i0=i+m_Xmove;//计算该像素在新dib中的坐标
39 j0=j+m_Ymove;
40 if(i0>=0&&i0<wide&&j0>=0&&j0<height)//判断是否在新图范围内
41 {
42 lpDst=(LPBYTE)temp+lLineBytes*(height-1-j0)+i0;
43 *lpDst=*lpSrc;//复制像素
44 }
45 else
46 {
47 //lpDst=(LPBYTE)temp+lLineBytes*(height-1+j)+i;
48 //下面这个完全多余
49 //*((unsigned char*)lpDst)=255;//对于原图中没有的像素直接赋值为255
50 }
51 }
52 }
53 memcpy(p_data,temp,lLineBytes*height);//赋值平移后的图像:将temp指向的数据复制到p_data
54 delete []temp;//释放内存
55 }
56 void JHBHDib::JingXiang(bool fangshi)//镜像变换,true是水平镜像,false是垂直镜像
57 {
58 LPBYTE lpSrc;
59 LPBYTE p_data;
60 LPBYTE lpDst;
61 LPBYTE temp;
62 LONG i,j;
63 long height=dib->GetHeight();
64 long width=dib->GetWidth();
65 p_data=dib->GetData();
66 temp=new BYTE[width*height];
67 if(fangshi)
68 {
69 for(j=0;j<height;j++)
70 {
71 for(i=0;i<width;i++)
72 {
73 lpSrc=(LPBYTE)p_data+width*j+i;
74 lpDst=(LPBYTE)temp+width*(j+1)-i-1;//一定要减一
75 *lpDst=*lpSrc;
76 }
77 }
78 }
79 else{
80 for(i=0;i<width;i++)
81 for(j=0;j<height;j++)
82 {
83 lpSrc=(LPBYTE)p_data+j*width+i;
84 lpDst=(LPBYTE)temp+width*(height-j-1)+i;
85 *lpDst=*lpSrc;
86 }
87 }
88 memcpy(p_data,temp,width*height);
89 delete []temp;
90 }
91
92 LPBYTE JHBHDib::FangDa(LPBYTE temp,int xzoom,int yzoom,long width,long height,int newwidth,int newheight)//图像的放大,这里改了xzoom和yzoom,因为程序下面要用的也是int,不如直接给int
93 {
94 LPBYTE p_data;//指向原图像
95 p_data=dib->GetData();
96 LPBYTE lpSrc;//指向原像素
97 LPBYTE lpDst;
98 long i,j,i0,j0;//,height,width;
99 int srclinebyte=((width*8+31)/32)*4;
100 int dstlinebyte=((newwidth*8+31)/32)*4;
101 for(j=0;j<height;j++)
102 for(i=0;i<width;i++)
103 {
104 lpSrc=(LPBYTE)p_data+srclinebyte*j+i;
105 for(j0=0;j0<yzoom;j0++)
106 for(i0=0;i0<xzoom;i0++)
107 {
108 lpDst=(LPBYTE)temp+dstlinebyte*(j*yzoom+j0)+i*xzoom+i0;
109 // 行数 新点坐标
110 *lpDst=*lpSrc;
111 }
112 }
113 return temp;
114 }
115
116 void JHBHDib::SuoXiao(float xzoom,float yzoom)
117 {
118 long width,height,newwidth,newheight,i,j,i0,j0;
119 LPBYTE p_data,temp,lpSrc,lpDst;
120
121 temp=dib->GetData();
122 p_data=temp;
123 width=dib->GetWidth();
124 height=dib->GetHeight();
125 newwidth=(long)(width*xzoom+0.5);
126 newheight=(long)(height*yzoom+0.5);
127 int newlinebytes,linebytes;
128
129 if(dib->GetInfo()->bmiHeader.biBitCount==8)//现在就针对灰度图进行运算
130 {
131 linebytes=((width*8+31)/32)*4;
132 newlinebytes=((newwidth*8+31)/32)*4;
133 temp=new BYTE[newlinebytes*newheight];
134 for(j=0;j<newheight;j++)
135 for(i=0;i<newwidth;i++)
136 {
137 lpDst=(LPBYTE)temp+newlinebytes*j+i;
138 //计算该点在原图中的位置
139 j0=(LONG)(j/yzoom+0.5);
140 i0=(LONG)(i/xzoom+0.5);
141 if((i0>=0&&i0<width)&&(j0>=0&&j0<height))//虽然这里加了判断,但是感觉理论上是不可能不在原图的
142 {
143 lpSrc=(LPBYTE)p_data+j0*linebytes+i0;
144 *lpDst=*lpSrc;
145 }
146 else
147 {
148 *lpDst=255;
149 }
150 }
151
152 for(j=0;j<height;j++)
153 for(i=0;i<width;i++)
154 {
155 if(j<newheight&&i<newwidth)
156 {
157 lpDst=(LPBYTE)temp+newlinebytes*j+i;
158 *p_data=*lpDst;
159 }
160 else *p_data=0;
161 p_data++;
162 }
163 delete[]temp;
164 //这里的处理方式和放大是不一样的
165
166 }
167 }
168 LPBYTE JHBHDib::ZhuanZhi()
169 {
170 long width=dib->GetWidth();
171 long height=dib->GetHeight();
172 int linebytes=((width*8+31)/32)*4;
173 int newlinebytes=(height*8+31)/32*4;
174 int i,j;
175 LPBYTE lpSrc,lpDst,temp,p_data;
176 temp=new BYTE[newlinebytes*width];
177 memset(temp,(BYTE)0,newlinebytes*width);
178 p_data=dib->GetData();
179 for(j=0;j<height;j++)
180 for(i=0;i<width;i++)
181 {
182 lpSrc=(LPBYTE)p_data+linebytes*j+i;
183 lpDst=(LPBYTE)temp+newlinebytes*i+j;
184 *lpDst=*lpSrc;
185 }
186 //不用下面这些书因为,图像转置后由于有四字节对齐,所以大小可能会变大,所以,dib类里面销毁的时候大小不对,会出现错误
187 dib->GetInfo()->bmiHeader.biHeight=width;
188 dib->GetInfo()->bmiHeader.biWidth=height;
189 dib->GetInfo()->bmiHeader.biSizeImage=newlinebytes*width;
190 //memcpy(p_data,temp,height*width);//这里这样写linebytes*newlinebytes,否则4字节对齐数据不全会出现边缘彩色
191 //delete(temp);
192 return temp;
193 }
194 double JHBHDib::RADIAN(double RotateAngle)
195 {
196 return RotateAngle*M_PI/180;//要有最上面那个define才行
197 }
198
199 LPBYTE JHBHDib::XuanZhuan(double RotateAngle)//原理看懂了,以后不懂的话看pdf版的就可以了
200 {
201 DWORD DstBufSize;
202 LPBYTE lpTempPtr,lpPtr,lpSrc,lpTemp;
203 double SrcX1,SrcY1,SrcX2,SrcY2,SrcX3,SrcY3,SrcX4,SrcY4;
204 double DstX1,DstY1,DstX2,DstY2,DstX3,DstY3,DstX4,DstY4;
205 DWORD Wold,Hold,Wnew,Hnew;
206 DWORD x0,y0,x1,y1;
207 double cosa,sina;//cos(a),sin(a)
208 double num1,num2;
209
210 //角度到弧度的变化
211 RotateAngle=(double)RADIAN(RotateAngle);
212 cosa=(double)cos((double)RotateAngle);
213 sina=(double)sin((double)RotateAngle);
214
215 //CString s;
216 //s.Format(_T("%lf"),RotateAngle);//将RotateAngle转化为字符串
217 //MessageBox(NULL,s,_T("提示"),MB_OK);//用来测试对不对
218
219 lpSrc=dib->GetData();
220 Wold=dib->GetWidth();
221 Hold=dib->GetHeight();
222 //原图的4个角的坐标
223 SrcX1=(double)(-0.5*Wold);
224 SrcY1=(double)(0.5*Hold);
225 SrcX2=(double)(0.5*Wold);
226 SrcY2=(double)(0.5*Hold);
227 SrcX3=(double)(-0.5*Wold);
228 SrcY3=(double)(-0.5*Hold);
229 SrcX4=(double)(0.5*Wold);
230 SrcY4=(double)(-0.5*Hold);
231 //新图的四个角坐标
232 DstX1=cosa*SrcX1+sina*SrcY1;
233 DstY1=-sina*SrcX1+cosa*SrcY1;
234 DstX2=cosa*SrcX2+sina*SrcY2;
235 DstY2=-sina*SrcX2+cosa*SrcY2;
236 DstX3=cosa*SrcX3+sina*SrcY3;
237 DstY3=-sina*SrcX3+cosa*SrcY3;
238 DstX4=cosa*SrcX4+sina*SrcY4;
239 DstY4=-sina*SrcX4+cosa*SrcY4;
240 //新图的宽度和高度
241 Wnew=(DWORD)(max(fabs(DstX4-DstX1),fabs(DstX3-DstX2))+0.5);
242 Hnew=(DWORD)(max(fabs(DstY4-DstY1),fabs(DstY3-DstY2))+0.5);
243 //计算矩阵中的两个常数,这样不用每次都算
244 num1=(double)(-0.5*Wnew*cosa-0.5*Hnew*sina+0.5*Wold);
245 num2=(double)(0.5*Wnew*sina-0.5*Hnew*cosa+0.5*Hold);
246
247 //这里我选择linebytes,书上不是
248 int newlinebytes=((Wnew*8+31)/32)*4;
249 int linebytes=((Wold*8+31)/32)*4;
250
251 DstBufSize=newlinebytes*Hnew;
252 //DstBufSize=Wnew*Hnew;
253 //原书填入的是白色,虽然我认为要填黑色,但是为了显示效果,还是先暂且选择白色
254 //关于缓冲区,有时候书上是linebytes,有时候又是简单的高和宽的乘积??
255 lpTempPtr=new BYTE[DstBufSize];
256 memset(lpTempPtr,(BYTE)255,DstBufSize);
257 lpTemp=lpTempPtr;//保存住总入口
258 for(y1=0;y1<Hnew;y1++)
259 {
260 for(x1=0;x1<Wnew;x1++)
261 {
262 //x0,y0是该点在原图上的坐标
263 x0=(DWORD)(x1*cosa+y1*sina+num1);
264 y0=(DWORD)(-1.0f*x1*sina+y1*cosa+num2);
265 if(x0>=0&&x0<Wold&&y0>=0&&y0<Hold)//在原图范围内
266 {
267 lpPtr=lpSrc+y0*linebytes+x0;
268 //lpPtr=lpSrc+y0*Wold+x0;//像原书这样是不对的
269 lpTempPtr=lpTemp+y1*newlinebytes+x1;
270 //lpTempPtr=lpTemp+y1*Wnew+x1;
271 *lpTempPtr=*lpPtr;
272
273 //lpTempPtr=lpTemp;
274
275 }
276 }
277 }
278 dib->GetInfo()->bmiHeader.biHeight=Hnew;
279 dib->GetInfo()->bmiHeader.biWidth=Wnew;
280 dib->GetInfo()->bmiHeader.biSizeImage=newlinebytes*Hnew;
281
282 return lpTemp;
283 }
View Code

三、在菜单栏添加函数,并添加相应的事件处理程序:

第三章 图像的几何变换

四、MfcPictureProcessingDlg.cpp中的事件处理程序,即我们辛苦编写的调用函数:

第三章 图像的几何变换第三章 图像的几何变换
  1 void CMfcPictureProcessingDlg::On32789()//图像平移
2 {
3 CDib dib;
4 if(filePath.Compare(_T(""))!=0)
5 {
6 dib.LoadFile(filePath);
7 if(dib.m_valid)
8 {
9 CDC* pDC=GetDC();
10 JHBHDib jdib;
11 jdib.GetDib(&dib);
12 PingYiTiShi p;
13 p.DoModal();
14 if(p.ifok==1)//如果点击了确认键而不是点击退出的叉
15 {
16 jdib.PingYi(p.GetX(),p.GetY());//核心就在这里
17 CViewImage imageview;
18 imageview.GetDib(&dib);
19 imageview.OnDraw2(pDC,dib.GetWidth()+5,0);
20 }
21 }
22 }
23 else{
24 MessageBox(_T("请先选择文件!"),_T("提示"),MB_OK);
25 }
26 }
27
28
29 void CMfcPictureProcessingDlg::On32790()//水平镜像
30 {
31 CDib dib;
32 if(filePath.Compare(_T(""))!=0)
33 {
34 dib.LoadFile(filePath);
35 if(dib.m_valid)
36 {
37 CDC* pDC=GetDC();
38 JHBHDib jdib;
39 jdib.GetDib(&dib);
40 jdib.JingXiang(true);
41 CViewImage imageview;
42 imageview.GetDib(&dib);
43 imageview.OnDraw2(pDC,dib.GetWidth()+5,0);
44 }
45 }
46 else{
47 MessageBox(_T("请先选择文件!"),_T("提示"),MB_OK);
48 }
49 }
50
51
52 void CMfcPictureProcessingDlg::On32791()//垂直镜像
53 {
54 CDib dib;
55 if(filePath.Compare(_T(""))!=0)
56 {
57 dib.LoadFile(filePath);
58 if(dib.m_valid)
59 {
60 CDC* pDC=GetDC();
61 JHBHDib jdib;
62 jdib.GetDib(&dib);
63 jdib.JingXiang(false);
64 CViewImage imageview;
65 imageview.GetDib(&dib);
66 imageview.OnDraw2(pDC,dib.GetWidth()+5,0);
67 DWORD a=dib.bitmapFileHeader.bfSize;
68 }
69 }
70 else{
71 MessageBox(_T("请先选择文件!"),_T("提示"),MB_OK);
72 }
73 }
74
75
76
77 void CMfcPictureProcessingDlg::On32793()//图像放大
78 {
79 CDib dib;
80 if(filePath.Compare(_T(""))!=0)
81 {
82 dib.LoadFile(filePath);
83 if(dib.m_valid)
84 {
85 CDC *pDC=GetDC();
86 JHBHDib jdib;
87 jdib.GetDib(&dib);
88
89 int newwidth,newheight,width,height;
90 float xzoom,yzoom;//先暂且设为int
91 FangDaTiShi p;
92 p.DoModal();
93 if(p.ifok==1)
94 {
95 xzoom=p.Getx();
96 yzoom=p.Gety();
97 width=dib.GetWidth();
98 height=dib.GetHeight();
99 newwidth=(int)(width*xzoom+0.5);//四舍五入的时候是直接截取小数部分的
100 newheight=(int)(height*yzoom+0.5);
101
102 //图像的width并不一定是真正的,实际图像中是4字节对齐存储的
103 int linebyte=((newwidth*8+31)/32)*4;
104 LPBYTE temp;
105 temp=new BYTE[linebyte*newheight];//这里其实平移那个函数已经有提醒了;这样写就是承认一个像素是8位,否认了单色图和真彩色
106 memset(temp,(BYTE)0,linebyte*newheight);
107 LPBYTE b;
108 b=jdib.FangDa(temp,xzoom,yzoom,width,height,newwidth,newheight);
109 BITMAPINFO* p=dib.GetInfo();
110 p->bmiHeader.biWidth=newwidth;
111 p->bmiHeader.biHeight=newheight;
112
113 p->bmiHeader.biSizeImage=linebyte*height;
114 CViewImage imageview;
115 imageview.GetDib(&dib);
116 imageview.OnDraw3(pDC,b,width+5,0);
117 delete[]b;
118 }
119 }
120 }
121 else{
122 MessageBox(_T("请先选择文件!"),_T("提示"),MB_OK);
123 }
124 }
125
126
127
128
129
130 void CMfcPictureProcessingDlg::OnBnClickedButton1()//退出按钮
131 {
132 CDialogEx::OnOK();
133
134 }
135
136
137 void CMfcPictureProcessingDlg::On32794()//图像缩小
138 {
139 if(filePath.Compare(_T(""))!=0)
140 {
141 CDib dib;
142 dib.LoadFile(filePath);
143 if(dib.m_valid)
144 {
145 CDC *pDC=GetDC();
146 JHBHDib jdib;
147 jdib.GetDib(&dib);
148 FangDaTiShi p;
149 //p.SetDlgItemTextW(IDD_Big,_T("缩小"));//设置窗口的标题,不行,有中断
150 p.DoModal();
151 if(p.ifok==1)
152 {
153 float xzoom=p.Getx();
154 float yzoom=p.Gety();
155 int width=dib.GetWidth();
156 int height=dib.GetHeight();
157 long newwidth=(long)(width*xzoom+0.5);
158 long newheight=(long)(height*yzoom+0.5);
159 jdib.SuoXiao(xzoom,yzoom);
160 //这里按照书上的,给出了和放大不一样的解决方案,不用改info,直接在显示的时候将显示的大小划定为新大小,其他的废数据不用管
161 //::StretchDIBits(pDC->GetSafeHdc(),width+5,0,newwidth,newheight,0,0,newwidth,newheight,dib.GetData(),dib.GetInfo(),DIB_RGB_COLORS,SRCCOPY);
162 CViewImage imageview;
163 imageview.GetDib(&dib);
164 imageview.OnDraw4(pDC,width+5,0,newwidth,newheight);
165 }
166 }
167 }
168 else{
169 MessageBox(_T("请先选择文件!"),_T("提示"),MB_OK);
170 }
171 }
172
173
174 void CMfcPictureProcessingDlg::On32795()//图像转置
175 {
176 if(filePath.Compare(_T(""))!=0)
177 {
178 CDib dib;
179 dib.LoadFile(filePath);
180 if(dib.m_valid)
181 {
182 CDC *pDC=GetDC();
183 JHBHDib jdib;
184 jdib.GetDib(&dib);
185 int width=dib.GetWidth();
186 LPBYTE temp=jdib.ZhuanZhi();
187 CViewImage imageview;
188 imageview.GetDib(&dib);
189 imageview.OnDraw3(pDC,temp,width+5,0);
190 //::StretchDIBits(pDC->GetSafeHdc(),width+5,0,dib.GetWidth(),dib.GetHeight(),0,0,dib.GetWidth(),dib.GetHeight(),temp,dib.GetInfo(),DIB_RGB_COLORS,SRCCOPY);
191 delete[]temp;
192 }
193 }
194 else{
195 MessageBox(_T("请先选择文件!"),_T("提示"),MB_OK);
196 }
197
198 }
199
200
201 void CMfcPictureProcessingDlg::On32796()//旋转按钮
202 {
203 if(filePath.Compare(_T(""))!=0)
204 {
205 CDib dib;
206 dib.LoadFile(filePath);
207 if(dib.m_valid)
208 {
209 CDC *pDC=GetDC();
210 JHBHDib jdib;
211 int width=dib.GetWidth();
212 XuanZhuanDlg d;
213 d.DoModal();
214 if(d.ifok)
215 {
216 jdib.GetDib(&dib);
217 LPBYTE t=jdib.XuanZhuan(d.GetAngle());//旋转30度
218
219 //发现一个问题,我这里没有实现调色板,也可以实现画图
220 //::StretchDIBits(pDC->GetSafeHdc(),width+5,0,dib.GetWidth(),dib.GetHeight(),0,0,dib.GetWidth(),dib.GetHeight(),t,dib.GetInfo(),DIB_RGB_COLORS,SRCCOPY);
221 CViewImage imageview;
222 imageview.GetDib(&dib);
223 imageview.OnDraw3(pDC,t,width+5,0);
224 delete[]t;
225 }
226 }
227 }
228 else{
229 MessageBox(_T("请先选择文件!"),_T("提示"),MB_OK);
230 }
231 }
View Code

程序中我写了很多个用于输入参数的Dlg,代码这里没有填,有遇到的话请到本页一开始的GitHub中下载相应源码(❤ ω ❤)。

 

、运行代码看效果,我写的都是很简单的,没有什么健壮性:

打开过程跟之前的一样,详见:http://www.cnblogs.com/studylyn/p/7349819.html

比如说,这是垂直镜像的效果:

第三章 图像的几何变换

 

 六、结束了(❤ ω ❤),有问题欢迎留言讨论!

第三章 图像的几何变换