Python OpenCV入门到精通学习日记:图像的几何变换
前言
几何变换,顾名思义就是修改图像的几何结构,例如大小,角度和形状等等,让图像呈现不同的效果。这些几何变换的操作一般都涉及复杂且精密的计算,Opencv将这些计算过程全部都包装成了各种灵活的方法,我们只需要修改一些参数,就能实现图像的变换效果。
仿射变换是难点内容
图像的几何变换
1 缩放
顾名思义就是缩小和放大,Opencv提供了resize()
方法可以随意修改图像的大小比例。
dst = cv2.resize(src, dsize, fx, fy, interpolation)
参数说明:
src:原始图像。
dsize:输出图像的大小,格式为(宽,高),单位为像素。
fx:可选参数。水平方向的缩放比例。
fy:可选参数。垂直方向的缩放比例。
interpolation:可选参数。缩放的插值方式。在图像缩小或放大时需要删减或补充像素,该参数可以指定使用哪种算法对像素进行增减。建议使用默认值。
dst:缩值之后的图像。
resize()
方法有两种使用方法,一种是通过dsize参数实现缩放,另一种是通过fx和fy参数实现缩放。
1.1 dsize参数实现缩放
dsize参数的格式是一个元组,例如(100,200),表示将图像按照宽100,高200像素的大小进行缩放。如果使用dsize参数,就可以不用写fx和fy参数。
示例:将一个图像按照宽100像素、高100像素的大小进行缩小,再按照宽600像素、高600像素的大小进行放大。
import cv2
img = cv2.imread("img.png")
dst1 = cv2.resize(img,(100,100))
dst2 = cv2.resize(img,(600,600))
cv2.imshow("img",img)
cv2.imshow("dst1",dst1)
cv2.imshow("dst2",dst2)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
1.2 fx和fy参数实现缩放
使用fx,fy参数时,dsize参数必须为None,否则fx和fy参数会失效。而且fx,fy参数可以使用浮点值,小于1的值表示缩小,大于1的值表示放大。
- 新图像宽度 = round(fx * 原图像宽度)
- 新图像高度 = round(fy * 原图像宽度)
示例:将一个图像宽缩小到原来的1/3、高缩小到原来的1/2,再将图像宽放大2倍,高也放大2倍
img = cv2.imread("img.png")
dst1 = cv2.resize(img,None,fx=1/3,fy=1/2)
dst2 = cv2.resize(img,None,fx=2,fy=2)
cv2.imshow("dst1",dst1)
cv2.imshow("dst2",dst2)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
2 翻转
翻转就是沿着x,y轴翻转,主要是呈现出镜面或倒影的效果。
OpenCV通过cv2.flip()
方法实现翻转效果。
dst = cv2.flip(src, flipCode)
参数说明:
src:原始图像。
flipCode:翻转类型,类型值及含义如表所示。
dst:翻转之后的图像。
类型值 | 含义 |
---|---|
0 | 沿X轴翻转 |
正数 | 沿Y轴翻转 |
负数 | 同时沿X轴、Y轴翻转 |
代码都是千篇一律了,这里我就给出关键代码:
dst = cv2.flip(img,0)
dst = cv2.flip(img,1)
dst = cv2.flip(img,-1)
3 仿射变换(难点)
仿射变换是一种仅在二维平面中发生的几何变换,变换之后的图像仍然可以保持直线的“平直性”和“平行性”,也就是说原来的直线变换之后还是直线,平行线变换之后还是平行线。
这里借用书上原图:
OpenCV通过cv2. warpAffine()
方法实现仿射变换效果。
dst = cv2.warpAffine(src, M, dsize, flags, borderMode,borderValue)
参数说明:
src:原始图像。
M:一个2行3列的矩阵,根据此矩阵的值变换原图中的像素位置。
dsize:输出图像的尺寸大小。
flags:可选参数,插值方式,建议使用默认值。
borderMode:可选参数,边界类型,建议使用默认值。
borderValue:可选参数,边界值,默认为0,建议使用默认值。
dst:经过反射变换后输出图像。
M也被叫作仿射矩阵,实际上就是一个2×3的列表。
M = [[a, b, c],[d, e, f]]
图像做何种仿射变换,完全取决于M的值,仿射变换输出的图像按照以下公式进行计算:
新x = 原来x * a + 原来y * b + c
新y = 原来x * d + 原来y * e + f
原x和原y表示原始图像中像素的横坐标和纵坐标,新x与新y表示同一个像素经过仿射变换后在新图像中的横坐标和纵坐标。
M矩阵中的数字采用32位浮点格式,可以采用两种方式创建M。
- 创建一个全是0的M:
M = np.zeros((2,3),np.float32)
- 创建M的同时赋予具体数值:
M = np.float32([[1,2,3],[4,5,6]])
掌握了这个概念后,我们可以开始实现图像的平移,旋转和倾斜了。
3.1 平移
平移就是让图像中所有的像素同时沿着水平或垂直方向移动。
M = [[1,0,水平移动的距离],[0,1,垂直移动的距离]]
套入之前给的公式,计算得出:
新x = 原来x + 水平移动的距离
新y = 原来y + 垂直移动的距离
移动规律遵循:左负右正,上负下正
了解了这些之后我们可以进行尝试:将图像向右移动50像素、向下移动100像素。
import cv2,numpy as np
img = cv2.imread("img.png") # 图片被存储为三维数组(y,x,通道数)
y = len(img) # 这里 len(img) 返回的是图片数组的行数,即图片的高度
x = len(img[0]) # 这里 img[0] 是图片的第一行像素,len(img[0]) 返回的是这一行中像素列的数量,即图片的宽度
M = np.float32([[1,0,50],[0,1,100]])
dst = cv2.warpAffine(img,M,(x,y))
cv2.imshow("img",img)
cv2.imshow("dst",dst)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
3.2 旋转
让图像旋转也是通过M矩阵实现的,但得出这个矩阵需要做很复杂的运算,于是OpenCV提供了getRotationMatrix2D()
方法自动计算旋转图像的M矩阵。
M = cv2.getRotationMatrix2D(center, angle, scale)
参数说明:
center:旋转的中心点坐标。
angle:旋转的角度(不是弧度)。正数表示逆时针旋转,负数表示顺时针旋转。
scale:缩放比例,浮点类型。如果取值1,表示图像保持原来的比例。
M:getRotationMatrix2D()方法计算出的仿射矩阵。
示例:让图像逆时针旋转30°的同时缩小到原来的80%
import cv2
img = cv2.imread("img.png")
x = len(img[0])
y = len(img)
center = (y/2,x/2) # 坐标应为(y,x)
M = cv2.getRotationMatrix2D(center,30,0.8)
dst = cv2.warpAffine(img,M,(x,y))
cv2.imshow("img",img)
cv2.imshow("dst",dst)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
3.3 倾斜
Opencv需要定位图像的三个点来计算倾斜效果,这三个点分别是左上角A,右上角B,左下角C。Opencv会根据这三个点的位置变化来计算其他像素的位置变化。右下角的点会根据ABC三个点的变化自动计算。
这里借用书上原图:
让图像倾斜也是需要通过M矩阵实现的,但得出这个矩阵需要做很复杂的运算,于是OpenCV提供了getAffineTransform()
方法来自动计算倾斜图像的M矩阵。
M = cv2.getAffineTransform(src, dst)
参数说明:
src:原图3个点坐标,格式为3行2列的32位浮点数列表,例如:[[0, 1], [1, 0], [1, 1]]。
dst:倾斜图像的3个点坐标,格式与src一样。
M:getAffineTransform()方法计算出的仿射矩阵。
我们可以尝试让图像向右倾斜:
import cv2,numpy as np
img = cv2.imread("img.png")
x = len(img[0])
y = len(img)
p1 = np.zeros((3,2),np.float32) # 生成一个3*2的数组并向里面填入数据作为原来的三个点
p1[0] = [0,0]
p1[1] = [x-1,0]
p1[2] = [0,y-1]
p2 = np.zeros((3,2),np.float32) # 生成一个3*2的数组并向里面填入数据作为修改的三个点
p2[0] = [50,0]
p2[1] = [x-1,0]
p2[2] = [0,y-1]
M = cv2.getAffineTransform(p1,p2)
dst = cv2.warpAffine(img,M,(x,y))
cv2.imshow("img",img)
cv2.imshow("dst",dst)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
同理,如果想让图像向左倾斜,需要移动右上角和左下角的点
4 透视
仿射变换是让图像在二维中变形,那么透视就是让图像在三维中变形。从不同角度来观察物体,会看见不同的变形画面,这种变形的画面就是透视图。
为了让大家直观感受,这里引用书中原图:
Opencv中需要通过定位图像的4个点来计算透视效果,这四个点就是倾斜功能提及的三个点和右下角一个点,Opencv根据这四个点的位置变化来计算其他的像素的位置变化。但是透视效果不能保证图像的“平直性”和“平行性”。
Opencv通过warpPerspective()
来实现透视效果。
dst = cv2.warpPerspective(src, M, dsize, flags,borderMode, borderValue)
参数说明:
src:原始图像。
M:一个3行3列的矩阵,根据此矩阵的值变换原图中的像素位置。
dsize:输出图像的尺寸大小。
flags:可选参数,插值方式,建议使用默认值。
borderMode:可选参数,边界类型,建议使用默认值。
borderValue:可选参数,边界值,默认为0,建议使用默认值。
dst:经过透视变换后输出图像。
透视也是需要M矩阵进行复杂的运算,在Opencv中提供了getPerspectiveTransform()
方法自动计算M矩阵。
语法和前面的getAffineTransform()
很像,唯一不同的就是getPerspectiveTransform()
的src和dst参数是四个点的坐标,格式为4*2的32位浮点数列表。
学到这里,我们可以模拟从底部观察图像了:
import cv2
import numpy as np
img = cv2.imread("img.png")
x = len(img[0])
y = len(img)
p1 = np.zeros((4,2),np.float32)
p1[0] = [0,0]
p1[1] = [x-1,0]
p1[2] = [0,y-1]
p1[3]= [x-1,y-1]
p2 = np.zeros((4,2),np.float32)
p2[0] = [90,0]
p2[1] = [x-91,0]
p2[2] = [0,y-1]
p2[3] = [x-1,y-1]
M = cv2.getPerspectiveTransform(p1,p2)
dst = cv2.warpPerspective(img,M,(x,y))
cv2.imshow("img",img)
cv2.imshow("dst",dst)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
小结
今天难点主要在M矩阵上,书中并没有提到M矩阵的计算内容,只是给了我们调用函数,可以直接处理图像,使用大家只要熟练掌握并灵活运用M矩阵,就能够对图像进行几何变换操作。
明天开始学图像的阈值处理。
有些代码没有标注注释,看不懂的可以评论区讨论,看见必回。
标签:img,dst,cv2,像素,Opencv,几何变换,参数,苦学,图像 From: https://blog.csdn.net/qq_71745595/article/details/140512785