将纹理映射到网格时,Vtk 在节点之间插入不正确的颜色

Vtk inserts incorrect color between nodes when mapping texture to mesh

您好,我正在尝试使用 Mayavi 和 vtk 的 Python 绑定将纹理映射到 3d 网格。我正在可视化 .obj 波前。这个对象是一张脸的 3D 照片。纹理图像是由三张二维照片合成的。

网格中的每个节点在图像中都有一个 (uv) 坐标,它定义了它的颜色。网格的不同区域从图像的不同部分绘制颜色。为了说明这一点,我用这个替换了实际的纹理图像:

并将其映射到网格。

我遇到的问题是鼻子周围的问题。在红色和绿色的边界处有一个蓝色的轮廓。在线框模式下对该区域的仔细检查表明,这不是 uv 映射的问题,而是 vtk 如何在两个节点之间插值颜色的问题。出于某种原因,它在两个节点之间添加了一块蓝色,其中一个是红色,一个是绿色。

这在使用真实纹理进行可视化时会导致严重问题

有没有办法强制vtk选择一个或另一个相邻节点的颜色作为它们之间的颜色?我尝试打开 "edge-clamping",但这并没有达到任何效果。

我使用的代码如下,您可以从这里访问相关文件 https://www.dropbox.com/sh/ipel0avsdiokr10/AADmUn1-qmsB3vX7BZObrASPa?dl=0 但我希望这是一个简单的解决方案。

from numpy import *
from mayavi import mlab
from tvtk.api import tvtk
import os
from vtk.util import numpy_support

def  obj2array(f):
    """function for reading a Wavefront obj"""
    if type(f)==str:
            if os.path.isfile(f)==False:
                    raise ValueError('obj2array: unable to locate file ' + str(f))
            f =open(f)

    vertices = list()
    connectivity = list()
    uv = list()
    vt = list()


    fcount = 0


    for l in f:
                line = l.rstrip('\n')

                data = line.split()
                if len(data)==0:
                        pass
                else:

                        if data[0] == 'v':
                            vertices.append(atleast_2d(array([float(item) for item in data[1:4]])))

                        elif data[0]=='vt':
                            uv.append(atleast_2d(array([float(item) for item in data[1:3]])))


                        elif data[0]=='f':

                            nverts = len(data)-1 # number of vertices comprising each face

                            if fcount == 0:    #on first face establish face format

                                fcount = fcount + 1
                                if data[1].find('/')==-1:  #Case 1
                                    case = 1

                                elif data[1].find('//')==True:
                                    case = 4
                                elif len(data[1].split('/'))==2:
                                    case = 2
                                elif len(data[1].split('/'))==3:
                                    case = 3



                            if case == 1:
                                f = atleast_2d([int(item) for item in data[1:len(data)]])
                                connectivity.append(f)

                            if case == 2:
                                splitdata = [item.split('/') for item in data[1:len(data)]]
                                f = atleast_2d([int(item[0]) for item in splitdata])

                                connectivity.append(f)

                            if case == 3:
                                splitdata = [item.split('/') for item in data[1:len(data)]]
                                f = atleast_2d([int(item[0]) for item in splitdata])
                                connectivity.append(f)


                            if case == 4:
                                splitdata = [item.split('//') for item in data[1:len(data)]]
                                f = atleast_2d([int(item[0]) for item in splitdata])

                                connectivity.append(f)


    vertices = concatenate(vertices, axis = 0)
    if len(uv)==0:
        uv=None
    else:
        uv = concatenate(uv, axis = 0)

    if len(connectivity) !=0:
            try:
                    conarray = concatenate(connectivity, axis=0)
            except ValueError:
                    if triangulate==True:
                            conarray=triangulate_mesh(connectivity,vertices)

                    else:
                            raise ValueError('obj2array: not all faces triangles?')
            if conarray.shape[1]==4:
                    if triangulate==True:
                            conarray=triangulate_mesh(connectivity,vertices)



    return vertices, conarray,uv



# load texture image
texture_img = tvtk.Texture(interpolate = 1,edge_clamp=1)
texture_img.input = tvtk.BMPReader(file_name='HM_1_repose.bmp').output

#load obj
verts, triangles, uv = obj2array('HM_1_repose.obj')

# make 0-indexed
triangles = triangles-1

surf = mlab.triangular_mesh(verts[:,0],verts[:,1],verts[:,2],triangles)

tc=numpy_support.numpy_to_vtk(uv)

pd = surf.mlab_source.dataset._vtk_obj.GetPointData()
pd.SetTCoords(tc)
surf.actor.actor.mapper.scalar_visibility=False
surf.actor.enable_texture = True
surf.actor.actor.texture = texture_img
mlab.show(stop=True)

您可以关闭所有插值(在您的示例中将 interpolate = 1 更改为 interpolate = 0),但是没有办法仅在它会跨子插值的地方关闭插值纹理图像——至少在没有编写自己的片段着色器的情况下。这可能看起来很粗糙。

另一种解决方案是在不属于演员面部的每个位置创建 3 个具有透明纹素的纹理图像。然后每次渲染具有相同纹理坐标但不同图像的相同几何体(即,有 3 个演员,每个角色具有相同的多数据但不同的纹理图像)。

我也 运行 研究了这个确切的问题,发现发生这种情况的原因是因为 VTK 假设在渲染演员和关联的 vtkTexture。然而,在我的例子和 OP 的例子中,有相邻的三角形被映射到图像的不同部分,所以它们有非常不同的 uv 坐标。共享这些相邻面的点只能有一个与之关联的 uv 坐标(或 Tcoord),但它们实际上需要 2 个(或更多,具体取决于您的情况)。

我的解决方案是遍历并复制位于 seams/borders 上的这些点,并创建一个新的 vtkCellArray,其三角形具有这些重复的 pointId。然后我简单地用新的三角形替换了 vtkPolyData Polys() 列表。复制点并为每个需要它的三角形更新现有的 pointId 会容易得多,但我找不到正确更新单元格的方法。