OpenCV-Python系列之图像的几何变换
上次教程中我们谈及到OpenCV的缩放旋转以及平移,它们本质上都属于简单的几何变换,本次我们将讨论稍微复杂的一下几何变换。
图像翻转
图像翻转有多种方式,包括使用固定的API以及使用numpy进行操作等等。
使用flip函数实现翻转
函数原型:
flip(src, flipCode[, dst]) -> dst
· src 输入图片
· flipCode 翻转代码
· 1 水平翻转 Horizontally (图片第二维度是column)
· 0 垂直翻转 *Vertically * (图片第一维是row)
· -1 同时水平翻转与垂直反转 Horizontally & Vertically
我们来看代码:
import numpy as np import cv2 from matplotlib import pyplot as plt img = cv2.imread('cat.jpg') def bgr2rbg(img): ''' 将颜色空间从BGR转换为RBG ''' return img[:,:,::-1] # 水平翻转 flip_h = cv2.flip(img, 1) # 垂直翻转 flip_v = cv2.flip(img, 0) # 同时水平翻转与垂直翻转 flip_hv = cv2.flip(img, -1) plt.subplot(221) plt.title('SRC') plt.imshow(bgr2rbg(img)) plt.subplot(222) plt.title('Horizontally') plt.imshow(bgr2rbg(flip_h)) plt.subplot(223) plt.title('Vertically') plt.imshow(bgr2rbg(flip_v)) plt.subplot(224) plt.title('Horizontally & Vertically') plt.imshow(bgr2rbg(flip_hv)) plt.show()
代码实现了三种翻转的方式:水平翻转、垂直翻转以及水平垂直翻转。我们来看效果:
使用numpy的索引进行图像反转
我们刚刚使用了OpenCV的API进行了图像翻转的操作,实际上仅仅使用numpy也是相当的方便,在numpy中使用img[:,::-1]则代表为矩阵水平翻转,使用img[::-1]则为矩阵垂直翻转,所以我们不难看出,水平与垂直同时翻转则为img[::-1, ::-1]。
我们来看代码:
import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('cat.jpg') height,width,channel = img.shape # 水平翻转 flip_h = img[:,::-1] # 垂直翻转 flip_v = img[::-1] # 水平垂直同时翻转 flip_hv = img[::-1, ::-1] def bgr2rbg(img): ''' 将颜色空间从BGR转换为RBG ''' return img[:,:,::-1] plt.subplot(221) plt.title('SRC') plt.imshow(bgr2rbg(img)) plt.subplot(222) plt.title('Horizontally') plt.imshow(bgr2rbg(flip_h)) plt.subplot(223) plt.title('Vertically') plt.imshow(bgr2rbg(flip_v)) plt.subplot(224) plt.title('Horizontally & Vertically') plt.imshow(bgr2rbg(flip_hv)) plt.show()
效果仍然相同:
利用wrapAffine实现翻转
事实上,利用OpenCV的wrapAffine也可以进行图像的翻转,在上一节的教程中,我们已经讲述了利用相关函数实现旋转以及平移的操作,从本质上来讲,它们的原理基本上相同,现在我们来看一下用它实现翻转的原理:
我们假设width 代表图像的宽度 height代表图像的高度:
水平翻转的变换矩阵为:
垂直翻转的变换矩阵则为:
如若同时进行水平翻转与垂直翻转,那么有:
据此我们来看实现的代码:
import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('cat.jpg') height,width,channel = img.shape # 水平翻转 M1 = np.float32([[-1, 0, width], [0, 1, 0]]) flip_h = cv2.warpAffine(img, M1, (width, height)) # 垂直翻转 M2 = np.float32([[1, 0, 0], [0, -1, height]]) flip_v = cv2.warpAffine(img, M2, (width, height)) # 水平垂直同时翻转 M3 = np.float32([[-1, 0, width], [0, -1, height]]) flip_hv = cv2.warpAffine(img, M3, (width, height)) def bgr2rbg(img): ''' 将颜色空间从BGR转换为RBG ''' return img[:,:,::-1] plt.subplot(221) plt.title('SRC') plt.imshow(bgr2rbg(img)) plt.subplot(222) plt.title('Horizontally') plt.imshow(bgr2rbg(flip_h)) plt.subplot(223) plt.title('Vertically') plt.imshow(bgr2rbg(flip_v)) plt.subplot(224) plt.title('Horizontally & Vertically') plt.imshow(bgr2rbg(flip_hv)) plt.show()
实验效果仍然跟之前两种方法的一样,在这里就不一一演示了。
仿射变换
实际上,之前我们讲过的缩放旋转平移以及刚刚讲述的翻转它们本质上都属于图像的仿射变换,现在我们讨论一个函数,算是对仿射变换的一个总结:
M=cv2.GetAffineTransform(src, dst)
src:原始图像中的三个点的坐标
dst:变换后的这三个点对应的坐标
M:根据三个对应点求出的仿射变换矩阵
在前面原理的讲述中,我们也知道了仿射变换通常是对于一个2*3的矩阵:
考虑到我们要使用矩阵 和 对二维向量 做变换, 所以也能表示为下列形式:
现在我们来大致看一下代码:
import cv2 as cv import matplotlib.pyplot as plt import numpy as np img = cv.imread('cat.jpg') rows,cols,ch = img.shape pts1 = np.float32([[50,50],[200,50],[50,200]]) pts2 = np.float32([[10,100],[200,50],[100,250]]) M = cv.getAffineTransform(pts1,pts2) dst = cv.warpAffine(img,M,(cols,rows)) plt.subplot(121),plt.imshow(img),plt.title('Input') plt.subplot(122),plt.imshow(dst),plt.title('Output') plt.show()
我们可以看到一只扭曲的猫。
Perspective Transformation
我们平时可能用过各种扫描的软件,比如像扫描全能王这种软件,我们在使用的过程中会发现,即便是你图片拍的比较斜,软件后期仍然可以对你的图片进行校正,然后从而进行图像增强处理,这种图像矫正操作,实际上就是图像的Perspective变换(关键字屏蔽,大家自行了解)。现在的很多案例比如车牌识别,答题卡判卷以及文字识别等等,它们都是以图像的Perspective变换将图像矫正之后才进行下一步的操作的。
之前我们在 仿射变换简介 里面讲解的是仿射变换AffineTransform。那么Perspective变换与仿射变换之间最大的区别是什么呢?仿射变换可以用三个点确定一个变换,而Perspective换则不一定,所以可以将仿射变换作为投影变换的一个特例。 需要四个点,才能确定Perspective变换。
OpenCV提供了warpPerspective( )函数来实现图片的Perspective变换,只需要输入梯形四个顶点的坐标和目标画布四个角的坐标,即可自动完成转换。核心代码只有两行:首先读取两个坐标数组,计算变换矩阵;然后根据变换矩阵对原图进行Perspective变换,并输出到目标画布。
函数原型:
cv2.getPerspectiveTransform(src, dst) → retval
src:源图像中待测矩形的四点坐标
sdt:目标图像中矩形的四点坐标
返回由源图像中矩形到目标图像矩形变换的矩阵,接下来引出下面这个函数:
cv2.warpPerspective(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) → dst
src:输入图像
M:变换矩阵
dsize:目标图像shape
flags:插值方式,interpolation方法INTER_LINEAR或INTER_NEAREST
borderMode:边界补偿方式,BORDER_CONSTANT or BORDER_REPLICATE
borderValue:边界补偿大小,常值,默认为0
或者我们可以使用另一个函数:
cv2.perspectiveTransform(src, m[, dst]) → dst
src:输入的2通道或者3通道的图片
m:变换矩阵
这两个函数返回的相同的规格的图片。
对于Perspective变换,我们需要3x3变换矩阵。即使在转换后,直线也将保持直线。要找到此变换矩阵,在输入图像上有4个点,在输出图像上需要相应的点。在这4个点中,其中3个不应共线。然后可以通过函数cv.getPerspectiveTransform找到转换矩阵。然后将cv.warpPerspective应用于此3x3转换矩阵,我们来看代码:
import cv2 as cv import matplotlib.pyplot as plt import numpy as np img = cv.imread('sudoku.png') 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 = cv.getPerspectiveTransform(pts1,pts2) dst = cv.warpPerspective(img,M,(300,300)) plt.subplot(121),plt.imshow(img),plt.title('Input') plt.subplot(122),plt.imshow(dst),plt.title('Output') plt.show()
可以看到,图像对于给定的坐标已经进行了矫正,这对于之后的形态学的操作是相当方便的。