从一点开始的多行中保留一行

2024-09-24 08:34:47 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在从事一个使用OpenCV和python的项目,但一直在解决这个小问题

我在列表中存储了许多行的端点坐标。有时出现一种情况,即从一个点检测到多条线。从这些线条中,我想保留最短长度的线条,并消除所有其他线条,这样我的图像将不包含从其中绘制多条线条的点

我的变量存储最初检测到的所有线的信息(两个端点的坐标),如下所示:

var = [[Line1_EndPoint1, Line1_EndPoint2],
       [Line2_EndPoint1, Line2_EndPoint2],
       [Line3_EndPoint1, Line3_EndPoint2],
       [Line4_EndPoint1, Line4_EndPoint2],
       [Line5_EndPoint1, Line5_EndPoint2]]

其中,LineX_endopy(线号“X”,该线的端点“Y”)为[X,Y]类型,其中X和Y是图像中该点的坐标

有人能建议我如何解决这个问题吗

您可以修改行数据的存储方式。如果修改,请解释您的数据结构及其创建方式

此类数据的示例:

[[[551, 752], [541, 730]], 
 [[548, 738], [723, 548]],
 [[285, 682], [226, 676]],
 [[416, 679], [345, 678]],
 [[345, 678], [388, 674]],
 [[249, 679], [226, 676]],
 [[270, 678], [388, 674]],
 [[472, 650], [751, 473]],
 [[751, 473], [716, 561]],
 [[731, 529], [751, 473]]]

Python代码将是可观的


Tags: 数据项目图像列表方式端点opencv线条
2条回答

我认为基于熊猫编写解决方案更容易。 原因是:

  • 我可以使用列名(代码可读性更好)
  • PandasAPI功能更强大,尽管它的工作速度比“pure”Numpy

进行如下工作:

  1. var转换为数据帧:

     lines = pd.DataFrame(var.reshape(10,4), columns=pd.MultiIndex.from_product(
         (['P1', 'P2'], ['x','y'])))
    

    的初始部分为:

         P1        P2     
          x    y    x    y
     0  551  752  541  730
     1  548  738  723  548
     2  285  682  226  676
     3  416  679  345  678
    
  2. 计算每条线长度的平方:

     lines[('', 'lgth')] = (lines[('P1', 'x')] - lines[('P2', 'x')]) ** 2\
         + (lines[('P1', 'y')] - lines[('P2', 'y')]) ** 2
     lines.columns = lines.columns.droplevel()
    

    我故意在长度为的正方形处“停止”,因为它是 足够比较长度(计算根不会改变 比较结果)

    还要注意的是,需要列上的第一级多索引 只是为了更容易地表达感兴趣的列。此外,他们将 不需要,所以我把它扔了

    这次我把行的全部内容放到

          x    y    x    y    lgth
     0  551  752  541  730     584
     1  548  738  723  548   66725
     2  285  682  226  676    3517
     3  416  679  345  678    5042
     4  345  678  388  674    1865
     5  249  679  226  676     538
     6  270  678  388  674   13940
     7  472  650  751  473  109170
     8  751  473  716  561    8969
     9  731  529  751  473    3536
    
  3. 下一步是计算数据帧,其中所有点(开始和结束 每行的末尾)以及(平方)的长度位于相同的列中 对应的行:

     points = pd.concat([lines.iloc[:,[0, 1, 4]],
         lines.iloc[:,[2, 3, 4]]], keys=['P1', 'P2'])\
         .sort_values(['x', 'y', 'lgth']).reset_index(level=1)
    

    现在,我使用iloc来指定列(第一次用于起始点) 第二个是终点)。 为了更容易阅读这个数据帧,我传递了,以包含“origin” 然后我对行进行排序

    内容是:

         level_1    x    y    lgth
     P2        5  226  676     538
     P2        2  226  676    3517
     P1        5  249  679     538
     P1        6  270  678   13940
     P1        2  285  682    3517
     P1        4  345  678    1865
     P2        3  345  678    5042
     P2        4  388  674    1865
     P2        6  388  674   13940
     P1        3  416  679    5042
     P1        7  472  650  109170
     P2        0  541  730     584
     P1        1  548  738   66725
     P1        0  551  752     584
     P2        8  716  561    8969
     P2        1  723  548   66725
     P1        9  731  529    3536
     P2        9  751  473    3536
     P1        8  751  473    8969
     P2        7  751  473  109170
    

    注意,例如,点226676出现两次。第一次发生的时候 直线5和第二直线2(直线var和直线中的指数

  4. 要查找要删除的行的索引,请运行:

     toDrop = points[points.duplicated(subset=['x', 'y'])]\
         .level_1.reset_index(drop=True);
    

    为了更容易理解这段代码是如何工作的,请一步一步地运行它 检查每个步骤的结果

    结果是:

     0    2
     1    3
     2    6
     3    8
     4    7
     Name: level_1, dtype: int64
    

    请注意,上面的左列只是索引(这无关紧要)。 真正的信息在右边的列(值)中

  5. 要显示应保留的行,请运行:

     result = lines.drop(toDrop)
    

    获取:

          x    y    x    y   lgth
     0  551  752  541  730    584
     1  548  738  723  548  66725
     4  345  678  388  674   1865
     5  249  679  226  676    538
     9  731  529  751  473   3536
    

    上述结果不包含,例如:

    • 直线2,当点226时,676出现在直线5
    • 线3,当点345时,678出现在线4

    只有这些行(23)被删除,因为它们是 比第二条提到的两条线都长(参见前面的部分结果)

也许这就足够了,或者如果您需要从 var(原始的Numpy数组),并将结果保存到另一个数组中 变量,运行:

var2 = np.delete(var, toDrop, axis=0)

ANumpy解决方案

与我的第一个答案相同的结果可以通过单独 在Numpy

首先定义两个函数中的一个:

  1. 计算直线长度的平方:

     def sqLgth(line):
         p1, p2 = line
         return (p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2
    
  2. 将向量(1D数组)转换为列数组(a2D数组) 使用单个列:

     def toColumn(tbl):
         return tbl.reshape(-1, 1)
    

两者都将在以后使用

然后进行如下操作:

  1. 获取行数:

     lineNo = var.shape[0]
    
  2. 生成线索引(在数组中lineInd列的内容 (稍后将创建)):

     id = np.repeat(np.arange(lineNo), 2)
    
  3. 生成“原始指标”(1-开始,2-结束),以便于分析 对于任何中间打印输出:

     origin = np.tile(np.array([1, 2]), lineNo)
    
  4. 计算线长度(点中lgth列的内容):

     lgth = np.repeat([ sqLgth(line) for line in var ], 2)
    
  5. 创建包含一些附加数据(连续数据)的点列表 列包含originlineIndxylgth):

     points = np.hstack([toColumn(origin), toColumn(id),
         var.reshape(-1, 2), toColumn(lgth)])
    
  6. 计算要排序的“标准数组”:

     r = np.core.records.fromarrays(points[:, 2:].transpose(),
         names='x, y, lgth')
    
  7. 排序(按xylgth):

     points = points[r.argsort()]
    
  8. 计算点的“反向唯一索引”:

     _, inv = np.unique(points[:,2:4], axis=0, return_inverse=True)
    
  9. inv移动1个位置:

     rInv = np.roll(inv,1)
    

    将在下一步中使用,以获取上一个元素

  10. 生成要删除的行索引列表:

    toDrop = points[[ i for i in range(2 * lineNo)
        if inv[i] == rInv[i] ], 1]
    

    行索引(在数组中)是重复点(元素)的索引 在inv中等于上一个元素)

    列索引(1)-指定lineInd

    整个结果(toDrop)是“拥有”行的索引列表 (包含重复的点)

  11. 生成结果:var从 上一步:

    var2 = np.delete(var, toDrop, axis=0)
    

要打印减少的行列表,可以运行:

for line in var2:
    print(f'{line[0]}, {line[1]}')

结果是:

[551 752], [541 730]
[548 738], [723 548]
[345 678], [388 674]
[249 679], [226 676]
[731 529], [751 473]

要完全理解此代码的工作原理,请执行以下操作:

  • 分别执行每个步骤
  • 打印结果
  • 将其与前面步骤中的打印输出进行比较

有时,即使是一些表达式,单独打印也是有意义的 (部分说明),例如var.reshape(-1, 2)-转换 var(形状为(10,2,2))转换为2D点阵列(每行为 一点)

当然,整个结果与我第一个回答中的结果一样, 但正如你所写,你在熊猫方面几乎没有经验,现在你可以了 比较两种方法并查看熊猫允许执行的情况 更简单、更直观的东西

例如,按某些列排序或查找重复的行。 在熊猫中,这是一个单一指令的问题,具有适当的 参数,而在Numpy中,您必须使用更多指令 并知道各种细节和技巧如何做到同样

相关问题 更多 >