obj文件、mtl文件和png文件转换成一个obj文件
在进行毕设的时候需要对三维重建的效果进行展示,于是准备使用gradio包来展示。但是在展示的时候发现,gradio三维展示模块读取的.obj类型的三维模型是没有颜色的。原因是三维重建系统会生成三个文件mesh.obj, mesh.mtl, texture.png。其中.obj负责mesh的几何形状,.mtl是一些纹理定义,.png是贴图。其中.obj会在文件的内容里引用.mtl文件,.ntl文件又会引用.png贴图。在本地使用meshlab这类的软件,只要这三个文件在同一目录下,就可以一起打开这三个文件来得到有颜色的模型,而gradio是不支持这样一起打开多个文件的。
于是为了在gradio演示的时候正确打开有颜色的模型,需要对这三个文件处理成一个文件。看了一些其他项目的gradio网页和他们加载的obj模型,发现由于obj文件有不同的格式,可以将原来三个文件转换为一个.obj文件。这样就可以直接在gradio上面展示了。
具体转换的过程如下:
1 转换前obj文件的格式
原obj 格式主要有以下 4 种关键字:
- v顶点
v 1.000000 -1.000000 -1.000000
v
表示顶点位置,值分别为 x、y、z,即每个顶点在 X、Y、Z 轴的坐标。
- vt顶点的纹理坐标
vt 0.748573 0.750412
意义:绘制模型的三角面片时,每个顶点取像素点时对应的纹理图片上的坐标。纹理图片的坐标指的是,纹理图片如果被放在屏幕上显示时,以屏幕左下角为原点的坐标。
- f面
f 5/1 1/2 4/3
格式为f v/vt v/vt v/vt。即表示第5行v的颜色为第1行vt坐标对应的颜色,即表示第1行v的颜色为第2行vt坐标对应的颜色,即表示第4行v的纹理为第3行vt坐标对应的颜色。而这三个点组成一个三角面。
2 转换后obj文件 格式
- v顶点
v 1.000000 -1.000000 -1.000000 0.748573 0.750412 0.750412
v
表示顶点位置,值分别为 x、y、z,R, G, B即每个顶点在 X、Y、Z 轴的坐标和其RGB值。
这里小数是将RGB/255得到
- f面
f 5 1 4
第5行的v、第1行的v、第4行的v组成一个三角面
3 转换的代码
v = []
vt = []
f = []
vt_rgb = []
with open(objpath, mode='r', encoding='utf-8') as file_obj:
temp = file_obj.readlines()
for line in temp:
if(line.startswith('v ')):
v.append(line.split(' ')[1:-1])
elif(line.startswith('vt ')):
vt.append(map(float, line.split(' ')[1:-1]))
elif(line.startswith('f ')):
f.append(line.split(' ')[1:-1])
#print(len(v),len(vt))
img = cv2.imread(os.path.join(os.path.dirname(objpath),'albedo.png'))
height,width,bgra = img.shape
for i in range(len(vt)):
temp_vt = list(vt[i])
h = temp_vt[0] * height
w = temp_vt[1] * width
#邻近过滤 uv方向需要倒过来
color = img[width - int(w)-1][int(h)-1]
red = float(color[2]/255)
green = float(color[1]/255)
blue = float(color[0]/255)
vt_rgb.append([red, green, blue])
f_out = []
for i, fi in enumerate(f):
for j in range(3):
tem = fi[j].split('/')
t = []
vi, vti = int(tem[0])-1, int(tem[1])-1
if len(v[vi]) == 3:
v[vi].append(str(vt_rgb[vti][0]))
v[vi].append(str(vt_rgb[vti][1]))
v[vi].append(str(vt_rgb[vti][2]))
t.append(str(vi+1))
f_out.append(t)
with open(os.path.dirname(objpath)+'/output.obj', mode='w', encoding='utf-8') as file_obj:
for i in range(len(v)):
line = 'v ' + v[i][0] + ' ' + v[i][1] + ' ' + v[i][2] + ' ' + v[i][3] + ' ' + v[i][4] + ' ' + v[i][5] + ' ' '\n'
file_obj.write(line)
for i in range(len(f_out)):
line = 'f ' + f_out[i][0] + ' ' + f_out[i][1] + ' ' + f_out[i][2] + '\n'
file_obj.write(line)