【问题标题】:How to implement a 3D boolean operation in MATLAB to make an intersection like in Blender (or any other 3D software)?如何在 MATLAB 中实现 3D 布尔运算以在 Blender(或任何其他 3D 软件)中进行交集?
【发布时间】:2017-11-06 17:32:42
【问题描述】:

我正在从 3 个单独的二进制图像创建 3D 图像,这些图像是用 3 个相机拍摄的。我有相应的校准并且知道设置(见下文)。由于图像预处理主要在 MATLAB 中完成,我想在那里实现所有内容。

我的代码目前的想法是根据相机校准挤出二维二进制图像。典型的二值图像如下所示:

在 MATLAB 中挤出的图像如下所示:

通过所有 3 个摄像头和分箱算法,我可以创建最终的 3D 形状。到目前为止,这工作正常,但需要很长时间来计算,因为我需要创建大量的挤压步骤才能获得良好的表面。

我现在正在考虑通过重新创建我在 Blender 等 3D 建模软件中执行的过程来加快这一进程。在那里,我还挤压二值图像的轮廓,并通过为轮廓创建样条线轻松创建交点,挤压它们并使用布尔运算符。这是一个带有 2 个挤压图像的 Blender 示例:

我不知道如何在 MATLAB 中实现这样的东西。我想在挤压“管”的顶端和底端创建我的二元轮廓的两个实例,然后定义各个点之间的面,然后创建一个交点。点创建没有问题,但面定义和交点(布尔运算符)是。有人知道如何实现吗?

【问题讨论】:

    标签: matlab 3d boolean blender intersection


    【解决方案1】:

    这在 MATLAB 中可能不是一件容易的事,但它是可能的。我将在此处概述一组步骤,以两个相交的圆柱体为例...

    创建四面体网格:

    第一步是为您的拉伸创建一个四面体网格。如果您要挤出的 2D 二进制图像是凸的并且没有孔,您可以使用 delaunayTriangulation 函数来执行此操作:

    DT = delaunayTriangulation(P);
    

    这里,P 包含挤压的“端盖”的坐标点(即管两端的面)。但是,在生成四面体网格时,delaunayTriangulation 不允许您指定 constrained edges,因此它最终会填充挤出中的孔或凹面。 可能在其他工具箱中有一些更好的网格生成替代品,例如 Partial Differential Equations Toolbox,但我无法访问它们,也无法谈论它们的适用性。

    如果自动网格生成选项不起作用,您必须自己构建四面体网格并将该数据传递给 triangulation。这可能很棘手,但我将向您展示如何为圆柱体执行此操作的一些步骤,这可能会帮助您找出更多涉及的形状。下面,我们构建了一组坐标点P1 和一个M-by-4 矩阵T1,其中每一行包含对定义一个四面体的P1 行的索引:

    % Create circle coordinates for the end caps:
    N = 21;
    theta = linspace(0, 2*pi, N).';
    x = sin(theta(1:(end-1)));
    y = cos(theta(1:(end-1)))+0.5;
    z = ones(N-1, 1);
    
    % Build tetrahedrons for first cylinder, aligned along the z axis:
    P1 = [0 0.5 -1; ...  % Center point of bottom face
          x y -z; ...    % Edge coordinates of bottom face
          0 0.5 1; ...   % Center point of top face
          x y z];        % Edge coordinates of top face
    cBottom = ones(N-1, 1);      % Row indices for bottom center coordinate
    cEdgeBottom1 = (2:N).';      % Row indices for bottom edge coordinates
    cEdgeBottom2 = [3:N 2].';    % Shifted row indices for bottom edge coordinates
    cTop = cBottom+N;            % Row indices for top center coordinate
    cEdgeTop1 = cEdgeBottom1+N;  % Row indices for top edge coordinates
    cEdgeTop2 = cEdgeBottom2+N;  % Shifted row indices for top edge coordinates
    % There are 3 tetrahedrons per radial slice of the cylinder: one that includes the
    % bottom face and half of the side face (all generated simultaneously by the first row
    % below), one that includes the other half of the side face (second row below), and one
    % that includes the top face (third row below):
    T1 = [cEdgeBottom1 cEdgeBottom2 cEdgeTop1 cBottom; ...
          cEdgeBottom2 cEdgeTop1 cEdgeTop2 cBottom; ...
          cEdgeTop1 cEdgeTop2 cTop cBottom];
    TR1 = triangulation(T1, P1);
    

    为了更好地可视化圆柱体是如何被分成四面体的,下面是一个分解视图的动画:

    现在我们可以创建第二个圆柱体,偏移并旋转,使其与 x 轴对齐并与第一个相交:

    % Build tetrahedrons for second cylinder:
    P2 = [P1(:, 3) -P1(:, 2) P1(:, 1)];
    T2 = T1;
    TR2 = triangulation(T2, P2);
    
    % Plot cylinders:
    tetramesh(TR1, 'FaceColor', 'r', 'FaceAlpha', 0.6);
    hold on;
    tetramesh(TR2, 'FaceColor', 'g', 'FaceAlpha', 0.6);
    axis equal;
    xlabel('x');
    ylabel('y');
    zlabel('z');
    

    这是visualize them的情节:

    寻找交叉区域:

    一旦我们有了体积的四面体表示,我们就可以generate a grid of points 覆盖相交区域并使用pointLocation 函数来确定两个圆柱体内的点:

    nGrid = 101;
    [X, Y, Z] = meshgrid(linspace(-1, 1, nGrid));
    QP = [X(:) Y(:) Z(:)];
    indexIntersect = (~isnan(pointLocation(TR1, QP))) & ...
                     (~isnan(pointLocation(TR2, QP)));
    mask = double(reshape(indexIntersect, [nGrid nGrid nGrid]));
    

    我们现在有体积数据mask,其中包含零和一,它们定义了交叉区域。您制作的网格越精细(通过调整nGrid),这将更准确地表示圆柱之间的真实相交区域。

    生成 3D 表面:

    您可能希望根据这些数据创建一个曲面,定义相交区域的边界。有几种方法可以做到这一点。一种是使用isosurface 生成表面,然后您可以使用featureEdges 对其进行可视化。例如:

    [F, V] = isosurface(mask, 0.5);
    TR = triangulation(F, V);
    FE = featureEdges(TR, pi/6).';
    xV = V(:, 1);
    yV = V(:, 2);
    zV = V(:, 3);
    trisurf(TR, 'FaceColor', 'c', 'FaceAlpha', 0.8, 'EdgeColor', 'none');
    axis equal;
    xlabel('x');
    ylabel('y');
    zlabel('z');
    hold on;
    plot3(xV(FE), yV(FE), zV(FE), 'k');
    

    以及由此产生的情节:

    另一种选择是创建一个“体素化”的类似 Minecraft 的表面,正如我所说明的 here

    [X, Y, Z, C] = build_voxels(permute(mask, [2 1 3]));
    hSurface = patch(X, Y, Z, 'c', ...
                     'AmbientStrength', 0.5, ...
                     'BackFaceLighting', 'unlit', ...
                     'EdgeColor', 'none', ...
                     'FaceLighting', 'flat');
    axis equal;
    view(-37.5, 30);
    set(gca, 'XLim', [0 101], 'YLim', [25 75], 'ZLim', [0 102]);
    xlabel('x');
    ylabel('y');
    zlabel('z');
    grid on;
    light('Position', get(gca, 'CameraPosition'), 'Style', 'local');
    

    以及由此产生的情节:

    【讨论】:

    • 非常感谢新手提供这个详细的方法。我开始为我的数据实现这一点,如果我理解正确的话,会有一些问题。 1. 对于三角剖分部分我需要手动定义网格的顶点(点)和边吗?所以我可以对我的“shapetube”进行三角测量? 2. 在您为圆柱示例描述边缘创建的地方,您会得到每个切片 3 个四面体。我理解有顶面和底面的两者。那么第三个是否包括底面和顶面?
    • @Katl: 1) 是的,您可能需要手动完成,因为用于生成三角形网格的典型工具 (delaunayTriangulation) 会填充凹处(例如穿过管子的孔) .在某些工具箱中可能还有其他用于网格生成的选项,我将在答案中添加这些选项。 2)抱歉,如果我如何分解圆柱体的描述不完全清楚。我很快会在答案中添加一个分解图,希望能把它弄清楚。
    • 非常直观和详细的答案。是否可以在不为 3D 交叉蒙版网格化的情况下执行此操作?由于面相交,因此这些面已经包含相交体积。不知道如何寻找相交的面孔。我试图运行一个对于网格来说太大的模拟。 stackoverflow.com/questions/50219554/…
    • @Leo:这将非常复杂。一般步骤是:1)遍历所有面; 2) 遍历 最近的 面到每个面(不是所有其他面,因为这需要一段时间); 3)找到每个面的平面之间的交线; 4)将该线剪辑到每个面的限制; 5)用那条线切断每个面在另一个体积之外的部分; 6) 移除完全在另一个体积之外的所有剩余面; 7)所有剩余的面给你一个完整的交叉表面。
    猜你喜欢
    • 2017-08-25
    • 1970-01-01
    • 1970-01-01
    • 2017-11-08
    • 1970-01-01
    • 2012-02-15
    • 2017-03-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多