python 连通域_连通域的原理与Python实现
二值圖像連通域
二值圖像分析最基礎的也是最重要的方法之一就是連通域標記,它是所有二值圖像分析的基礎。它通過對二值圖像中目標像素的標記,讓每個單獨的連通區域形成一個被標識的塊,進一步的我們就可以獲取這些塊的輪廓、外接矩形、質心、不變矩等幾何參數。
連通區域的定義一般有兩種,分為4鄰接和8鄰接。下面這幅圖中,如果考慮4鄰接,則有3個連通域,8鄰接則是2個連通域。
從連通區域的定義可以知道,一個連通域是由具有相同像素值的相鄰像素組成像素集合,因此,我們就可以通過這兩個條件在圖像中尋找連通區域,對于找到的每個連通域,我們賦予其一個唯一的標識( Label ),以區別其他連通域。
連通域分析的基本算法有兩種: 1) Two-Pass 兩遍掃描 2) Seed-Filling 種子填充法。
Two-Pass 算法
兩遍掃描法( Two-Pass ),正如其名,指的就是通過掃描兩遍圖像,將圖像中存在的所有連通域找出并標記。
(1)第一次掃描:
訪問當前像素 B(x,y) ,如果 B(x,y) == 1:
a、如果 B(x,y) 的領域中標簽值都為0,則賦予 B(x,y) 一個新的 label :
label += 1, B(x,y) = label;
b、如果B(x,y)的領域中有像素值 > 1的像素Neighbors:
1)將Neighbors中的最小值賦予給 B(x,y) :
B(x,y) = min{Neighbors}
2)記錄Neighbors中各個值(label)之間的相等關系,即這些值(label)同屬同一個連通區域;
(2)第二次掃描:
訪問當前像素 B(x,y) ,如果 B(x,y) > 1:
a、找到與 label = B(x,y) 同屬相等關系的一個最小 label 值,賦予給 B(x,y) ;
完成掃描后,圖像中具有相同 label 值的像素就組成了同一個連通區域。
另外,我在代碼實現的過程中想到另外一種 Two-Pass 的方式(即掃描兩遍圖像的方式)實現,就是第二次掃描與 (1) 同樣的過程,只是方向換成從右下到左上。我后面的 Two-Pass 代碼是使用我自己想到的方法實現的,自己使用了幾個例子測試了下,目前沒出現啥問題。
Seed-Filling 算法
種子填充方法來源于計算機圖形學,常用于對某個圖形進行填充。它基于區域生長算法。我的理解就是遞歸遍歷。
附上兩種方法的 Python 的實現
import cv2
import numpy as np
# 4鄰域的連通域和 8鄰域的連通域
# [row, col]
NEIGHBOR_HOODS_4 = True
OFFSETS_4 = [[0, -1], [-1, 0], [0, 0], [1, 0], [0, 1]]
NEIGHBOR_HOODS_8 = False
OFFSETS_8 = [[-1, -1], [0, -1], [1, -1],
[-1, 0], [0, 0], [1, 0],
[-1, 1], [0, 1], [1, 1]]
def reorganize(binary_img: np.array):
index_map = []
points = []
index = -1
rows, cols = binary_img.shape
for row in range(rows):
for col in range(cols):
var = binary_img[row][col]
if var < 0.5:
continue
if var in index_map:
index = index_map.index(var)
num = index + 1
else:
index = len(index_map)
num = index + 1
index_map.append(var)
points.append([])
binary_img[row][col] = num
points[index].append([row, col])
return binary_img, points
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]):
label = 256
if binary_img[row][col] < 0.5:
continue
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
label = neighbor_val if neighbor_val < label else label
if label == 255:
label_idx += 1
label = label_idx
binary_img[row][col] = label
return binary_img
# binary_img: bg-0, object-255; int
def Two_Pass(binary_img: np.array, neighbor_hoods):
if neighbor_hoods == NEIGHBOR_HOODS_4:
offsets = OFFSETS_4
elif neighbor_hoods == NEIGHBOR_HOODS_8:
offsets = OFFSETS_8
else:
raise ValueError
binary_img = neighbor_value(binary_img, offsets, False)
binary_img = neighbor_value(binary_img, offsets, True)
return binary_img
def recursive_seed(binary_img: np.array, seed_row, seed_col, offsets, num, max_num=100):
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)
var = binary_img[neighbor_row][neighbor_col]
if var < max_num:
continue
binary_img = recursive_seed(binary_img, neighbor_row, neighbor_col, offsets, num, max_num)
return binary_img
# max_num 表示連通域最多存在的個數
def Seed_Filling(binary_img, neighbor_hoods, max_num=100):
if neighbor_hoods == NEIGHBOR_HOODS_4:
offsets = OFFSETS_4
elif neighbor_hoods == NEIGHBOR_HOODS_8:
offsets = OFFSETS_8
else:
raise ValueError
num = 1
rows, cols = binary_img.shape
for row in range(rows):
for col in range(cols):
var = binary_img[row][col]
if var <= max_num:
continue
binary_img = recursive_seed(binary_img, row, col, offsets, num, max_num=100)
num += 1
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(255)
print("原始二值圖像")
print(binary_img)
print("Two_Pass")
binary_img = Two_Pass(binary_img, NEIGHBOR_HOODS_8)
binary_img, points = reorganize(binary_img)
print(binary_img, points)
print("Seed_Filling")
binary_img = Seed_Filling(binary_img, NEIGHBOR_HOODS_8)
binary_img, points = reorganize(binary_img)
print(binary_img, points)
總結
以上是生活随笔為你收集整理的python 连通域_连通域的原理与Python实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: tcl股票代码
- 下一篇: python的回收机制_Python垃圾