计算机视觉1 - 数元素数目
0. 任务目标
- 统计每幅图中各化学元素的数目;
- 统计AI和Fe元素、Fe和P元素、AI和P元素重叠的数目,并且以图像的形式展示出两两元素的重叠情况;
- 统计AI、Fe和P元素三者重叠的数目,并以图像的形式展示出三种元素重叠情况。
- 无论原始图像中的化学元素是何种颜色,均可实现本目标。
1. 实现任务
计算机视觉常用的方法是使用OpenCV的库函数对图像进行处理。因此我们先利用 cv2.imread()
函数读入图像数据。本例子中,共拥有3张图片,分别是Al,Fe和P(即铝、铁、磷)三种化学元素。下面是三张照片的样子。
读取的代码如下:
1
2
3
4
5
6
7
8
9
# Set paths
al_path = os.path.join('image', 'Al.jpg')
fe_path = os.path.join('image', 'Fe.jpg')
p_path = os.path.join('image', 'P.jpg')
# Read images
al = cv2.imread(al_path)
fe = cv2.imread(fe_path)
p = cv2.imread(p_path)
注意,这里的路径最好不要直接写死,而是用python os库中提供的 os.path.join()
函数进行拼接处理。该函数能够自行判断当前运行环境而选择路径的分隔符,无论是Linux还是Windows都不会出错。
读入图片后,我们发现图片中的左上角logo处和左下角的 50nm 标识都不是我们需要的东西,需要剔除。因此我们需要对输入图像进行预处理:
为了去除左上角的logo,根据观察可以看到,logo读取进来的图像矩阵所在的行数均在74行及以内,因此我们处理的时候可以针对logo所在的行数进行删除。
为了去除左下角的 50nm 标识,我们需要识别出白色区域并去除。
为了实现上面的目标,我们先将图片从RGB色彩通道转换为HSV(HSB)色彩通道。OpenCV的库函数也提供了相应的转换。我们平常所见的图片绝大多数都是RGB通道,即利用Red(红)、Green(绿)、Blue(蓝)三种颜色及其浓度来表示一张彩色图片。在8Bit色深的RGB图像中,RGB三个通道的取值区间均为[0, 255]。而HSV色彩通道是利用Hue(色调)、Saturation(饱和度)、Value(明度)三值来表示颜色。不同于RGB图像,HSV图像是根据色调H来确定颜色的,而RGB图像一般需要通过三个值共同参与来表达一种颜色。
处理代码如下:
1
2
3
4
5
6
# RGB TO HSV
def rgb2hsv(img):
hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# split H, S, V
h, s, v = cv2.split(hsv_img)
return [h, s, v]
显示HSV图像三通道的代码如下:
1
2
3
4
5
6
def show_hsv_image(imgs: list):
cv2.imshow('H', imgs[0])
cv2.imshow('S', imgs[1])
cv2.imshow('V', imgs[2])
cv2.waitKey(0)
cv2.destroyAllWindows()
未经处理前,Al的分拆HSV通道后的图像如下:
在HSV图像中,查询可知黑色点范围如下:
- H: [0, 180]
- S: [0, 255]
- V: [0, 46]
白色点范围如下:
- H: [0, 180]
- S: [0, 30]
- V: [221, 255]
logo区域的像素点较为明亮,因此可以直接利用位置和V的值进行判断去除。
图像预处理代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def remove_light(split_img: list):
h, s, v = split_img
for i in range(h.shape[0]):
for j in range(h.shape[1]):
# Remove black points
if 0 <= h[i, j] <= 180 and 0 <= v[i, j] <= 46:
h[i, j] = 0
s[i, j] = 0
v[i, j] = 0
# Remove white points
elif 0 <= h[i, j] <= 180 and 221 <= v[i, j] <= 255 and 0 <= s[i, j] <= 30:
h[i, j] = 0
s[i, j] = 0
v[i, j] = 0
# Remove logo area
if i <= 70 and 180 <= v[i, j] <= 255:
h[i, j] = 0
s[i, j] = 0
v[i, j] = 0
return [h, s, v]
统计元素量时,为了方便统计,我们把有颜色的像素点都统计为1个元素。而我们已在上一步去除了杂色点,因此这一步可以直接数图像V通道的点数进行统计。统计函数如下:
1
2
3
4
5
6
7
8
9
10
# Counting
def count_points(img: list):
h, s, v = img
counter = 0
for i in range(v.shape[0]):
for j in range(v.shape[1]):
if v[i, j] != 0:
counter += 1
return counter
统计重叠量时,依旧是利用上一步的思想进行统计,但是这时候我们需要两张图片的交集,即需要多加一个“与”的判断步骤。为了显示图片,我们还需要返回合并后的HSV矩阵。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def count2overlap(img1: list, img2: list):
h1, s1, v1 = img1
h2, s2, v2 = img2
h0 = np.zeros(h1.shape, dtype=np.uint8)
s0 = np.zeros(s1.shape, dtype=np.uint8)
v0 = np.zeros(v1.shape, dtype=np.uint8)
counter = 0
for i in range(v1.shape[0]):
for j in range(v1.shape[1]):
if v1[i, j] != 0 and v2[i, j] != 0:
h0[i, j] = h1[i, j] + h2[i, j]
s0[i, j] = s1[i, j] + s2[i, j]
v0[i, j] = v1[i, j] + v2[i, j]
counter += 1
return counter, [h0, s0, v0]
三张图片的元素重叠量也是同理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def count3overlap(img1: list, img2: list, img3: list):
h1, s1, v1 = img1
h2, s2, v2 = img2
h3, s3, v3 = img3
h0 = np.zeros(h1.shape, dtype=np.uint8)
s0 = np.zeros(s1.shape, dtype=np.uint8)
v0 = np.zeros(v1.shape, dtype=np.uint8)
counter = 0
for i in range(v1.shape[0]):
for j in range(v1.shape[1]):
if v1[i, j] != 0 and v2[i, j] != 0 and v3[i, j] != 0:
h0[i, j] = h1[i, j] + h2[i, j] + h3[i, j]
s0[i, j] = s1[i, j] + s2[i, j] + s3[i, j]
v0[i, j] = v1[i, j] + v2[i, j] + v3[i, j]
counter += 1
return counter, [h0, s0, v0]
至此,我们本例子的目标基本完成。由于我们将重要的功能封装成函数且与具体颜色无关,因此无论原始图像中的化学元素是何种颜色,只需修改图像路径,均可实现本目标。
完整代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import os.path
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Set paths
al_path = os.path.join('image', 'Al.jpg')
fe_path = os.path.join('image', 'Fe.jpg')
p_path = os.path.join('image', 'P.jpg')
# Read images
al = cv2.imread(al_path)
fe = cv2.imread(fe_path)
p = cv2.imread(p_path)
# RGB TO HSV
def rgb2hsv(img):
hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# split H, S, V
h, s, v = cv2.split(hsv_img)
return [h, s, v]
# Plot image
def show_image3(imgs: list):
plt.figure(figsize=(15, 15))
for i, img in enumerate(imgs):
plt.subplot(1, 3, i + 1)
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.axis('off')
plt.show()
# Plot HSV image
def show_hsv_image(imgs: list):
cv2.imshow('H', imgs[0])
cv2.imshow('S', imgs[1])
cv2.imshow('V', imgs[2])
cv2.waitKey(0)
cv2.destroyAllWindows()
# Remove black and white points and logo
def remove_light(split_img: list):
h, s, v = split_img
for i in range(h.shape[0]):
for j in range(h.shape[1]):
# Remove black points
if 0 <= h[i, j] <= 180 and 0 <= v[i, j] <= 46:
h[i, j] = 0
s[i, j] = 0
v[i, j] = 0
# Remove white points
elif 0 <= h[i, j] <= 180 and 221 <= v[i, j] <= 255 and 0 <= s[i, j] <= 30:
h[i, j] = 0
s[i, j] = 0
v[i, j] = 0
# Remove logo area
if i <= 70 and 180 <= v[i, j] <= 255:
h[i, j] = 0
s[i, j] = 0
v[i, j] = 0
return [h, s, v]
# Counting
def count_points(img: list):
h, s, v = img
counter = 0
for i in range(v.shape[0]):
for j in range(v.shape[1]):
if v[i, j] != 0:
counter += 1
return counter
images = [al, fe, p]
# show_image3(images)
hsv_al = rgb2hsv(al)
hsv_fe = rgb2hsv(fe)
hsv_p = rgb2hsv(p)
show_hsv_image(hsv_al)
# pre-process
processed_al = remove_light(hsv_al)
processed_fe = remove_light(hsv_fe)
processed_p = remove_light(hsv_p)
# counting
counter_al = count_points(processed_al)
counter_fe = count_points(processed_fe)
counter_p = count_points(processed_p)
print("Al元素的点数:", counter_al)
print("Fe元素的点数:", counter_fe)
print("P元素的点数:", counter_p)
# 求解重叠量,并将两者的重叠量画出来
def count2overlap(img1: list, img2: list):
h1, s1, v1 = img1
h2, s2, v2 = img2
h0 = np.zeros(h1.shape, dtype=np.uint8)
s0 = np.zeros(s1.shape, dtype=np.uint8)
v0 = np.zeros(v1.shape, dtype=np.uint8)
counter = 0
for i in range(v1.shape[0]):
for j in range(v1.shape[1]):
if v1[i, j] != 0 and v2[i, j] != 0:
h0[i, j] = h1[i, j] + h2[i, j]
s0[i, j] = s1[i, j] + s2[i, j]
v0[i, j] = v1[i, j] + v2[i, j]
counter += 1
return counter, [h0, s0, v0]
# 求解三者的重叠量
def count3overlap(img1: list, img2: list, img3: list):
h1, s1, v1 = img1
h2, s2, v2 = img2
h3, s3, v3 = img3
h0 = np.zeros(h1.shape, dtype=np.uint8)
s0 = np.zeros(s1.shape, dtype=np.uint8)
v0 = np.zeros(v1.shape, dtype=np.uint8)
counter = 0
for i in range(v1.shape[0]):
for j in range(v1.shape[1]):
if v1[i, j] != 0 and v2[i, j] != 0 and v3[i, j] != 0:
h0[i, j] = h1[i, j] + h2[i, j] + h3[i, j]
s0[i, j] = s1[i, j] + s2[i, j] + s3[i, j]
v0[i, j] = v1[i, j] + v2[i, j] + v3[i, j]
counter += 1
return counter, [h0, s0, v0]
# Get 2 overlap
overlap_al_fe, overlap_img_af = count2overlap(processed_al, processed_fe)
overlap_al_p, overlap_img_ap = count2overlap(processed_al, processed_p)
overlap_fe_p, overlap_img_fp = count2overlap(processed_fe, processed_p)
overlap_img_af = cv2.cvtColor(cv2.merge(overlap_img_af), cv2.COLOR_HSV2BGR)
overlap_img_ap = cv2.cvtColor(cv2.merge(overlap_img_ap), cv2.COLOR_HSV2BGR)
overlap_img_fp = cv2.cvtColor(cv2.merge(overlap_img_fp), cv2.COLOR_HSV2BGR)
print("Al和Fe的重叠量:", overlap_al_fe)
print("Al和P的重叠量:", overlap_al_p)
print("Fe和P的重叠量:", overlap_fe_p)
# show_image3([overlap_img_af, overlap_img_ap, overlap_img_fp])
# Get 3 overlap
overlap_all, overlap_img_all = count3overlap(processed_al, processed_fe, processed_p)
overlap_img_all = cv2.cvtColor(cv2.merge(overlap_img_all), cv2.COLOR_HSV2BGR)
print("Al, Fe和P的重叠量:", overlap_all)
# cv2.imshow('Overlay with Al, Fe and P elements', overlap_img_all)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
# Save images
if not os.path.exists('image_out'):
os.makedirs('image_out')
cv2.imwrite(os.path.join('image_out', 'overlap_img_af.jpg'), overlap_img_af)
cv2.imwrite(os.path.join('image_out', 'overlap_img_ap.jpg'), overlap_img_ap)
cv2.imwrite(os.path.join('image_out', 'overlap_img_fp.jpg'), overlap_img_fp)
cv2.imwrite(os.path.join('image_out', 'overlap_img_all.jpg'), overlap_img_all)
最终的得到的重叠图片如下: