图象的几何变换
平移(translation)变换大概是几何变换中最简单的一种了。如下图,初始坐标为(x0,y0)的点经过平移(tx,ty)(以向右,向下为正方向)后,坐标变为(x1,y1)。这两点之间的关系是x1=x0+tx ,y1=y0+ty。
如下图所示
以矩阵的形式表示为
我们更关心的是它的逆变换:
这是因为:我们想知道的是平移后的图象中每个象素的颜色。例如我们想知道,新图中左上角点的RGB值是多少?很显然,该点是原图的某点经过平移后得到的,这两点的颜色肯定是一样的,所以只要知道了原图那点的RGB值即可。那么到底新图中的左上角点对应原图中的哪一点呢?将左上角点的坐标(0,0)入公式(2.2),得到x0=-tx ,y0=-ty;所以新图中的(0,0)点的颜色和原图中(-tx , -ty)的一样。这样就存在一个问题:如果新图中有一点(x1,y1),按照公式(2.2)得到的(x0,y0)不在原图中该怎么办?通常的做法是,把该点的RGB值统一设成(0,0,0)或者(255,255,255)。
旋转(rotation)有一个绕着什么转的问题,通常的做法是以图象的中心为圆心旋转。在我们熟悉的坐标系中,将一个点顺时针旋转a角后的坐标变换公式,如下图所示,r为该点到原点的距离,在旋转过程中,r保持不变;b为r与x轴之间的夹角。
旋转前:x0=rcosb;y0=rsinb
旋转a角度后:
x1=rcos(b-a)=rcosbcosa+rsinbsina=x0cosa+y0sina;
y1=rsin(b-a)=rsinbcosa-rcosbsina=-x0sina+y0cosa;
以矩阵的形式表示:
上面的公式中,坐标系xoy是以图象的中心为原点,向右为x轴正方向,向上为y轴正方向。它和以图象左上角点为原点o’,向右为x’轴正方向,向下为y’轴正方向的坐标系x’o’y’之间的转换关系如何呢
设图象的宽为w,高为h,容易得到:
逆变换为:
理解了上述理论基础,其实在C#中我们有现成的方法函数进行操作,我们可以利用Graphics对象所生成的g.RotateTransform方法函数来对图像进行旋转操作。图像旋转后我们还需要将旋转所得到的图像填充到指定的矩形区域中,在这里我们使用了g.FillRectangle方法函数来进行填充。
private void panel2_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
this.panel2.Refresh();
Graphics g = e.Graphics;
int angel=Convert.ToInt16(this.numericUpDown4.Value);
System.Drawing.Bitmap temp=new Bitmap(filepath);
TextureBrush brush=new TextureBrush(temp);
g.RotateTransform(flaot(angel)); g.FillRectangle(brush,0,0,this.ClientRectangle.Width,this.ClientRectangle.Height);
return;
}
假设放大因子为ratio,(为了避免新图过大或过小,我们在程序中限制0.25≤ratio≤4),缩放(zoom)的变换矩阵很简单:
由于放大图象时产生了新的象素,以及浮点数的操作,得到的坐标可能并不是整数,这一点我们在介绍旋转时就提到了。我们采用的做法是找与之最临近的点。实际上,更精确的做法是采用插值(interpolation),即利用邻域的象素来估计新的象素值。其实我们前面的做法也是一种插值,称为最邻近插值(Nearest Neighbour Interpolation)。下面先介绍线形插值(Linear Interpolation)。
线形插值使用原图中两个值来构造所求坐标处的值。举一个一维的例子。下图所示,如果已经知道了两点x0,x2处的函数值f(x0),f(x2),现在要求x1处的函数值f(x1)。我们假设函数是线形的,利用几何知识可以知道
f(x1)=(f(x2)-f(x0))(x1-x0)/(x2-x0)+f(x0)
在图象处理中需要将线形插值扩展到二维的情况,即采用双线形插值(Bilinear Intrepolation),
线形插值的示意图 |
双线形插值的示意图 |
已知a、b、c、d四点的灰度,要求e点的灰度,可以先在水平方向上由a,b线形插值求出g、c、d线形插值求出f,然后在垂直方向上由g,f线形插值求出e。
线形插值基于这样的假设:原图的灰度在两个象素之间是线形变化的。一般情况下,这种插值的效果还不错。更精确的方法是采用曲线插值(Curvilinear Interpolation),即认为象素之间的灰度变化规律符合某种曲线,但这种处理的计算量是很大的。
同样的,我们可以利用Graphics对象所生成的g.FillRectangle方法函数来对图像进行缩放操作。图像缩放后我们还需要将缩放所得到的图像填充到指定的矩形区域中,同样在这里我们使用了g.FillRectangle方法函数来进行填充。
private void panel2_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
this.panel2.Refresh();
Graphics g = e.Graphics;
float fx=(float)(this.numericUpDown1.Value/10);
float fy=(float)(this.numericUpDown2.Value/10);
System.Drawing.Bitmap temp=new Bitmap(filepath);
TextureBrush brush=new TextureBrush(temp);
g.ScaleTransform(fx,fy); </