二值图连通域检测
-
文中提到有两种方法 ,重点研究了Two-pass
-
如果将图换成如下的0-1方式,不是0-255方式
binary_img = np.zeros((4,7), dtype=np.int16) index= [[0,2],[0,5], [1,0],[1,1],[1,2],[1,4],[1,5],[1,6], [2,2],[2,5], [3,1],[3,2],[3,4],[3,6]] for i in index: binary_img [i[0],i[1]] = np.int16(1)
-
则作者给出的代码几乎不能使用,如下所示
def neighbor_value(binary_img:np.array, offsets,reverse=False): rows,cols= binary_img.shape label_idx= 0 rows_ =[0,rows,1] if reverse==False else [rows-1,-1,-1] cols_ = [0,cols,1] if reverse==False else [cols-1,-1,-1] for row in range(rows_[0],rows_[1],rows_[2]): for col in range(cols_[0],cols_[1],cols_[2]): if binary_img[row][col] < 0.5 : continue minlabel=256 for offset in offsets: neighbor_row= min(max(0,row+offset[0]), rows-1) neighbor_col = min(max(0,col+offset[1]),cols-1) neighbor_val = binary_img[neighbor_row,neighbor_col] if neighbor_val <0.5 : continue if neighbor_val < minlabel: minlabel = neighbor_val if minlabel== 255: label_idx+=1 minlabel = label_idx binary_img[row][col] = minlabel return binary_img
-
if minlabel== 255这句包含了太多的中间逻辑
1.假定了连通域个数小于255,一般也够用了
2.3X3当前块内可能有多个255 其他都是0
3.没有写出来的else 则说明取的是八邻域中非255的元素的最小值 是minlabel -
OK , 用0-1 binary_iamge来实现下 ,要做的是展开上述逻辑
def neighbor_value(binary_img:np.array, offsets,reverse=False):
rows,cols= binary_img.shape
label_idx= 1 #从2号块开始赋值
rows_ =[0,rows,1] if reverse==False else [rows-1,-1,-1]
cols_ = [0,cols,1] if reverse==False else [cols-1,-1,-1]
for row in range(rows_[0],rows_[1],rows_[2]):
for col in range(cols_[0],cols_[1],cols_[2]):
if binary_img[row][col] < 0.5 : continue
Neighbors=[]
for offset in offsets:
neighbor_row= min(max(0,row+offset[0]), rows-1)
neighbor_col = min(max(0,col+offset[1]),cols-1)
neighbor_val = binary_img[neighbor_row,neighbor_col]
if neighbor_val <0.5 : continue
Neighbors.append(neighbor_val)
toLabel= 0
#here len(Neighbors) >=0
#包含多个1 和一个1 的情况
#第一次遍历时候标号从正向递增 ,反向再次遍历时候 Neighbors的最小值为2
#即只走else分支
if max(Neighbors) ==1:
label_idx+=1
toLabel=label_idx
else :
#根据数学原理, Neighbors所有元素非0 肯定有大于1的元素就是最小号块
#排开1 的所有非0元素的最小值
toLabel=min([x for x in Neighbors if x !=1 ])
binary_img[row][col] = toLabel
return binary_img
- 下面是完整的python代码
OFFSET_8 = [[-1,-1],[0,-1],[1,-1],
[-1,0],[0,0],[1,0],
[-1,1],[0,1],[1,1],
]
def neighbor_value(binary_img:np.array, offsets,reverse=False):
rows,cols= binary_img.shape
label_idx= 1 #从2号块开始赋值
rows_ =[0,rows,1] if reverse==False else [rows-1,-1,-1]
cols_ = [0,cols,1] if reverse==False else [cols-1,-1,-1]
for row in range(rows_[0],rows_[1],rows_[2]):
for col in range(cols_[0],cols_[1],cols_[2]):
if binary_img[row][col] < 0.5 : continue
Neighbors=[]
for offset in offsets:
neighbor_row= min(max(0,row+offset[0]), rows-1)
neighbor_col = min(max(0,col+offset[1]),cols-1)
neighbor_val = binary_img[neighbor_row,neighbor_col]
if neighbor_val <0.5 : continue
Neighbors.append(neighbor_val)
toLabel= 0
#here len(Neighbors) >=0
#包含多个1 和一个1 的情况
#第一次遍历时候标号从正向递增 ,反向再次遍历时候 Neighbors的最小值为2
#即只走else分支
if max(Neighbors) ==1:
label_idx+=1
toLabel=label_idx
else :
#根据数学原理, Neighbors所有元素非0 肯定有大于1的元素就是最小号块
#排开1 的所有非0元素的最小值
toLabel=min([x for x in Neighbors if x !=1 ])
binary_img[row][col] = toLabel
return binary_img
def Two_Pass(binary_img:np.array, neighbor_hoods):
offsets= OFFSET_8
binary_img = neighbor_value(binary_img, offsets,False)
print("first pass")
print(binary_img)
binary_img = neighbor_value(binary_img, offsets, True)
return binary_img
if __name__=="__main__":
binary_img = np.zeros((4,7), dtype=np.int16)
index= [[0,2],[0,5],
[1,0],[1,1],[1,2],[1,4],[1,5],[1,6],
[2,2],[2,5],
[3,1],[3,2],[3,4],[3,6]]
for i in index:
binary_img [i[0],i[1]] = np.int16(1)
print("original binary image")
print(binary_img)
print("tow_pass")
binary_img = Two_Pass(binary_img,NEIGHBOUR_HOODS_8)
print(binary_img)
#using 1 test
#original binary image
#[[0 0 1 0 0 1 0]
# [1 1 1 0 1 1 1]
# [0 0 1 0 0 1 0]
# [0 1 1 0 1 0 1]]
#tow_pass
#first pass
#[[0 0 2 0 0 3 0]
# [4 2 2 0 3 3 3]
# [0 0 2 0 0 3 0]
# [0 2 2 0 3 0 3]]
#[[0 0 2 0 0 3 0]
# [2 2 2 0 3 3 3]
# [0 0 2 0 0 3 0]
# [0 2 2 0 3 0 3]]
go-on ,继续研究下区域生长算法
- 回溯算法 ,树中每个节点可以选择的元素是八邻域
- 终止条件:已经被标注的像素点或超出图像区间的点
- 每次找到新的区块进行同样的查找逻辑
- 包括主函数 和一个递归函数,两者都要用的元素是否为0 和是否已经被标注的判断
- 完整代码
#区域生长算法 Seed-Filling
def recursive_seed(binary_img:np.array, seed_row,seed_col,offsets,num,max_num ):
#一般终止条件写在这里
# if var in [0,2..100] #说明已经标注
var = binary_img[seed_row,seed_col]
if var == 0 or var<=max_num and var >=2 :return
rows,cols= binary_img.shape
binary_img[seed_row][seed_col] = num
#回溯: 每层遍历时候可以选择的对象
for offset in offsets:
neighbor_row= min(max(0,seed_row+offset[0]), rows-1)
neighbor_col = min(max(0,seed_col+offset[1]),cols-1)
#if var ... 终止条件 也可以写在循环中
recursive_seed(binary_img, neighbor_row, neighbor_col,offsets, num,max_num)
#max_num=100 说明最多标注一百块
def Seed_Filling(binary_img, max_num=100):
offsets = OFFSET_8
num=2 #从2开始编号
rows,cols= binary_img.shape
for row in range(rows):
for col in range(cols):
var = binary_img[row][col]
# if var in [0,2..100] #说明已经标注
if var == 0 or var<=max_num and var >=2 :continue
recursive_seed(binary_img, row,col, offsets,num,max_num)
num+=1
if __name__=="__main__":
binary_img = np.zeros((4,7), dtype=np.int16)
index= [[0,2],[0,5],
[1,0],[1,1],[1,2],[1,4],[1,5],[1,6],
[2,2],[2,5],
[3,1],[3,2],[3,4],[3,6]]
for i in index:
binary_img [i[0],i[1]] = np.int16(1)
print("original binary image")
print(binary_img)
print("seed filling")
Seed_Filling(binary_img)
print(binary_img)
## 区域生长 result
#original binary image
#[[0 0 1 0 0 1 0]
# [1 1 1 0 1 1 1]
# [0 0 1 0 0 1 0]
# [0 1 1 0 1 0 1]]
#seed filling
#[[0 0 2 0 0 3 0]
# [2 2 2 0 3 3 3]
# [0 0 2 0 0 3 0]
# [0 2 2 0 3 0 3]]
#请按任意键继续. . .