如何在分割中找到边界之间的最大值?
How to find max value between boundary in segmentation?
这里有个问题:我想在不规则形状的分割图像中找到实际的最大边界宽度(this)
下面我 post 一些我用于测试的示例图像
到目前为止,我设法获得了边界和骨架线,但是如何测量垂直于骨架线的等高线之间的距离?
def get_skeleton(image_path):
im = cv2.imread(img_path , cv2.IMREAD_GRAYSCALE)
binary = im > filters.threshold_otsu(im)
skeleton = morphology.skeletonize(binary)
return skeleton
skeleton = get_skeleton(img_path)
plt.imshow(skeleton, cmap="gray")
def get_boundary(image_path):
reading_Img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
reading_Img = cv2.cvtColor(reading_Img,cv2.COLOR_BGR2RGB)
canny_Img = cv2.Canny(reading_Img,90,100)
contours,_ = cv2.findContours(canny_Img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
canvas = np.zeros_like(reading_Img)
boundary = cv2.drawContours(canvas , contours, -1, (255, 0, 0), 1)
return boundary
boundary = get_boundary(img_path)
plt.imshow(boundary)
Sample input image
编辑:
首先感谢您的回答,我想补充更多关于我正在尝试做的事情的细节。
所以我做了一个检测混凝土裂缝的分割模型(它们可以是任何形状,垂直的,水平的,对角线的等等)现在我需要确定它们的最大宽度并画一条线来显示它发生的位置。
我发现中轴 returns 与边界的距离,通过过滤最大值,我能够获得宽度(参见下面的 colab)及其在中轴上的坐标。现在我需要画一条线连接边界之间的宽度,但我不知道如何找到这样一条线的坐标。
我想到了一个算法,从中轴上最大距离出现的点开始,扩展直到找到边界,但我不知道如何实现它。
这张图片显示了我需要的东西:
找到点的 x 和 y 后,我将能够计算 2 点之间的欧氏距离
dist=sqrt((y2-y1)^2+(x2-x1)^2)
请查看我在 colab notebook 中的代码:https://colab.research.google.com/drive/1NvEyfrxpKGJ1kxjP48PGNB_UUSp6f6Ze?usp=sharing
示例输入图像:
我做的第一件事就是让你的图像保持灰度,不需要转换到 3 个通道来寻找轮廓。其次是将 boundary
图像转换为二进制,使其与 skeleton
图像相同。然后我简单地将两者相加得到both
图像。
然后我仔细查看组合 both
图像的每一行(因为您正在寻找垂直距离)并寻找 True
的元素,即边界或骨架像素。我在这一点上做了一个简化的假设 - 我只搜索了边界后跟 单个 骨架像素然后是第二个边界的情况,我明白这可能并不总是案例,但我把那个特别头疼的问题留给你解决。
之后,这只是跟踪逐行浏览图像时记录的最大和最小距离的情况。 (编辑:可能有比我做的更简洁的方法,但希望你能理解)
import numpy as np
import matplotlib.pyplot as plt
import cv2
from skimage import filters
from skimage import morphology
def get_skeleton(image_path):
im = cv2.imread(image_path , cv2.IMREAD_GRAYSCALE)
binary = im > filters.threshold_otsu(im)
skeleton = morphology.skeletonize(binary)
return skeleton
def get_boundary(image_path):
reading_Img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
canny_Img = cv2.Canny(reading_Img, 90, 100)
contours,_ = cv2.findContours(canny_Img,cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
canvas = np.zeros_like(reading_Img)
boundary = cv2.drawContours(canvas, contours, -1, (255, 0, 0), 1)
binary = boundary > filters.threshold_otsu(boundary)
return binary
skeleton = get_skeleton("LtqlM.png")
boundary = get_boundary("LtqlM.png")
both = skeleton + boundary
max_dist = 0
min_dist = 100000
for idx in range(both.shape[0]): # counting through rows
row = both[idx, :]
lines = np.where(row==True)[0]
if len(lines) == 3:
dist_1 = lines[1] - lines[0]
dist_2 = lines[2] - lines[1]
if (dist_1 > dist_2) and dist_1 > max_dist:
max_dist = dist_1
if (dist_2 > dist_1) and dist_2 > max_dist:
max_dist = dist_2
if (dist_1 < dist_2) and dist_1 < min_dist:
min_dist = dist_1
if (dist_2 < dist_1) and dist_2 < min_dist:
min_dist = dist_2
print("Maximum distance = ", max_dist)
print("Minimum distance = ", min_dist)
plt.imshow(both)
从您的方法开始,您可以使用中轴函数
- 在找到的点插入轴的方向
- 从方向推导正交
- 看正交到达边界的地方
下面的示例显示了原理并适用于您的示例图像。但我敢肯定会有一些尚未考虑的边界条件。我把它留给你让它对真实的实时数据具有鲁棒性。
import cv2
import numpy as np
from skimage.morphology import medial_axis
from skimage import img_as_ubyte
delta = 3 # delta index for interpolation
# get crack
im = cv2.imread("img.png", cv2.IMREAD_GRAYSCALE)
rgb = cv2.cvtColor(im, cv2.COLOR_GRAY2RGB) # rgb just for demo purpose
_, crack = cv2.threshold(im, 127, 255, cv2.THRESH_BINARY)
# get medial axis
medial, distance = medial_axis(im, return_distance=True)
med_img = img_as_ubyte(medial)
med_contours, _ = cv2.findContours(med_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cv2.drawContours(rgb, med_contours, -1, (255, 0, 0), 1)
med_pts = [v[0] for v in med_contours[0]]
# get point with maximal distance from medial axis
max_idx = np.argmax(distance)
max_pos = np.unravel_index(max_idx, distance.shape)
max_dist = distance[max_pos]
coords = np.array([max_pos[1], max_pos[0]])
print(f"max distance from medial axis to boundary = {max_dist} at {coords}")
# interpolate orthogonal of medial axis at coords
idx = next(i for i, v in enumerate(med_pts) if (v == coords).all())
px1, py1 = med_pts[(idx-delta) % len(med_pts)]
px2, py2 = med_pts[(idx+delta) % len(med_pts)]
orth = np.array([py1 - py2, px2 - px1]) * max(im.shape)
# intersect orthogonal with crack and get contour
orth_img = np.zeros(crack.shape, dtype=np.uint8)
cv2.line(orth_img, coords + orth, coords - orth, color=255, thickness=1)
gap_img = cv2.bitwise_and(orth_img, crack)
gap_contours, _ = cv2.findContours(gap_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
gap_pts = [v[0] for v in gap_contours[0]]
# determine the end points of the gap contour by negative dot product
n = len(gap_pts)
gap_ends = [
p for i, p in enumerate(gap_pts)
if np.dot(p - gap_pts[(i-1) % n], gap_pts[(i+1) % n] - p) < 0
]
print(f"Maximum gap found from {gap_ends[0]} to {gap_ends[1]}")
cv2.line(rgb, gap_ends[0], gap_ends[1], color=(0, 0, 255), thickness=1)
cv2.imwrite("test_out.png", rgb)
从距离最大的像素开始,您可以探索同心方形图层,直到找到背景像素。然后在最后一层找到欧氏距离最短的背景像素。第二个端点是对称的。
这里有个问题:我想在不规则形状的分割图像中找到实际的最大边界宽度(this) 下面我 post 一些我用于测试的示例图像 到目前为止,我设法获得了边界和骨架线,但是如何测量垂直于骨架线的等高线之间的距离?
def get_skeleton(image_path):
im = cv2.imread(img_path , cv2.IMREAD_GRAYSCALE)
binary = im > filters.threshold_otsu(im)
skeleton = morphology.skeletonize(binary)
return skeleton
skeleton = get_skeleton(img_path)
plt.imshow(skeleton, cmap="gray")
def get_boundary(image_path):
reading_Img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
reading_Img = cv2.cvtColor(reading_Img,cv2.COLOR_BGR2RGB)
canny_Img = cv2.Canny(reading_Img,90,100)
contours,_ = cv2.findContours(canny_Img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
canvas = np.zeros_like(reading_Img)
boundary = cv2.drawContours(canvas , contours, -1, (255, 0, 0), 1)
return boundary
boundary = get_boundary(img_path)
plt.imshow(boundary)
Sample input image
编辑:
首先感谢您的回答,我想补充更多关于我正在尝试做的事情的细节。 所以我做了一个检测混凝土裂缝的分割模型(它们可以是任何形状,垂直的,水平的,对角线的等等)现在我需要确定它们的最大宽度并画一条线来显示它发生的位置。
我发现中轴 returns 与边界的距离,通过过滤最大值,我能够获得宽度(参见下面的 colab)及其在中轴上的坐标。现在我需要画一条线连接边界之间的宽度,但我不知道如何找到这样一条线的坐标。
我想到了一个算法,从中轴上最大距离出现的点开始,扩展直到找到边界,但我不知道如何实现它。
这张图片显示了我需要的东西:
找到点的 x 和 y 后,我将能够计算 2 点之间的欧氏距离
dist=sqrt((y2-y1)^2+(x2-x1)^2)
请查看我在 colab notebook 中的代码:https://colab.research.google.com/drive/1NvEyfrxpKGJ1kxjP48PGNB_UUSp6f6Ze?usp=sharing
示例输入图像:
我做的第一件事就是让你的图像保持灰度,不需要转换到 3 个通道来寻找轮廓。其次是将 boundary
图像转换为二进制,使其与 skeleton
图像相同。然后我简单地将两者相加得到both
图像。
然后我仔细查看组合 both
图像的每一行(因为您正在寻找垂直距离)并寻找 True
的元素,即边界或骨架像素。我在这一点上做了一个简化的假设 - 我只搜索了边界后跟 单个 骨架像素然后是第二个边界的情况,我明白这可能并不总是案例,但我把那个特别头疼的问题留给你解决。
之后,这只是跟踪逐行浏览图像时记录的最大和最小距离的情况。 (编辑:可能有比我做的更简洁的方法,但希望你能理解)
import numpy as np
import matplotlib.pyplot as plt
import cv2
from skimage import filters
from skimage import morphology
def get_skeleton(image_path):
im = cv2.imread(image_path , cv2.IMREAD_GRAYSCALE)
binary = im > filters.threshold_otsu(im)
skeleton = morphology.skeletonize(binary)
return skeleton
def get_boundary(image_path):
reading_Img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
canny_Img = cv2.Canny(reading_Img, 90, 100)
contours,_ = cv2.findContours(canny_Img,cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
canvas = np.zeros_like(reading_Img)
boundary = cv2.drawContours(canvas, contours, -1, (255, 0, 0), 1)
binary = boundary > filters.threshold_otsu(boundary)
return binary
skeleton = get_skeleton("LtqlM.png")
boundary = get_boundary("LtqlM.png")
both = skeleton + boundary
max_dist = 0
min_dist = 100000
for idx in range(both.shape[0]): # counting through rows
row = both[idx, :]
lines = np.where(row==True)[0]
if len(lines) == 3:
dist_1 = lines[1] - lines[0]
dist_2 = lines[2] - lines[1]
if (dist_1 > dist_2) and dist_1 > max_dist:
max_dist = dist_1
if (dist_2 > dist_1) and dist_2 > max_dist:
max_dist = dist_2
if (dist_1 < dist_2) and dist_1 < min_dist:
min_dist = dist_1
if (dist_2 < dist_1) and dist_2 < min_dist:
min_dist = dist_2
print("Maximum distance = ", max_dist)
print("Minimum distance = ", min_dist)
plt.imshow(both)
从您的方法开始,您可以使用中轴函数
- 在找到的点插入轴的方向
- 从方向推导正交
- 看正交到达边界的地方
下面的示例显示了原理并适用于您的示例图像。但我敢肯定会有一些尚未考虑的边界条件。我把它留给你让它对真实的实时数据具有鲁棒性。
import cv2
import numpy as np
from skimage.morphology import medial_axis
from skimage import img_as_ubyte
delta = 3 # delta index for interpolation
# get crack
im = cv2.imread("img.png", cv2.IMREAD_GRAYSCALE)
rgb = cv2.cvtColor(im, cv2.COLOR_GRAY2RGB) # rgb just for demo purpose
_, crack = cv2.threshold(im, 127, 255, cv2.THRESH_BINARY)
# get medial axis
medial, distance = medial_axis(im, return_distance=True)
med_img = img_as_ubyte(medial)
med_contours, _ = cv2.findContours(med_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cv2.drawContours(rgb, med_contours, -1, (255, 0, 0), 1)
med_pts = [v[0] for v in med_contours[0]]
# get point with maximal distance from medial axis
max_idx = np.argmax(distance)
max_pos = np.unravel_index(max_idx, distance.shape)
max_dist = distance[max_pos]
coords = np.array([max_pos[1], max_pos[0]])
print(f"max distance from medial axis to boundary = {max_dist} at {coords}")
# interpolate orthogonal of medial axis at coords
idx = next(i for i, v in enumerate(med_pts) if (v == coords).all())
px1, py1 = med_pts[(idx-delta) % len(med_pts)]
px2, py2 = med_pts[(idx+delta) % len(med_pts)]
orth = np.array([py1 - py2, px2 - px1]) * max(im.shape)
# intersect orthogonal with crack and get contour
orth_img = np.zeros(crack.shape, dtype=np.uint8)
cv2.line(orth_img, coords + orth, coords - orth, color=255, thickness=1)
gap_img = cv2.bitwise_and(orth_img, crack)
gap_contours, _ = cv2.findContours(gap_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
gap_pts = [v[0] for v in gap_contours[0]]
# determine the end points of the gap contour by negative dot product
n = len(gap_pts)
gap_ends = [
p for i, p in enumerate(gap_pts)
if np.dot(p - gap_pts[(i-1) % n], gap_pts[(i+1) % n] - p) < 0
]
print(f"Maximum gap found from {gap_ends[0]} to {gap_ends[1]}")
cv2.line(rgb, gap_ends[0], gap_ends[1], color=(0, 0, 255), thickness=1)
cv2.imwrite("test_out.png", rgb)
从距离最大的像素开始,您可以探索同心方形图层,直到找到背景像素。然后在最后一层找到欧氏距离最短的背景像素。第二个端点是对称的。