赞
踩
图像几何变换又称为图像空间变换,是各种图像处理算法的基础。它是在不改变图像内容的情况下,对图像像素进行空间几何变换的处理方式。它将一幅图像中的坐标位置映射到另一幅图像中的新坐标位置,其实质是改变像素的空间位置,估算新空间位置上的像素值。
图像的几何变换包括透视变换和仿射变换,透视变换又称为投影变换、投射变换、投影映射,透视变换是将图片投影到一个新的视平面,它是二维(x,y)到三维(X,Y,Z)、再到另一个二维(x’,y’)空间的映射。
仿射变换又称为图像仿射映射,可以认为是透视变换的一种特殊情况,是透视变换的子集,仿射变换是从二维空间到自身的映射,是指在几何中,一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间,也就是图像仿射变换等于图像线性变换和平移的组合。
仿射变换包括平移(translation)、旋转(rotation)、缩放(scaling)、错切(shear )四种类型:
在《https://blog.csdn.net/LaoYuanPython/article/details/113788380 图像仿射变换原理3:仿射变换类型及变换矩阵详解》中介绍了包括平移(translation)、旋转(rotation)、缩放(scaling)、错切(shear )四种类型仿射变换的变换矩阵。
平移变换基本仿射变换矩阵
旋转变换基本仿射变换矩阵(θ为顺时针旋转角度)
水平错切基本仿射变换矩阵(α为错切角)
垂直错切基本仿射变换矩阵(β为错切角)
缩放变换基本仿射变换矩阵(kx和ky分布为水平缩放因子和垂直缩放因子)
上面介绍的仿射矩阵实际上并不是标准称呼上的仿射矩阵,而是一种用于两个平面间进行透视变换的3*3
单应性矩阵,只是单应矩阵对应仿射变换时,最后一行固定为(0,0,1),而真正的仿射矩阵是2*3
的矩阵,比单应性矩阵少一行,OpenCV中warpAffine函数使用的矩阵就是2*3
的矩阵。
在OpenCV中,仿射变换可以通过函数warpAffine来支持,当然部分单独的函数也可以进行某个特定的变换,如缩放和旋转就有单独的变换函数。
warpAffine(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None)
getRotationMatrix2D用于获取一个旋转二维图像的仿射变换矩阵。
getRotationMatrix2D(center, angle, scale)
getRotationMatrix2D返回值为一2*3
复合旋转变换仿射矩阵。按照OpenCV官方介绍,getRotationMatrix2D得到的矩阵为:
在《https://blog.csdn.net/LaoYuanPython/article/details/113841635 图像仿射变换原理4:组合变换及对应变换矩阵》中介绍:
绕指定点旋转进行组合变换时,参考点p(m,n)顺时针旋转θ的组合变换的齐次坐标表示公式为:
上述公式中θ为正表示是顺时针旋转,与getRotationMatrix2D中的angle参数取值方式相反,由于cos(-θ)=cosθ,sin(-θ)=-sinθ,因此实际上getRotationMatrix2D中旋转正值的角度时对应的上述矩阵计算公式中sin函数前面的符号需要取反(正号变副号、副号变正号)。
而缩放的齐次坐标表示公式为:
用缩放矩阵左乘平移矩阵则可以得到顺时针旋转同时进行缩放的齐次坐标表示公式:
当等比例缩放且缩放因子等于s时,上述公式中的kx、ky使用s替换。则最后的组合变换矩阵为:
可以看到,将getRotationMatrix2D的参数angle(逆时针旋转为正)的角度变为上述组合矩阵变换公式中的-θ(顺时针旋转为正)、getRotationMatrix2D中的center.x、center.y分别使用m、n替换,取组合变换矩阵的前2行,则二者结果等价。因此getRotationMatrix2D函数获得的变换矩阵和上述组合变换矩阵连乘的结果相同。
getAffineTransform通过确认源图像中不在同一直线的三个点对应的目标图像的位置,来获取对应仿射变换矩阵,从而用该仿射变换矩阵对图像进行统一的仿射变换。
retval = cv.getAffineTransform(src, dst)
图像文件名:f:\pic\dogAndCat.JPG
,本图像比较大,所以装入后长和宽等比例缩小50%。
在使用自定义函数构造变换矩阵时,使用了在付费专栏文章《https://blog.csdn.net/LaoYuanPython/article/details/113879385 图像仿射变换原理5:组合变换矩阵的OpenCV-Python实现》介绍的如下自定义函数:
constructAffineMatrix(rotationAngle=0,xShearAngle=0,yShearAngle=0,translationX=0,translationY=0,scaleX=1,scaleY=1)
"""
:param rotationAngle: 旋转角度,图像旋转时使用,逆时钟为正、顺时针为负,如顺时针旋转30°,则值为-30
:param xShearAngle: 水平错切角,水平错切时使用
:param yShearAngle: 垂直错切角,垂直错切时使用
:param translationX: x轴平移距离
:param translationY: y轴平移距离
:param scaleX: 水平方向缩放因子
:param scaleY: 竖直方向缩放因子
:param bTMT: 是否返回3*3矩阵,为False返回2*3矩阵,为True返回3*3矩阵,默认值为False
:return: 构建的3*3矩阵
补充说明:
本函数只能构建旋转、错切、平移、缩放四种情况的一种矩阵,参数只取一种情况进行矩阵构造,
取的情况按照旋转、错切、平移、缩放从高到低的优先级排列,高优先级的值非0则低优先级的值忽略。
如果返回的矩阵为3*3矩阵,如果该矩阵立即调用warpAffine进行仿射变换,需要通过切片方式取前2行传入warpAffine,如果需要与其他仿射矩阵相乘,则必须保持3*3矩阵,相乘的结果再进行切片处理,因为两个2*3的矩阵之间没法相乘(矩阵乘法要求第一个矩阵的列数等于第二个矩阵的行数)
"""
matrixMultiply(*mList)#矩阵连乘函数,将输入的n个矩阵按从左到右方式相乘,返回结果矩阵
了解相关知识根据函数的说明自己实现以上函数并不难,且该部分仅在自定义实现仿射矩阵时需要,对仿射矩阵原理不感兴趣的可以忽略。
下面代码将图像读入后每按键一次在当前位置再逆时针旋转5°。使用两种方式实现,一种是直接使用OpenCV函数getRotationMatrix2D构建仿射矩阵,示例代码为函数rotationByOpenCVMat,一种是使用自定义函数构建仿射矩阵,示例代码为函数myrotation。
def myrotation(imgfile):
img = cv2.imread(imgfile)
img = cv2.resize(img, None,fx=0.5, fy=0.5)
cv2.putText(img, 'https://blog.csdn.net/LaoYuanPython', (100, 158), fontFace=cv2.FONT_HERSHEY_SIMPLEX,fontScale=0.5, color=(255, 0, 0))
cv2.imshow('srcimg', img)
rows,cols,c = img.shape
#求原图像中心点
centerX = int(cols/2)
centerY = int(rows/2)
angle = 0 #初始角度
incAngle = 5 #每次增加角度
factor = 1 # 目标图像缩放因子,为了应对可能变换后图像范围扩大的情况如放到图像,为1表示不扩大
edge = int(sqrt(cols*cols+rows*rows)*factor) #目标图像边长设置为矩形的对角线长度,为了应对矩形绕中心旋转变换后图像范围扩大的情况
destCenter = int(edge / 2) #获取目标图像中心点
M1 = constructAffineMatrix(translationX=-1*centerX, translationY=-1 * centerY) #构建参考点(图像中心)平移到原点的平移矩阵
M3 = constructAffineMatrix(translationX=destCenter, translationY=destCenter) #构建参考点移动到目标图像中心位置的反向平移矩阵
flag = cv2.INTER_LINEAR #| cv2.WARP_INVERSE_MAP# | cv2.WARP_FILL_OUTLIERS
while True:
#复合变换矩阵 = 反向平移矩阵M3*旋转矩阵M2*平移矩阵M1
M2 = constructAffineMatrix(rotationAngle=angle) #构建基础旋转矩阵
M4 = np.matmul(M2,M1) #平移矩阵乘旋转矩阵
M = np.matmul(M3, M4) #再乘反向平移矩阵
M = matrixMultiply(M3,M2,M1) #构建复合旋转矩阵
dst1 = cv2.warpAffine(img, M[0:2], (edge,edge), flags=flag) #复合旋转变换
dst2= cv2.warpAffine(img, M2[0:2], (edge, edge), flags=flag) #基本旋转变换
cv2.imshow('rotationAndTranslation', dst1)
cv2.imshow('rotationOnly', dst2)
ch = cv2.waitKey(0)
if ch == 27:break
angle = (angle + incAngle) % 360 # 图像角度每次增加incAngle
cv2.destroyAllWindows()
def rotationByOpenCVMat(imgfile):
img = cv2.imread(imgfile)
img = cv2.resize(img,None,fx=0.5,fy=0.5)
cv2.putText(img, 'https://blog.csdn.net/LaoYuanPython', (100, 158), fontFace=cv2.FONT_HERSHEY_SIMPLEX,fontScale=0.5, color=(255, 0, 0))
rows, cols, c = img.shape
# 求原图像中心点
centerX = int(cols / 2)
centerY = int(rows / 2)
factor = 1 # 目标图像缩放因子,为了应对可能变换后图像范围扩大的情况如放到图像
edge = int(sqrt(cols * cols + rows * rows) * factor) # 目标图像边长设置为矩形的对角线长度,为了应对矩形绕中心旋转变换后图像范围扩大的情况,为1表示不扩大
destCenter = int(edge/2)
angle = 0 # 初始角度
incAngle = 5 # 每次增加角度
flag = cv2.INTER_LINEAR
while True:
M = cv2.getRotationMatrix2D((centerX,centerY),angle,1) # 构建复合旋转矩阵
dst = cv2.warpAffine(img, M, (edge,edge), flags=flag) # 复合旋转变换
cv2.imshow('rotationUsinggetRotationMatrix2D', dst)
ch = cv2.waitKey(0)
if ch == 27: break
angle = (angle + incAngle) % 360 # 图像角度每次增加incAngle
cv2.destroyAllWindows()
rotationByOpenCVMat(r'f:\pic\dogAndCat.JPG')
myrotation(r'f:\pic\dogAndCat.JPG')
由于图像是长方形的,以图像为中心的旋转过程会导致图像内容超出图像大小,因此在上述代码中将目标图像设置为了正方形,边长为原图像的对角线长度。
详细案例请见《https://blog.csdn.net/LaoYuanPython/article/details/113924512 openCV仿射变换:getAffineTransform的案例》的介绍。
本节介绍了仿射变换的概念、类型、基本仿射变换矩阵、OpenCV-Python与仿射变换相关的主要函数及语法说明,并提供了两种不同方式实现的图像旋转和任选三个点将圈定子图放大的示例。通过阅读相关内容可以有助于大家理解仿射变换的概念和仿射变换的OpenCV-Python实现方法。
仿射变换涉及一些基础知识的理解如齐次坐标、变换矩阵,对于熟悉线性代数的人理解起来很容易,但对于未学过或者象老猿这种学过又还给老师的人来说则理解很困难,为此老猿花了40余天时间温习了部分线性代数知识,并查找各种资料,结合自己的理解写了如下仿射变换原理的博文:
这些仿射变换原理方面的博文浓缩了仿射变换相关的基础知识,可以使得完全不了解仿射变换的人员理解仿射变换的基本原理和具体应用矩阵的构建,不过相关知识发布在了付费专栏,感兴趣的同仁可以订阅后阅读。
更多图像处理的介绍请参考专栏《OpenCV-Python图形图像处理 https://blog.csdn.net/laoyuanpython/category_9979286.html》和《https://blog.csdn.net/laoyuanpython/category_10581071.html OpenCV-Python初学者疑难问题集》相关文章。
更多图像处理的数学基础知识请参考专栏《人工智能数学基础 https://blog.csdn.net/laoyuanpython/category_10382948.html》
如果阅读本文于您有所获,敬请点赞、评论、收藏,谢谢大家的支持!
如果对文章内容存在疑问,可以在博客评论区留言,或关注:老猿Python 微信公号发消息咨询。
参考资料:
前两个专栏都适合有一定Python基础但无相关知识的小白读者学习,第三个专栏请大家结合《https://blog.csdn.net/laoyuanpython/category_9979286.html OpenCV-Python图形图像处理 》的学习使用。
对于缺乏Python基础的同仁,可以通过老猿的免费专栏《https://blog.csdn.net/laoyuanpython/category_9831699.html 专栏:Python基础教程目录)从零开始学习Python。
如果有兴趣也愿意支持老猿的读者,欢迎购买付费专栏。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。