Vtk inserts incorrect color between nodes when mapping texture to mesh

怎甘沉沦 提交于 2020-06-17 07:20:25

问题


Hi I am trying to map a texture to 3d mesh using Mayavi and Python bindings of vtk. I am visualising an .obj wavefront. This obj is 3D photograph of a face. The texture image is a composite of three 2D photographs.

Each node in the mesh has an (uv) co-ordinate in the image, which defines its color. Different regions of the mesh draw their colours from different sections of the image. To illustrate this I have replaced the actual texture image with this one:

And mapped this to the mesh instead.

The problem I am having is illustrated around the nose. At the border between red and green there is an outline of blue. Closer inspection of this region in wireframe mode shows that it is not a problem with the uv mapping, but with how vtk is interpolating colour between two nodes. For some reason it is adding a piece of blue in between two nodes where one is red and one is green.

This causes serious problems when visualising using the real texture

Is there a way to force vtk to choose the colour of one or the other neighbouring nodes for the colour between them? I tried turning "edge-clamping" on, but this did not achieve anything.

The code that I am using is below and you can access the files in question from here https://www.dropbox.com/sh/ipel0avsdiokr10/AADmUn1-qmsB3vX7BZObrASPa?dl=0 but I hope this is a simple solution.

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)

回答1:


You can turn off all interpolation (change interpolate = 1 to interpolate = 0 in your example), but there is not a way to turn off interpolation at just the places where it would interpolate across sub-images of the texture – at least not without writing your own fragment shader. This will likely look crude.

Another solution would be to create 3 texture images with transparent texels at each location that is not part of the actor's face. Then render the same geometry with the same texture coordinates but a different image each time (i.e., have 3 actors each with the same polydata but a different texture image).




回答2:


I just ran into this exact problem as well and found that the reason this happens is because VTK assumes there's a 1-to-1 relationship between points in the polydata and uv coordinates when rendering the actor and associated vtkTexture. However, in my case and the case of OP, there are neighboring triangles that are mapped to different sections the the image, so they have very different uv coordinates. The points that share these neighboring faces can only have one uv coordinate (or Tcoord) associated with it, but they actually need 2 (or more, depending on your case).

My solution was to loop through and duplicate these points that lie on the the seams/borders and create a new vtkCellArray with triangles with these duplicated pointIds. Then I simply replaced the vtkPolyData Polys() list with the new triangles. It would have been much easier to duplicate the points and update the existing pointIds for each of the triangles that needed it, but I couldn't find a way to update the cells properly.



来源:https://stackoverflow.com/questions/39137374/vtk-inserts-incorrect-color-between-nodes-when-mapping-texture-to-mesh

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!