本文是在借鉴了https://www.cnblogs.com/ronny/p/img_aly_01.html这篇文章之后,对连接域标记进行了实现,原作者是在cpp下完成的,为了方便,我用python实现一下并做一些记录。
一、实现原理
这里选择基于行程的标记,以下图为例,这里借用原文章中的图片:
1、首先,我们自左向右,自上向下的遍历图片,以[start, end]这种形式记录白色块的起始列,对于第一行我们有[2,6], [10,13]两块,第二行是[5,6],[8,9]两块。
2、标号,以及制作等价对,我们按序给各块进行标号,注意如果该块与其上一行的某块连通,那么他继承连通块的标号,如果与上一行的好多块都连通,那么继承最小的标号,与其他连通块就是等价对关系。具体来看一下,第一行两块,不和别人连通,所以[2,6]是1号,[10,13]是2号;下一行,[5,6]与[2,6]连通,[5,6]也是1号,同理[8,9]是2号;第三行,[1,3]与其他都不连接,[1,3]是3号,[6,7]比较特殊,他和1号,2号都连通,那么他是1号,而且添加等价对[1号,2号]。
3、等价对融合,假如我们有好多等价对:[[1号,2号],[2号,5号],[5号,7号],[6号,8号],[8号,9号]]
我们需要把他们整理成:[[1号,2号,5号,7号],[6号,8号,9号]]这样的样子,原作者采用了深度树来实现这个整理
4、将属于等价的标号统一再标号,对于上面的例子就是:
[2,6] , [10,13] , [5,6] , [8,9] , [6,7]这是一整块是1号
[1,3]是2号
二、实现
首先是把所有的块统计出来:
def findRun(image):
row, col = image.shape
# 一共有多少个块
run_num = 0
# 每个块在第几行
rowRun = []
# 每个块的开始结束列
startRun = []
endRun = []
for i in range(row):
image_row = image[i,:]
# 开头就是白
if image_row[0] >= 150:
rowRun.append(i)
startRun.append(0)
run_num += 1
for j in range(1,col):
# 前面是白,到这变成了黑,则为一个run的结束点
if image_row[j-1] >= 150 and image_row[j] <= 10:
endRun.append(j)
# 前面是黑,到这变成了白,则为一个run的起始点
elif image_row[j-1] <= 10 and image_row[j] >= 150:
rowRun.append(i)
startRun.append(j)
run_num += 1
# 结尾还是白
if image_row[col-1] >= 150:
endRun.append(col-1)
return run_num, startRun, endRun, rowRun
标号,以及统计其中的等价对:
def findEquivalences(run_num, startRun, endRun, rowRun, offset=0):
rowCur = 0
# 上一行的第一个run的序号
preRowFirstRun = 0
# 上一行的最后一个run的序号
preRowLastRun = -1
# 当前行的第一个run的序号
curRowFirstRun = 0
# 各run标记
run_label = [0 for i in range(run_num)]
# 等价表
run_equivalences = []
name_index = 1
# 对每一个run
for i in range(run_num):
# 换行更新run序号
if not rowRun[i] == rowCur:
rowCur = rowRun[i]
preRowFirstRun = curRowFirstRun
preRowLastRun = i
curRowFirstRun = i
if preRowFirstRun < preRowLastRun:
for j in range(preRowFirstRun, preRowLastRun):
# 若和上一层的run有连通
# 开头在其他块结尾前且结尾在其他块开头后,且是当前块的上一行中的块
if(startRun[i] <= (endRun[j] + offset) and endRun[i] >= (startRun[j] - offset) and rowRun[j] == rowRun[i]-1):
# 没赋标号则继承
if run_label[i] == 0:
run_label[i] = run_label[j]
# 已经赋标号了但是标号不一样则置为等价对
elif not run_label[i] == run_label[j]:
run_equivalences.append([run_label[i], run_label[j]])
# 没有赋值且没有连通域,则为新的块
if run_label[i] == 0:
run_label[i] = name_index
name_index += 1
return run_equivalences, run_label
这里的offset参数是增加了一定的余量,不用严格的要求必须4连或者8连才是连通,有一定量的错开也是连通。
通过深度树节点迭代,将等价的标号重新标号为统一标号:
def emergeEquivalences(run_equivalences, run_label):
equivalences_set = []
# 对每一个等价对
for equivalences in run_equivalences:
# 如果已经被纳入某个等价集合中,则pass
has_set = False
for set in equivalences_set:
if (equivalences[0] in set) and (equivalences[1] in set):
has_set = True
break
if has_set:
continue
# 若还没有,开始二叉搜索
# already_set:当前搜索进展
# equivalences:当前搜索节点
# run_equivalences:总集
def search(already_set, equivalences, run_equivalences):
# 对每一个等价对
lift_search =[]
right_search = []
for equivalence in run_equivalences:
# 如果已经被纳入某个当前集合中,则pass
has_set = False
if (equivalence[0] in already_set) and (equivalence[1] in already_set):
has_set = True
if has_set:
continue
# 若没有,判断是否与当前节点等价
# 左
if equivalences[0] in equivalence:
lift_search.append(equivalence)
if equivalences[1] in equivalence:
right_search.append(equivalence)
# 如果左右都没了,返回当前集合
if len(lift_search)== 0 and len(right_search) == 0:
return already_set
# 若还可以延申
else:
# 先扩充already_set
for set in lift_search:
already_set += set
for set in right_search:
already_set += set
# 此处应该对already_set去重
# 迭代寻找接下来的节点
for set in lift_search:
already_set += search(already_set, set, run_equivalences)
for set in right_search:
already_set += search(already_set, set, run_equivalences)
return already_set
one_set = []
one_set = search(one_set, equivalences, run_equivalences)
equivalences_set.append(one_set)
# 根据equivalences_set重新标号
name_index = 1
for equivalences in equivalences_set:
for index in range(len(run_label)):
if run_label[index] in equivalences:
run_label[index] = name_index
name_index += 1
return run_label
把同标号的色素进行上色:
def drawConnectedDomain(img, run_label, startRun, endRun, rowRun):
max_index = max(run_label)
min_index = min(run_label)
for run_index in range(len(run_label)):
row = rowRun[run_index]
for col in range(startRun[run_index], endRun[run_index]+1):
img[row,col] = (run_label[run_index] / max_index) * 255
cv2.imshow('result', img)
cv2.waitKey()
这是原图和处理后的一个图,简单的把10个块,按25.5的色差进行赋值上色,还算okk。