Q.1. 通道交换
读取图像,然后将 RGB 通道替换成 BGR 通道。
注意,cv2.imread() 的系数是按 BGR 顺序排列的!其中的变量 red 表示的是仅有原图像红通道的 imori.jpg。
import cv2
#读取图像
img = cv2.imread("imori.jpg")
cv2.imshow("imori", img)#显示读取图像
cv2.waitKey(0)
cv2.destroyAllWindows()
#通道交换
b = img[:, :, 0].copy()#提取图像的蓝色通道
g = img[:, :, 1].copy()#提取图像的绿色通道
r = img[:, :, 2].copy()#提取图像的红色通道
# RGB > BGR,显示为RGB图像
img[:, :, 0] = r
img[:, :, 1] = g
img[:, :, 2] = b
cv2.imwrite("out1.jpg", img)
cv2.imshow("result1", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出:
Q.2. 灰度化(Grayscale)
将图像灰度化吧!灰度是一种图像亮度的表示方法,通过下式计算:
Y = 0.2126 R + 0.7152 G + 0.0722 B
import cv2
import numpy as np
#读取图像
img = cv2.imread("imori.jpg").astype(np.float)
b = img[:, :, 0].copy()
g = img[:, :, 1].copy()
r = img[:, :, 2].copy()
# 图像灰度化
out = 0.2126 * r + 0.7152 * g + 0.0722 * b
out = out.astype(np.uint8)#astype修改数据类型为uint8
cv2.imwrite("out2.jpg", out)
cv2.imshow("result2", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出:
Q.3. 二值化(Thresholding)
把图像进行二值化!二值化是将图像使用黑和白两种值表示的方法。这里我们将灰度的阈值设置为 128 来进行二值化,即:
y = { 0 (if y < 128)
255 (else)
import cv2
import numpy as np
# 读取图像
img = cv2.imread("imori.jpg").astype(np.float)
b = img[:, :, 0].copy()
g = img[:, :, 1].copy()
r = img[:, :, 2].copy()
# 图像二值化
th = 128
out[out < th] = 0
out[out >= th] = 255
cv2.imwrite("out3.jpg", out)
cv2.imshow("result3", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出:
Q.4. 大津二值化算法(Otsu’s Method)
使用大津算法来二值化图像吧!大津算法,也被称作最大类间方差法,是一种可以自动确定二值化中阈值的算法,从类内方差和类间方差的比值计算得来:
小于阈值 t 的类记作 0,大于阈值 t 的类记作 1;
w0 和 w1 是被阈值 t 分开的两个类中的像素数占总像素数的比率(满足 w0+w1=1);
S0^2, S1^2 是这两个类中像素值的方差;
M0, M1 是这两个类的像素值的平均值;
也就是说:
类内方差:Sw^2 = w0 * S0^2 + w1 * S1^2
类间方差:Sb^2 = w0 * (M0 - Mt)^2 + w1 * (M1 - Mt)^2 = w0 * w1 * (M0 - M1) ^2
图像所有像素的方差:St^2 = Sw^2 + Sb^2 = (const)
根据以上的式子,我们用以下的式子计算分离度:
分离度 X = Sb^2 / Sw^2 = Sb^2 / (St^2 - Sb^2)
也就是说:
argmax_{t} X = argmax_{t} Sb^2
换言之,如果使 Sb^2 = w0 * w1 * (M0 - M1) ^2 最大,就可以得到最好的二值化阈值 t。
import cv2
import numpy as np
#读取图像
img = cv2.imread("imori.jpg").astype(np.float)
H, W, C = img.shape#取彩色图片的长、宽、通道。
# Grayscale
out = 0.2126 * img[..., 2] + 0.7152 * img[..., 1] + 0.0722 * img[..., 0]
out = out.astype(np.uint8)
#确定大津二值化的阈值
max_sigma = 0#初值化
max_t = 0
#寻找使Sb^2 = w0 * w1 * (M0 - M1) ^2 最大的,得到最好的二值化阈值t。
for _t in range(1, 255):
v0 = out[np.where(out < _t)]
m0 = np.mean(v0) if len(v0) > 0 else 0.
w0 = len(v0) / (H * W)
v1 = out[np.where(out >= _t)]
m1 = np.mean(v1) if len(v1) > 0 else 0.
w1 = len(v1) / (H * W)
sigma = w0 * w1 * ((m0 - m1) ** 2)
if sigma > max_sigma:
max_sigma = sigma
max_t = _t
# 进行二值化
print("threshold >>", max_t)
th = max_t
out[out < th] = 0
out[out >= th] = 255
cv2.imwrite("out4.jpg", out)
cv2.imshow("result4", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出:
Q.5. HSV 变换
将使用 HSV 表示色彩的图像的色相反转吧!
HSV 即使用**色相(Hue)、饱和度(Saturation)、明度(Value)**来表示色彩的一种方式。
色相:将颜色使用0到360度表示,就是平常所说的颜色名称,如红色、蓝色。色相与数值按下表对应:
红 黄 绿 青色 蓝色 品红 红
0 60 120 180 240 300 360
饱和度:是指色彩的纯度,饱和度越低则颜色越黯淡( 0<= S < 1);
明度:即颜色的明暗程度。数值越高越接近白色,数值越低越接近黑色 ( 0 <= V < 1);
从 RGB 色彩表示转换到 HSV 色彩表示通过以下方式计算:
R,G,B的值在[0, 1]之间:
Max = max(R,G,B)
Min = min(R,G,B)
H = { 0 (if Min=Max)
60 x (G-R) / (Max-Min) + 60 (if Min=B)
60 x (B-G) / (Max-Min) + 180 (if Min=R)
60 x (R-B) / (Max-Min) + 300 (if Min=G)
V = Max
S = Max - Min
从 HSV 色彩表示转换到 RGB 色彩表示通过以下方式计算:
C = S
H’ = H / 60
X = C (1 - |H’ mod 2 - 1|)
(R,G,B) = (V - C) (1,1,1) + { (0, 0, 0) (if H is undefined)
(C, X, 0) (if 0 <= H’ < 1)
(X, C, 0) (if 1 <= H’ < 2)
(0, C, X) (if 2 <= H’ < 3)
(0, X, C) (if 3 <= H’ < 4)
(X, 0, C) (if 4 <= H’ < 5)
(C, 0, X) (if 5 <= H’ < 6)
请将色相反转(色相值加180),然后再用 RGB 色彩空间表示图片。
import cv2
import numpy as np
#读取图像
img = cv2.imread("imori.jpg").astype(np.float32) / 255.
# RGB > HSV,(使用HSV表示色彩的图像的色相反转)
out = np.zeros_like(img)#构建一个与img图像大小相同的矩阵,赋值为0
max_v = np.max(img, axis=2).copy()#求序列的最值,最少接收一个参数,axis:默认为列向( axis=0),axis = 1 时为行方向的最值;axis=2,通道方向最值
min_v = np.min(img, axis=2).copy()
min_arg = np.argmin(img, axis=2)
H = np.zeros_like(max_v)#构建一个与max_v图像大小相同的矩阵,赋值为0
#if Min=Max
H[np.where(max_v == min_v)] = 0
#if min == B
ind = np.where(min_arg == 0)
H[ind] = 60 * (img[..., 1][ind] - img[..., 2][ind]) / (max_v[ind] - min_v[ind]) + 60
#if min == R
ind = np.where(min_arg == 2)
H[ind] = 60 * (img[..., 0][ind] - img[..., 1][ind]) / (max_v[ind] - min_v[ind]) + 180
#if min == G
ind = np.where(min_arg == 1)
H[ind] = 60 * (img[..., 2][ind] - img[..., 0][ind]) / (max_v[ind] - min_v[ind]) + 300
V = max_v.copy()
S = max_v.copy() - min_v.copy()
#色相反转
H = (H + 180) % 360
# HSV > RGB,(从 HSV 色彩表示转换到 RGB 色彩)
C = S
H_ = H / 60
X = C * (1 - np.abs(H_ % 2 - 1))
Z = np.zeros_like(H)#构建一个与img图像大小相同的矩阵,赋值为0
vals = [[Z, X, C], [Z, C, X], [X, C, Z], [C, X, Z], [C, Z, X], [X, Z, C]]
for i in range(6):##不明白什么意思!!!
ind = np.where((i <= H_) & (H_ < (i + 1)))
out[..., 0][ind] = (V - C)[ind] + vals[i][0][ind]
out[..., 1][ind] = (V - C)[ind] + vals[i][1][ind]
out[..., 2][ind] = (V - C)[ind] + vals[i][2][ind]
out[np.where(max_v == min_v)] = 0
out = (out * 255).astype(np.uint8)
#保存结果
cv2.imwrite("out5.jpg", out)
cv2.imshow("result5", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出:
Q.6. 减色处理
这里我们将图像的值由2563压缩至43,即将 RGB 的值只取 {32, 96, 160, 224}。这被称作色彩量化。色彩的值按照下面的方式定义:
val = { 32 ( 0 <= val < 64)
96 ( 64 <= val < 128)
160 (128 <= val < 192)
224 (192 <= val < 256)
import cv2
import numpy as np
# 读取图像
img = cv2.imread("imori.jpg")
# 减色处理
out = img.copy()
out = out // 64 * 64 + 32# out // 64除法,商取整
cv2.imwrite("out6.jpg", out)
cv2.imshow("result6", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出:
Q.7. 平均池化(Average Pooling)
将图片按照固定大小网格分割,网格内的像素值取网格内所有像素的平均值。我们将这种把图片使用均等大小网格分割,并求网格内代表值的操作称为池化(Pooling)。池化操作是卷积神经网络(Convolutional Neural Network)中重要的图像处理方式。平均池化按照下式定义:
v = 1/|R| * Sum_{i in R} v_i
请把大小为 128x128 的imori.jpg使用 8x8 的网格做平均池化。
import cv2
import numpy as np
# 读取图像
img = cv2.imread("imori.jpg")
# 平均池化
out = img.copy()
H, W, C = img.shape#取彩色图片的长、宽、通道。
G = 8#使用8x8的网格做平均池化
Nh = int(H / G)#池化的长
Nw = int(W / G)#池化的宽
for y in range(Nh):
for x in range(Nw):
for c in range(C):
out[G*y:G*(y+1), G*x:G*(x+1), c] = np.mean(out[G*y:G*(y+1), G*x:G*(x+1), c]).astype(np.int)#取网格内所有像素的平均值,并取整,进行平均池化
# 保存结果
cv2.imwrite("out7.jpg", out)
cv2.imshow("result7", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出:
Q.8. 最大池化(Max Pooling)
网格内的值不取平均值,而是取网格内的最大值进行池化操作。
import cv2
import numpy as np
# 读取图片
img = cv2.imread("imori.jpg")
# 最大池化
out = img.copy()
H, W, C = img.shape#取彩色图片的长、宽、通道。
G = 8#使用8x8的网格做最大池化
Nh = int(H / G)#池化的长
Nw = int(W / G)#池化的宽
for y in range(Nh):
for x in range(Nw):
for c in range(C):
out[G*y:G*(y+1), G*x:G*(x+1), c] = np.max(out[G*y:G*(y+1), G*x:G*(x+1), c])#取网格内所有像素的最大值,并取整,进行最大池化
# 保存结果
cv2.imwrite("out8.jpg", out)
cv2.imshow("result8", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出:
Q.9. 高斯滤波(Gaussian Filter)
使用高斯滤波器(3x3 大小,标准差 s=1.3)来对imori_noise.jpg进行降噪处理吧!
高斯滤波器是一种可以使图像平滑的滤波器,用于去除噪声。可用于去除噪声的滤波器还有中值滤波器(参见问题10),平滑滤波器(参见问题11)、LoG 滤波器(参见问题19)。
高斯滤波器将中心像素周围的像素按照高斯分布加权平均进行平滑化。这样的(二维)权值通常被称为卷积核或者滤波器。
但是,由于图像的长宽可能不是滤波器大小的整数倍,因此我们需要在图像的边缘补0。这种方法称作 Zero Padding。并且权值(卷积核)要进行归一化操作(sum g = 1)。
权值 g(x,y,s) = 1/ (ssqrt(2 * pi)) * exp( - (x^2 + y^2) / (2s^2))
标准差 s = 1.3 的 8 近邻 高斯滤波器如下:
1 2 1
K = 1/16 [ 2 4 2 ]
1 2 1
import cv2
import numpy as np
# 读取图片
img = cv2.imread("imori_noise.jpg")
H, W, C = img.shape#取彩色图片的长、宽、通道。
# 高斯滤波
#使用高斯滤波器(3x3 大小,标准差 s=1.3)来对imori_noise.jpg进行降噪处理!
K_size = 3#滤波器的大小
sigma = 1.3#滤波器的标准差
## 零填充
pad = K_size // 2
out = np.zeros((H + pad * 2, W + pad * 2, C), dtype=np.float)
out[pad:pad + H, pad:pad + W] = img.copy().astype(np.float)#图像复制在中间
## 卷积核
K = np.zeros((K_size, K_size), dtype=np.float)#初始化滤波器
for x in range(-pad, -pad + K_size):
for y in range(-pad, -pad + K_size):
K[y + pad, x + pad] = np.exp(-(x ** 2 + y ** 2) / (2 * (sigma ** 2)))
K /= (sigma * np.sqrt(2 * np.pi))
K /= K.sum()#权值(卷积核)进行归一化操作(sum K = 1)。
tmp = out.copy()
for y in range(H):
for x in range(W):
for c in range(C):
out[pad + y, pad + x, c] = np.sum(K * tmp[y:y + K_size, x:x + K_size, c])#进行卷积。将9个值加起来,就是中心点的高斯模糊的值
out = out[pad:pad + H, pad:pad + W].astype(np.uint8)#输出结果
# 保存结果
cv2.imwrite("out9.jpg", out)
cv2.imshow("result9", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出:
Q.10 中值滤波(Median filter)
使用中值滤波器(3x 3大小)来对imori_noise.jpg进行降噪处理吧!
中值滤波器是一种可以使图像平滑的滤波器。这种滤波器用滤波器范围内(在这里是3x3)像素点的中值进行滤波,在这里也采用 Zero Padding。
import cv2
import numpy as np
# 读取图像
img = cv2.imread("imori_noise.jpg")
H, W, C = img.shape
# 中值滤波
K_size = 3
## 零填充
pad = K_size // 2
out = np.zeros((H + pad * 2, W + pad * 2, C), dtype=np.float)#零矩阵
out[pad:pad + H, pad:pad + W] = img.copy().astype(np.float)#图像复制在中间
tmp = out.copy()
for y in range(H):
for x in range(W):
for c in range(C):
out[pad + y, pad + x, c] = np.median(tmp[y:y + K_size, x:x + K_size, c])#使用中值进行滤波
out = out[pad:pad + H, pad:pad + W].astype(np.uint8)
# 保存结果
cv2.imwrite("out10.jpg", out)
cv2.imshow("result10", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出: