图像的几何变化
一、基本概念
在不改变图像的内容的前提下, 对图像的像素进行空间几何变化;
包括 图像的平移、镜像变换、缩放和旋转等;
假设原始图像的 f(x0, y0),经过几何变化产生的目标图像为 g(x1, y1), 则空间变换(映射)关系为:
x1 = s(x0, y0)
y1 = t(x0, y0)
那么, s(x0, y0), t(x0, y0) 为由 f(x0, y0) 到 g(x1, y1) 的坐标变换函数;
求解出坐标变换函数后,开始计算变换后的图像的像素为止,然后插值到变换后的图像上。
二、插值法
对于数字图像而言,像素的坐标是 离散型非负整数;在进行控件坐标转换的时候,可能会产生浮点坐标;
例如 (11, 11) --> (5.5, 5.5), 这种坐标值是无效的,插值法就是用来处理这些浮点数的;
常见的插值法:
1、最邻近插值法
2、双线性插值法
3、二次立方插值法;
4、三次立方插值法等
最邻近插值法
假设 u,v 是 0<u, v<1 的小数,(i+u, j+v)为输入的像素坐标,待求的像素灰度值 f(i+u, j+v), 根据 u,v 的值不同, f(i+u, j+v) 的值也是不同的;
弊端:最近邻的计算量很小,但是会造成图像灰度上的不连续;在灰度变化密集的时候,会有锯齿的情况。
双线性插值法
对于目标像素,设置坐标通过反向变换得到浮点坐标 (i+u, j+v);
其中 i, j 都是非负整数;u,v 是 [0,1)
区间的浮点数; 这个像素的值 f(i+u, j+v) 由原图坐标 (i,j), (i+1,j), (i, j+1), (i+1, j+1) 所对应的周围4个像素值决定。
相比最近邻插值法,双线性的计算量很大,但是缩放之后的图像质量高,不会出现像素值不连续的情况。
双线性插值法 具有 低通过滤器的特性,算是图像的高频分量,图像在轮廓上会变得模糊;
差值算法的实现
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('lena.jpg')
# 封装方法来显示图片
def cv_show(img):
cv2.imshow('w title',img)
waitret = cv2.waitKey(2000)
# wait = cv2.waitKey()
print(waitret)
cv2.destroyAllWindows()
resize
resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) -> dst
- 默认差值算法为双线性插值;
- src:输入图像;
- 返回:输出图像
- dsize:输出图像的大小;如果当前参数为0,原始图像缩放的大小需要按照公式进行计算:dsize = Size(round(fx * src.cols), round(fy * src.rows))。fx 和 fy 是宽度和高度的缩放比例。
- interpolation 表示差值的方式
cv2.resize()
img.shape
# (263, 263, 3)
三、图像的缩放
分为 水平缩放系数 和 垂直缩放系数;
- < 1 变小
- = 1 不变
> 1
变大
缩放原理
$ x = x_0 * fx \( \) y = y_0 * fy $
- fx : 水平缩放系数
- fy : 垂直缩放系数
- $ (x_0, y_0) $ : 缩放之前的坐标
- (x, y) : 缩放之后的坐标
img1 = cv2.resize(img, (120, 120))
plt.imshow(img1)
img2 = cv2.resize(img, (500, 500))
plt.imshow(img)
img3 = cv2.resize(img, (500, 500), interpolation=cv2.INTER_LINEAR)
plt.imshow(img3)
四、图像的平移
warpAffine 方法
warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) -> dst
. @brief Applies an affine transformation to an image.
-
插补方法的标志组合(见#InterpolationFlags) 和可选的标志 #WARP_INVERSE_MAP,这意味着M是逆变换 (\(\texttt{dst}\rightarrow\texttt{src}\))
-
borderMode 像素外推方法(见#BorderTypes);当borderMode=#BORDER_TRANSPARENT时,它意味着目标图像中对应于源图像中的“异常值”的像素不被函数修改。
调用
cv2.warpAffine
arr1 = np.array([[1,0, 100], [0,1,100]], np.float32)
img4 = cv2.warpAffine(img, arr1, img.shape[:2])
五、图像的旋转
让图像按照某一点旋转到指定的角度,图像旋转之后不会变形,但是垂直对称轴和水平对称轴 会发生改变,旋转后的坐标 和 原始图像的坐标已经不能通过简单地加减乘除来得到映射;
确定参数:图像的旋转中心,旋转角度,缩放因子;
步骤:
- 得到变换矩阵
- 通过函数进行变换
注意:
- 无论是平移还是旋转,都会出现图像被裁剪的问题(比如旋转 75°的时候);
因此需要先计算输出图像的尺寸,来避免数据的损失。
getRotationMatrix2D 方法
getRotationMatrix2D(center, angle, scale) -> retval
. @brief Calculates an affine matrix of 2D rotation.
这个方法计算了下面的矩阵
这个变换将旋转中心映射到它自己。如果这不是目标,调整转换。
- center:原始图片的旋转中心 Center of the rotation in the source image.
- angle :旋转角度。正值表示逆时针旋转(假设坐标原点为左上角)。
- scale :各向同性的比例因子。
h,w,n = img.shape
mat = cv2.getRotationMatrix2D((w/2, h/2), -90,1)
img5 = cv2.warpAffine(img, mat, img.shape[:2]) # img.shape[:2] 取图片的宽高
plt.imshow(img5)
mat = cv2.getRotationMatrix2D((w/2, h/2), -135,1)
img6 = cv2.warpAffine(img, mat, (int(img.shape[0]*1.5), int(img.shape[1]*1.6))
img.shape[0] * 1.5 # 394.5
int(img.shape[1]*1.6) # 420
六、镜像变换
理论
分为 水平镜像和垂直镜像
水平镜像以 图像垂直中线 为轴;
垂直镜像以 水平中线为轴,将图像的上半部分和下半部分对调
变换原理
水平镜像 $ x = width - x, y = y_0 $
垂直镜像 $ x = x_0, y = height - y_0 $
flip 函数
flip(src, flipCode[, dst]) -> dst
- flipCode : 如何反转数组
- 0 垂直反转
- 1 水平翻转
- 负数(如-1),同时翻转
. @sa transpose , repeat , completeSymm
使用
img6 = cv2.flip(img,0)
plt.imshow(img6)