我们在处理图像时,往往会遇到需要对图像进行几何变换的问题。图像的几何变换是图像处理和图像分析的基础内容之一,不仅提供了产生某些图像的可能,还可以使图像处理和分析的程序简单化,特别是图像具有一定的规律性时,一个图像可以由另一个图像通过几何变换来实现。所以,为了提高图像处理和分析程序设计的速度和质量,开拓图像程序应用范围的新领域,对图像进行几何变换是十分必要的。
图像的几何变换不改变图像的像素值,而是改变像素所在的几何位置。从变换的性质来分,图像的几何变换有图像的位置变换(平移、镜像、旋转)、图像的形状变换(放大、缩小、错切)等基本变换以及图像的复合变换。其中,使用最频繁的是图像的缩放和旋转,不论照片、图画、书报还是医学X光和卫星遥感图像,都会用到这两项技术。
作者:Xiou
测试原图:
图像平移是将一幅图像中所有的点都按照指定的平移量在水平、垂直方向移动,平移后的图像与原图像相同。平移后的图像上的每一点都可以在原图像中找到对应的点。
OpenCV提供了函数warpAffine,该函数使用指定的矩阵变换源图像,声明如下:
warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]])
参数:
src表示输入的图像;
M表示2×3变换矩阵;
dsize表示输出图像的大小;
dst表示大小为dsize且类型与src相同的输出图像;
flags表示插值方法的组合;
borderMode表示边界模式;
borderValue表示在边界为常量的情况下使用的值,默认为0。
代码实例:
import numpy as np
import cv2 as cv
img = cv.imread(r'test.jpg', 1)
rows, cols, channels = img.shape
M = np.float32([[1,0,100],[0,1,50]])
res = cv.warpAffine(img, M, (cols, rows))
cv.imshow('img', res)
cv.waitKey(0)
cv.destroyAllWindows()
输出结果:
图像的旋转是数字图像处理中一个非常重要的环节,是图像的几何变换手法之一。图像的旋转算法是图像处理的基础算法。在数字图像处理过程中。经常要用到旋转,比如在进行图像扫描时,需要运用旋转实现图像的倾斜校正;在进行多幅图像的比较和模式识别、对图像进行剪裁和拼接前,都需要进行图像的旋转处理。
一般图像的旋转是以图像的中心为原点的,将图像上的所有像素都旋转一个相同的角度。图像的旋转变换时图像的位置变换,旋转后图像的大小一般会改变。在图像旋转变换中既可以把转出显示区域的图像截去,也可以扩大图像范围以显示所有的图像。
图像旋转是指图像以某一点为中心旋转一定的角度,形成一幅新的图像的过程。当然这个点通常就是图像的中心。既然是按照中心旋转,自然会有这样一个属性:旋转前和旋转后的点离中心的位置不变。
可以利用OpenCV提供的库函数getRotationMatrix2D来实现图像旋转。该函数用来计算出旋转矩阵,声明如下:
getRotationMatrix2D(center, angle, scale)
参数
center表示旋转的中心点;
angle表示旋转的角度;
scale表示图像缩放因子。
该函数的返回值为一个2×3的矩阵,其中矩阵前两列代表旋转、最后一列代表平移。
代码实例:
import cv2
import numpy as np
img=cv2.imread('test.jpg')
rows,cols=img.shape[:2]
M=cv2.getRotationMatrix2D((cols/2,rows/2),60,1.2)
#第一个参数是旋转中心,第二个参数是旋转角度,第三个因子是旋转后的缩放因子
shuchu=cv2.warpAffine(img,M,(2*cols,rows)) # 第三个参数是输出图像的尺寸中心,图像的宽和高
while(1):
cv2.imshow('Result',shuchu)
if cv2.waitKey(1)&0xFF==27:
break
cv2.destroyAllWindows()
输出结果:
不出现裁剪的旋转
代码实例:
import cv2
import numpy as np
img=cv2.imread('test.jpg')
def rotate_bound(image, angle):
# 抓取图像的尺寸,然后确定中心
(h, w) = image.shape[:2]
(cX, cY) = (w // 2, h // 2)
# 抓取旋转矩阵(应用角度的负数顺时针旋转),然后抓取正弦和余弦
# (即矩阵的旋转分量)
M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)
cos = np.abs(M[0, 0])
sin = np.abs(M[0, 1])
# 计算图像的新边界尺寸
nW = int((h * sin) + (w * cos))
nH = int((h * cos) + (w * sin))
# 调整旋转矩阵以考虑平移
M[0, 2] += (nW / 2) - cX
M[1, 2] += (nH / 2) - cY
# 执行实际旋转并返回图像
shuchu=cv2.warpAffine(image, M, (nW, nH))
while (1):
cv2.imshow('shuchu', shuchu)
if cv2.waitKey(1) & 0xFF == 27:
break
rotate_bound(img,45)
在上述代码中,首先抓取图像的尺寸,然后确定中心,接着抓取旋转矩阵、正弦和余弦,再计算图像的新边界尺寸,调整旋转矩阵以考虑平移,最后执行实际旋转并返回图像。
仿射变换是一种常用的图像几何变换。平移、旋转、缩放、翻转、剪切等变换都属于仿射变换。使用仿射变换矩阵能够方便地描述图像的线性变换以及平移等非线性变换。仿射变换的功能是从二维坐标到二维坐标之间的线性变换,且保持二维图形的“平直性”和“平行性”。
在OpenCV中,进行仿射变换的函数是warpAffine,声明如下:
warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]])
其中,
src表示输入图像;
dst表示输出图像,尺寸由dsize指定,图像类型与原图像一致;
M表示2×3的变换矩阵;
dsize指定图像输出尺寸;
flags表示插值算法标识符,如果为WARP_INVERSE_MAP,则有默认值INTER_LINEAR;
borderMode表示边界像素模式,有默认值BORDER_CONSTANT;
borderValue表示边界取值,有默认值Scalar(),即0。
代码实例:
import numpy as np
import cv2 as cv
img = cv.imread(r'test.jpg', 1)
rows, cols, channels = img.shape
p1 = np.float32([[0,0], [cols-1,0], [0,rows-1]])
p2 = np.float32([[0,rows*0.3], [cols*0.8,rows*0.2], [cols*0.15,rows*0.7]])
M = cv.getAffineTransform(p1, p2)
dst = cv.warpAffine(img, M, (cols,rows))
cv.imshow('original', img)
cv.imshow('result', dst)
cv.waitKey(0)
cv.destroyAllWindows()
在上述代码中,getRotationMatrix2D先计算出旋转矩阵,再利用函数warpAffine实现旋转。仿射变换中的平移、旋转、缩放、翻转(翻转镜像除了用三点法求仿射变换矩阵外,还可以用flip函数实现)和错切主要是一个仿射映射矩阵的设置,灵活地设置不同的变换矩阵可以得到不同的变换效果。这里的仿射变换要想得到很好的效果,如旋转以后图像的大小不变而且图像位于窗口中心,就需要进行窗口大小调整和旋转后图像的平移操作。
图像比例缩放是指将给定的图像在x轴方向按比例缩放fx倍,在y轴方向按比例缩放fy倍,从而获得一幅新的图像。如果fx=fy,即在x轴方向和y轴方向缩放的比率相同,就称这样的比例缩放为图像的全比例缩放。如果fx≠fy,图像的比例缩放会改变原始图像的像素间的相对位置,产生几何畸变。
OpenCV中,实现图像缩放的函数是resize,函数声明如下:
resize(src, dsize[, dst[, fx[, fy[, interpolation]]]])
参数
src表示原图像;
dst表示输出图像;
dsize表示目标图像的大小;
fx表示在x轴上的缩放比例;
fy表示在y轴上的缩放比例;
interpolation表示插值方式,包括INTER_NN(最近邻插值)、INTER_LINEAR(默认值,双线性插值)、INTER_AREA(使用像素关系重采样,当图像缩小时该方法可以避免波纹出现,当图像放大时类似于INTER_NN方法)、INTER_CUBIC(立方插值)。
注意:dsize、fx和fy不能同时为零。
代码实例:
import numpy as np
import cv2
def resizeImage(image,width=None,height=None,inter=cv2.INTER_AREA):
newsize = (width,height)
#获取图像尺寸
(h,w) = image.shape[:2]
if width is None and height is None:
return image
#高度算缩放比例
if width is None:
n = height/float(h)
newsize = (int(n*w),height)
else :
n = width/float(w)
newsize = (width,int(h*n))
# 缩放图像
newimage = cv2.resize(image, newsize, interpolation=inter)
return newimage
imageOriginal = cv2.imread("lakeWater.jpg")
cv2.imshow("Original", imageOriginal)
#获取图像尺寸
w = width=imageOriginal.shape[1]
h = width=imageOriginal.shape[2]
print ("Image size:",w,h)
#放大2倍
newimage = resizeImage(imageOriginal,w*2,h*2,cv2.INTER_LINEAR)
cv2.imshow("New", newimage)
#保存缩放后的图像
cv2.imwrite('newimage.jpg',newimage)
#缩小5倍
newimage2 = resizeImage(imageOriginal,int(w/5),int(h/5),cv2.INTER_LINEAR)
cv2.imwrite('newimage2.jpg',newimage2)
cv2.imshow('result', newimage)
cv2.waitKey(0)
cv2.destroyAllWindows()
在上述代码中,我们先显示了原图,然后先后利用resize函数对图像进行放大2倍和缩小5倍处理。
输出结果: