用C++实现图像旋转变换
用C++实现图像旋转变换

这里主要讨论以图象的中心为圆心旋转。
旋转之后若要保持目标区域大小不变,则整幅图像变大;若要保持整幅图像的大小不变,则旋转出去的部分需要裁剪掉。
旋转前的图旋转后的图旋转后保持原图大小,转出的部分被裁掉以顺时针旋转为例来堆到旋转变换公式。
如下图所示。
旋转前:x0=rcosb;y0=rsinb旋转a角度后:x1=rcos(b-a)=rcosbcosa+rsinbsina=x0cosa+y0sinay 1=rsin(b-a)=rsinbcosa-rcosbsina=-xsina+ycosa矩阵形式为逆变换为上面的公式是以图像的左下角为原点旋转的。
现我们要以图像的中心为原点旋转。
因此需要先将坐标平移到图像中心,如下所示设图象的宽为w,高为h,容易得到:逆变换为现在可以分三步来完成旋转变换:1. 将坐标系x'o'y'平移到xoy ;2. 在xoy坐标系下作旋转变换;3.变换后将坐标系平移回原来位置。
用矩阵表示就是其中R表示旋转变换矩阵。
当旋转不改变图像大小时,T 与T' 互为逆矩阵;当旋转后图像变大时,T 与T'不是逆矩阵关系,因为图像变大了,第一次平移和第二次平移坐标系的距离不一样。
因此当图像变大时,公式应该如下:由于算法实现过程中我们需要的是逆变换的公式,因此我们只写出逆变换的表达式,如下:其中wn ,hn 表示新图像的宽和高,wo, ho 表示原图像的宽和高。
这样,对于新图中的每一点,我们就可以根据上面逆变换公式求出对应原图中的点,得到它的灰度。
如果超出原图范围,则设置为背景色。
要注意的是,由于有浮点运算,计算出来点的坐标可能不是整数,采用取整处理,即找最接近的点,这样会带来一些误差(图象可能会出现锯齿)。
更精确的方法是采用插值,这里暂不讨论。
C++代码如下:/** rotate.cpp* 图像旋转变换(顺时针)* Created on: 2011-10-10* Author: LiChanghai*/// 以图像中心为坐标原点,旋转后不改变图像大小// 函数返回值为指针,指向新申请的内存区域// 因为新图像大小改变了,需要返回新图像的尺寸// 因此形参的高度和宽度都采用指针变量#include<string.h>#include<stdlib.h>#include<cmath>#define pi 3.1415926535unsigned char* rotate(unsigned char*pImage, int*width, int*height, int biBitCount, float angle){//定义以图像中心为原点的坐标系下原图像和新图像的四个角点坐标float src_x1, src_y1, src_x2, src_y2, src_x3, src_y3, src_x4, src_y4;float dst_x1, dst_y1, dst_x2, dst_y2, dst_x3, dst_y3, dst_x4, dst_y4;//定义新图像的高度和宽度int wnew, hnew;//定义计算过程中需要的相关变量float sina, cosa, temp1, temp2, alpha;//角度转化为弧度alpha=pi*angle/180;cosa = float(cos(double(alpha)));sina = float(sin(double(alpha)));//原图像的四个角点的坐标src_x1 = float(-0.5*(*width)); src_y1 = float(0.5*(*height));src_x2 = float(0.5*(*width)); src_y2 = src_y1;src_x3 = src_x1; src_y3 = float(-0.5*(*height));src_x4 = src_x2; src_y4 = src_y3;//计算新图像的四个角点坐标dst_x1 = cosa*src_x1+sina*src_y1;dst_y1 = -sina*src_x1+cosa*src_y1;dst_x2 = cosa*src_x2+sina*src_y2;dst_y2 = -sina*src_x2+cosa*src_y2;dst_x3 = cosa*src_x3+sina*src_y3;dst_y3 = -sina*src_x3+cosa*src_y3;dst_x4 = cosa*src_x4+sina*src_y4;dst_y4 = -sina*src_x4+cosa*src_y4;//计算新图像的高度和宽度float t1 = fabs(dst_x4-dst_x1), t2 = fabs(dst_x3-dst_x2);wnew = int(t1>t2 ? t1:t2);t1 = fabs(dst_y4-dst_y1), t2 = fabs(dst_y3-dst_y2);hnew = int(t1>t2 ? t1:t2);// 计算旋转变换中的两个中间变量,便于以后计算temp1=float( -0.5*wnew*cosa+0.5*hnew*sina+0.5*(*width));temp2=float(-0.5*wnew*sina-0.5*hnew*cosa+0.5*(*height));//计算原图像和新图像每行像素所占的字节数(必须是4的倍数)int lineByte = ((*width) * biBitCount/8+3)/4*4;int lineByte2=(wnew * biBitCount/8+3)/4*4;//申请新的位图数据存储空间unsigned char*pImage2;pImage2=new unsigned char[lineByte2*hnew];//将新图像设置为背景色memset(pImage2, 0, lineByte2*hnew);//遍历新图像的每一个像素进行判断int x, y, x0, y0; // x0, y0为原图像中对应坐标for(y=0; y<hnew; y++)for(x=0; x<wnew; x++){x0= int(x*cosa-y*sina+temp1);y0= int(x*sina+y*cosa+temp2);//如果在原图像范围内则复制像素值if( (x0>=0) && (x0<(*width)) && (y0>=0) && (y0<(*height))){*(pImage2+lineByte2*y+x) = *(pImage+lineByte*y0+x0);}}//修改原图像的高度和宽度*width = wnew;*height = hnew;delete [ ] pImage; //释放原内存空间return pImage2;}该程序在Eclipse上调试通过,结果正确。
opencv 图像翻转旋转

opencv 图像翻转、旋转转自:/watkinsong/article/details/91896491.图像左右翻转、翻转90度opencv中并没有直接封装图像旋转任意角度的函数,一般我们可以使用仿射变换获得旋转后的图像,这时候可以进行任意角度的旋转,但是如果我们需要将图像旋转90度,例如只是对图像进行左右翻转,或者旋转90度将图像放倒,那么如果还使用仿射变换,显得有些不是很简单,有点过于复杂。
实际上可以使用求转置矩阵的方式将图像旋转90度,然后可以沿着指定的坐标轴对旋转后的图像进行翻转变化。
使用transpose(src, dst);对目标图像进行转置变换,可以将垂直的图像变为水平放置。
然后使用flip()函数对图像进行翻转。
整个过程非常简单,可以看下下面的代码就非常清晰的了解了。
// ImageFlip.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include "opencv/cv.h"#include "opencv/highgui.h"#include "stdio.h"#include "iostream"using namespace cv;using namespace std;int _tmain(int argc, _TCHAR* argv[]) {Mat src = imread("lena.jpg");Mat dst;transpose(src, dst);Mat dst2;flip(dst, dst2, 1); // flip by y axis Mat dst3;flip(dst, dst3, 0); // flip by x axis Mat dst4;flip(dst, dst4, -1); // flip by both axises imshow("src", src);imshow("dst", dst);imshow("dst2", dst2);imshow("dst3", dst3);imshow("dst4", dst4); cvWaitKey();return 0;}实验结果:原始图像:转置以后:flip(dst, dst2, 1); // flip by y axis2、任意角度旋转、同时缩放(输出图像大小与输入图像大小相同,容易造成图像不全)下面这份代码用于实现对图像的缩放与旋转。
编程实现一幅图像的平移、镜像、旋转、缩小和放大

课程设计任务书学生姓名:专业班级:通信1003班指导教师:郭志强工作单位:信息工程学院题目: 通信工程应用技术初始条件:(1)使用matlab软件进行操作(2)选择一个图像进行处理要求完成的主要任务:(包括课程设计工作量及其技术要求,以及说明书撰写等具体要求)(1)编程实现一幅图像的平移、镜像、旋转、缩小和放大。
(2)给出所用算法的理论依据和必要的推导过程,给出原始图像和处理后的图像。
时间安排:第15周:安排任务,布置题目;第15—18周:设计仿真,撰写报告第19周:完成设计,提交报告,答辩指导教师签名:年月日系主任(或责任教师)签名:年月日目录摘要 (I)Abstract ........................................................................................................................................................... I I 1 MA TLAB简介 .. (1)1.1 MA TLAB用途 (1)2图像选择及变换 (4)2.1 原始图像选择读取 (4)2.2 图像放大和缩小 (6)2.2.1 图像放大缩小的知识 (6)2.2.2 函数说明及参数选择 (8)2.2.3 源程序及运行结果 (8)2.3 图像任意角度的旋转 (10)2.3.1 函数说明及参数选择 (10)2.3.2 源程序及运行结果 (10)2.4 图像的平移 (12)2.4.1 函数说明及参数选择 (12)2.4.2 源程序及运行结果 (13)2.5 图像经过镜像 (13)3.5.1 函数说明及参数选择 (13)2.5.2 源程序及运行结果 (14)4 感悟体会小结 (17)5 参考文献 (18)附录 (19)全部源程序代码: (19)摘要MATLAB是—套高性能的数值计算和可视化软件,它集数值分析、矩阵运算、信号处理和图形显示于一体,构成—个方便的、界面友好的用户环境。
数字图像处理学

数字图像处理学数字图像处理(digital image processing)是通过计算机对图像进行去除噪声、增强、复原、分割、提取特征等处理的方法和技术。
数字图像处理的产生和迅速发展主要受三个因素的影响:一是计算机的发展;二就是数学的发展(特别就是离散数学理论的创办和健全);三是广泛的农牧业、林业、环境、军事、工业和医学等方面的应用需求的增长。
一、实验内容:主要是图像的几何变换的编程实现,具体包括图像的读取、改写,图像平移,图像的镜像,图像的转置,比例缩放,旋转变换等,具体要求如下:1、编程同时实现图像位移,建议位移后的图像大小维持不变;2、编程实现图像的镜像;3、编程同时实现图像的单位矩阵;4、编程实现图像的比例缩放,要求分别用双线性插值和最近邻插值两种方法来实现,并比较两种方法的缩放效果;5、编程同时实现以任一角度对图像展开旋转变换,建议分别用双线性插值和最近邻插值两种方法去同时实现,并比较两种方法的转动效果。
二、实验目的和意义:本实验的目的就是并使学生熟识并掌控图像处理编程环境,掌控图像位移、镜像、单位矩阵和转动等几何变换的方法,并能够通过程序设计同时实现图像文件的读、写下操作方式,及图像位移、镜像、单位矩阵和转动等几何变换的程序实现。
三、实验原理与主要框架:3.1实验所用编程环境:visualc++(简称vc)是微软公司提供的基于c/c++的应用程序集成开发工具、vc拥有丰富的功能和大量的扩展库,使用它能有效的创建高性能的windows应用程序和web应用程序。
vc除了提供更多高效率的c/c++编译器外,还提供更多了大量的可以器重类和组件,包含知名的谷歌基础类库(mfc)和活动模板类库(atl),因此它就是软件开发人员不可多得的开发工具。
vc丰富的功能和大量的扩展库,类的重用特性以及它对函数库、dll库的支持能使程序更好的模块化,并且通过向导程序大大简化了库资源的使用和应用程序的开发,正由于vc具有明显的优势,因而我选择了它来作为数字图像几何变换的开发工具。
C#图片处理之:旋转图片任意角度.

C#图⽚处理之:旋转图⽚任意⾓度.拍摄的数码相⽚偶尔也有拍歪的时候。
没关系,我们还是可以⽤C#来处理图⽚。
/// <summary>/// 任意⾓度旋转/// </summary>/// <param name="bmp">原始图Bitmap</param>/// <param name="angle">旋转⾓度</param>/// <param name="bkColor">背景⾊</param>/// <returns>输出Bitmap</returns>public static Bitmap KiRotate(Bitmap bmp, float angle, Color bkColor)...{int w = bmp.Width + 2;int h = bmp.Height + 2;PixelFormat pf;if (bkColor == Color.Transparent)...{pf = PixelFormat.Format32bppArgb;}else...{pf = bmp.PixelFormat;}Bitmap tmp = new Bitmap(w, h, pf);Graphics g = Graphics.FromImage(tmp);g.Clear(bkColor);g.DrawImageUnscaled(bmp, 1, 1);g.Dispose();GraphicsPath path = new GraphicsPath();path.AddRectangle(new RectangleF(0f, 0f, w, h));Matrix mtrx = new Matrix();mtrx.Rotate(angle);RectangleF rct = path.GetBounds(mtrx);Bitmap dst = new Bitmap((int)rct.Width, (int)rct.Height, pf);g = Graphics.FromImage(dst);g.Clear(bkColor);g.TranslateTransform(-rct.X, -rct.Y);g.RotateTransform(angle);g.InterpolationMode = InterpolationMode.HighQualityBilinear;g.DrawImageUnscaled(tmp, 0, 0);g.Dispose();tmp.Dispose();return dst;}最近论坛⾥好像有观点认为C#不适合做图⽚处理。
一种基于FPGA的图像旋转实现技术

一种基于FPGA的图像旋转实现技术曹厚德;刘剑波【摘要】图像旋转是一种常用的数字图像处理技术,目前已广泛应用于各领域.绝大多数数字图像显示系统采取从左向右,从上到下的扫描刷新方式.图像数据也是以同样的方式进行存储.旋转功能的实现能够使图像旋转90°、180°和270°后显示在LCD屏幕上.图像旋转技术的研究与应用通常都集中在软件实现技术领域,提出一种基于硬件的通用技术平台,具有极强适应性的解决方案.【期刊名称】《计算机应用与软件》【年(卷),期】2011(028)007【总页数】3页(P89-91)【关键词】图像旋转;FPGA;扫描刷新;Altera【作者】曹厚德;刘剑波【作者单位】上海广电计算机有限公司上海200233;上海广电计算机有限公司上海200233【正文语种】中文0 引言视频应用是当前消费类电子领域的热点,在视频应用中需要用到大量的数字图像处理算法和模块。
专用的ASIC芯片往往无法同时满足各种特定应用的多媒体视频处理需求;而采用软件实现则运算时间过长、实时性较差。
本文提出一种采用FDP250K-II FPGA芯片实现图像旋转技术的应用解决方案。
该方案采用FDP250K-II FPGA芯片作为液晶数字媒体播放机中的后级图像处理模块,实现视场中心的校正、旋转、精度的计算、实时图像插值等数字视频信号的流水处理,再由Altera公司的EP2S30F484C5N芯片自带的LVDS收发模块实现和上海广电计算机有限公司的液晶数字媒体播放机的控制电路、TFT LCD面板之间的视频传输。
1 采用FDP250K-II的图像旋转技术应用解决方案基于FPGA的图像可旋转液晶数字媒体播放机是传统的多媒体电子媒体播放机替代产品。
系统由控制电路板和TFT LCD面板通过若干LVDS信号对连接组成。
采用FDP250K-II的图像旋转技术应用解决方案如图1所示,在系统控制电路板和TFT LCD面板之间插入一块FDP250K-II应用电路板。
c语言实现图像的旋转与平移

实验二图象的几何变换参考资料1 平移平移(translation)变换是几何变换中最简单的一种。
初始坐标为(x0,y0)的点经过平移(t x,t y)(以向右,向下为正方向)后,坐标变为(x1,y1)。
这两点之间的关系是x1=x0+t x ,y1=y0+t y。
下面给出Translation的源代码。
算法的思想是先将所有区域填成白色,然后找平移后显示区域的左上角点(x0,y0) 和右下角点(x1,y1) ,分几种情况进行处理。
先看x方向(width指图象的宽度)(1)t x≤-width:很显然,图象完全移出了屏幕,不用做任何处理;(2)-width<tx≤0:图象区域的x范围从0到width-|tx|,对应原图的范围从|tx|到width;(3)0< t x <width:图象区域的x范围从t x到width,对应原图的范围从0到width - t x ;(4)t x≥width:图象完全移出了屏幕,不用做任何处理。
y方向是对应的(height表示图象的高度):(1)t y≤-height,图象完全移出了屏幕,不用做任何处理;(2)-height<t y≤0,图象区域的y范围从0到height-|t y|,对应原图的范围从|t y|到height;(3)0<t y<height ,图象区域的y范围从t y到height,对应原图的范围从0到height-t y;(4)t y≥height,图象完全移出了屏幕,不用做任何处理。
这种做法利用了位图存储的连续性,即同一行的象素在内存中是相邻的。
利用memcpy函数,从(x0,y0)点开始,一次可以拷贝一整行(宽度为x1-x0),然后将内存指针移到(x0,y0+1)处,拷贝下一行。
这样拷贝(y1-y0)行就完成了全部操作,避免了一个一个象素的计算,提高了效率。
Translation的源代码如下:int xOffset=0,yOffset=0;BOOL Translation(HWND hWnd){DLGPROC dlgInputBox = NULL;DWORD OffBits,BufSize; LPBITMAPINFOHEADER lpImgData;LPSTR lpPtr;HLOCAL hTempImgData; LPBITMAPINFOHEADER lpTempImgData;LPSTR lpTempPtr;int SrcX0,SrcY0,SrcX1,SrcY1;int DstX0,DstY0,DstX1,DstY1;int RectWidth,RectHeight;BOOL xVisible,yVisible;HDC hDc;HFILE hf;int i;//出现对话框,输入x偏移量xOffset,和y偏移量yOffsetdlgInputBox = (DLGPROC) MakeProcInstance ( (FARPROC)InputBox,ghInst ); DialogBox (ghInst, "INPUTBOX", hWnd, dlgInputBox);FreeProcInstance ( (FARPROC) dlgInputBox );//OffBits为BITMAPINFOHEADER结构长度加调色板的大小OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER);BufSize=OffBits+bi.biHeight*LineBytes;//要开的缓冲区的大小//为新产生的位图分配缓冲区内存if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL){MessageBox(hWnd,"Error alloc memory!","Error Message",MB_OK|MB_ICONEXCLAMATION);return FALSE; //失败,返回}//lpImgData为指向原来位图数据的指针lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);//lpTempImgData为指向新产生位图数据的指针lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData); lpPtr=(char *)lpImgData;lpTempPtr=(char *)lpTempImgData;//将新的缓冲区中的每个字节都填成255,这样以后未处理的象素就是白色memset(lpTempPtr,(BYTE)255,BufSize);//两幅图之间的头信息,包括调色板都是相同的,所以直接拷贝头和调色板memcpy(lpTempPtr,lpPtr,OffBits);//xVisible为FALSE时,表示x方向已经移出了可显示的范围xVisible=TRUE;if( xOffset<= -bi.biWidth )xVisible=FALSE;else if( xOffset<=0){DstX0=0; //表示移动后,有图区域的左上角点的x坐标DstX1=bi.biWidth+xOffset; //表示移动后,有图区域的右下角点的x坐标}else if ( xOffset<bi.biWidth){DstX0=xOffset;DstX1=bi.biWidth;}elsexVisible=FALSE;SrcX0=DstX0-xOffset; //对应DstX0在原图中的x坐标SrcX1=DstX1-xOffset; //对应DstX1在原图中的x坐标RectWidth=DstX1-DstX0; //有图区域的宽度//yVisible为FALSE时,表示y方向已经移出了可显示的范围yVisible=TRUE;if( yOffset<= -bi.biHeight )yVisible=FALSE;else if( yOffset<=0){DstY0=0; //表示移动后,有图区域的左上角点的y坐标DstY1=bi.biHeight+yOffset; //表示移动后,有图区域的右下角点的y坐标}else if ( yOffset<bi.biHeight){DstY0=yOffset;DstY1=bi.biHeight;}elseyVisible=FALSE;SrcY0=DstY0-yOffset; //对应DstY0在原图中的y坐标SrcY1=DstY1-yOffset; //对应DstY1在原图中的y坐标RectHeight=DstY1-DstY0; //有图区域的高度if( xVisible && yVisible){ //x,y方向都没有完全移出可显示的范围for(i=0;i<RectHeight;i++){ //拷贝每一行//lpPtr指向要拷贝的那一行的最左边的象素对应在原图中的位//置。
opencv中缩放旋转模板匹配原理

Opencv是一个开源计算机视觉库,可用于处理图像和视频。
在Opencv中,缩放、旋转和模板匹配是常见的图像处理操作,了解这些操作的原理对于深入理解Opencv的工作原理非常重要。
本文将对Opencv中缩放、旋转和模板匹配的原理进行详细介绍,希望能帮助读者更好地掌握图像处理的相关知识。
一、缩放原理1.1 缩放的概念在图像处理中,缩放是指改变图像的尺寸大小。
通常情况下,我们会将图像缩小或放大到需要的大小。
Opencv中提供了resize函数来实现图像的缩放操作。
1.2 缩放的原理Opencv中的resize函数使用了插值算法来实现图像的缩放。
常见的插值算法包括最近邻插值、双线性插值和立方插值。
在进行图像缩放时,resize函数会根据目标图像的大小和原始图像的大小,使用插值算法来计算新图像中每个像素的数值。
这样就可以实现图像的缩放操作。
二、旋转原理2.1 旋转的概念旋转是指改变图像的旋转角度,使得图像产生旋转的效果。
Opencv 中提供了getRotationMatrix2D和warpAffine函数来实现图像的旋转操作。
2.2 旋转的原理Opencv中的旋转操作是通过仿射变换来实现的。
使用getRotationMatrix2D函数来计算出旋转的变换矩阵。
使用warpAffine函数来对图像进行仿射变换,从而实现图像的旋转效果。
在进行仿射变换时,Opencv会对图像进行插值计算,以确保图像旋转后的质量和清晰度。
三、模板匹配原理3.1 模板匹配的概念模板匹配是一种在图像中寻找指定模式的方法。
在Opencv中,模板匹配可以用来在一幅图像中搜索和识别特定的模式。
Opencv中提供了matchTemplate和minMaxLoc函数来实现模板匹配操作。
3.2 模板匹配的原理Opencv中的模板匹配是通过将一个模板图像与另一幅目标图像进行比较来实现的。
将模板图像和目标图像转换为灰度图像。
使用matchTemplate函数对目标图像进行模板匹配计算,得到一个结果图像。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
用C++实现图像旋转变换
(代码较长,
本人使用C++实现了一个类似GDI+ Matrix的C++几何变换类TransformMatrix。
略,参见我的BLOG文章《实现完整的图像平面几何变换》)。
我所说的“实现完整的图像平面几何变换”,是指可以通过TransformMatrix::Multiply函数或者更直接的变换矩阵成员设置去实现“完整的”图像
几何变换,除非其不能使用平面几何变换矩阵进行描述(如梯形变换我就没想到怎么实现,
也许其超出了平面几何变换矩阵范畴?),或者不能进行实际的几何变换(不可逆);“实现
完整的图像几何变换”的另一层含义是下面的图像变换执行函数可实现TransformMatrix
所能表示的任意图像几何变换,而不必去写一个个具体的,如缩放、旋转变换函数等。
C/C++ code
// 获取子图数据
BOOL GetSubBitmapData(CONST BitmapData *data, INT x, INT y, INT width, INT height, BitmapDa {
if (x < 0)
{
width += x;
x = 0;
}
if (x + width > (INT)data->Width)
width = (INT)data->Width - x;
if (width <= 0) return FALSE;
if (y < 0)
{
height += y;
y = 0;
}
if (y + height > (INT)data->Height)
height = (INT)data->Height - y;
if (height <= 0) return FALSE;
sub->Width = width;
sub->Height = height;
sub->Stride = data->Stride;
sub->Scan0 = (CHAR*)data->Scan0 + y * data->Stride + (x << 2);
return TRUE;
}
// 执行图像数据几何变换
VOID Transform(BitmapData *dest, INT x, INT y, CONST BitmapData *source, TransformMatrix *m {
// 复制几何变换矩阵对象
TransformMatrix m(matrix);
// 几何变换矩阵绝对增加平移量x, y
m.GetElements().dx += x;
m.GetElements().dy += y;
// 按几何变换矩阵计算并获取目标图像数据子数据
float fx, fy, fwidth, fheight;
m.GetTransformSize(source->Width, source->Height, fx, fy, fwidth, fheight);
BitmapData dst;
if (!GetSubBitmapData(dest, (INT)fx, (INT)fy,
(INT)(fwidth + 0.999999f), (INT)(fheight + 0.999999f), &dst))
return;
// 获取几何变换逆矩阵
if (!m.Invert()) return;
// 如果子图数据与目标图像原点不一致,几何变换矩阵相对增加平移量fx, fy
if (fx > 0.0f || fy > 0.0f)
{
if (fx < 0.0f) fx = 0.0f;
else if (fy < 0.0f) fy = 0.0f;
m.Translate(fx, fy);
}
// 设置子图扫描线指针及行偏移宽度
UINT *pix = (UINT*)dst.Scan0;
INT dstOffset = (dst.Stride >> 2) - dst.Width;
// 几何变换逆矩阵的平移量为与子图原点对应的源图起始坐标点
MatrixElements e = m.GetElements();
float xs = e.dx;
float ys = e.dy;
// 逐点计算并复制源图几何变换后的数据到目标子图
for (y = 0; y < (INT)dst.Height; y ++, pix += dstOffset, xs += e.m21, ys += e.m22)
{
float xs0 = xs;
float ys0 = ys;
for (x = 0; x < (INT)dst.Width; x ++, pix ++, xs0 += e.m11, ys0 += e.m12)
{
INT x0 = xs0 < 0.0f? (INT)(xs0 - 0.5f) : (INT)(xs0 + 0.5f);
INT y0 = ys0 < 0.0f? (INT)(ys0 - 0.5f) : (INT)(ys0 + 0.5f);
if (y0 >= 0 && y0 < (INT)source->Height && x0 >= 0 && x0 < (INT)source->Width) *pix = *(UINT*)((CHAR*)source->Scan0 + y0 * source->Stride + (x0 << 2));
}
}
}
函数特点:
1、可以实现任意的图像几何变换(只要TransformMatrix能正确表达的,即变换矩阵
可逆);
2、采用了GDI+ 的BitmapData结构(转换为32位ARGB像素格式),而并非任何具体
的图像格式,保证了其通用性;
3、函数使用浮点数运算,但在计算像素点位置时避免了通常的浮点数乘除运算,既提高了一定的运算速度,也为以后修改为定点数运算奠定了基础;
4、函数采用临近像素插值,且没有边界像素处理代码,像素复制质量较差。
可以看出,Transform函数的着重点在于特点(1),在实际的实现代码中,可以把它作为一个框架进行扩充和修改。
下面是例子运行截图:
GDI+位图旋转45度
VCL位图缩放与剪切组合变换。