图像的仿射变换

  仿射变换是指图像可以通过一系列的几何变换来实现平移、旋转等多种操作。该变换能够保持图像的平直性和平行性。平直性是指图像经过仿射变换后,直线仍然是直线;平行性是指图像在完成仿射变换后,平行线仍然是平行线。

  在OpenCV中仿射函数为cv2.warpAffine(),它通过一个变换矩阵(映射矩阵)M 实现变换,具体为:dst(𝑥, 𝑦) = src(𝑀11𝑥 + 𝑀12𝑦 + 𝑀13, 𝑀21𝑥 + 𝑀22𝑦 + 𝑀23)。如下图所示,通过一个变换矩阵M将原始图像O变换为仿射图像R
在这里插入图片描述
图1  仿射图像R=变换矩阵M × 原始图像O

   利用仿射函数cv2.warpAffine()可以实现对图像的旋转,其函数的语法格式如下:dst = cv2.warpAffine( src, M, dsize[, flags[, borderMode[, borderValue]]] )

参数解析:
dst :仿射变换后输出的图像,该图像的类型与原始图像的类型相同,由dsize 决定它的的实际大小。
src :要进行仿射变换的原始图像。
M :一个 2×3 的变换矩阵。使用不同的变换矩阵可以实现不同的仿射变换。
dsize:输出图像的尺寸大小。
flags :插值方法,默认为 INTER_LINEAR。当该值为 WARP_INVERSE_MAP 时,意味着 M 是逆变换类型,实现从目标图像 dst 到原始图像 src 的逆变换。具体可选值可见下表。

类型说明
cv2.INTER_NEAREST最临近插值
cv2.INTER_LINEAR双线性插值(默认方式)
cv2.INTER_CUBIC三次样条插值。首先对源图像附近的 4×4 近邻区域进行三次样条拟合,然后将目标像素对应的三次样条值作为目标图像对应像素点的值
cv2.INTER_AREA区域插值,根据当前像素点周边区域的像素实现当前像素点的采样。该方法类似最临近插值方式
cv2.INTER_LANCZOS4一种使用 8×8 近邻的 Lanczos 插值方法
cv2.INTER_LINEAR_EXACT位精确双线性插值
cv2.INTER_MAX差值编码掩码
cv2.WARP_FILL_OUTLIERS标志,填补目标图像中的所有像素。如果它们中的一些对应源图像中的奇异点(离群值),则将它们设置为零
cv2.WARP_INVERSE_MAP标志,逆变换。例如,极坐标变换: 如果 flag 未被设置,则进行转换:dst(∅, 𝜌) = src(𝑥, 𝑦);如果 flag 被设置,则进行转换:dst(x, y) = src(∅, ρ)

borderMode :边类型, 默认为 BORDER_CONSTANT。当 该值为 BORDER_TRANSPARENT 时,意味着目标图像内的值不做改变,这些值对应原始图像内的异常值。
borderValue:边界值,默认为 0。

  由此可见,通过转换矩阵M将原始图像src转换为目标图像dst:dst(𝑥, 𝑦) = src(𝑀11𝑥 + 𝑀12𝑦 + 𝑀13, 𝑀21𝑥 + 𝑀22𝑦 + 𝑀23)

  因此所进行仿射变换种类完全取决于转换矩阵 M

1. 平移

  通过转换矩阵M可以实现将原始图像src转换为目标图像dst: dst(𝑥, 𝑦) = src(𝑀11𝑥 + 𝑀12𝑦 + 𝑀13, 𝑀21𝑥 + 𝑀22𝑦 + 𝑀23)

  将原始图像src向右侧移动150个像素、向下方移动250个像素,其对应关系为:dst (x, y) = src (x + 100, y + 200)。完整的表达式为dst (x, y) = src (1·x + 0·y + 100, 0·x + 1·y + 200)

  根据上述表达式可以确定对应的转换矩阵 M 中各个元素的值为:

元素
𝑀111
𝑀120
𝑀13150
𝑀210
𝑀221
𝑀23250

  将上述值代入转换矩阵M,得到:
[ 1 0 150 0 1 200 ] \begin{bmatrix} 1&0&150\\ 0&1&200\\ \end{bmatrix} [1001150200]

程序示例一

利用仿射函数 cv2.warpAffine()实现对图像的平移操作。

import cv2
import numpy as np
lena = cv2.imread("./lena.jpg")  # 读取原图
height, width = lena.shape[:2]  # 获取图像的高度和宽度
x = 120 #向右侧移动 120 个像素
y = 200 #向下方移动 200 个像素
M = np.float32([[1, 0, x], [0, 1, y]])  #转换矩阵M
Panned_lena = cv2.warpAffine(lena, M, (width, height))
cv2.imshow("原始图像", lena)
cv2.imshow("平移图像", Panned_lena)
cv2.waitKey()
cv2.destroyAllWindows()

程序运行结果如下:
在这里插入图片描述
图2  原始图像

在这里插入图片描述
图3  平移图像

2. 旋转

  在使用函数 cv2.warpAffine()对图像进行旋转时,可以通过函数 cv2.getRotationMatrix2D()获取转换矩阵。该函数的语法格式为:retval=cv2.getRotationMatrix2D(center, angle, scale)

参数解析:
center :旋转中心点。
angle :旋转角度,正数表示逆时针旋转,负数表示顺时针旋转。
scale :变换尺度(缩放大小)。 利用函cv2.getRotationMatrix2D()可以直接生成要使用的转换矩阵 M。例如,想要以图像中心为圆点,逆时针旋转 60°,并将目标图像缩小为原始图像的 0.7 倍,则在调用函数 cv2.getRotationMatrix2D()生成转换矩阵 M 时所使用的语句为: M=cv2.getRotationMatrix2D((height/2,width/2),60,0.7)

程序示例二

通过函数 cv2.warpAffine()实现图像的旋转。

import cv2
lena = cv2.imread("./lena.jpg")  # 读取原图
height, width = lena.shape[:2]  # 获取图像的高度和宽度
# 以图像中心为圆点,逆时针旋转 60°,并将目标图像缩小为原始图像的 0.7 倍
M = cv2.getRotationMatrix2D((width/2, height/2), 60, 0.7)   #生成转换矩阵M
rotate_lena = cv2.warpAffine(lena, M, (width, height))
cv2.imshow("原始图像", lena)
cv2.imshow("旋转图像", rotate_lena)
cv2.waitKey()
cv2.destroyAllWindows()

程序运行结果如下:
在这里插入图片描述
图4  原始图像

在这里插入图片描述
图5  旋转图像

3. 更复杂的仿射变换

  对于更复杂仿射变换,OpenCV 提供了函数cv2.getAffineTransform()来生成仿射函数 cv2.warpAffine()所使用的转换矩阵 M。该函数的语法格式为:retval=cv2.getAffineTransform(src, dst)

参数解析:
src:输入图像的三个点坐标。
dst:输出图像的三个点坐标。

  在该函数中,参数 srcdst 是包含三个二维数组(x, y)点的数组。上述参数通过函数cv2.getAffineTransform()定义了两个平行四边形。srcdst 中的三个点分别对应平行四边形的左上角、右上角、左下角三个点。函数 cv2.warpAffine()以函数cv2.getAffineTransform()获取的转换矩阵 M 为参数,将 src 中的点仿射到 dst 中。函数 cv2.getAffineTransform()对所指定的点完成映射后,将所有其他点的映射关系按照指定点的关系计算确定。

程序示例三

设计程序完成图像仿射变换。

import cv2
import numpy as np
lena=cv2.imread('./lena.jpg')
rows,cols,ch=lena.shape #获取图像的行数、列数和色彩通道数
p1=np.float32([[0,0],[cols-1,0],[0,rows-1]])
p2=np.float32([[0,rows*0.33],[cols*0.85,rows*0.25],[cols*0.15,rows*0.7]])
M=cv2.getAffineTransform(p1,p2) #转换矩阵 M
dst=cv2.warpAffine(lena,M,(cols,rows))  # 将所有其他点的映射关系按照指定点的关系计算确定
cv2.imshow("原始图像",lena)
cv2.imshow("仿射变换图像",dst)
cv2.waitKey()
cv2.destroyAllWindows()

程序运行结果如下:
在这里插入图片描述
图5  原始图像
在这里插入图片描述
图6  仿射变换图像

4.透视

  仿射变换可以将矩形映射为任意平行四边形,而透视变换可以将矩形映射为任意四边形。通过函数cv2.warpPerspective()可以实现透视变换,其语法为dst = cv2.warpPerspective( src, M, dsize[, flags[, borderMode[, borderValue]]] )

参数解析:
dst :透视处理后的输出图像,与原始图像具有相同的类型。dsize 决定输出图像的实际大小。
src:要进行透视变换的图像。
M :一个 3×3 的变换矩阵。
dsize :输出图像的尺寸大小。
flags :插值方法,默认为 INTER_LINEAR。当该值为WARP_INVERSE_MAP 时,意味着 M 是逆变换类型,能实现从目标图像 dst 到原始图像 src 的逆变换,具体可选值参见下表。

类型说明
cv2.INTER_NEAREST最临近插值
cv2.INTER_LINEAR双线性插值(默认方式)
cv2.INTER_CUBIC三次样条插值。首先对源图像附近的 4×4 近邻区域进行三次样条拟合,然后将目标像素对应的三次样条值作为目标图像对应像素点的值
cv2.INTER_AREA区域插值,根据当前像素点周边区域的像素实现当前像素点的采样。该方法类似最临近插值方式
cv2.INTER_LANCZOS4一种使用 8×8 近邻的 Lanczos 插值方法
cv2.INTER_LINEAR_EXACT位精确双线性插值
cv2.INTER_MAX差值编码掩码
cv2.WARP_FILL_OUTLIERS标志,填补目标图像中的所有像素。如果它们中的一些对应源图像中的奇异点(离群值),则将它们设置为零
cv2.WARP_INVERSE_MAP标志,逆变换。例如,极坐标变换: 如果 flag 未被设置,则进行转换:dst(∅, 𝜌) = src(𝑥, 𝑦);如果 flag 被设置,则进行转换:dst(x, y) = src(∅, ρ)

borderMode :边类型, 默认为 BORDER_CONSTANT 。 当 该值为 BORDER_TRANSPARENT 时,意味着目标图像内的值不做改变,这些值对应原始图像内的异常值。
borderValue :边界值,默认为 0。

  与仿射变换函数一样,同样可以使用一个函数来生成函数 cv2.warpPerspective()所使用的转换矩
阵。该函数是 cv2.getPerspectiveTransform(),其语法格式为:retval = cv2.getPerspectiveTransform( src, dst )

参数解析:
src :输入图像的四个顶点的坐标。
dst :输出图像的四个顶点的坐标。


  与仿射变换函数cv2.getAffineTransform()不同的。需要注意的是, src 参数和 dst 参数是包含四个点的数组,实际使用中,可以根据需要控制 src 中的四个点映射到 dst 中的四个点。

程序示例四

设计程序,完成图像透视。

import cv2
import numpy as np
lena=cv2.imread('./lena.jpg')    #导入原图
rows,cols=lena.shape[:2] #获取图像的行数和列数
pts1 = np.float32([[150,50],[400,50],[60,450],[310,450]])
pts2 = np.float32([[50,50],[rows-50,50],[50,cols-50],[rows-50,cols-50]])
M=cv2.getPerspectiveTransform(pts1,pts2)    #转换矩阵M
dst=cv2.warpPerspective(lena,M,(cols,rows))
cv2.imshow("lena",lena)
cv2.imshow("dst",dst)
cv2.waitKey()
cv2.destroyAllWindows()

  在指定原始图像中的平行四边形顶点pts1,指定目标图像中矩形的四个顶点pts2,使用M=cv2.getPerspectiveTransform(pts1,pts2)生成转换矩阵 M。接下来,使用语句dst=cv2.warpPerspective(img,M,(cols,rows))完成从平行四边形到矩形的转换。程序运行结果如下:
在这里插入图片描述
图7  原始图像
在这里插入图片描述
图8  透视变换图像

5. 重映射

  把一幅图像中的某个位置的像素点放置到另一幅图像内的指定位置的过程为图像的重映射。OpenCV提供的重映射函数 cv2.remap()可以实现自定义的方式的重映射。其语法格式为dst = cv2.remap( src, map1, map2, interpolation[, borderMode[, borderValue]] )

参数解析:
dst :目标图像,与src 有相同的大小和类型。
src :原始图像。
map1 :该参数有两种可能的值:①表示(x,y)点的一个映射;②表示 CV_16SC2 , CV_32FC1, CV_32FC2 类型(x,y)点的 x 值。
map2 :该参数有两种可能的值:①当 map1 表示(x,y)时,该值为空。②当 map1 表示(x,y)点的 x 值时,该值是 CV_16UC1, CV_32FC1 类型(x,y)点的 y 值。
Interpolation :插值方式,这里不支持 INTER_AREA 方法,具体值参见下表。

类型说明
cv2.INTER_NEAREST最临近插值
cv2.INTER_LINEAR双线性插值(默认方式)
cv2.INTER_CUBIC三次样条插值。首先对源图像附近的 4×4 近邻区域进行三次样条拟合,然后将目标像素对应的三次样条值作为目标图像对应像素点的值
cv2.INTER_LANCZOS4一种使用 8×8 近邻的 Lanczos 插值方法
cv2.INTER_LINEAR_EXACT位精确双线性插值
cv2.INTER_MAX差值编码掩码
cv2.WARP_FILL_OUTLIERS标志,填补目标图像中的所有像素。如果它们中的一些对应源图像中的奇异点(离群值),则将它们设置为零
cv2.WARP_INVERSE_MAP标志,逆变换。例如,极坐标变换: 如果 flag 未被设置,则进行转换:dst(∅, 𝜌) = src(𝑥, 𝑦);如果 flag 被设置,则进行转换:dst(x, y) = src(∅, ρ)

borderMode :边界模式。当该值为 BORDER_TRANSPARENT 时,表示目标图像内的对应源图像内奇异点的像素不会被修改。
borderValue:边界值,默认为 0。

  重映射是通过修改像素点的位置得到一幅新图像。在构建新图像时,需要确定新图像中每个像素点在原始图像中的位置。因此,映射函数的作用是查找新图像像素在原始图像内的位置

  重映射是将新图像像素映射到原始图像的过程,因此被称为反向映射。在函数 cv2.remap()中,参数 map1 和参数 map2 用来说明反向映射,map1 针对的是坐标 xmap2 针对的是坐标 y

  因为参数 map1 和参数 map2 的值是浮点数,所以目标图像可以映射回一个非整数的值,这意味着目标图像可以“反向映射”到原始图像中两个像素点之间的位置(当然,该位置是不存在像素值的)。而且由于参数 map1 和参数 map2 的值是浮点数,所以通过函数 cv2.remamp()实现的映射关系变得更加随意,可以通过自定义映射参数实现不同形式的映射。

5.1 复制

  在映射时,通过将 map1map2的值分别设定为对应位置上的 x 轴坐标和 y 轴坐标值,可以让函数 cv2.remap()实现图像复制。

程序示例五

使用函数 cv2.remap()完成对图像的复制。

import cv2
import numpy as np
lena = cv2.imread("./lena.jpg")
rows, cols = lena.shape[:2]
map1 = np.zeros(lena.shape[:2], np.float32)	#设置x轴方向的坐标
map2 = np.zeros(lena.shape[:2], np.float32)	#设置y轴方向的坐标

for i in range(rows):
    for j in range(cols):
        map1.itemset((i, j), j)
        map2.itemset((i, j), i)
        
rst = cv2.remap(lena, map1, map2, cv2.INTER_LINEAR)
cv2.imshow("原始图像", lena)
cv2.imshow("复制图像", rst)
cv2.waitKey()
cv2.destroyAllWindows()

程序运行结果如下

在这里插入图片描述
图9  原始图像

在这里插入图片描述
图10  复制图像

5.2 绕x轴旋转

  如果想要图像绕x轴翻转,则在映射过程中需要满足:①x坐标轴的值保持不变;②y坐标轴的值以x轴为对称轴进行交换。

  反映在 map1map2 上就是map1 的值保持不变,map2 的值调整为“总行数-1-当前行号”。

  需要注意,OpenCV 中行号的下标是从 0 开始的,所以在对称关系中存在“当前行号+对称行号=总行数-1”的关系。因此在绕着 x 轴翻转时,map2 中当前行的行号调整为“总行数-1-当前行号”。

程序示例六

使用函数 cv2.remap()实现对图像绕 x 轴翻转。

import cv2
import numpy as np
lena=cv2.imread("./lena.jpg")
rows,cols=lena.shape[:2]
map1 = np.zeros(lena.shape[:2],np.float32)
map2 = np.zeros(lena.shape[:2],np.float32)

for i in range(rows):
    for j in range(cols):
        map1.itemset((i,j),j)
        map2.itemset((i,j),rows-1-i)
rst=cv2.remap(lena,map1,map2,cv2.INTER_LINEAR)

cv2.imshow("原始图像",lena)
cv2.imshow("绕x轴旋转图像",rst)
cv2.waitKey()
cv2.destroyAllWindows()

程序运行结果如下
在这里插入图片描述
图11  原始图像

在这里插入图片描述
图12  绕x轴旋转图像

5.3 绕y轴旋转

  如果想要图像绕y轴翻转,则在映射过程中需要满足:①y坐标轴的值保持不变;②x坐标轴的值以y轴为对称轴进行交换。

  反映在 map1map2 上就是map2 的值保持不变,map1 的值调整为“总行数-1-当前列号”。

  需要注意,OpenCV 中列号的下标是从 0 开始的,所以在对称关系中存在“当前列号+对称列号=总列数-1”的关系。因此在绕着 y 轴翻转时,map2 中当前行的行号调整为“总列数-1-当前列号”。

程序示例七

使用函数 cv2.remap()实现对图像绕 y 轴翻转。

import cv2
import numpy as np
lena=cv2.imread("./lena.jpg")
rows,cols=lena.shape[:2]
map1 = np.zeros(lena.shape[:2],np.float32)
map2 = np.zeros(lena.shape[:2],np.float32)

for i in range(rows):
    for j in range(cols):
        map1.itemset((i,j),cols-1-j)
        map2.itemset((i,j),i)
rst=cv2.remap(lena,map1,map2,cv2.INTER_LINEAR)

cv2.imshow("原始图像",lena)
cv2.imshow("绕y轴旋转图像",rst)
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述
图13  原始图像

在这里插入图片描述
图14  绕y轴旋转图像

5.4 绕x轴、y轴翻转

  如果想让图像绕着 x 轴、y 轴翻转,意味着在映射过程中:①x 坐标轴的值以 y 轴为对称轴进行交换;②y 坐标轴的值以 x 轴为对称轴进行交换

  反映在 map1map2 上: map1 的值调整为“总列数-1-当前列号”; map2 的值调整为“总行数-1-当前行号”。

程序示例八

使用函数 cv2.remap()实现图像绕 x 轴、y 轴翻转。

import cv2
import numpy as np
lena=cv2.imread("./lena.jpg")
rows,cols=lena.shape[:2]
map1 = np.zeros(lena.shape[:2],np.float32)
map2 = np.zeros(lena.shape[:2],np.float32)

for i in range(rows):
    for j in range(cols):
        map1.itemset((i,j),cols-j-1)
        map2.itemset((i,j),rows-1-i)
rst=cv2.remap(lena,map1,map2,cv2.INTER_LINEAR)

cv2.imshow("原始图像",lena)
cv2.imshow("绕x轴、y轴旋转图像",rst)
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述
图15  原始图像

在这里插入图片描述
图16  绕x轴、y轴旋转图像

5.5 x轴、y轴互换

  如果想让图像的 x 轴、y 轴互换,意味着在映射过程中,对于任意一点,都需要将其 x 轴、y 轴坐标互换。反映在 map1 和 map2 上就是:①map1 的值调整为所在行的行号;②map2 的值调整为所在列的列号。

  需要注意的是,如果行数和列数不一致,上述运算可能存在值无法映射的情况。默认情况下,无法完成映射的值会被处理为 0

程序示例九

使用函数 cv2.remap()实现图像绕 x 轴、y 轴互换。

import cv2
import numpy as np
lena=cv2.imread("./lena.jpg")
rows,cols=lena.shape[:2]
map1 = np.zeros(lena.shape[:2],np.float32)
map2 = np.zeros(lena.shape[:2],np.float32)

for i in range(rows):
    for j in range(cols):
        map1.itemset((i,j),i)
        map2.itemset((i,j),j)
rst=cv2.remap(lena,map1,map2,cv2.INTER_LINEAR)

cv2.imshow("原始图像",lena)
cv2.imshow("x轴、y轴互换图像",rst)
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述
图17  原始图像

在这里插入图片描述
图18  x轴、y轴互换图像

  • 20
    点赞
  • 106
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值