图片裁剪的两种方法
图像处理时常常需要裁剪,填充图像,这里给出两种裁剪方式
1 使用cv2.split 将图像先分成BGR三张,然后对每张使用数组形式的截取,然后填充avg
def pad_image_by_mean(image, pad):
# 这里先把bgr提取出来,分别计算平均值,分别pad,然后合并
b, g, r = cv2.split(image)
avg_b, avg_g, avg_r = np.mean(b), np.mean(g), np.mean(r)
b = np.pad(b, ((pad, pad), (pad, pad)), mode='constant', constant_values=avg_b)
g = np.pad(g, ((pad, pad), (pad, pad)), mode='constant', constant_values=avg_g)
r = np.pad(r, ((pad, pad), (pad, pad)), mode='constant', constant_values=avg_r)
image = cv2.merge((b, g, r))
return image
哈哈,这是最开始的一个方法,估计效率一般,然后看到SiamMask代码中的裁剪,不得不说,厉害了老铁
2 使用cv2.warpAffine 这个仿射变换函数进行裁剪,填充,还能放缩
原理解释:https://blog.csdn.net/windowsyun/article/details/78158747
为了避免链接失效,我还是抄一遍吧
import cv2
import numpy as np
def crop_hwc(image, bbox, out_sz, padding=(0, 0, 0)):
a = (out_sz-1) / (bbox[2]-bbox[0])
b = (out_sz-1) / (bbox[3]-bbox[1])
c = -a * bbox[0]
d = -b * bbox[1]
mapping = np.array([[a, 0, c],
[0, b, d]]).astype(np.float)
print(mapping)
crop = cv2.warpAffine(image, mapping, (out_sz, out_sz), borderMode=cv2.BORDER_CONSTANT, borderValue=padding)
return crop
if __name__ == '__main__':
image = cv2.imread("A.jpg")
cv2.imshow("ori", image)
avg = np.mean(image, axis=(0, 1))
crop = crop_hwc(image, [400, 400, 700, 700], 300, avg)
cv2.imshow("crop", crop)
cv2.waitKey()
什么是仿射变换?
旋转 (线性变换)
缩放操作(线性变换)
平移 (向量加)
放射变化的矩阵形式
图像的变换要对图像的每一个像素点进行操作,假设其中的一个像素点的坐标是(x,y),我们用矩阵形式表示:
我们通常使用 2 * 3 矩阵来表示仿射变换。
矩阵 A是旋转矩阵, B 是平移矩阵, M就是变换矩阵
经过仿射变换后的点的矩阵坐标是T,我们已经知道放射变换就是线性变换加上平移,用矩阵表示的话就是
$$ T = AX+B $$
也可以写成
$$ T = M \cdot \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} $$
即
$$ T = \begin{bmatrix} a_{00}x+a_{01}y+b_{00} \\ a_{10}x+a_{11}y+b_{01} \end{bmatrix} $$
使用代码
平移
来看一下之前说过的图像平移的代码
import cv2
import numpy as np
img = cv2.imread('p.png', 0)
rows, cols = img.shape
M = np.float32([[1, 0, 200], [0, 1, 100]])
dst = cv2.warpAffine(img, M, (cols, rows))
cv2.imshow("ori", img)
cv2.imshow('img', dst)
cv2.waitKey()
其中
M = np.float32([[1, 0, 200], [0, 1, 100]])
将这个二维矩阵的值带入T,得到经过仿射变换后的点的坐标是(x+200,y+100),即将整个图像平移(200,100)
旋转
再看图像旋转的代码
import cv2
img = cv2.imread('123.jpg')
rows, cols = img.shape[:2]
M = cv2.getRotationMatrix2D((cols / 2, rows / 2), 90, 1)
dst = cv2.warpAffine(img, M, (cols, rows)) # 仿射变换,以后再说
cv2.imshow("ori", img)
cv2.imshow('dst', dst)
cv2.waitKey()
大部分同上类似,只是其中的 M 矩阵不同
(cols / 2, rows / 2) 旋转中心位置
90 指旋转角度
1 指放缩比例
M = cv2.getRotationMatrix2D((cols / 2, rows / 2), 90, 1)
cv2.getRotationMatrix2D这个函数就是生成图像旋转的所需要的矩阵
那么如何通过仿射变换任意变换图形呢?
我们需要源图像和目标图像上分别一一映射的三个点来定义仿射变换
示例代码:
import cv2
import numpy as np
def paint_point(frame, x, y, color=(0, 0, 255), point_size=6):
cv2.circle(frame, (x, y), point_size, color, -1)
if __name__ == '__main__':
img = cv2.imread('123.jpg')
h, w, ch = img.shape
pts1 = np.float32([[0, 0], [w - 1, 0], [0, h - 1]]) #选取了一个位于左上角的一个三角形
pts2 = np.float32([[w * 0.2, h * 0.1], [w * 0.9, h * 0.2], [w * 0.1, h * 0.9]]) #对应到目标位置的三角形
for i in range(3):
paint_point(img, pts1[i][0], pts1[i][1], color=(0, 0, 255))
paint_point(img, pts2[i][0], pts2[i][1], color=(255, 0, 0))
M = cv2.getAffineTransform(pts1, pts2)
dst = cv2.warpAffine(img, M, (w, h))
cv2.imshow('image', dst)
cv2.imshow("ori", img)
cv2.waitKey()