原标题:【OpenCV+Python】图像缩放旋转平移与几何变换
图像缩放旋转平移
在OpenCV中我们经常会用到图像的缩放,旋转以及平移,比如有的时候我们想对一类图片进行操作,这个时候我们就需要对这一类的图片进行尺寸统一,在以后深度学习的学习中,我们也会发现,对于数据集的训练,它们的数据集样本通常都是尺寸统一的。
▼ 缩放
在OpenCV中,使用函数cv2.resize实现对图像的缩放。
语法格式为:
dst = cv2.resize( src, dsize[ ,fx[, fy[ ,interpolation]]])
src:原图片
dsize:缩放图片尺寸,如果dsize=0,默认计算方式如下dsize=Size(round(fx∗src.cols),round(fy∗src.rows))
fx fy和dsize不能同时为0 fx,fy分别是在x,y轴上的缩放系数,默认取0时,fx=(double)dsize.width/src.cols ,fy=(double)dsize.height/src.cols,默认参数为0可以不写如里面。
interpolation:差值方式使用默认即可。默认的是INTER_LINEAR - a bilinear interpolation (used by default)现行差值,如下图所示。
我们习惯的坐标表示是:先x横坐标,再y纵坐标。在图像处理中,这种惯性思维经常情况下是错误的。
因为在计算机中,图像是以矩阵的形式保存的,先行后列。所以,一张宽×高×颜色通道=480×256×3的图片会保存在一个256×480×3 的三维张量中。图像处理时也是按照这种思想进行计算的(其中就包括 OpenCV 下的图像处理),即高×宽×颜色通道。
但是问题来了,cv2.resize这个API却是个小例外。因为它的参数输入却是 宽×高×颜色通道。
我们来看代码:
import cv2
pic = cv2.imread( "cat.jpg")
cv2.imshow( "org",pic)
pic = cv2.resize(pic, (400, 400), interpolation=cv2.INTER_CUBIC)
cv2.imshow('res', pic)
cv2.waitKey(0)
cv2.destroyAllWindows
先看原图效果:
再看缩放之后的效果:
我们发现,用这种方法缩小之后的宽高比跟原图并非一样,现在我们可以进行代码的改进:
import cv2
pic = cv2.imread( "cat.jpg")
cv2.imshow( "org",pic)
pic = cv2.resize(pic, (0, 0), fx=0.5, fy=0.5,
interpolation=cv2.INTER_NEAREST)
cv2.imshow('res', pic)
cv2.waitKey(0)
cv2.destroyAllWindows
我们直接来看缩放之后的图像:
在这里,如果我们不直接指定缩放后大小,则通过fx和fy直接指定缩放比例,0.5则长宽都为原来一半。
▼ 旋转
OpenCV中对图像的旋转主要是先通过getRotationMatrix2D函数得到图像的旋转矩阵,然后再通过仿射变换函数warpAffine得到旋转后的图像。
我们看一下函数:
cv2.getRotationMatrix2D(center, angle, scale)
center:旋转中心点 (cx, cy) ,你可以随意指定。
angle:旋转的角度 单位是角度 逆时针方向为正方向,角度为正值代表逆时针。
scale:缩放倍数,值等于1.0代表尺寸不变。
该函数返回的就是仿射变换矩阵M,之后我们将这个仿射变换的矩阵传入到下一个函数:
cv2.warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) → dst
src:输入图像。
M:变换矩阵。
dsize:输出图像的大小。
flags:插值方法的组合(int 类型!)
borderMode:边界像素模式(int 类型!)
borderValue:边界填充值; 默认情况下,它为0。
现在我们来讲述一下图像旋转的原理。
我们根据公式:
可以得出:
所以对应的变换矩阵为:
我们来看代码:
import cv2
img = cv2.imread( "cat.jpg")
rows,cols = img.shape[:2]
M = cv2.getRotationMatrix2D((cols/2,rows/2),90,1)
dst = cv2.warpAffine(img,M,(cols,rows))
cv2.imshow('res', dst)
cv2.waitKey(0)
cv2.destroyAllWindows
旋转之后的效果:
▼ 图像平移
图像的平移实际上就是矩阵的移动,它是最简单的一种空间变换,其表达式为:
其中(b0,b1) 是偏移量。
举个例子,如果是向右平移10个像素, 向下平移30个像素的话, 那么变换矩阵M:
我们来看源码:
import cv2
import numpy as np
img = cv2.imread('cat.jpg')
height,width,channel = img.shape
# 声明变换矩阵 向右平移10个像素, 向下平移30个像素
M = np.float32([[1, 0, 10], [0, 1, 30]])
# 进行2D 仿射变换
shifted = cv2.warpAffine(img, M, (width, height))
cv2.imshow('res', shifted)
cv2.waitKey(0)
cv2.destroyAllWindows
效果演示:
如若需要修改平移的方位,只需要改动矩阵的值就可以。
图像-几何转换
上次教程中我们谈及到OpenCV的缩放旋转以及平移,它们本质上都属于简单的几何变换,本次我们将讨论稍微复杂的一下几何变换。
▼ 图像翻转
图像翻转有多种方式,包括使用固定的API以及使用numpy进行操作等等。
(1)使用flip函数实现翻转
函数原型:
flip(src, flipCode[, dst]) -> dst
src:输入图片
flipCode:翻转代码
1:水平翻转 Horizontally (图片第二维度是column)
0:垂直翻转 *Vertically * (图片第一维是row)
-1:同时水平翻转与垂直反转 Horizontally & Vertically
我们来看代码:
importnumpy asnp
importcv2
frommatplotlib importpyplot asplt
img = cv2.imread( 'cat.jpg')
defbgr2rbg(img):
'''
将颜色空间从BGR转换为RBG
'''
returnimg[:,:,:: -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
代码实现了三种翻转的方式:水平翻转、垂直翻转以及水平垂直翻转。我们来看效果:
(2)使用numpy的索引进行图像反转
我们刚刚使用了OpenCV的API进行了图像翻转的操作,实际上仅仅使用numpy也是相当的方便。在numpy中使用img[:,::-1]则代表为矩阵水平翻转,使用img[::-1]则为矩阵垂直翻转,所以我们不难看出,水平与垂直同时翻转则为img[::-1, ::-1]。
我们来看代码:
importcv2
importnumpy asnp
frommatplotlib importpyplot asplt
img = cv2.imread( 'cat.jpg')
height,width,channel = img.shape
# 水平翻转
flip_h = img[:,:: -1]
# 垂直翻转
flip_v = img[:: -1]
# 水平垂直同时翻转
flip_hv = img[:: -1, :: -1]
defbgr2rbg(img):
'''
将颜色空间从BGR转换为RBG
'''
returnimg[:,:,:: -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
效果仍然相同:
(3)利用wrapAffine实现翻转
事实上,利用OpenCV的wrapAffine也可以进行图像的翻转,在上一节的教程中,我们已经讲述了利用相关函数实现旋转以及平移的操作,从本质上来讲,它们的原理基本上相同,现在我们来看一下用它实现翻转的原理:
我们假设width 代表图像的宽度 height代表图像的高度:
水平翻转的变换矩阵为:
垂直翻转的变换矩阵则为:
如若同时进行水平翻转与垂直翻转,那么有:
据此我们来看实现的代码:
importcv2
importnumpy asnp
frommatplotlib importpyplot asplt
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))
defbgr2rbg(img):
'''
将颜色空间从BGR转换为RBG
'''
returnimg[:,:,:: -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
之前我们在 仿射变换简介 里面讲解的是仿射变换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
可以看到,图像对于给定的坐标已经进行了矫正,这对于之后的形态学的操作是相当方便的。返回搜狐,查看更多
责任编辑: