|
| 1 | +--- |
| 2 | +title: 计算机视觉 - 1.数元素数目 |
| 3 | +date: 2024-03-18 09:44:08 +0800 |
| 4 | +categories: [Python, CV] |
| 5 | +tags: [Python, CV] # TAG names should always be lowercase |
| 6 | +--- |
| 7 | + |
| 8 | +# 计算机视觉1 - 数元素数目 |
| 9 | + |
| 10 | +## 0. 任务目标 |
| 11 | + |
| 12 | +1. 统计每幅图中各化学元素的数目; |
| 13 | +2. 统计AI和Fe元素、Fe和P元素、AI和P元素重叠的数目,并且以图像的形式展示出两两元素的重叠情况; |
| 14 | +3. 统计AI、Fe和P元素三者重叠的数目,并以图像的形式展示出三种元素重叠情况。 |
| 15 | +4. 无论原始图像中的化学元素是何种颜色,均可实现本目标。 |
| 16 | + |
| 17 | +## 1. 实现任务 |
| 18 | + |
| 19 | +计算机视觉常用的方法是使用OpenCV的库函数对图像进行处理。因此我们先利用 `cv2.imread()` 函数读入图像数据。本例子中,共拥有3张图片,分别是Al,Fe和P(即铝、铁、磷)三种化学元素。下面是三张照片的样子。 |
| 20 | + |
| 21 | +<img src="D:\Coder\Github Repos\StandardL.github.io\assets\img\posts\2024-03-18-计算机视觉1\Al.jpg" alt="Al" style="zoom:50%;" /> |
| 22 | + |
| 23 | +<img src="D:\Coder\Github Repos\StandardL.github.io\assets\img\posts\2024-03-18-计算机视觉1\Fe.jpg" alt="Fe" style="zoom:50%;" /> |
| 24 | + |
| 25 | +<img src="D:\Coder\Github Repos\StandardL.github.io\assets\img\posts\2024-03-18-计算机视觉1\P.jpg" alt="P" style="zoom:50%;" /> |
| 26 | + |
| 27 | +读取的代码如下: |
| 28 | + |
| 29 | +```python |
| 30 | +# Set paths |
| 31 | +al_path = os.path.join('image', 'Al.jpg') |
| 32 | +fe_path = os.path.join('image', 'Fe.jpg') |
| 33 | +p_path = os.path.join('image', 'P.jpg') |
| 34 | + |
| 35 | +# Read images |
| 36 | +al = cv2.imread(al_path) |
| 37 | +fe = cv2.imread(fe_path) |
| 38 | +p = cv2.imread(p_path) |
| 39 | +``` |
| 40 | + |
| 41 | +注意,这里的路径最好不要直接写死,而是用python os库中提供的 `os.path.join()` 函数进行拼接处理。该函数能够自行判断当前运行环境而选择路径的分隔符,无论是Linux还是Windows都不会出错。 |
| 42 | + |
| 43 | +--- |
| 44 | + |
| 45 | +读入图片后,我们发现图片中的左上角logo处和左下角的 50nm 标识都不是我们需要的东西,需要剔除。因此我们需要对输入图像进行预处理: |
| 46 | + |
| 47 | +- 为了去除左上角的logo,根据观察可以看到,logo读取进来的图像矩阵所在的行数均在74行及以内,因此我们处理的时候可以针对logo所在的行数进行删除。 |
| 48 | + |
| 49 | +- 为了去除左下角的 50nm 标识,我们需要识别出白色区域并去除。 |
| 50 | + |
| 51 | +为了实现上面的目标,我们先将图片从RGB色彩通道转换为HSV(HSB)色彩通道。OpenCV的库函数也提供了相应的转换。我们平常所见的图片绝大多数都是RGB通道,即利用Red(红)、Green(绿)、Blue(蓝)三种颜色及其浓度来表示一张彩色图片。在8Bit色深的RGB图像中,RGB三个通道的取值区间均为[0, 255]。而HSV色彩通道是利用Hue(色调)、Saturation(饱和度)、Value(明度)三值来表示颜色。不同于RGB图像,HSV图像是根据色调H来确定颜色的,而RGB图像一般需要通过三个值共同参与来表达一种颜色。 |
| 52 | + |
| 53 | +处理代码如下: |
| 54 | + |
| 55 | +```python |
| 56 | +# RGB TO HSV |
| 57 | +def rgb2hsv(img): |
| 58 | + hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) |
| 59 | + # split H, S, V |
| 60 | + h, s, v = cv2.split(hsv_img) |
| 61 | + return [h, s, v] |
| 62 | +``` |
| 63 | + |
| 64 | +显示HSV图像三通道的代码如下: |
| 65 | + |
| 66 | +```python |
| 67 | +def show_hsv_image(imgs: list): |
| 68 | + cv2.imshow('H', imgs[0]) |
| 69 | + cv2.imshow('S', imgs[1]) |
| 70 | + cv2.imshow('V', imgs[2]) |
| 71 | + cv2.waitKey(0) |
| 72 | + cv2.destroyAllWindows() |
| 73 | +``` |
| 74 | + |
| 75 | +未经处理前,Al的分拆HSV通道后的图像如下: |
| 76 | + |
| 77 | + |
| 78 | + |
| 79 | +在HSV图像中,查询可知黑色点范围如下: |
| 80 | + |
| 81 | +- H: [0, 180] |
| 82 | +- S: [0, 255] |
| 83 | +- V: [0, 46] |
| 84 | + |
| 85 | +白色点范围如下: |
| 86 | + |
| 87 | +- H: [0, 180] |
| 88 | +- S: [0, 30] |
| 89 | +- V: [221, 255] |
| 90 | + |
| 91 | +logo区域的像素点较为明亮,因此可以直接利用位置和V的值进行判断去除。 |
| 92 | + |
| 93 | +图像预处理代码如下: |
| 94 | + |
| 95 | +```python |
| 96 | +def remove_light(split_img: list): |
| 97 | + h, s, v = split_img |
| 98 | + for i in range(h.shape[0]): |
| 99 | + for j in range(h.shape[1]): |
| 100 | + # Remove black points |
| 101 | + if 0 <= h[i, j] <= 180 and 0 <= v[i, j] <= 46: |
| 102 | + h[i, j] = 0 |
| 103 | + s[i, j] = 0 |
| 104 | + v[i, j] = 0 |
| 105 | + # Remove white points |
| 106 | + elif 0 <= h[i, j] <= 180 and 221 <= v[i, j] <= 255 and 0 <= s[i, j] <= 30: |
| 107 | + h[i, j] = 0 |
| 108 | + s[i, j] = 0 |
| 109 | + v[i, j] = 0 |
| 110 | + # Remove logo area |
| 111 | + if i <= 70 and 180 <= v[i, j] <= 255: |
| 112 | + h[i, j] = 0 |
| 113 | + s[i, j] = 0 |
| 114 | + v[i, j] = 0 |
| 115 | + |
| 116 | + return [h, s, v] |
| 117 | +``` |
| 118 | + |
| 119 | +--- |
| 120 | + |
| 121 | +统计元素量时,为了方便统计,我们把有颜色的像素点都统计为1个元素。而我们已在上一步去除了杂色点,因此这一步可以直接数图像V通道的点数进行统计。统计函数如下: |
| 122 | + |
| 123 | +```python |
| 124 | +# Counting |
| 125 | +def count_points(img: list): |
| 126 | + h, s, v = img |
| 127 | + counter = 0 |
| 128 | + for i in range(v.shape[0]): |
| 129 | + for j in range(v.shape[1]): |
| 130 | + if v[i, j] != 0: |
| 131 | + counter += 1 |
| 132 | + |
| 133 | + return counter |
| 134 | +``` |
| 135 | + |
| 136 | +--- |
| 137 | + |
| 138 | +统计重叠量时,依旧是利用上一步的思想进行统计,但是这时候我们需要两张图片的交集,即需要多加一个“与”的判断步骤。为了显示图片,我们还需要返回合并后的HSV矩阵。 |
| 139 | + |
| 140 | +```python |
| 141 | +def count2overlap(img1: list, img2: list): |
| 142 | + h1, s1, v1 = img1 |
| 143 | + h2, s2, v2 = img2 |
| 144 | + h0 = np.zeros(h1.shape, dtype=np.uint8) |
| 145 | + s0 = np.zeros(s1.shape, dtype=np.uint8) |
| 146 | + v0 = np.zeros(v1.shape, dtype=np.uint8) |
| 147 | + counter = 0 |
| 148 | + for i in range(v1.shape[0]): |
| 149 | + for j in range(v1.shape[1]): |
| 150 | + if v1[i, j] != 0 and v2[i, j] != 0: |
| 151 | + h0[i, j] = h1[i, j] + h2[i, j] |
| 152 | + s0[i, j] = s1[i, j] + s2[i, j] |
| 153 | + v0[i, j] = v1[i, j] + v2[i, j] |
| 154 | + counter += 1 |
| 155 | + |
| 156 | + return counter, [h0, s0, v0] |
| 157 | +``` |
| 158 | + |
| 159 | +三张图片的元素重叠量也是同理。 |
| 160 | + |
| 161 | +```python |
| 162 | +def count3overlap(img1: list, img2: list, img3: list): |
| 163 | + h1, s1, v1 = img1 |
| 164 | + h2, s2, v2 = img2 |
| 165 | + h3, s3, v3 = img3 |
| 166 | + h0 = np.zeros(h1.shape, dtype=np.uint8) |
| 167 | + s0 = np.zeros(s1.shape, dtype=np.uint8) |
| 168 | + v0 = np.zeros(v1.shape, dtype=np.uint8) |
| 169 | + counter = 0 |
| 170 | + for i in range(v1.shape[0]): |
| 171 | + for j in range(v1.shape[1]): |
| 172 | + if v1[i, j] != 0 and v2[i, j] != 0 and v3[i, j] != 0: |
| 173 | + h0[i, j] = h1[i, j] + h2[i, j] + h3[i, j] |
| 174 | + s0[i, j] = s1[i, j] + s2[i, j] + s3[i, j] |
| 175 | + v0[i, j] = v1[i, j] + v2[i, j] + v3[i, j] |
| 176 | + counter += 1 |
| 177 | + |
| 178 | + return counter, [h0, s0, v0] |
| 179 | +``` |
| 180 | + |
| 181 | +至此,我们本例子的目标基本完成。由于我们将重要的功能封装成函数且与具体颜色无关,因此无论原始图像中的化学元素是何种颜色,只需修改图像路径,均可实现本目标。 |
| 182 | + |
| 183 | +--- |
| 184 | + |
| 185 | +完整代码如下: |
| 186 | + |
| 187 | +```python |
| 188 | +import os.path |
| 189 | +import cv2 |
| 190 | +import numpy as np |
| 191 | +import matplotlib.pyplot as plt |
| 192 | + |
| 193 | +# Set paths |
| 194 | +al_path = os.path.join('image', 'Al.jpg') |
| 195 | +fe_path = os.path.join('image', 'Fe.jpg') |
| 196 | +p_path = os.path.join('image', 'P.jpg') |
| 197 | + |
| 198 | +# Read images |
| 199 | +al = cv2.imread(al_path) |
| 200 | +fe = cv2.imread(fe_path) |
| 201 | +p = cv2.imread(p_path) |
| 202 | + |
| 203 | + |
| 204 | +# RGB TO HSV |
| 205 | +def rgb2hsv(img): |
| 206 | + hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) |
| 207 | + # split H, S, V |
| 208 | + h, s, v = cv2.split(hsv_img) |
| 209 | + return [h, s, v] |
| 210 | + |
| 211 | + |
| 212 | +# Plot image |
| 213 | +def show_image3(imgs: list): |
| 214 | + plt.figure(figsize=(15, 15)) |
| 215 | + for i, img in enumerate(imgs): |
| 216 | + plt.subplot(1, 3, i + 1) |
| 217 | + plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) |
| 218 | + plt.axis('off') |
| 219 | + plt.show() |
| 220 | + |
| 221 | + |
| 222 | +# Plot HSV image |
| 223 | +def show_hsv_image(imgs: list): |
| 224 | + cv2.imshow('H', imgs[0]) |
| 225 | + cv2.imshow('S', imgs[1]) |
| 226 | + cv2.imshow('V', imgs[2]) |
| 227 | + cv2.waitKey(0) |
| 228 | + cv2.destroyAllWindows() |
| 229 | + |
| 230 | + |
| 231 | +# Remove black and white points and logo |
| 232 | +def remove_light(split_img: list): |
| 233 | + h, s, v = split_img |
| 234 | + for i in range(h.shape[0]): |
| 235 | + for j in range(h.shape[1]): |
| 236 | + # Remove black points |
| 237 | + if 0 <= h[i, j] <= 180 and 0 <= v[i, j] <= 46: |
| 238 | + h[i, j] = 0 |
| 239 | + s[i, j] = 0 |
| 240 | + v[i, j] = 0 |
| 241 | + # Remove white points |
| 242 | + elif 0 <= h[i, j] <= 180 and 221 <= v[i, j] <= 255 and 0 <= s[i, j] <= 30: |
| 243 | + h[i, j] = 0 |
| 244 | + s[i, j] = 0 |
| 245 | + v[i, j] = 0 |
| 246 | + # Remove logo area |
| 247 | + if i <= 70 and 180 <= v[i, j] <= 255: |
| 248 | + h[i, j] = 0 |
| 249 | + s[i, j] = 0 |
| 250 | + v[i, j] = 0 |
| 251 | + |
| 252 | + return [h, s, v] |
| 253 | + |
| 254 | + |
| 255 | +# Counting |
| 256 | +def count_points(img: list): |
| 257 | + h, s, v = img |
| 258 | + counter = 0 |
| 259 | + for i in range(v.shape[0]): |
| 260 | + for j in range(v.shape[1]): |
| 261 | + if v[i, j] != 0: |
| 262 | + counter += 1 |
| 263 | + |
| 264 | + return counter |
| 265 | + |
| 266 | + |
| 267 | +images = [al, fe, p] |
| 268 | +# show_image3(images) |
| 269 | + |
| 270 | +hsv_al = rgb2hsv(al) |
| 271 | +hsv_fe = rgb2hsv(fe) |
| 272 | +hsv_p = rgb2hsv(p) |
| 273 | +show_hsv_image(hsv_al) |
| 274 | + |
| 275 | +# pre-process |
| 276 | +processed_al = remove_light(hsv_al) |
| 277 | +processed_fe = remove_light(hsv_fe) |
| 278 | +processed_p = remove_light(hsv_p) |
| 279 | + |
| 280 | +# counting |
| 281 | +counter_al = count_points(processed_al) |
| 282 | +counter_fe = count_points(processed_fe) |
| 283 | +counter_p = count_points(processed_p) |
| 284 | + |
| 285 | +print("Al元素的点数:", counter_al) |
| 286 | +print("Fe元素的点数:", counter_fe) |
| 287 | +print("P元素的点数:", counter_p) |
| 288 | + |
| 289 | + |
| 290 | +# 求解重叠量,并将两者的重叠量画出来 |
| 291 | +def count2overlap(img1: list, img2: list): |
| 292 | + h1, s1, v1 = img1 |
| 293 | + h2, s2, v2 = img2 |
| 294 | + h0 = np.zeros(h1.shape, dtype=np.uint8) |
| 295 | + s0 = np.zeros(s1.shape, dtype=np.uint8) |
| 296 | + v0 = np.zeros(v1.shape, dtype=np.uint8) |
| 297 | + counter = 0 |
| 298 | + for i in range(v1.shape[0]): |
| 299 | + for j in range(v1.shape[1]): |
| 300 | + if v1[i, j] != 0 and v2[i, j] != 0: |
| 301 | + h0[i, j] = h1[i, j] + h2[i, j] |
| 302 | + s0[i, j] = s1[i, j] + s2[i, j] |
| 303 | + v0[i, j] = v1[i, j] + v2[i, j] |
| 304 | + counter += 1 |
| 305 | + |
| 306 | + return counter, [h0, s0, v0] |
| 307 | + |
| 308 | + |
| 309 | +# 求解三者的重叠量 |
| 310 | +def count3overlap(img1: list, img2: list, img3: list): |
| 311 | + h1, s1, v1 = img1 |
| 312 | + h2, s2, v2 = img2 |
| 313 | + h3, s3, v3 = img3 |
| 314 | + h0 = np.zeros(h1.shape, dtype=np.uint8) |
| 315 | + s0 = np.zeros(s1.shape, dtype=np.uint8) |
| 316 | + v0 = np.zeros(v1.shape, dtype=np.uint8) |
| 317 | + counter = 0 |
| 318 | + for i in range(v1.shape[0]): |
| 319 | + for j in range(v1.shape[1]): |
| 320 | + if v1[i, j] != 0 and v2[i, j] != 0 and v3[i, j] != 0: |
| 321 | + h0[i, j] = h1[i, j] + h2[i, j] + h3[i, j] |
| 322 | + s0[i, j] = s1[i, j] + s2[i, j] + s3[i, j] |
| 323 | + v0[i, j] = v1[i, j] + v2[i, j] + v3[i, j] |
| 324 | + counter += 1 |
| 325 | + |
| 326 | + return counter, [h0, s0, v0] |
| 327 | + |
| 328 | + |
| 329 | +# Get 2 overlap |
| 330 | +overlap_al_fe, overlap_img_af = count2overlap(processed_al, processed_fe) |
| 331 | +overlap_al_p, overlap_img_ap = count2overlap(processed_al, processed_p) |
| 332 | +overlap_fe_p, overlap_img_fp = count2overlap(processed_fe, processed_p) |
| 333 | +overlap_img_af = cv2.cvtColor(cv2.merge(overlap_img_af), cv2.COLOR_HSV2BGR) |
| 334 | +overlap_img_ap = cv2.cvtColor(cv2.merge(overlap_img_ap), cv2.COLOR_HSV2BGR) |
| 335 | +overlap_img_fp = cv2.cvtColor(cv2.merge(overlap_img_fp), cv2.COLOR_HSV2BGR) |
| 336 | +print("Al和Fe的重叠量:", overlap_al_fe) |
| 337 | +print("Al和P的重叠量:", overlap_al_p) |
| 338 | +print("Fe和P的重叠量:", overlap_fe_p) |
| 339 | +# show_image3([overlap_img_af, overlap_img_ap, overlap_img_fp]) |
| 340 | + |
| 341 | +# Get 3 overlap |
| 342 | +overlap_all, overlap_img_all = count3overlap(processed_al, processed_fe, processed_p) |
| 343 | +overlap_img_all = cv2.cvtColor(cv2.merge(overlap_img_all), cv2.COLOR_HSV2BGR) |
| 344 | +print("Al, Fe和P的重叠量:", overlap_all) |
| 345 | +# cv2.imshow('Overlay with Al, Fe and P elements', overlap_img_all) |
| 346 | +# cv2.waitKey(0) |
| 347 | +# cv2.destroyAllWindows() |
| 348 | + |
| 349 | +# Save images |
| 350 | +if not os.path.exists('image_out'): |
| 351 | + os.makedirs('image_out') |
| 352 | +cv2.imwrite(os.path.join('image_out', 'overlap_img_af.jpg'), overlap_img_af) |
| 353 | +cv2.imwrite(os.path.join('image_out', 'overlap_img_ap.jpg'), overlap_img_ap) |
| 354 | +cv2.imwrite(os.path.join('image_out', 'overlap_img_fp.jpg'), overlap_img_fp) |
| 355 | +cv2.imwrite(os.path.join('image_out', 'overlap_img_all.jpg'), overlap_img_all) |
| 356 | + |
| 357 | +``` |
| 358 | + |
| 359 | +最终的得到的重叠图片如下: |
| 360 | + |
| 361 | +- Al和Fe: |
| 362 | + |
| 363 | + <img src="D:\Coder\Github Repos\StandardL.github.io\assets\img\posts\2024-03-18-计算机视觉1\overlap_img_af.jpg" alt="overlap_img_af" style="zoom:50%;" /> |
| 364 | + |
| 365 | +- Al和P: |
| 366 | + |
| 367 | + <img src="D:\Coder\Github Repos\StandardL.github.io\assets\img\posts\2024-03-18-计算机视觉1\overlap_img_ap.jpg" alt="overlap_img_ap" style="zoom:50%;" /> |
| 368 | + |
| 369 | +- Fe和P: |
| 370 | + |
| 371 | + <img src="D:\Coder\Github Repos\StandardL.github.io\assets\img\posts\2024-03-18-计算机视觉1\overlap_img_fp.jpg" alt="overlap_img_fp" style="zoom:50%;" /> |
| 372 | + |
| 373 | +- 三者重叠: |
| 374 | + |
| 375 | + <img src="D:\Coder\Github Repos\StandardL.github.io\assets\img\posts\2024-03-18-计算机视觉1\overlap_img_all.jpg" alt="overlap_img_all" style="zoom:50%;" /> |
0 commit comments