【Emgu CV教程】5.5、几何变换之WarpAffine()仿射变换

仿射变换,又是一个很复杂的数学概念,具体的描述还是读者们自己取百度一下吧。我找了一段其它网友对于图像仿射变换的描述:仿射变换是指图像可以通过一系列的几何变换来实现平移、旋转等多种操作。该变换能够保持图像的平直性和平行性,平直性是指图像经过仿射变换后,直线仍然是直线;平行性是指图像在完成仿射变换后,平行线仍然是平行线。

作者水平有限,具体的数学原理和实现过程暂时就不解释了。这里只用代码告诉大家它能干什么。先看一下WarpAffine()函数的说明:

public static void WarpAffine(
    IInputArray src,  // 输入图像
    IOutputArray dst, // 输出图像
    IInputArray mapMatrix, // 一个2X3的变换矩阵
    Size dsize, // 变换后的大小
    Inter interMethod = Inter.Linear, // 插值方法标志,不明白
    Warp warpMethod = Warp.Default,  // 扭曲方法,不明白
    BorderType borderMode = BorderType.Constant, // 边类型,不明白
    MCvScalar borderValue = default(MCvScalar)  // 边界值,默认为 0
)

这个函数比较复杂,好多参数我都不知道什么意思,不过不耽误使用。以一张  东北虎.jpg(宽度995,高度597)为例,下面逐个功能介绍:

1、图形平移

比如想要让图像右移120像素,下移50像素,运行以下代码:

Mat tempMat = srcMat.Clone();
Mat dstMat = new Mat();
Matrix<float> mapMatrix = new Matrix<float>(2, 3, 1);
mapMatrix[0, 0] = 1;
mapMatrix[0, 1] = 0;
mapMatrix[0, 2] = 120;
mapMatrix[1, 0] = 0;
mapMatrix[1, 1] = 1;
mapMatrix[1, 2] = 50;
CvInvoke.WarpAffine(tempMat, dstMat, mapMatrix, new System.Drawing.Size(tempMat.Cols, tempMat.Rows)); // 进行仿射变换
CvInvoke.Imshow("Result Mat, " + dstMat.Size.ToString(), dstMat);

结果是什么样呢,看下图,输出的图像大小没变,但是移动过的部分用黑色填充,原始图像也丢失了一部分细节?

2、三点法变换

三点法就是仿射变换由平移、缩放、旋转、翻转和错切最终得到结果。具体含义都是线性代数方面的知识,看不懂。读者只需要了解使用方法就行。三个原始点和三个变换后的点对应关系是:

P1=(0,0)   -------->   P1(Cols * 0.15,Rows * 0.05)

P2=(Cols - 1,0)  -------->    P2(Cols * 0.91,Rows * 0.15)

P3=(0,Rows - 1)     -------->     P3 (Cols * 0.33,Rows 0.72)

实现上面仿射变换的代码如下:

Mat tempMat = srcMat.Clone();
Mat dstMat = new Mat();

PointF[] pointSrc = new PointF[] { new PointF(0, 0), new PointF(tempMat.Cols - 1, 0), new PointF(0, tempMat.Rows - 1) };
PointF[] pointDst = new PointF[] { new PointF((float)(tempMat.Cols * 0.15), (float)(tempMat.Rows * 0.05)), new PointF((float)(tempMat.Cols * 0.91), (float)(tempMat.Rows * 0.15)), new PointF((float)(tempMat.Cols * 0.33), (float)(tempMat.Rows * 0.72)) };
Mat rotationMatrix = CvInvoke.GetAffineTransform(pointSrc, pointDst);
float[,,] fArray = rotationMatrix.ToImage<Gray, float>().Data;

CvInvoke.WarpAffine(tempMat, dstMat, rotationMatrix, srcMat.Size);

CvInvoke.Circle(tempMat, new System.Drawing.Point(0, 0), 10, new MCvScalar(255, 0, 0), -1); // 原始P1,蓝色
CvInvoke.Circle(tempMat, new System.Drawing.Point(tempMat.Cols - 1, 0), 10, new MCvScalar(0, 255, 0), -1); // 原始P2,绿色
CvInvoke.Circle(tempMat, new System.Drawing.Point(0, tempMat.Rows - 1), 10, new MCvScalar(0, 0, 255), -1); // 原始P3,红色
CvInvoke.Circle(dstMat, new System.Drawing.Point((int)(tempMat.Cols * 0.15), (int)(tempMat.Rows * 0.05)), 10, new MCvScalar(255, 0, 0), -1); // 目标P1,蓝色
CvInvoke.Circle(dstMat, new System.Drawing.Point((int)(tempMat.Cols * 0.91), (int)(tempMat.Rows * 0.15)), 10, new MCvScalar(0, 255, 0), -1); // 目标P2,绿色
CvInvoke.Circle(dstMat, new System.Drawing.Point((int)(tempMat.Cols * 0.33), (int)(tempMat.Rows * 0.72)), 10, new MCvScalar(0, 0, 255), -1); // 目标P3,红色
CvInvoke.Imshow("Source Mat, " + tempMat.Size.ToString(), tempMat);
CvInvoke.Imshow("Result Mat, " + dstMat.Size.ToString(), dstMat);

首先要计算出三个原始点和三个目标点,各自存储到PointF[]类型的数组中,再用GetAffineTransform()获取变换矩阵fArray,再次执行WarpAffine()函数就可以,同样,输出的图像大小没变。

3、利用GetRotationMatrix2D()函数获取变换矩阵

还有一种仿射变换的方法,假如我希望原始的图片,以任意一点为中心坐标,旋转一个角度,这种变换方式,就可以使用GetRotationMatrix2D()函数。要先获取旋转矩阵,再执行WarpAffine()函数。

比如让  东北虎.jpg  以左上角Point(0,0)为旋转中心,逆时针旋转15度,并且整幅图像缩小为原来的0.65倍,效果是这样的:

代码怎么写呢,看下面:

Mat tempMat = srcMat.Clone();
Mat mapMatrix = new Mat();
PointF p = new PointF(0, 0);
double angle = Double.Parse(-15);
double scale = Double.Parse(0.65);
CvInvoke.GetRotationMatrix2D(p, angle, scale, mapMatrix);
float[,,] fArray = mapMatrix.ToImage<Gray, float>().Data;
Mat dstMat = new Mat();
CvInvoke.WarpAffine(tempMat, dstMat, mapMatrix, srcMat.Size);
CvInvoke.Imshow("Result Mat, " + dstMat.Size.ToString(), dstMat);

GetRotationMatrix2D()就三个参数,旋转中心、旋转角度、缩放比例。旋转中心和缩放比例都好理解,旋转角度要注意,顺时针旋转,角度参数angle是负数,逆时针旋转是正负数。输出的图像大小还是没变。

4、总结

上面三个例子之所以没有改变输出图像大小,是因为在执行WarpAffine()时,dsize参数就是原始图像的尺寸。仿射变换就是能缩放、平移、旋转、和更复杂的变换。但是记住啊,仿射变换的平直性和平行性,还是以三点法为例,调整的参数不变,代码也不变,换个更明显的例子:

变换后的图像,虽然外形改变了,但直线没有变形,平行线也没变形,明白了吧。

原创不易,请勿抄袭。共同进步,相互学习。  

  • 25
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值