opencv:question1~10
博主自大一暑假开始接触图像处理,在学习一段时间的理论基础后,于大二开学之际开始代码的编写,主要参考资料是 ImageProcessing100Wen-master,现将所学总结为博客,每篇10问
main content
本篇处理的主要问题是图像的一些最基本操作,二值化、池化、滤波等
original material
原始图片包括一张普通蛤蟆的图片 imori.jpg,
以及加了噪声的图片 imori_noise.jpg
trouble shooting
q1 通道交换
将图像RGB通道替换为BGR:
###############################基础知识###############################
opencv读取图片的默认像素排列是BGR,需要转换成RGB
img[:,:,2]代表R通道,也就是红色分量图像;
img[:,:,1]代表G通道,也就是绿色分量图像;
img[:,:,0]代表B通道,也就是蓝色分量图像;
import cv2
# 可以使用相对路径,但似乎是需要在同一盘符
img = cv2.imread('D:\study\e_book\picture_processing\ImageProcessing100Wen-master\Question_01_10\imori.jpg')
r = img[:,:,2].copy()
g = img[:,:,1].copy()
b = img[:,:,0].copy()
img[:,:,0] = r
img[:,:,1] = g
img[:,:,2] = b
cv2.imshow("通道转换完成", img)
# 确保窗口能够正常关闭
cv2.waitKey(0) # 表示图片停留的时间, 0表示按任意键退出
cv2.destroyAllWindows() # 表示清除所有的方框界面
效果:
q2 灰度化
###############################基础知识###############################
RGB图像灰度化方法:
Gray=max(B+G+R)
将彩色图像中的三分量亮度的最大值作为灰度图的灰度值
Gray=(B+G+R)/3
将彩色图像中的三分量亮度求平均得到一个灰度图;
Gray= 0.072169 * B + 0.715160 * G + 0.212671 * R
OpenCV开放库所采用的灰度权值
Gray= 0.11 * B + 0.59 * G + 0.3 * R
从人体生理学角度所提出的一种权值(人眼对绿色的敏感最高,对蓝色敏感最低)
import cv2
import numpy as np
img = cv2.imread(
'D:\study\e_book\picture_processing\ImageProcessing100Wen-master\Question_01_10\imori.jpg'
)
r = img[:, :, 2].copy()
g = img[:, :, 1].copy()
b = img[:, :, 0].copy()
img_out = 0.2126 * r + 0.7152 * g + 0.0722 * b
img_out = img_out.astype(np.uint8)
cv2.imshow("result", img_out)
cv2.waitKey(0)
cv2.destroyAllWindows()
效果:
q3 二值化
# 先将图片转换为灰度图,再进行二值化
import cv2
import numpy as np
img = cv2.imread(
'D:\study\e_book\picture_processing\ImageProcessing100Wen-master\Question_01_10\imori.jpg'
)
r = img[:, :, 2].copy()
g = img[:, :, 1].copy()
b = img[:, :, 0].copy()
# 灰度图:grey-scale map
img_gsm = 0.2126 * r + 0.7152 * g + 0.0722 * b
img_gsm = img_gsm.astype(np.uint8)
#binarization
th = 128 # 这里暂定阈值为128
img_gsm[img_gsm < th] = 0
img_gsm[img_gsm >= th] = 255
cv2.imshow("result", img_gsm)
cv2.waitKey(0)
cv2.destroyAllWindows()
效果:
q3 大津二值化算法(Otsu’s Method)
大津二值化算法:利用阈值将原图像分成前景,背景两个图象。当取最佳阈值时,背景应该与前景差别最大,在otsu算法中,衡量差别的标准是最大类间方差
算法主要步骤如下:
代码实现如下:
import cv2
import numpy as np
# 直接将数据转换成float32,方便后续计算
img = cv2.imread(r'D:\study\e_book\picture_processing\ImageProcessing100Wen-master\Question_01_10\imori.jpg').astype(np.float32)
# 灰度化
def rgb2gray(img):
b = img[:, :, 0].copy()
g = img[:, :, 1].copy()
r = img[:, :, 2].copy()
gray_img = 0.2126 * r + 0.7152 * g + 0.0722 * b
gray_img = gray_img.astype(np.uint8)
return gray_img
# 大津二值化算法
def otsu(gray_img):
h = gray_img.shape[0]
w = gray_img.shape[1]
threshold_t = 0
max_g = 0
# 遍历每一个灰度层
for t in range(255): # t即为所求阈值
n0 = gray_img[np.where(gray_img < t)] # 小于阈值的像素,前景
n1 = gray_img[np.where(gray_img >= t)] # 大于阈值的像素,背景
w0 = len(n0) / (h * w) # 前景像素数量占总像素数量的比例
w1 = len(n1) / (h * w) # 背景像素数量占总像素数量的比例
u0 = np.mean(n0) if len(n0) > 0 else 0. # 前景平均灰度
u1 = np.mean(n1) if len(n0) > 0 else 0. # 背景平均灰度
g = w0 * w1 * (u0 - u1) ** 2 # 类间方差
if g > max_g:
max_g = g
threshold_t = t # 更新阈值
print('类间方差最大阈值:', threshold_t)
gray_img[gray_img < threshold_t] = 0
gray_img[gray_img >= threshold_t] = 255
return gray_img
gray_img = rgb2gray(img)
otsu_img = otsu(gray_img)
cv2.imshow('otsu_img ', otsu_img )
cv2.waitKey(0)
cv2.destroyAllWindows()
该算法来自 大津二值化算法OTSU的理解
q5 HSV变换
HSV:使用色相(Hue)、饱和度(saturation)、明度(Value)来表示色彩
属性 | 概念 |
---|---|
色相H | 是指能够比较确切地表示某种颜色色别的名称 |
饱和度S | 色彩的鲜艳程度 |
明度V | 指色彩的亮度 |
R G B → H S V : RGB\rightarrow HSV: RGB→HSV: |
M a x = m a x ( R , G , B ) M i n = m i n ( R , G , B ) Max = max(R,G,B) \\ Min = min(R,G,B) Max=max(R,G,B)Min=min(R,G,B)
H
=
{
0
i
f
M
i
n
=
M
a
x
60
∗
(
G
−
R
)
/
(
M
a
x
−
M
i
n
)
+
60
i
f
M
i
n
=
B
60
∗
(
B
−
G
)
/
(
M
a
x
−
M
i
n
)
+
180
i
f
M
i
n
=
G
60
∗
(
G
−
R
)
/
(
M
a
x
−
M
i
n
)
+
60
i
f
M
i
n
=
B
H = \left\{ \begin{array}{lrl} 0 & if &Min=Max \\ 60*(G-R)/(Max-Min) + 60 & if &Min = B \\ 60*(B-G)/(Max-Min) + 180 &if &Min = G \\ 60*(G-R)/(Max-Min) + 60 &if &Min = B \\ \end{array} \right.
H=⎩⎪⎪⎨⎪⎪⎧060∗(G−R)/(Max−Min)+6060∗(B−G)/(Max−Min)+18060∗(G−R)/(Max−Min)+60ififififMin=MaxMin=BMin=GMin=B
S
=
M
a
x
−
M
i
n
S = Max - Min
S=Max−Min
V
=
m
a
x
V = max
V=max
H
S
V
→
R
G
B
:
HSV\rightarrow RGB:
HSV→RGB:
C
=
S
H
′
=
H
60
X
=
C
⋅
(
1
−
∣
H
′
m
o
d
2
−
1
∣
)
(
R
,
G
,
B
)
=
(
V
−
C
)
⋅
(
1
,
1
,
1
)
+
{
(
0
,
0
,
0
)
(
i
f
H
i
s
u
n
d
e
f
i
n
e
d
)
(
C
,
X
,
0
)
(
i
f
0
≤
H
′
<
1
)
(
X
,
C
,
0
)
(
i
f
1
≤
H
′
<
2
)
(
0
,
C
,
X
)
(
i
f
2
≤
H
′
<
3
)
(
0
,
X
,
C
)
(
i
f
3
≤
H
′
<
4
)
(
X
,
0
,
C
)
(
i
f
4
≤
H
′
<
5
)
(
C
,
0
,
X
)
(
i
f
5
≤
H
′
<
6
)
C = S \\ H' = \frac{H}{60}\\ X = C\cdot (1-|H' \quad mod\quad 2-1|) \\ \ \\ (R,G,B)=(V-C)\cdot (1,1,1)+\left\{ \begin{array}{ll} (0,0,0)&(if\ H \ is\ undefined) \\ (C,X,0)&(if\quad 0 \leq H' <1) \\ (X,C,0)&(if\quad 1 \leq H' <2) \\ (0,C,X)&(if\quad 2 \leq H' <3) \\ (0,X,C)&(if\quad 3 \leq H' <4) \\ (X,0,C)&(if\quad 4 \leq H' <5) \\ (C,0,X)&(if\quad 5 \leq H' <6) \\ \end{array} \right.
C=SH′=60HX=C⋅(1−∣H′mod2−1∣) (R,G,B)=(V−C)⋅(1,1,1)+⎩⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎧(0,0,0)(C,X,0)(X,C,0)(0,C,X)(0,X,C)(X,0,C)(C,0,X)(if H is undefined)(if0≤H′<1)(if1≤H′<2)(if2≤H′<3)(if3≤H′<4)(if4≤H′<5)(if5≤H′<6)
代码如下:
import cv2
import numpy as np
img = cv2.imread('D:/study/e_book/picture_processing/ImageProcessing100Wen-master/Question_01_10/imori.jpg')
img = img.copy()/ 255.
hsv = np.zeros_like(img, dtype=np.float32)
max_v = np.max(img, axis=2).copy() #求每一个像素RGB中的最大值
min_v = np.min(img, axis=2).copy() #求每一个像素RGB中的最小值
min_arg = np.argmin(img, axis=2) #给出最小值对应的索引
##############################BGR->HSV###################################
## min == max
hsv[..., 0][np.where(max_v == min_v)] = 0
## if min == B
ind = np.where(min_arg == 0)
hsv[..., 0][ind] = 60 * (img[..., 1][ind] - img[..., 2][ind]) / (max_v[ind] - min_v[ind] + 60)
## if min == R
ind = np.where(min_arg == 2)
hsv[..., 0][ind] = 60 * (img[..., 0][ind] - img[..., 1][ind]) / (max_v[ind] - min_v[ind]) + 180
## if min == G
ind = np.where(min_arg == 1)
hsv[..., 0][ind] = 60 * (img[..., 2][ind] - img[..., 0][ind]) / (max_v[ind] - min_v[ind]) + 300
hsv[..., 1] = max_v.copy() - min_v.copy()
hsv[..., 2] = max_v.copy()
cv2.imshow('using_hsv',hsv)
cv2.waitKey(0)
cv2.destroyAllWindows()
###############################色相反转###################################
hsv_transform = hsv
hsv_transform[..., 0] = (hsv[..., 0] + 180) % 360
cv2.imshow('hsv_transform',hsv_transform)
cv2.waitKey(0)
cv2.destroyAllWindows()
out = np.zeros_like(img)
################################用RGB显示###############################
H = hsv[..., 0]
S = hsv[..., 1]
V = hsv[..., 2]
C = S
H_ = H / 60.
X = C * (1 - np.abs( H_ % 2 - 1))
Z = np.zeros_like(H)
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 = np.clip(out, 0, 1)
out = (out * 255).astype(np.uint8)
cv2.imshow('result', out)
cv2.waitKey(0)
cv2.destroyAllWindows()
效果:
q6 减色处理
将图像的RGB值由 25 6 3 256^3 2563 压缩至 4 3 4^3 43
import cv2
img = cv2.imread('D:\study\e_book\picture_processing\ImageProcessing100Wen-master\Question_01_10\imori.jpg')
result = 32 + img // 64 * 64
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
效果:
q7 平均池化
###############################基础知识##################################
img.shape[0]:图像的垂直尺寸,即高度
img.shape[1]:图像的水平尺寸,即宽度
img.shape[2]:图像通道数
import cv2
import numpy as np
kernel_shape = 8
img = cv2.imread('D:\study\e_book\picture_processing\ImageProcessing100Wen-master\Question_01_10\imori.jpg')
H, W, C = img.shape # 通道:channel
out = img.copy()
H_num = int(H/kernel_shape)
W_num = int(W/kernel_shape)
for y in range(W_num):
for x in range(H_num):
for c in range(C):
out[kernel_shape*y:kernel_shape*(y+1), kernel_shape*x:kernel_shape*(x+1), c] = \
np.mean(out[kernel_shape*y:kernel_shape*(y+1), kernel_shape*x:kernel_shape*(x+1), c]).astype(np.int)
cv2.imshow("result", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
效果:
q8 最大池化
import cv2
import numpy as np
kernel_shape = 8
img = cv2.imread('D:\study\e_book\picture_processing\ImageProcessing100Wen-master\Question_01_10\imori.jpg')
H, W, C = img.shape # 通道:channel
out = img.copy()
H_num = int(H/kernel_shape)
W_num = int(W/kernel_shape)
for y in range(W_num):
for x in range(H_num):
for c in range(C):
out[kernel_shape*y:kernel_shape*(y+1), kernel_shape*x:kernel_shape*(x+1), c] = \
np.max(out[kernel_shape*y:kernel_shape*(y+1), kernel_shape*x:kernel_shape*(x+1), c]).astype(np.int)
cv2.imshow("result", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
q9 高斯滤波
高斯滤波可以使图像平滑,用于去除噪声,算法具体实现步骤如下图所示:
代码如下:
import cv2
import numpy as np
img = cv2.imread(
'D:/study/e_book/picture_processing/ImageProcessing100Wen-master/Question_01_10/imori_noise.jpg'
)
if len(img.shape) == 3:
H, W, C = img.shape
else:
img = np.expand_dims(img, axis=-1) # 扩展最后一个参数
H, W, C = img.shape
############################ 边缘填充0 ######################################
k_size = 3 # 卷积核尺寸为3*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)
## 准备卷积核
sigma = 1.3
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 /= (2 * np.pi * sigma * sigma)
K /= K.sum()
########################## 对图像进行卷积操作 ###########################
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])
out = np.clip(out, 0, 255) ## numpy.clip(a, a_min, a_max)函数将a元素限制在a_min与a_max之间,>max : 让其等于max, <min : 让其等于min
out = out[pad: pad + H, pad: pad + W].astype(np.uint8)
cv2.imshow("result", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
效果如下:
q10 中值滤波
与高斯滤波相比,改变了卷积核
import cv2
import numpy as np
img = cv2.imread(
'D:/study/e_book/picture_processing/ImageProcessing100Wen-master/Question_01_10/imori_noise.jpg'
)
if len(img.shape) == 3:
H, W, C = img.shape
else:
img = np.expand_dims(img, axis=-1) # 扩展最后一个参数
H, W, C = img.shape
## 边缘填0
k_size = 3 # 卷积核尺寸为3*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)
## 准备卷积核
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.sum(K * tmp[y: y + k_size, x: x + k_size, c])
out = np.clip(out, 0, 255) ## numpy.clip(a, a_min, a_max)函数将a元素限制在a_min与a_max之间,>max : 让其等于max, <min : 让其等于min
out = out[pad: pad + H, pad: pad + W].astype(np.uint8)
cv2.imshow("result", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
效果如下:
summary
博主花了一周的时间,将 ImageProcessing100Wen-master 前10个小问的代码基本理解清楚,之前看书所学的一些基础理论,在代码实现时也起到了不可替代的作用,希望接下来能坚持下去