检查顶点在相机视图中是不是可见并渲染或被遮挡

Posted

技术标签:

【中文标题】检查顶点在相机视图中是不是可见并渲染或被遮挡【英文标题】:Checking if vertex is visible in camera view and render or occluded检查顶点在相机视图中是否可见并渲染或被遮挡 【发布时间】:2020-04-17 00:30:51 【问题描述】:

我正在处理一项机器学习任务,我正在尝试使用 Blender 生成合成图像作为神经网络的训练数据集。为此,我必须在渲染图像中找到对象的边界框。

到目前为止,我的代码很大程度上基于建议的in this thread,但这并没有考虑顶点是否可见或被另一个对象遮挡。所需的结果确实与here 解释的完全相同。我已经尝试过那里给出的建议,但它不起作用。我不明白是因为我给 ray_cast 函数提供了错误的输入(因为 bpy API 真的很糟糕),或者仅仅是因为函数的性能很差,正如我在其他地方读到的那样。我现在的代码是:

import bpy
import numpy as np

def boundingbox(scene, camera, obj, limit = 0.3):
    #  Get the inverse transformation matrix.
    matrix = camera.matrix_world.normalized().inverted()
    #  Create a new mesh data block, using the inverse transform matrix to undo any transformations.
    dg = bpy.context.evaluated_depsgraph_get()
    #    eval_obj = bpy.context.object.evaluated_get(dg)
    eval_obj = obj.evaluated_get(dg)
    mesh = eval_obj.to_mesh()
    mesh.transform(obj.matrix_world)
    mesh.transform(matrix)

    #  Get the world coordinates for the camera frame bounding box, before any transformations.
    frame = [-v for v in camera.data.view_frame(scene=scene)[:3]]
    origin = camera.location
    lx = []
    ly = []

    for v in mesh.vertices:
        co_local = v.co
        z = -co_local.z
        direction =  (co_local - origin)


        result = scene.ray_cast(view_layer=bpy.context.window.view_layer, origin=origin,
                                      direction= direction) # interested only in the first return value
        intersection = result[0]
        met_obj = result[4]
        if intersection:
            if met_obj.type == 'CAMERA':
                intersection = False


        if z <= 0.0 or (intersection == True and (result[1] - co_local).length > limit):
            #  Vertex is behind the camera or another object; ignore it.
            continue
        else:
            # Perspective division
            frame = [(v / (v.z / z)) for v in frame]

        min_x, max_x = frame[1].x, frame[2].x
        min_y, max_y = frame[0].y, frame[1].y

        x = (co_local.x - min_x) / (max_x - min_x)
        y = (co_local.y - min_y) / (max_y - min_y)

        lx.append(x)
        ly.append(y)

    eval_obj.to_mesh_clear()

    #  Image is not in view if all the mesh verts were ignored
    if not lx or not ly:
        return None

    min_x = np.clip(min(lx), 0.0, 1.0)
    min_y = np.clip(min(ly), 0.0, 1.0)
    max_x = np.clip(max(lx), 0.0, 1.0)
    max_y = np.clip(max(ly), 0.0, 1.0)

    #  Image is not in view if both bounding points exist on the same side
    if min_x == max_x or min_y == max_y:
        return None

    # Figure out the rendered image size
    render = scene.render
    fac = render.resolution_percentage * 0.01
    dim_x = render.resolution_x * fac
    dim_y = render.resolution_y * fac

    # return box in the form (top left x, top left y),(width, height)
    return (
        (round(min_x * dim_x),  # X
         round(dim_y - max_y * dim_y)),  # Y
        (round((max_x - min_x) * dim_x),  # Width
         round((max_y - min_y) * dim_y))  # Height
    )

我还尝试将光线从顶点投射到相机位置(而不是相反)并使用here 解释的小立方体解决方法,但无济于事。有人可以帮我弄清楚如何正确地做到这一点或提出其他策略吗?

【问题讨论】:

你有没有运气弄明白? 【参考方案1】:

我必须解决一个非常相似的问题

这是我使用的代码

def BoundingBoxFinal(obj,cam):
from bpy_extras.object_utils import world_to_camera_view
scene = bpy.context.scene
# needed to rescale 2d coordinates
render = scene.render
render_scale = scene.render.resolution_percentage / 100
res_x = render.resolution_x *render_scale
res_y = render.resolution_y *render_scale
# use generator expressions () or list comprehensions []
mat = obj.matrix_world
verts = [vert.co for vert in obj.data.vertices]
for i in range(len(verts)):
    verts[i] = obj.matrix_world @ verts[i]


coords_2d = [world_to_camera_view(scene, cam, coord) for coord in verts]

# 2d data printout:
rnd = lambda i: round(i)


X_max = max(coords_2d[0])
Y_max = max(coords_2d[1])
X_min = min(coords_2d[0])
Y_min = min(coords_2d[1])

verts_2d =[]
for x, y, distance_to_lens in coords_2d:
    verts_2d.append(tuple((rnd(res_x*x), rnd(res_y-res_y*y))))

Y_max = max(verts_2d, key = lambda i : i[1])[1]
X_max = max(verts_2d, key = lambda i : i[0])[0]
Y_min = min(verts_2d, key = lambda i : i[1])[1]
X_min = min(verts_2d, key = lambda i : i[0])[0]

verts_2d.clear()

return(
    X_min,
    Y_min,
    X_max,
    Y_max,
    obj.data.name.split('.')[0]
)

【讨论】:

【参考方案2】:

我试图在场景的渲染图像中找到对象的遮挡级别。 我所做的是,我创建了一种大小与渲染分辨率相同的地图(简单的二维数组)。然后我做了如下...

for each object in the scene:
    for each vertex of the object:
        (x', y', z') = convert the vertex from local(obj.data.vertices[i].co) to world view
        (x, y, z) = convert the world view vertex(x', y', ') to the 2d camera view
        # this x, y is the 2d coordinates and that z is the distance of the point from camera
        update the 2d array with the id(corresponding to the object closer to the camera)

最后你可以检查顶点(对象obj的一部分)是否可见,你需要做的就是你需要在最终渲染图像中投影那个顶点,比如说(x, y)。现在我们只需要检查该 map/2D 数组是否在 index(x, y) 处具有 obj 的 id。如果是,则意味着在渲染图像中坐标 (x, y) 处的 obj 顶点是可见的,如果不可见,则在 (x, y) 处,map/2d 数组具有其他对象的 id。在这种情况下,可以得出结论,坐标 (x, y) 处的渲染图像中对象obj 的顶点被场景中的某个其他对象覆盖(该特定顶点和相机之间存在另一个对象)。

这只是对数字的巧妙处理,你会得到你想要的。 如果您需要更多解释/代码,请在 cmets 中告诉我。 如果你们中的任何人发现这种方法有什么问题,也请告诉我。 您的 cmets 将不胜感激

【讨论】:

以上是关于检查顶点在相机视图中是不是可见并渲染或被遮挡的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL遮挡查询

在错误位置渲染的顶点(OpenGl)

检查 ARReferenceImage 在相机视图中是不是不再可见

使用矩阵反转顶点缠绕顺序

depthmap怎么算智能度

在c中的3d渲染:显示故障