首页 > 代码库 > 第三章 图像的几何变换
第三章 图像的几何变换
声明:这一章开始的代码都是本人实际演示过的,按照本人的操作可以运行。程序并没有很强的健壮性,只能对正确的操作做出符合预期的响应。
整本书的代码文件、测试图片和程序运行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 };
二、建立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=http://www.mamicode.com/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=http://www.mamicode.com/(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=http://www.mamicode.com/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=http://www.mamicode.com/(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=http://www.mamicode.com/(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=http://www.mamicode.com/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=http://www.mamicode.com/(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=http://www.mamicode.com/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=http://www.mamicode.com/(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=http://www.mamicode.com/*lpDst; 159 } 160 else *p_data=http://www.mamicode.com/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=http://www.mamicode.com/dib->GetData(); 179 for(j=0;j<height;j++) 180 for(i=0;i<width;i++) 181 { 182 lpSrc=http://www.mamicode.com/(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=http://www.mamicode.com/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 }
三、在菜单栏添加函数,并添加相应的事件处理程序:
四、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 }
程序中我写了很多个用于输入参数的Dlg,代码这里没有填,有遇到的话请到本页一开始的GitHub中下载相应源码(? ω ?)。
五、运行代码看效果,我写的都是很简单的,没有什么健壮性:
打开过程跟之前的一样,详见:http://www.cnblogs.com/studylyn/p/7349819.html
比如说,这是垂直镜像的效果:
六、结束了(? ω ?),有问题欢迎留言讨论!
第三章 图像的几何变换