该部分将对基本的几何变换进行学习,几何变换的原理大多都是相似,只是变换矩阵不同,因此,我们以最常用的平移和旋转为例进行学习。在深度学习领域,我们常用平移、旋转、镜像等操作进行数据增广;在传统CV领域,由于某些拍摄角度的问题,我们需要对图像进行矫正处理,而几何变换正是这个处理过程的基础,因此了解和学习几何变换也是有必要的。
这次我们带着几个问题进行,以旋转为例: * 1:变换的形式(公式)是什么?
2:旋转中心是什么?毕竟以不同位置为旋转中心得到的结果是不一样的。
3:采用前向映射还是反向映射?(反向映射更为有效)
4:采用反向映射后,采用何种插值算法?最常用的的是双线性插值,OpenCV也是默认如此。
了解几何变换的概念与应用
理解平移、旋转的原理
掌握在OpenCV框架下实现平移、旋转操作
1、平移、旋转的原理
2、OpenCV代码实践
3、动手实践并打卡(读者完成)
变换形式
先看第一个问题,变换的形式。与OpencV不同的是这里采取冈萨雷斯的《数字图像处理_第三版》的变换矩阵方式,关于OpenCV的策略可以看它的官方文档。根据冈萨雷斯书中的描述,仿射变换的一般形式如下:
式中的T就是变换矩阵,其中 (v,w)为原坐标,(x,y) 为变换后的坐标,不同的变换对应不同的矩阵,这里也贴出来吧,一些常见的变换矩阵及作用如下表:
也就是说,我们根据自己的目的选择不同变换矩阵就可以了。
坐标系变换
再看第二个问题,变换中心,对于缩放、平移可以以图像坐标原点(图像左上角为原点)为中心变换,这不用坐标系变换,直接按照一般形式计算即可。而对于旋转和偏移,一般是以图像中心为原点,那么这就涉及坐标系转换了。
我们都知道,图像坐标的原点在图像左上角,水平向右为 X 轴,垂直向下为 Y 轴。数学课本中常见的坐标系是以图像中心为原点,水平向右为 X 轴,垂直向上为 Y 轴,称为笛卡尔坐标系。看下图:
因此,对于旋转和偏移,就需要3步(3次变换):
将输入原图图像坐标转换为笛卡尔坐标系;
进行旋转计算。旋转矩阵前面已经给出了;
将旋转后的图像的笛卡尔坐标转回图像坐标。
先看下图:
在图像中我们的坐标系通常是AB和AC方向的,原点为A,而笛卡尔直角坐标系是DE和DF方向的,原点为D。 令图像表示为M×N的矩阵,对于点A而言,两坐标系中的坐标分别是(0,0)和(-N/2,M/2),则图像某像素点(x’,y’)转换为笛卡尔坐标(x,y)转换关系为,x为列,y为行:
逆变换为:
于是,根据前面说的3个步骤(3次变换),旋转(顺时针旋转)的变换形式就为,3次变换就有3个矩阵:
反向映射
看第3个问题,在冈萨雷斯的《数字图像处理_第三版》中说的很清楚,前向映射就是根据原图用变换公式直接算出输出图像相应像素的空间位置,那么这会导致一个问题:可能会有多个像素坐标映射到输出图像的同一位置,也可能输出图像的某些位置完全没有相应的输入图像像素与它匹配,也就是没有被映射到,造成有规律的空洞(黑色的蜂窝状)。更好的一种方式是采用 反向映射(Inverse Mapping):扫描输出图像的位置(x,y),通过 (为T的逆矩阵)计算输入图像对应的位置 (v,w),通过插值方法决定输出图像该位置的灰度值。
插值
第4个问题,采用反向映射后,需通过插值方法决定输出图像该位置的值,因此需要选择插值算法。通常有最近邻插值、双线性插值,双三次插值等,OpencV默认采用双线性插值,我们也就采用双线性插值。
python实现
只是改变图像的尺寸大小,cv2.resize()可以实现这个功能。在缩放时推荐cv2.INTER_AREA,在拓展时推荐cv2.INTER_CUBIC(慢)和cv2.INTER_LINEAR。默认情况下所有改变图像尺寸大小的操作使用的是插值法都是cv2.INTER_LINEAR。
import cv2
img = cv2.imread('storm.png')
#下面的None本应该是输出图像的尺寸,但是因为后面我们设置了缩放因子,所以,这里为None
res = cv2.resize(img,None,fx=2,fy=2,interpolation=cv2.INTER_CUBIC)
#or
#这里直接设置输出图像的尺寸,所以不用设置缩放因子
height , width =img.shape[:2]
res = cv2.resize(img,(2*width,2*height),interpolation=cv2.INTER_CUBIC)
while(1):
cv2.imshow('res',res)
cv2.imshow('img',img)
if cv2.waitKey(1)&0xFF == 27:
break
cv2.destroyAllWindows()
如果想要沿(x,y)方向移动,移动的距离为(tx,ty)可以以下面方式构建移动矩阵。
可以使用Numpy数组构建矩阵,数据类型是np.float32,然后传给函数cv2.warpAffine()
函数cv2.warpAffine() 的第三个参数的是输出图像的大小,它的格式
应该是图像的(宽,高)。应该记住的是图像的宽对应的是列数,高对应的是行数。
对一个图像旋转角度θ,需要使用下面的旋转矩阵。
但OpenCVC允许在任意地方进行旋转,所以矩阵应该为
其中α = scale · cos θ
为构建旋转矩阵,OpenCV提供了一个函数cv2.getRotationMatrix2D。
import cv2
img = cv2.imread('storm.png',0)
rows,cols=img.shape
#这里的第一个参数为旋转中心,第二个为旋转角度,第三个为旋转后的缩放因子
#可以通过设置旋转中心,缩放因子以及窗口大小来防止旋转后超出边界的问题。
M=cv2.getRotationMatrix2D((cols/2,rows/2),45,0.6)
#第三个参数是输出图像的尺寸中心
dst=cv2.warpAffine(img,M,(2*cols,2*rows))
while(1):
cv2.imshow('img',dst)
if cv2.waitKey(1)==27:
break
cv2.destroyAllWindows()
在仿射变换中,原图中所有平行线在结果图像中同样平行。为创建这个矩阵,需要从原图像中找到三个点以及他们在输出图像中的位置,然后cv2.getAffineTransForm()会创建一个2X3的矩阵。最后这个矩阵会被传给函数cv2.warpAffine()
# 4、仿射变换
import cv2
import numpy as np
from matplotlib import pyplot as plt
img=cv2.imread(''storm.png')
rows,cols,ch = img.shape
pts1 = np.float32([50,50],[200,50],[50,200])
pts2 = np.float32([10,100],[200,50],[100,250])
#行,列,通道数
M = cv2.getAffineTransform(pts1,pts2)
dst = cv2.warpAffine(img,M,(cols,rows))
plt.subplot(121,plt.imshow(img),plt.title('Input'))
plt.subplot(121,plt.imshow(img),plt.title('output'))
plt.show()
对于视角变换,我们需要一个3x3变换矩阵。在变换前后直线还是直线。需要在原图上找到4个点,以及他们在输出图上对应的位置,这四个点中任意三个都不能共线,可以有函数cv2.getPerspectiveTransform()构建,然后这个矩阵传给函数cv2.warpPerspective()
# 5、透射变换
import cv2
import numpy as np
from matplotlib import pyplot as plt
img=cv2.imread('storm.png',1)
rows,cols,ch=img.shape
pts1 = np.float32([[56,65],[368,52],[28,387],[389,390]])
pts2 = np.float32([[0,0],[300,0],[0,300],[300,300]])
M=cv2.getPerspectiveTransform(pts1,pts2)
dst=cv2.warpPerspective(img,M,(300,300))
plt.subplot(121,plt.imshow(img),plt.title('Input'))
plt.subplot(122,plt.imshow(img),plt.title('Output'))
plt.show()
相关技术文档、博客、教材、项目推荐
opencv文档: https://docs.opencv.org/3.1.0/da/d54/groupimgproctransform.html#ga0203d9ee5fcd28d40dbc4a1ea4451983
博客:
https://blog.csdn.net/weixin_40647819/article/details/87912122
https://www.jianshu.com/p/18cd12e776e1
https://blog.csdn.net/whuhan2013/article/details/53814026
python版本:
https://blog.csdn.net/g11d111/article/details/79978582
https://www.kancloud.cn/aollo/aolloopencv/264331 http://www.woshicver.com/FifthSection/42%E5%9B%BE%E5%83%8F%E5%87%A0%E4%BD%95%E5%8F%98%E6%8D%A2/