0. 图像几何变换

图像几何变换是指对图像进行缩放、平移、旋转、仿射变换、偏移变换等。首先明确一点,图像是以矩阵存储的,所以对图像的操作即是对矩阵的操作,对图像的平移、旋转、变换等就是对矩阵的平移、旋转、变换。

我们知道在线性代数中,要使得矩阵X变换到矩阵Y,需要一个变换矩阵设为M,偏移量设为B,可以用公式表示:opencv合成图像 opencv图像几何变换_缩放

一下为常用的变换矩阵M(有可能和其他地方给出的矩阵不太一样,这取决于是opencv合成图像 opencv图像几何变换_python_02还是opencv合成图像 opencv图像几何变换_opencv_03):

opencv合成图像 opencv图像几何变换_计算机视觉_04

1. 扩展缩放

当图像放大时,会有更多的屏幕点来表示图像,产生的新的点的像素值怎么办?就需要图像内插的方法
图像内插:用已知数据来估计未知位置的数值的处理。放大、收缩、旋转和几何校正等任务。基本的三种内插方法(放大图像像素增加)

  • a. 最邻近内插法:四个近邻的像素放大后产生田字区域,每个口中的像素值取该口顶点的值,将明显产生马赛克感觉
  • opencv合成图像 opencv图像几何变换_opencv_05

  • b. 双线性内插:用4领域估计给定位置灰度值用
  • opencv合成图像 opencv图像几何变换_opencv合成图像_06

  • a. 双三次内插,8邻域
  • opencv合成图像 opencv图像几何变换_python_07

来看openCV关于图像缩放的函数


  • dsize:将图像缩放到什么尺寸,比如(500,500)即将img缩放到(500,500)
  • fx和fy:行和列的缩放比例因子,比如fx=2,fy=2即将img长宽各放大2倍
  • interpolation:插值方法,效果越好,速度越慢

插值方法

插值方法

cv2.INTER_NEAREST

最近邻插值

cv2.INTER_LINEAR

双线性插值(默认)

cv2.INTER_CUBIC

双线性插值

cv2.INTER_AREA

使用像素区域关系重新采样(推荐)

import cv2

img=cv2.imread("color.jpg")

# 长*0.7,宽*0.7
img_small=cv2.resize(img,None,fx=0.7,fy=0.7,interpolation=cv2.INTER_AREA)

# 直接缩放到(256,256)
img_256=cv2.resize(img,(256,256),interpolation=cv2.INTER_AREA)

cv2.imshow("img",img)
cv2.imshow("img_small",img_small)
cv2.imshow("img_256",img_256)
cv2.waitKey(0)

opencv合成图像 opencv图像几何变换_opencv合成图像_08

2. 平移

如果沿(x,y)方向移动,移动的距离是opencv合成图像 opencv图像几何变换_python_09,则变换矩阵为:
opencv合成图像 opencv图像几何变换_python_10

我们只需要将img矩阵和M矩阵做运算即可,在这里我们使用:


如何定义矩阵M?使用numpy:
M = np.array([[1, 0, 100], [0, 1, 100]],dtype=np.float32)

import cv2
import numpy as np

img = cv2.imread("color.jpg")
size=img.shape[0:2] # 获取 图像的宽高

# 定义t_x=100  t_y=100的变换矩阵
M = np.array([[1, 0, 100], [0, 1, 100]],dtype=np.float32)

# 传入img和变换矩阵,定义输出图像的尺寸为size(size就是上面定义的原图大小)
res = cv2.warpAffine(img, M, size)

cv2.imshow("",res)
cv2.waitKey(0)

3. 旋转

如果旋转角度为θ,则变换矩阵应该为:
opencv合成图像 opencv图像几何变换_opencv合成图像_11

import cv2
import numpy as np

img = cv2.imread("color.jpg")
size=img.shape[0:2]

# np.pi:兀
theta=np.pi/3  # 60°

sin=np.sin(theta)
cos=np.cos(theta)


M = np.array([[cos, -sin,0], [sin, cos,0]],dtype=np.float32)
print(M)
res = cv2.warpAffine(img, M, size)

cv2.imshow("img",img)
cv2.imshow("res",res)
cv2.waitKey(0)

opencv合成图像 opencv图像几何变换_缩放_12


使用openCV提供的函数来定义矩阵M:


  • center:旋转中心,比如(cols/2,rows/2)代表以图像中心为选择点
  • theta:旋转角度,比如60代表旋转60°
  • flag:缩放因子,比如0.6代表旋转后图像缩到0.6倍
import cv2
import numpy as np

img = cv2.imread("color.jpg")
rows,cols=img.shape[0:2]

# 定义一个以图像中心为旋转点,旋转45°,缩放0.6倍的变换矩阵M
M=cv2.getRotationMatrix2D((rows/2,cols/2),45,0.6)

res = cv2.warpAffine(img, M,(rows,cols))

cv2.imshow("img",img)
cv2.imshow("res",res)
cv2.waitKey(0)

opencv合成图像 opencv图像几何变换_opencv_13

4. 仿射变换

什么是仿射变换?仿射变换后的图像中,原来是平行的两条线现在任然是平行的。

opencv合成图像 opencv图像几何变换_opencv合成图像_14


由于透视效果,原本两队边应该平行,但拍摄出来不平行,所以仿射矫正后两队边依然不平行,但是已经有比较好的效果了(如果要做到极致当然需要其他方法)

现在假设我们有原图A图像和标准图B图像,那么如果通过仿射变换将A图转换为B图?

  • 通过A和B的关系,求出变换矩阵M
  • 通过A和M做运算得到B
    如何求矩阵M?比如上图的二维码中,三个角的方块角位置探测图像,原图和结果图都有三个位置探测图像,那么通过原图和结果图的位置探测图像是一一对应的,也就能列举三个方程,从而解出矩阵M。当然OpenCV提供了这些方法

  • pts1:代表原图的三个点
  • pts2:代表结果图的三个点

  • img:原图
  • M:变换矩阵
  • dsize:输出图的大小
import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread("QR.JPG") # 二维码原图像
rows, cols = img.shape[0:2] # 获取图像宽高

# 分别代表原图中三个位置探测图像的坐标
pts1 = np.float32([[132, 378], [83, 195], [256, 92]]) 
# 分别代表标准二维码中三个位置探测图像的坐标
pts2 = np.float32([[34, 395], [34, 34], [395, 34]])

M = cv2.getAffineTransform(pts1, pts2) #计算原图到标准图的变换矩阵M

# 根据矩阵M将img转变到标准图像(但由于透视效果,原本两队边应该平行,但拍摄
# 出来不平行,所以仿射矫正后两队边依然不平行,但是已经有比较好的效果了)
res = cv2.warpAffine(img, M, (rows*2, cols*2)) 

plt.rcParams['font.sans-serif']=['SimHei']
plt.subplot(121),plt.imshow(img),plt.title('原图像')
plt.subplot(122),plt.imshow(res),plt.title('结果图')

plt.show()

5. 透视变换

未完待续