【问题标题】:How to use np.where in another np.where (conext: ray tracing)如何在另一个 np.where 中使用 np.where (conext: ray tracking)
【发布时间】:2019-10-03 05:59:01
【问题描述】:

问题是:如何在同一个语句中使用两个np.where,像这样(过于简单化):

np.where((ndarr1==ndarr2),np.where((ndarr1+ndarr2==ndarr3),True,False),False)

如果没有达到第一个条件语句,则避免计算第二个条件语句。

我的第一个目标是在三角形中找到一条射线的交点,如果有的话。这个问题可以通过这个算法解决(在stackoverflow上找到):

def intersect_line_triangle(q1,q2,p1,p2,p3):
    def signed_tetra_volume(a,b,c,d):
        return np.sign(np.dot(np.cross(b-a,c-a),d-a)/6.0)

    s1 = signed_tetra_volume(q1,p1,p2,p3)
    s2 = signed_tetra_volume(q2,p1,p2,p3)

    if s1 != s2:
        s3 = signed_tetra_volume(q1,q2,p1,p2)
        s4 = signed_tetra_volume(q1,q2,p2,p3)
        s5 = signed_tetra_volume(q1,q2,p3,p1)
        if s3 == s4 and s4 == s5:
           n = np.cross(p2-p1,p3-p1)
           t = np.dot(p1-q1,n) / np.dot(q2-q1,n)
           return q1 + t * (q2-q1)
    return None

这里有两个条件语句:

  1. s1!=s2
  2. s3==s4 & s4==s5

现在因为我有 >20k 的三角形要检查,我想同时在所有三角形上应用这个函数。

第一个解决方案是:

s1 = vol(r0,tri[:,0,:],tri[:,1,:],tri[:,2,:])
s2 = vol(r1,tri[:,0,:],tri[:,1,:],tri[:,2,:])

s3 = vol(r1,r2,tri[:,0,:],tri[:,1,:])
s4 = vol(r1,r2,tri[:,1,:],tri[:,2,:])
s5 = vol(r1,r2,tri[:,2,:],tri[:,0,:])

np.where((s1!=s2) & (s3+s4==s4+s5),intersect(),False)

其中 s1,s2,s3,s4,s5 是包含每个三角形的值 S 的数组。问题是,这意味着我必须为所有三角形计算 s3、s4 和 s5。

现在理想的是仅当语句 1 为真时才计算语句 2(和 s3、s4、s5),如下所示:

check= np.where((s1!=s2),np.where((compute(s3)==compute(s4)) & (compute(s4)==compute(s5), compute(intersection),False),False)

(为了简化解释,我只是说“计算”而不是整个计算过程。这里,“计算”只对适当的三角形进行)。

现在这个选项当然不起作用(并且计算 s4 两次),但我很乐意就类似的过程提出一些建议

【问题讨论】:

  • 使用掩码数组。它们允许您计算条件,然后仅在条件适用的情况下计算其他内容
  • 条件 1 very 不是不太可能失败吗?这意味着即使您能够短路电路,您也只能保存对条件 2 的一小部分评估?
  • 您好,感谢您的快速解答!我会尝试第一个选项,然后再找你。关于第二条评论,它实际上在“简化”网格中发生了很多。在检查条件一之前和之后的长度时,我从 15k 到 9k 个三角形。
  • @RolandSireyjol 啊,我明白了,我被你的函数名 signed_tetra_volume 愚弄了,我相信这不是有符号四面体体积,而是有符号四面体体积的符号,也就是方向。
  • 是的,这是另一个帖子的功能,很抱歉造成混淆。顺便说一句,因为我们只使用符号,除以 6 是没有用的。在另一点上,请注意,此函数会为某些特定三角形返回 0(可能当所有 3 个点都在同一条线上时,我正在调查它)。因此,像 s3=s4 或 s4=s5 这样的条件不成立,尽管光线穿过三角形)。如果您想了解有关此的更多详细信息,我可以提供一些信息并在找到后更新修复程序。

标签: python-3.x numpy optimization geometry


【解决方案1】:

以下是我使用掩码数组来回答这个问题的方法:

    loTrue= np.where((s1!=s2),False,True)
    s3=ma.masked_array(np.sign(dot(np.cross(r0r1, r0t0), r0t1)),mask=loTrue)
    s4=ma.masked_array(np.sign(dot(np.cross(r0r1, r0t1), r0t2)),mask=loTrue)
    s5=ma.masked_array(np.sign(dot(np.cross(r0r1, r0t2), r0t0)),mask=loTrue)
    loTrue= ma.masked_array(np.where((abs(s3-s4)<1e-4) & ( abs(s5-s4)<1e-4),True,False),mask=loTrue)

    #also works when computing s3,s4 and s5 inside loTrue, like this:        
    loTrue= np.where((s1!=s2),False,True)
    loTrue= ma.masked_array(np.where(
            (abs(np.sign(dot(np.cross(r0r1, r0t0), r0t1))-np.sign(dot(np.cross(r0r1, r0t1), r0t2)))<1e-4) &
            (abs(np.sign(dot(np.cross(r0r1, r0t2), r0t0))-np.sign(dot(np.cross(r0r1, r0t1), r0t2)))<1e-4),True,False)
            ,mask=loTrue)

请注意,相同的过程,当不使用这种方法时,是这样完成的:

    s3= np.sign(dot(np.cross(r0r1, r0t0), r0t1)  /6.0)
    s4= np.sign(dot(np.cross(r0r1, r0t1), r0t2)  /6.0)
    s5= np.sign(dot(np.cross(r0r1, r0t2), r0t0)  /6.0)
    loTrue= np.where((s1!=s2) & (abs(s3-s4)<1e-4) & ( abs(s5-s4)<1e-4) ,True,False)

两者都给出了相同的结果,但是,当仅在此过程中循环 10k 次迭代时,不使用掩码数组会更快! (不使用掩码数组时为 26 秒,使用掩码数组时为 31 秒,仅在一行中使用掩码数组时为 33 秒(不分别计算 s3、s4 和 s5,或之前计算 s4)。

结论:这里解决了使用嵌套数组的问题(注意,掩码表示不会计算它的位置,因此在验证条件时,首先必须将 loTri 设置为 False (0))。但是,在这种情况下,它并没有更快。

【讨论】:

    【解决方案2】:

    我可以从短路中获得小幅加速,但我不相信这值得额外的管理员。

    full computation 4.463818839867599 ms per iteration (one ray, 20,000 triangles)
    short ciruciting 3.0060838296776637 ms per iteration (one ray, 20,000 triangles)
    

    代码:

    import numpy as np
    
    def ilt_cut(q1,q2,p1,p2,p3):
        qm = (q1+q2)/2
        qd = qm-q2
        p12 = p1-p2
        aux = np.cross(qd,q2-p2)
        s3 = np.einsum("ij,ij->i",aux,p12)
        s4 = np.einsum("ij,ij->i",aux,p2-p3)
        ge = (s3>=0)&(s4>=0)
        le = (s3<=0)&(s4<=0)
        keep = np.flatnonzero(ge|le)
        aux = p1[keep]
        qpm1 = qm-aux
        p31 = p3[keep]-aux
        s5 = np.einsum("ij,ij->i",np.cross(qpm1,p31),qd)
        ge = ge[keep]&(s5>=0)
        le = le[keep]&(s5<=0)
        flt = np.flatnonzero(ge|le)
        keep = keep[flt]
        n = np.cross(p31[flt], p12[keep])
        s12 = np.einsum("ij,ij->i",n,qpm1[flt])
        flt = np.abs(s12) <= np.abs(s3[keep]+s4[keep]+s5[flt])
        return keep[flt],qm-(s12[flt]/np.einsum("ij,ij->i",qd,n[flt]))[:,None]*qd
    
    def ilt_full(q1,q2,p1,p2,p3):
        qm = (q1+q2)/2
        qd = qm-q2
        p12 = p1-p2
        qpm1 = qm-p1
        p31 = p3-p1
        aux = np.cross(qd,q2-p2)
        s3 = np.einsum("ij,ij->i",aux,p12)
        s4 = np.einsum("ij,ij->i",aux,p2-p3)
        s5 = np.einsum("ij,ij->i",np.cross(qpm1,p31),qd)
        n = np.cross(p31, p12)
        s12 = np.einsum("ij,ij->i",n,qpm1)
        ge = (s3>=0)&(s4>=0)&(s5>=0)
        le = (s3<=0)&(s4<=0)&(s5<=0)
        keep = np.flatnonzero((np.abs(s12) <= np.abs(s3+s4+s5)) & (ge|le))
        return keep,qm-(s12[keep]/np.einsum("ij,ij->i",qd,n[keep]))[:,None]*qd
    
    tri = np.random.uniform(1, 10, (20_000, 3, 3))
    p0, p1 = np.random.uniform(1, 10, (2, 3))
    
    from timeit import timeit
    A,B,C = tri.transpose(1,0,2)
    print('full computation', timeit(lambda: ilt_full(p0[None], p1[None], A, B, C), number=100)*10, 'ms per iteration (one ray, 20,000 triangles)')
    print('short ciruciting', timeit(lambda: ilt_cut(p0[None], p1[None], A, B, C), number=100)*10, 'ms per iteration (one ray, 20,000 triangles)')
    

    请注意,我对算法进行了一些尝试,因此这可能并非在每种边缘情况下都给出与您相同的结果。

    我改变了什么:

    • 我内联了 tetra 卷,这样可以节省一些重复的子计算
    • 我用射线的中点M 替换其中一个射线末端。这样可以节省计算一个四边形体积(s1s2),因为可以通过将四边形 ABCM 的体积与 s3s4、@987654329 的总和进行比较来检查光线是否穿过三角形 ABC 平面@(如果符号相同)。

    【讨论】:

    • 这种加速可能非常有用,特别是因为我正在为包含 3.5k 个元素的数据集执行此过程,每个元素有 20k 个面,每个元素有 11k 条光线。节省的每一点时间都会产生巨大的影响^^。感谢更新,我试试看。
    猜你喜欢
    • 2018-03-21
    • 1970-01-01
    • 2019-03-10
    • 1970-01-01
    • 2021-09-17
    • 2019-08-11
    • 1970-01-01
    • 2021-07-16
    • 1970-01-01
    相关资源
    最近更新 更多