通过图像中的插值连接虚线
joining dotted line by interpolation in an image
我有这张图片,如下所示。 这是一个二进制掩码
我使用以下代码创建了这张图片。基本上我只得到了那些白色像素的 x_idx
、y_idx
,而且我知道实际的图像大小,所以我首先创建一个空数组并在 x_idx
的帮助下填充这些行, y_idx
image = np.empty((x_shape, y_shape))
def line_to_img(linedf, image):
x_idx = linedf.x
y_idx = linedf.y
for i,j in zip(x_idx, y_idx):
image[i, j] = 1
return image
如您所见,所有像素都相同,除了两条线,一条在左边,一条在右边。
如你所见,右边的线是不连续的,我想通过一些插值的方法使这条线连续
我尝试了两种不同的方法来实现这一点,但到目前为止还没有成功
第一种方法使用 skimage
new_image = skimage.morphology.remove_small_holes(old_image, 40, connectivity=2, in_place=False)
输出:
输出解释:没有任何插值的相同图像
第二种方法使用cv2
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))
new_image = cv2.morphologyEx(old_image,cv2.MORPH_OPEN,kernel)
输出:
输出解释:由于某种原因删除了行
请帮助我如何完成此任务并在图像中插入线以获得连续线
编辑(用例): 基本上我只得到了那些白色像素的 x_idx
、y_idx
,而且我知道实际图像大小,所以我首先创建一个空数组并在 x_idx
和 y_idx
的帮助下填充这些行 我无法控制数据,就是这样,现在我想加入该行尺寸合适。基本上,我必须创建分割标签,其中线上方是一个标签,线下方是一个标签,左侧很好,我可以根据该线将图像分成两个标签,而中间部分将保留对于 class 1 即上半部分,虽然我确定右侧是单行,但我得到的数据已经降级,所以我希望这个插值进入画面
由于您只是想连接数据中的区域间隙,因此 Bresenham 算法(许多人都知道它是一种常见的画线算法)在这种情况下应该表现良好。
https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
伪算法:
- 从二进制掩码中获取所有 (x, y) 坐标对
- 按 X 从左到右排序(假设您有一条水平线,如果没有,您可以选择通过不同方式对各种分割掩码进行排序)
- 迭代每个坐标对并使用 Bresenham 连接它们。
实施:
import numpy as np
import cv2
from matplotlib import pyplot as plt
def _bresenham(x0: int, y0: int, x1: int, y1: int):
dx = x1 - x0
dy = y1 - y0
xsign = 1 if dx > 0 else -1
ysign = 1 if dy > 0 else -1
dx = abs(dx)
dy = abs(dy)
if dx > dy:
xx, xy, yx, yy = xsign, 0, 0, ysign
else:
dx, dy = dy, dx
xx, xy, yx, yy = 0, ysign, xsign, 0
D = 2 * dy - dx
y = 0
for x in range(dx + 1):
yield x0 + x * xx + y * yx, y0 + x * xy + y * yy
if D >= 0:
y += 1
D -= 2 * dx
D += 2 * dy
# Read in image and convert to binary mask
img = cv2.imread("C:\Test\so1.png", 0)
ret, thresh = cv2.threshold(img, 1, 255, cv2.THRESH_BINARY)
# Get xy coordinate list of points
pairs = []
points = np.nonzero(thresh)
points_row = points[0]
points_col = points[1]
for row, col in zip(points_row, points_col):
pairs.append((col, row))
# Sort coordinates by X
coords_sorted = sorted(pairs, key=lambda x: x[0])
# Apply bresenham algorithm
result_coords = []
for n in range(len(coords_sorted) - 1):
for p in _bresenham(coords_sorted[n][0], coords_sorted[n][1], coords_sorted[n + 1][0], coords_sorted[n + 1][1]):
result_coords.append(p)
# Update the binary mask with the connected lines
for x, y in result_coords:
thresh[y][x] = 255
plt.imshow(thresh, 'gray', vmin=0, vmax=255)
plt.show()
输出掩码:
这是一个使用等高线极值点的简单方法。
优势?
这种方法有一点优势。对于获得的每个轮廓,有 4 个极值点;这些是轮廓的最顶部、最底部、最右侧和最左侧的点。我们 仅 为每个轮廓迭代这 4 个点。与使用 Bresenham 算法的方法不同,该算法遍历图像中的每个 non-zero 点。
流量:
- 获取二值图像
- 执行形态学操作以连接附近的点
- 寻找轮廓
- 遍历为每个轮廓找到的极值点,并在最接近的点之间画一条线。
代码:
img = cv2.imread('image_path', 0)
img1 = cv2.imread(f, 1)
# binary image
th = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
# morphological operations
k1 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
dilate = cv2.morphologyEx(th, cv2.MORPH_DILATE, k1)
k2 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
erode = cv2.morphologyEx(dilate, cv2.MORPH_ERODE, k2)
# find contours
cnts1 = cv2.findContours(erode, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
#cnts = cnts[0] if len(cnts1) == 2 else cnts[1]
cnts = cnts1[0]
# For each contour, find the closest distance between their extreme points and join them
for i in range(len(cnts)):
min_dist = max(img.shape[0], img.shape[1])
cl = []
ci = cnts[i]
ci_left = tuple(ci[ci[:, :, 0].argmin()][0])
ci_right = tuple(ci[ci[:, :, 0].argmax()][0])
ci_top = tuple(ci[ci[:, :, 1].argmin()][0])
ci_bottom = tuple(ci[ci[:, :, 1].argmax()][0])
ci_list = [ci_bottom, ci_left, ci_right, ci_top]
for j in range(i + 1, len(cnts)):
cj = cnts[j]
cj_left = tuple(cj[cj[:, :, 0].argmin()][0])
cj_right = tuple(cj[cj[:, :, 0].argmax()][0])
cj_top = tuple(cj[cj[:, :, 1].argmin()][0])
cj_bottom = tuple(cj[cj[:, :, 1].argmax()][0])
cj_list = [cj_bottom, cj_left, cj_right, cj_top]
for pt1 in ci_list:
for pt2 in cj_list:
dist = int(np.linalg.norm(np.array(pt1) - np.array(pt2))) #dist = sqrt( (x2 - x1)**2 + (y2 - y1)**2 )
if dist < min_dist:
min_dist = dist
cl = []
cl.append([pt1, pt2, min_dist])
if len(cl) > 0:
cv2.line(erode, cl[0][0], cl[0][1], 255, thickness = 2)
最终结果:
我谈到了极值点,但它们位于何处?以下代码段显示:
# visualize extreme points for each contour
for c in cnts:
left = tuple(c[c[:, :, 0].argmin()][0])
right = tuple(c[c[:, :, 0].argmax()][0])
top = tuple(c[c[:, :, 1].argmin()][0])
bottom = tuple(c[c[:, :, 1].argmax()][0])
# Draw dots onto image
#cv2.drawContours(img1, [c], -1, (36, 255, 12), 2)
cv2.circle(img1, left, 2, (0, 50, 255), -1)
cv2.circle(img1, right, 2, (0, 255, 255), -1)
cv2.circle(img1, top, 2, (255, 50, 0), -1)
cv2.circle(img1, bottom, 2, (255, 255, 0), -1)
上面显示的极值点是从图像erode
.
的轮廓中获得的
我有这张图片,如下所示。 这是一个二进制掩码
我使用以下代码创建了这张图片。基本上我只得到了那些白色像素的 x_idx
、y_idx
,而且我知道实际的图像大小,所以我首先创建一个空数组并在 x_idx
的帮助下填充这些行, y_idx
image = np.empty((x_shape, y_shape))
def line_to_img(linedf, image):
x_idx = linedf.x
y_idx = linedf.y
for i,j in zip(x_idx, y_idx):
image[i, j] = 1
return image
如您所见,所有像素都相同,除了两条线,一条在左边,一条在右边。
如你所见,右边的线是不连续的,我想通过一些插值的方法使这条线连续
我尝试了两种不同的方法来实现这一点,但到目前为止还没有成功
第一种方法使用 skimage
new_image = skimage.morphology.remove_small_holes(old_image, 40, connectivity=2, in_place=False)
输出:
输出解释:没有任何插值的相同图像
第二种方法使用cv2
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))
new_image = cv2.morphologyEx(old_image,cv2.MORPH_OPEN,kernel)
输出:
输出解释:由于某种原因删除了行
请帮助我如何完成此任务并在图像中插入线以获得连续线
编辑(用例): 基本上我只得到了那些白色像素的 x_idx
、y_idx
,而且我知道实际图像大小,所以我首先创建一个空数组并在 x_idx
和 y_idx
的帮助下填充这些行 我无法控制数据,就是这样,现在我想加入该行尺寸合适。基本上,我必须创建分割标签,其中线上方是一个标签,线下方是一个标签,左侧很好,我可以根据该线将图像分成两个标签,而中间部分将保留对于 class 1 即上半部分,虽然我确定右侧是单行,但我得到的数据已经降级,所以我希望这个插值进入画面
由于您只是想连接数据中的区域间隙,因此 Bresenham 算法(许多人都知道它是一种常见的画线算法)在这种情况下应该表现良好。
https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
伪算法:
- 从二进制掩码中获取所有 (x, y) 坐标对
- 按 X 从左到右排序(假设您有一条水平线,如果没有,您可以选择通过不同方式对各种分割掩码进行排序)
- 迭代每个坐标对并使用 Bresenham 连接它们。
实施:
import numpy as np
import cv2
from matplotlib import pyplot as plt
def _bresenham(x0: int, y0: int, x1: int, y1: int):
dx = x1 - x0
dy = y1 - y0
xsign = 1 if dx > 0 else -1
ysign = 1 if dy > 0 else -1
dx = abs(dx)
dy = abs(dy)
if dx > dy:
xx, xy, yx, yy = xsign, 0, 0, ysign
else:
dx, dy = dy, dx
xx, xy, yx, yy = 0, ysign, xsign, 0
D = 2 * dy - dx
y = 0
for x in range(dx + 1):
yield x0 + x * xx + y * yx, y0 + x * xy + y * yy
if D >= 0:
y += 1
D -= 2 * dx
D += 2 * dy
# Read in image and convert to binary mask
img = cv2.imread("C:\Test\so1.png", 0)
ret, thresh = cv2.threshold(img, 1, 255, cv2.THRESH_BINARY)
# Get xy coordinate list of points
pairs = []
points = np.nonzero(thresh)
points_row = points[0]
points_col = points[1]
for row, col in zip(points_row, points_col):
pairs.append((col, row))
# Sort coordinates by X
coords_sorted = sorted(pairs, key=lambda x: x[0])
# Apply bresenham algorithm
result_coords = []
for n in range(len(coords_sorted) - 1):
for p in _bresenham(coords_sorted[n][0], coords_sorted[n][1], coords_sorted[n + 1][0], coords_sorted[n + 1][1]):
result_coords.append(p)
# Update the binary mask with the connected lines
for x, y in result_coords:
thresh[y][x] = 255
plt.imshow(thresh, 'gray', vmin=0, vmax=255)
plt.show()
输出掩码:
这是一个使用等高线极值点的简单方法。
优势?
这种方法有一点优势。对于获得的每个轮廓,有 4 个极值点;这些是轮廓的最顶部、最底部、最右侧和最左侧的点。我们 仅 为每个轮廓迭代这 4 个点。与使用 Bresenham 算法的方法不同,该算法遍历图像中的每个 non-zero 点。
流量:
- 获取二值图像
- 执行形态学操作以连接附近的点
- 寻找轮廓
- 遍历为每个轮廓找到的极值点,并在最接近的点之间画一条线。
代码:
img = cv2.imread('image_path', 0)
img1 = cv2.imread(f, 1)
# binary image
th = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
# morphological operations
k1 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
dilate = cv2.morphologyEx(th, cv2.MORPH_DILATE, k1)
k2 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
erode = cv2.morphologyEx(dilate, cv2.MORPH_ERODE, k2)
# find contours
cnts1 = cv2.findContours(erode, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
#cnts = cnts[0] if len(cnts1) == 2 else cnts[1]
cnts = cnts1[0]
# For each contour, find the closest distance between their extreme points and join them
for i in range(len(cnts)):
min_dist = max(img.shape[0], img.shape[1])
cl = []
ci = cnts[i]
ci_left = tuple(ci[ci[:, :, 0].argmin()][0])
ci_right = tuple(ci[ci[:, :, 0].argmax()][0])
ci_top = tuple(ci[ci[:, :, 1].argmin()][0])
ci_bottom = tuple(ci[ci[:, :, 1].argmax()][0])
ci_list = [ci_bottom, ci_left, ci_right, ci_top]
for j in range(i + 1, len(cnts)):
cj = cnts[j]
cj_left = tuple(cj[cj[:, :, 0].argmin()][0])
cj_right = tuple(cj[cj[:, :, 0].argmax()][0])
cj_top = tuple(cj[cj[:, :, 1].argmin()][0])
cj_bottom = tuple(cj[cj[:, :, 1].argmax()][0])
cj_list = [cj_bottom, cj_left, cj_right, cj_top]
for pt1 in ci_list:
for pt2 in cj_list:
dist = int(np.linalg.norm(np.array(pt1) - np.array(pt2))) #dist = sqrt( (x2 - x1)**2 + (y2 - y1)**2 )
if dist < min_dist:
min_dist = dist
cl = []
cl.append([pt1, pt2, min_dist])
if len(cl) > 0:
cv2.line(erode, cl[0][0], cl[0][1], 255, thickness = 2)
最终结果:
我谈到了极值点,但它们位于何处?以下代码段显示:
# visualize extreme points for each contour
for c in cnts:
left = tuple(c[c[:, :, 0].argmin()][0])
right = tuple(c[c[:, :, 0].argmax()][0])
top = tuple(c[c[:, :, 1].argmin()][0])
bottom = tuple(c[c[:, :, 1].argmax()][0])
# Draw dots onto image
#cv2.drawContours(img1, [c], -1, (36, 255, 12), 2)
cv2.circle(img1, left, 2, (0, 50, 255), -1)
cv2.circle(img1, right, 2, (0, 255, 255), -1)
cv2.circle(img1, top, 2, (255, 50, 0), -1)
cv2.circle(img1, bottom, 2, (255, 255, 0), -1)
上面显示的极值点是从图像erode
.