将seg.png
进行4-邻域连通域标记吧。
连通域标记(Connected Component Labeling)是将邻接的像素打上相同的标记的作业。
也就是说:
黒 黒 黒 黒
黒 白 白 黒
黒 白 黒 黒
黒 黒 黒 黒
将相邻的白色像素打上相同的标记。
像这样的像素组成的被标记的块被称为连通区域(Connected Component)。
在这里我们为4邻域的像素打上标记。另,在这里我们使用一种被称为Lookup Table的东西。
Lookup Table是这样的:
Source | Distination |
---|---|
1 | 1 |
2 | 2 |
3 | 1 |
一开始被打上1标签的像素(即Source=1
的像素)最终被分配到的标签为1(Distination=1
);一开始被打上3标签的像素(即Source =3
的像素)最终被分配的的标签也为1(Distination=1
)。
算法如下:
- 从左上角开始进行光栅扫描。
-
如果当前遍历到的像素
i(x,y)
是黑像素的什么也不干。如果是白像素,考察该像素的上方像素i(x,y-1)
和左边像素i(x-1,y)
,如果两个的取值都为0,将该像素分配一个新的标签。在这里我们用数字做标签,即1,2。
- 如果两个像素中有一个不为0(也就是说已经分配了标签),将上方和左边的像素分配的标签中数值较小的那一个(0除外)分配给当前遍历到的像素
i(x,y)
。在这里,将上方像素和左边像素的标签写入Lookup Table
的Source
,将当前遍历的像素i(x,y)
分配的标签写入Distination
。 -
最后,对照
Lookup Table
,对像素分配的标签由Source
变为Distination
。
像这样的话,邻接像素就可以打上同样的标签了。因为这里是做4-邻域连通域标记,所以我们只用考察上方像素和左边像素。
python实现:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Read image
img = cv2.imread("seg.png").astype(np.float32)
H, W, C = img.shape
label = np.zeros((H, W), dtype=np.int)
label[img[..., 0]>0] = 1
LUT = [0 for _ in range(H*W)]
n = 1
for y in range(H):
for x in range(W):
if label[y, x] == 0:
continue
c3 = label[max(y-1,0), x]
c5 = label[y, max(x-1,0)]
if c3 < 2 and c5 < 2:
n += 1
label[y, x] = n
else:
_vs = [c3, c5]
vs = [a for a in _vs if a > 1]
v = min(vs)
label[y, x] = v
minv = v
for _v in vs:
if LUT[_v] != 0:
minv = min(minv, LUT[_v])
for _v in vs:
LUT[_v] = minv
count = 1
for l in range(2, n+1):
flag = True
for i in range(n+1):
if LUT[i] == l:
if flag:
count += 1
flag = False
LUT[i] = count
COLORS = [[0, 0, 255], [0, 255, 0], [255, 0, 0], [255, 255, 0]]
out = np.zeros((H, W, C), dtype=np.uint8)
for i, lut in enumerate(LUT[2:]):
out[label == (i+2)] = COLORS[lut-2]
# Save result
cv2.imwrite("out.png", out)
cv2.imshow("result", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
输入(seg.png):
输出: