Intro

I extracted my mate’s (approximately 60×60 pixel) face from a Facebook picture, and overlaid it onto a depth-map for a similarly shaped face, then used a custom python script to create a 3d wavefront model of his face, and an accompanying .mtl file for the texture.

I will create a post soon properly dissecting the script, and commenting-in some better explanations.

Source Images:

Texture
Heightmap

Video:

Code:

Depthmap to mesh converter and material generator.

from PIL import Image,ImageDraw
Faces=[]

def load_heightmap(heightmap="Heightmap.png",texture="boon.png",Scale=1.25): ##Scale is inversed so scale=2 means half the resolution of the source image
    #vertices=[]   ##uncomment for 2d array of vertices
    verticesflat=[]
    faces=[]
    Height_Im=Image.open(heightmap)
    Tex_Im=Image.open(texture)
    Width=width=Height_Im.size[0]
    trueWidth=int(Width/Scale)-2
    Height=height=Height_Im.size[1]
    trueHeight=int(Height/Scale)-2
    tex_coord=[]
    colsflat=[]
    for yn in range(0,trueHeight,1):  ###iterate through each row
        y=yn*Scale
        #vertices.append([])##uncomment for 2d array of vertices
        for xn in range(0,trueWidth,1):##iterate through each pixel in the row
            x=xn*Scale
            depth=25-(Height_Im.getpixel((x,y))[0]+Height_Im.getpixel((x+1,y))[0]+Height_Im.getpixel((x+2,y))[0]+Height_Im.getpixel((x+3,y))[0])/(4*Scale)
            
            Col=Tex_Im.getpixel((x,y))
            verticesflat.append((xn,yn,depth))##uncomment for flat array of vertices
            tex_coord.append((xn/trueWidth,yn/trueHeight))
            colsflat.append(Col)
            #vertices[yn].append((x,y,depth))##uncomment for 2d array of vertices
            topleft=yn*trueWidth+xn
            topright=yn*trueWidth+xn+1
            bottomright=(yn+1)*trueWidth+xn+1
            bottomleft=(yn+1)*trueWidth+xn
            faces.append((topleft,topright,bottomright))
            faces.append((topleft,bottomright,bottomleft))
            
        xn=trueWidth-1
        x=xn*Scale
        depth=25-(Height_Im.getpixel((x,y))[0]+Height_Im.getpixel((x+1,y))[0]+Height_Im.getpixel((x+2,y))[0]+Height_Im.getpixel((x+3,y))[0])/(4*Scale)
        verticesflat.append((xn,yn,depth))##uncomment for flat array of vertices
        tex_coord.append((xn/trueWidth,yn/trueHeight))
        Col=Tex_Im.getpixel((x,y))
        colsflat.append((x,y,depth))##uncomment for flat array of vertices
    yn=trueHeight-1
    y=yn*Scale
    for xn in range(0,int(Width/Scale)-2,1):
        x=xn*Scale
        depth=25-(Height_Im.getpixel((x,y))[0]+Height_Im.getpixel((x+1,y))[0]+Height_Im.getpixel((x+2,y))[0]+Height_Im.getpixel((x+3,y))[0])/(4*Scale)
        
        Col=Tex_Im.getpixel((x,y))
        verticesflat.append((xn,yn,depth))##uncomment for flat array of vertices
        tex_coord.append((xn/trueWidth,yn/trueHeight))
        colsflat.append(Col)
        #vertices[yn].append((x,y,depth))##uncomment for 2d array of vertices
    xn=trueWidth-1
    x=xn*Scale
    depth=25-(Height_Im.getpixel((x,y))[0]+Height_Im.getpixel((x+1,y))[0]+Height_Im.getpixel((x+2,y))[0]+Height_Im.getpixel((x+3,y))[0])/(4*Scale)
    verticesflat.append((xn,yn,depth))##uncomment for flat array of vertices
    tex_coord.append((xn/trueWidth,yn/trueHeight))
    Col=Tex_Im.getpixel((x,y))
    colsflat.append((x,y,depth))##uncomment for flat array of vertices
    return verticesflat,colsflat,faces,tex_coord


def create_material(materialname,mapfile,maptype="Kd",ambientlighting=(1,1,1),diffusivelighting=(1,1,1),specularlighting=(1,1,1),specularexponent=1,refractiveindex=1,illuminationmodel=1):
    return """newmtl %s
Ka %f %f %f
Kd %f %f %f
Ks %f %f %f
Ns %f
Ni %f
d 1.000000
illum %s
map_%s %s"""%(materialname,ambientlighting[0],ambientlighting[1],ambientlighting[2],diffusivelighting[0],diffusivelighting[1],diffusivelighting[2],
                     specularlighting[0],specularlighting[1],specularlighting[2],specularexponent,refractiveindex,illuminationmodel,maptype,mapfile)

#heightmap="Heightmap.png"
#texture="boon.png"
def heightmap_texture_to_3d(heightmapfilename,texturefilename,filename="testout.obj",materialname="test",materialfilename="testmaterial.mtl",Scale=4):
    mesh=load_heightmap(heightmapfilename,texturefilename,Scale=Scale)
    fileout=open(filename,"w")
    fileout.write("mtllib %s\n"%materialfilename)
    for pos in range(len(mesh[0])):
        i=mesh[0][pos]
        col=mesh[1][pos]
        fileout.write("v %s %s %s\n"%(i[0],i[1],i[2]))

    for tex in mesh[3]:
        fileout.write("vt %s %s\n"%(str(tex[0])[:6],str(tex[1])[0:6]))
    fileout.flush()
    fileout.write("usemtl %s\n"%materialname)
    for face in mesh[2]:
        fileout.write("f %s/%s %s/%s %s/%s\n"%(face[0]+1,face[0]+1,face[1]+1,face[1]+1,face[2]+1,face[2]+1))

    fileout.flush()
    fileout.close()
    materialout=open(materialfilename,"w")
    materialout.write(create_material(materialname,texturefilename))
    materialout.flush()
    materialout.close()
heightmap_texture_to_3d("Heightmap.png","boon.png",Scale=1.25,filename="EnormousSmoothBoon.obj",materialname="boon",materialfilename="boon.mtl")

Related post:
.Obj in R5RS Scheme (Lisp)
R5RS Scheme 3d Extruder (Lisp)

Back to top