【问题标题】:How to automate editing of x,y-path如何自动编辑 x,y 路径
【发布时间】:2021-04-16 00:20:38
【问题描述】:

这是一个非常抽象的问题,不一定与语言/模块相关。抱歉没有具体说明。

我正在使用 OpenCV 在 Python 中制作一个对象跟踪程序(用于我工作的生物实验室)。到目前为止,我正在将显微镜视频转换成帧,然后检测周围游动的生物体。

视频中有很多这样的虫子:

我已经成功地找到了可以勾勒出 x,y 路径的方法。成功后,它看起来像这样(x,y 路径是绿色的边界):

但是,我经常得到看起来更像这样的 x,y 路径:

如您所见,x,y 路径包含蠕虫的大部分但不是所有边缘。我将把这条不正确的路径称为“边缘掩码”。我需要的是第二张图片。我可以向任何人解释如何识别这个边缘遮罩(“极端”凹度,在自身上循环),但我正在努力寻找一种能够在第三张图像中采用 x、y 路径的算法和在第二个返回接近的东西。有什么想法吗?

在 python 中,构成上面绿色掩码的 x,y 路径在我的程序中是这样存储的:

lst = [x0, y0, x1, y1, x2, y2, ..., xn, yn]

... (xn, yn) 点被视为也环绕并与 (x0, y0) 点连接。我正在使用这种形式,但如果需要,可以很容易地将其转换为元组列表。

我想象的是某种算法,它能够识别路径的其余部分完全“包含”(可以这么说)的路径部分,也就是 x,y 点在面罩内侧(如下图红色部分)。如果我能找到一种算法来识别红色路径上的点并删除它们,那么该路径将正确地包含/掩盖蠕虫。

我看到的一个问题是:仅靠凹度对此无济于事。虫子本身是弯曲的,它的右边缘完全是凹的,所以我不能单独用凹度来识别上图中的红线。

我会附上代码,但我拥有的代码不是问题,它正在寻找一种算法来调整我正在使用的代码的输出。我相信我的问题不应该要求也不能提供最低限度的可重现示例。我认为这就是为什么他们在提到使用 mre 时说“适当时……”。谢谢!


发布此内容后,我一直在思考,我想我知道该怎么做,我想听听人们的想法:

对于路径上的每个 x,y 点,我可以检查均匀分布的光线,以大致了解有多少百分比的光线与路径的另一部分相交。上面第四张图片中红色边缘上的点的“逃逸”射线的百分比比外面的要低得多,甚至低于蠕虫右侧凹面的那些点。我可以删除逃逸光线百分比较低的点(使用任意阈值)并且路径会很好。想法?


原始数据示例:

lst = [17, 297, 17, 303, 15, 305, 15, 309, 16, 310, 17, 310, 20, 313, 21, 313, 32, 324, 32, 326, 33, 326, 35, 328, 38,
       328, 39, 327, 39, 323, 38, 322, 37, 322, 36, 321, 36, 320, 35, 319, 35, 313, 36, 312, 42, 312, 43, 313, 44, 313,
       45, 314, 46, 314, 47, 315, 50, 315, 51, 316, 52, 316, 53, 317, 54, 317, 55, 318, 56, 318, 57, 319, 58, 319, 59,
       320, 60, 320, 62, 322, 63, 322, 67, 326, 70, 326, 74, 330, 75, 330, 78, 333, 79, 333, 83, 337, 83, 338, 84, 339,
       84, 340, 85, 340, 92, 347, 93, 347, 95, 349, 96, 349, 99, 352, 100, 352, 106, 358, 106, 359, 110, 363, 110, 364,
       112, 366, 113, 366, 116, 369, 117, 369, 119, 371, 119, 378, 118, 379, 112, 379, 110, 377, 109, 377, 105, 373,
       104, 373, 93, 362, 92, 362, 88, 358, 88, 357, 87, 357, 81, 351, 80, 351, 77, 348, 76, 348, 67, 339, 66, 339, 61,
       334, 60, 334, 58, 332, 57, 332, 56, 331, 55, 331, 54, 330, 48, 330, 48, 333, 49, 333, 50, 334, 51, 334, 52, 335,
       53, 335, 61, 343, 62, 343, 69, 350, 70, 350, 73, 353, 74, 353, 75, 354, 75, 355, 76, 355, 82, 361, 83, 361, 85,
       363, 85, 364, 86, 365, 87, 365, 99, 377, 100, 377, 104, 381, 105, 381, 107, 383, 108, 383, 109, 384, 111, 384,
       113, 386, 114, 386, 116, 388, 117, 388, 118, 389, 120, 389, 121, 390, 121, 392, 122, 393, 123, 393, 124, 394,
       125, 394, 126, 395, 127, 395, 128, 396, 128, 397, 129, 398, 130, 398, 133, 401, 142, 401, 143, 400, 143, 399,
       144, 398, 144, 394, 143, 393, 143, 389, 140, 386, 140, 385, 138, 383, 137, 383, 132, 378, 132, 377, 131, 376,
       130, 376, 128, 374, 128, 373, 127, 372, 127, 371, 119, 363, 118, 363, 114, 359, 114, 358, 113, 358, 110, 355,
       110, 353, 106, 349, 105, 349, 103, 347, 102, 347, 99, 344, 98, 344, 90, 336, 89, 336, 88, 335, 88, 334, 87, 333,
       87, 332, 85, 330, 84, 330, 81, 327, 80, 327, 79, 326, 78, 326, 76, 324, 75, 324, 73, 322, 72, 322, 68, 318, 67,
       318, 65, 316, 64, 316, 63, 315, 62, 315, 61, 314, 60, 314, 59, 313, 58, 313, 57, 312, 56, 312, 55, 311, 52, 311,
       51, 310, 50, 310, 49, 309, 48, 309, 47, 308, 46, 308, 45, 307, 44, 307, 43, 306, 42, 306, 41, 305, 40, 305, 39,
       304, 32, 304, 32, 308, 31, 309, 25, 309, 24, 308, 24, 302, 25, 301, 28, 301, 28, 299, 27, 299, 26, 298, 22, 298,
       21, 297]

【问题讨论】:

  • 你能告诉我们你用来提取这些“x-y”路径的代码吗?我不知道最终输出是坐标列表还是掩码。如果是蒙版,请尝试对图像执行一些形态学,例如使用适当形状的蒙版关闭。一旦您向我们提供更多详细信息,我可以提供针对您的预期输入和输出量身定制的解决方案。此外,根据 Stack Overflow 指南,您应该提供minimal reproducible example。请遵守这些准则。
  • @rayryeng 我已经调整了我上面的问题,希望我已经很好地解释了自己。就示例代码而言,如果我在这里提出问题查找算法是错误的,那很好,我可以放手。谢谢!
  • 刚刚又添加了一点,这次是在水平线下方。我认为这是我正在寻求的帮助的一个很好的例子。
  • 真正有用的是查看您如何识别原始路径。那里可能有很大的改进空间。
  • @MilesRobertson 关于算法的问题绝对是这里的主题。例如,看看我几年前提出的一个问题。这是一种算法,可以找到一组点,前提是它们沿着相同的方向轴。 stackoverflow.com/questions/29550785/…。只要您向我们展示您的所作所为以及您遇到困难的地方,这些绝对是主题。我也感谢您发布的答案。我现在最喜欢这个了。非常感谢您对我们社区的贡献!

标签: python opencv


【解决方案1】:

我继续编写了我的想法,到目前为止它似乎运行良好。借自this page关于查找两条线段是否相交。

def ccw(A, B, C):
    return (C[1]-A[1]) * (B[0]-A[0]) > (B[1]-A[1]) * (C[0]-A[0])


# Return true if line segments AB and CD intersect
def intersect(A, B, C, D):
    return ccw(A, C, D) != ccw(B, C, D) and ccw(A, B, C) != ccw(A, B, D)


def rayEscape(point, degree, path, radius, indx=-1):
    """Returns True if a ray going from 'point' at an angle of 'degree' (defined like in polar coordinates)
    never intercepts with path. False otherwise."""
    ray_end_point = (point[0] + radius * cos(degree), point[1] + radius * sin(degree))
    for i in range(0, len(path), 2):
        j = (i + 2) % len(path)
        seg_point_0 = path[i:i+2]
        seg_point_1 = path[j:j+2]
        if indx != i and indx != j and intersect(point, ray_end_point, seg_point_0, seg_point_1):
            return False
    return True


def deleteInsidePoints(path, num_rays, cutoff=0.0):
    """For each point, calculates the proportion of rays that go from the point and never hit any
    part of the path again. If the proportion is strictly greater than the cutoff, the point is kept in the return list.
    Otherwise, it's ignored."""
    final = []
    max_len = ((min(path[::2]) - max(path[::2])) ** 2 + (min(path[1::2]) - max(path[1::2])) ** 2) ** 0.5
    for i in range(0, len(path), 2):
        point = path[i:i+2]
        num_escape = 0
        for j in range(num_rays):
            num_escape += int(rayEscape(point, 2 * pi * j / num_rays, path, max_len, i))
        if num_escape / num_rays > cutoff:
            final.extend(point)
    return final

lst2 = deleteInsidePoints(lst, 25, 0.3)

这是两个现实生活中的应用:

在第二个中,你可以看到它在左侧删除并留下一条直线有点过分,但总体来说还是不错的。

旁注:我的代码在我第一次发布时略有编辑。我忘了让程序检查光线是否与路径的段相交包括光线来自的点。现在,rayEscape 函数中的“indx”变量允许跳过包含兴趣点的段。

【讨论】:

  • 谢谢!怀疑其他人是否会需要这个,但我想我不妨把它贴出来。
  • 我认为这些信息总是有用的。有人可能有类似的问题,尽管在不同的环境中,你的回答可能会让他们更接近他们正在寻找的解决方案。毕竟,我们正在努力在这里建立一个知识库!感谢四位您的贡献。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-08-12
  • 2012-11-21
  • 1970-01-01
  • 2010-11-25
  • 1970-01-01
  • 2011-02-14
  • 2017-05-25
相关资源
最近更新 更多