数字图像处理

图像基础

图片表示

二值图

只有2种取值

灰度图

unit8

8位灰度图(0~255)

二维矩阵(一个通道)

彩色图

三维矩阵(RGB三个通道)

真彩色

通道的分离和合并

1
2
3
4
5
img_bgr = cv.imread(img_path)
# 通道分离
b, g, r = cv.split(img_bgr)
# 通道合并
img_rgb = cv.merge([r, g, b])

彩色图转换成灰度图

1
2
3
4
5
6
7
# 三通道按权值加权 0.299 0.587 0.114
gray1 = 0.299 * r + 0.587 *g + 0.114 *b
# dtype = uint8
gray2 = np.uint8(gray1)
gray3 = gray1.astype(np.uint8)

gray4 = cv.cvtColor(img_bgr, cv.COLOR_BGR2GRAY)

图像二值化

1
2
3
4
5
6
thresh = 125
gray4[gray4 > thresh] = 255
gray4[gray4 <= thresh] = 0
# gray4 已经被二值化

ignore, img_bin = cv.threshold(gray_uint8_img, th1, th2, cv.THRESH_BINARY)

图像运算

图像相加

混合图像、添加噪声

1
2
3
4
5
# dtype = float64
img_add1 = cv.add(img1*0.5, img2*0.5)

# dtype = uint8
img_add2 = cv.addWeighted(img1, alpha, img2, beta, gamma)
图像相减

消除背景、差影法(比较差异,运动跟踪)

1
img_sub = cv.subtract(img1, img2)
图像相乘

掩膜mask

1
img = cv.multiply(img1, img2)
图像相除

校正设备、比较差异

1
img = cv.divide(img1, img2)

图像变换

线性变换

$$
s=b+kr
$$

1
img = cv.convertScaleAbs(img, alpha=1, beta=0)
非线性变换

$$
s=a+\frac{ln(r+1)}{blnc}
$$

Gamma变换

$$
s=cr^y
$$

y越大图像越亮

1
2
img = img / 255
img = np.power(img, y) * 255

图像处理

裁剪
1
2
3
4
5
# numpy
img = cv.imread(img_path)
# h * w * c
# y * x *c
img = img[20:100, 100:200, :]
放缩
1
2
3
# OpenCv
# (x, y)=(w, h)=(500,400)
img = cv.resize(img, (500, 400))
平移

仿射变换

1
2
3
# 坐标的映射矩阵M
M = np.array([...], dtype=np.float32)
cv.warpAffine(img, M, dsize)
错切变换
1
2
M = np.array([...], dtype=np.float32)
img = cv.warpAffine(img, M, dsize)
镜像变换
1
2
3
4
5
6
7
8
9
10
11
12
# 矩阵
M = np.array([...], dtype=np.float32)
img = cv.warpAffine(img, M, dsize)

# 垂直镜像
cv.flip(img, 0)

# 水平镜像
cv.flip(img, 1)

# 同时进行
cv.flit(img, -1)
旋转变换
1
2
3
4
5
6
7
8
9
10
11
12
# 旋转矩阵
M = np.array([...], dtype=np.float32)
img = cv.warpAffine(img, M, dsize)

# M = cv.getRotationMatrix2D(center, angle, scale)
h, w, c = img.shape
# center = (x, y)
M = cv.getRotationMatrix2D((w//2, h//2), 45)
img = cv.warpAffine(img, M, dsize)

# 顺时针逆时针旋转90°
img_rotate = cv.rotate(img, cv.ROTATE_90_CLOCKWISE)
透视变换
1
2
M = cv.getPerspectiveTransform(src, dst)
img = cv.warpPerspective(img, M, dsize)
小总结

像素值没变,像素位置变了。
所以实际上计算了一个坐标变换的矩阵M。

最近邻插值

逆向思维:小图插值变大图 —> 大图变小图

1
img1 = cv.resize(img, dsize, interpolation=cv.INTER_NEAREST)
双线性插值

考虑邻近的像素点,按照权值计算。

1
img1 = cv.resize(img, dsize, interpolation=cv.INTER_LINEAR_EXACT)

图像模糊

卷积
1
img = cv.filter2D(img, -1, kernel)
均值模糊
1
2
3
cv.blur(img, (5,5))

cv.boxFilter(img, -1, (5,5))
中值滤波
1
cv.medianBlur(img, 3)  # 奇数
高斯模糊
1
2
3
4
# sigma 方差
# 方差小则copy原图
# 方差大则和均值滤波差不多
cv.GaussianBlur(img, (5,5), sigmaX)
双边滤波

一般模糊会丢失边缘信息,而双边滤波可以保留边缘高频信息,平滑颜色相近的地方。

需要一直更新卷积核的值:

  1. 距离越远,加权值越小
  2. 颜色差异越大,加权值越小

缺点:对高频噪声无滤波效果

1
cv.bilateralFilter(img, -1, sigmaColor=50, sigmaSpace=3)

图像边缘

……