如何使用Opencv等值线单向描述线点
How to use Opencv contours to describe line points in a unidirectional way
我正在使用 opencvs findContour
来查找点来描述由线(不是多边形)组成的图像,如下所示:
cv::findContours(src, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
。
如果我没理解错的话,"cv2.connectedComponents" 方法给出了您要查找的内容。它为图像中的每个点分配一个标签,如果点连接,标签是相同的。通过执行此分配,不会发生重复。因此,如果您的线条是一个像素宽(例如边缘检测器或细化运算符的输出),则每个位置都会得到一个点。
编辑:
根据 OP 要求,线条应为 1 像素宽。为了实现这一点,在找到连接的组件之前应用了细化操作。步骤图像也已添加。
请注意,每个连接的分量点按y线的升序排列。
img_path = "D:/_temp/fig.png"
output_dir = 'D:/_temp/'
img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
_, img = cv2.threshold(img, 128, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)
total_white_pixels = cv2.countNonZero(img)
print ("Total White Pixels Before Thinning = ", total_white_pixels)
cv2.imwrite(output_dir + '1-thresholded.png', img)
#apply thinning -> each line is one-pixel wide
img = cv2.ximgproc.thinning(img)
cv2.imwrite(output_dir + '2-thinned.png', img)
total_white_pixels = cv2.countNonZero(img)
print ("Total White Pixels After Thinning = ", total_white_pixels)
no_ccs, labels = cv2.connectedComponents(img)
label_pnts_dic = {}
colored = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
i = 1 # skip label 0 as it corresponds to the backgground points
sum_of_cc_points = 0
while i < no_ccs:
label_pnts_dic[i] = np.where(labels == i) #where return tuple(list of x cords, list of y cords)
colored[label_pnts_dic[i]] = (random.randint(100, 255), random.randint(100, 255), random.randint(100, 255))
i +=1
cv2.imwrite(output_dir + '3-colored.png', colored)
print ("First ten points of label-1 cc: ")
for i in range(10):
print ("x: ", label_pnts_dic[1][1][i], "y: ", label_pnts_dic[1][0][i])
输出:
Total White Pixels Before Thinning = 6814
Total White Pixels After Thinning = 2065
First ten points of label-1 cc:
x: 312 y: 104
x: 313 y: 104
x: 314 y: 104
x: 315 y: 104
x: 316 y: 104
x: 317 y: 104
x: 318 y: 104
x: 319 y: 104
x: 320 y: 104
x: 321 y: 104
图片:
1.Thresholded
- 细化
- 彩色组件
编辑2:
在与 OP 讨论后,我明白只有一个(分散的)点列表是不够的。应该对点进行排序,以便可以对其进行跟踪。为实现该新逻辑,应在对图像应用细化后引入新逻辑。
- 查找极值点(具有单个 8-连通性邻居的点)
- 查找连接点(具有 3 向连接的点)
- 查找简单点(所有其他点)
- 从一个极值点开始追踪,直到到达另一个极值点或连接器。
- 提取行进路径。
- 检查连接点是否已经变成简单点并更新其状态。
- 重复
- 检查是否存在未从任何极值点到达的简单点的闭环,将每个闭环提取为附加路点。
extreme/connector/simple点分类代码
def filter_neighbors(ns):
i = 0
while i < len(ns):
j = i + 1
while j < len(ns):
if (ns[i][0] == ns[j][0] and abs(ns[i][1] - ns[j][1]) <= 1) or (ns[i][1] == ns[j][1] and abs(ns[i][0] - ns[j][0]) <= 1):
del ns[j]
break
j += 1
i += 1
def sort_points_types(pnts):
extremes = []
connections = []
simple = []
for i in range(pnts.shape[0]):
neighbors = []
for j in range (pnts.shape[0]):
if i == j: continue
if abs(pnts[i, 0] - pnts[j, 0]) <= 1 and abs(pnts[i, 1] - pnts[j, 1]) <= 1:#8-connectivity check
neighbors.append(pnts[j])
filter_neighbors(neighbors)
if len(neighbors) == 1:
extremes.append(pnts[i])
elif len(neighbors) == 2:
simple.append(pnts[i])
elif len(neighbors) > 2:
connections.append(pnts[i])
return extremes, connections, simple
img_path = "D:/_temp/fig.png"
output_dir = 'D:/_temp/'
img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
_, img = cv2.threshold(img, 128, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)
img = cv2.ximgproc.thinning(img)
pnts = cv2.findNonZero(img)
pnts = np.squeeze(pnts)
ext, conn, simple = sort_points_types(pnts)
for p in conn:
cv2.circle(img, (p[0], p[1]), 5, 128)
for p in ext:
cv2.circle(img, (p[0], p[1]), 5, 128)
cv2.imwrite(output_dir + "6-both.png", img)
print (len(ext), len(conn), len(simple))
编辑3:
通过以类似内核的方式检查邻居,在一次通过中对点进行分类的实现更加有效,这要归功于 !
注意:在调用此方法之前,图像应填充一个像素以避免边界检查或相当于在边界处涂黑像素。
def sort_points_types(pnts, img):
extremes = []
connections = []
simple = []
for p in pnts:
x = p[0]
y = p[1]
n = []
if img[y - 1,x] > 0: n.append((y-1, x))
if img[y - 1,x - 1] > 0: n.append((y-1, x - 1))
if img[y - 1,x + 1] > 0: n.append((y-1, x + 1))
if img[y,x - 1] > 0: n.append((y, x - 1))
if img[y,x + 1] > 0: n.append((y, x + 1))
if img[y + 1,x] > 0: n.append((y+1, x))
if img[y + 1,x - 1] > 0: n.append((y+1, x - 1))
if img[y + 1,x + 1] > 0: n.append((y+1, x + 1))
filter_neighbors(n)
if len(n) == 1:
extremes.append(p)
elif len(n) == 2:
simple.append(p)
elif len(n) > 2:
connections.append(p)
return extremes, connections, simple
一幅可视化极点和连接点的图像:
我正在使用 opencvs findContour
来查找点来描述由线(不是多边形)组成的图像,如下所示:
cv::findContours(src, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
。
如果我没理解错的话,"cv2.connectedComponents" 方法给出了您要查找的内容。它为图像中的每个点分配一个标签,如果点连接,标签是相同的。通过执行此分配,不会发生重复。因此,如果您的线条是一个像素宽(例如边缘检测器或细化运算符的输出),则每个位置都会得到一个点。
编辑:
根据 OP 要求,线条应为 1 像素宽。为了实现这一点,在找到连接的组件之前应用了细化操作。步骤图像也已添加。
请注意,每个连接的分量点按y线的升序排列。
img_path = "D:/_temp/fig.png"
output_dir = 'D:/_temp/'
img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
_, img = cv2.threshold(img, 128, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)
total_white_pixels = cv2.countNonZero(img)
print ("Total White Pixels Before Thinning = ", total_white_pixels)
cv2.imwrite(output_dir + '1-thresholded.png', img)
#apply thinning -> each line is one-pixel wide
img = cv2.ximgproc.thinning(img)
cv2.imwrite(output_dir + '2-thinned.png', img)
total_white_pixels = cv2.countNonZero(img)
print ("Total White Pixels After Thinning = ", total_white_pixels)
no_ccs, labels = cv2.connectedComponents(img)
label_pnts_dic = {}
colored = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
i = 1 # skip label 0 as it corresponds to the backgground points
sum_of_cc_points = 0
while i < no_ccs:
label_pnts_dic[i] = np.where(labels == i) #where return tuple(list of x cords, list of y cords)
colored[label_pnts_dic[i]] = (random.randint(100, 255), random.randint(100, 255), random.randint(100, 255))
i +=1
cv2.imwrite(output_dir + '3-colored.png', colored)
print ("First ten points of label-1 cc: ")
for i in range(10):
print ("x: ", label_pnts_dic[1][1][i], "y: ", label_pnts_dic[1][0][i])
输出:
Total White Pixels Before Thinning = 6814
Total White Pixels After Thinning = 2065
First ten points of label-1 cc:
x: 312 y: 104
x: 313 y: 104
x: 314 y: 104
x: 315 y: 104
x: 316 y: 104
x: 317 y: 104
x: 318 y: 104
x: 319 y: 104
x: 320 y: 104
x: 321 y: 104
图片:
1.Thresholded
- 细化
- 彩色组件
编辑2:
在与 OP 讨论后,我明白只有一个(分散的)点列表是不够的。应该对点进行排序,以便可以对其进行跟踪。为实现该新逻辑,应在对图像应用细化后引入新逻辑。
- 查找极值点(具有单个 8-连通性邻居的点)
- 查找连接点(具有 3 向连接的点)
- 查找简单点(所有其他点)
- 从一个极值点开始追踪,直到到达另一个极值点或连接器。
- 提取行进路径。
- 检查连接点是否已经变成简单点并更新其状态。
- 重复
- 检查是否存在未从任何极值点到达的简单点的闭环,将每个闭环提取为附加路点。
extreme/connector/simple点分类代码
def filter_neighbors(ns):
i = 0
while i < len(ns):
j = i + 1
while j < len(ns):
if (ns[i][0] == ns[j][0] and abs(ns[i][1] - ns[j][1]) <= 1) or (ns[i][1] == ns[j][1] and abs(ns[i][0] - ns[j][0]) <= 1):
del ns[j]
break
j += 1
i += 1
def sort_points_types(pnts):
extremes = []
connections = []
simple = []
for i in range(pnts.shape[0]):
neighbors = []
for j in range (pnts.shape[0]):
if i == j: continue
if abs(pnts[i, 0] - pnts[j, 0]) <= 1 and abs(pnts[i, 1] - pnts[j, 1]) <= 1:#8-connectivity check
neighbors.append(pnts[j])
filter_neighbors(neighbors)
if len(neighbors) == 1:
extremes.append(pnts[i])
elif len(neighbors) == 2:
simple.append(pnts[i])
elif len(neighbors) > 2:
connections.append(pnts[i])
return extremes, connections, simple
img_path = "D:/_temp/fig.png"
output_dir = 'D:/_temp/'
img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
_, img = cv2.threshold(img, 128, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)
img = cv2.ximgproc.thinning(img)
pnts = cv2.findNonZero(img)
pnts = np.squeeze(pnts)
ext, conn, simple = sort_points_types(pnts)
for p in conn:
cv2.circle(img, (p[0], p[1]), 5, 128)
for p in ext:
cv2.circle(img, (p[0], p[1]), 5, 128)
cv2.imwrite(output_dir + "6-both.png", img)
print (len(ext), len(conn), len(simple))
编辑3:
通过以类似内核的方式检查邻居,在一次通过中对点进行分类的实现更加有效,这要归功于
注意:在调用此方法之前,图像应填充一个像素以避免边界检查或相当于在边界处涂黑像素。
def sort_points_types(pnts, img):
extremes = []
connections = []
simple = []
for p in pnts:
x = p[0]
y = p[1]
n = []
if img[y - 1,x] > 0: n.append((y-1, x))
if img[y - 1,x - 1] > 0: n.append((y-1, x - 1))
if img[y - 1,x + 1] > 0: n.append((y-1, x + 1))
if img[y,x - 1] > 0: n.append((y, x - 1))
if img[y,x + 1] > 0: n.append((y, x + 1))
if img[y + 1,x] > 0: n.append((y+1, x))
if img[y + 1,x - 1] > 0: n.append((y+1, x - 1))
if img[y + 1,x + 1] > 0: n.append((y+1, x + 1))
filter_neighbors(n)
if len(n) == 1:
extremes.append(p)
elif len(n) == 2:
simple.append(p)
elif len(n) > 2:
connections.append(p)
return extremes, connections, simple
一幅可视化极点和连接点的图像: