小白学习记录
毕业设计《基于图像的农作物植株三维重建》涉及用深度学习进行三维重建,由于长春疫情影响无法拍摄农作物植株照片,因此只能用blender渲染合成数据集进行训练。
三维模型来自于模型组,其为SKP格式,由于blender不能直接导入SKP模型,还需要下载插件。渲染代码(担心影响毕设查重,暂时不放)借鉴了ShapeNet中的部分代码,其大致思路为:设置不同的欧拉角,将欧拉角转换为相机相应的四元数和位移参数,将该参数输入blender运行,得到不同视角的渲染图像。
最开始选择基于NeRF的VolSDF和NeuS等模型(在新视角图像生成任务上和NeRF效果接近,但其重建得到的三维模型精度更高)进行训练(训练方法参考我的另一篇博客),用渲染得到的RGB数据集进行训练,但多次尝试均失败(若合成数据集为灰色背景,则渲染图像则为全灰,换成白色背景就是全白;后面尝试加了个简单的场景,其渲染图像也与场景大概一致,但无法渲染出我想要的农作物植株,且提取到的三维模型也无法形成实体),包括更改场景以及光照条件。
为了确定问题是数据集本身的问题还是这几个模型不适用于合成数据(或许因为有gap),尝试用NeRF提供的lego数据集在VolSDF上进行训练,但发现NeRF采用的合成数据集全是RGBA格式(猜测可能是因为NeRF作者发现利用RGB图像训练效果不好,于是改用了RGBA格式,不然难以解释他们为何单独为合成数据集搞了一个输入格式)。我首先将lego图像转换为RGB格式,然后利用VolSDF训练,结果同样失败,因此我暂且认为问题不在于我准备的数据集。
接下来,我考虑直接在原始的NeRF上(jittor平台提供的代码)进行训练,但其相机参数文件与VolSDF和NeuS等要求不同,因此在此记录一下生成相机参数文件的代码:
import json
from math import cos, sin, pi
import math
# 将欧拉角转换为旋转矩阵
def xyz2r(x,y,z):
x, y, z = x*pi/180 ,y*pi/180 ,z*pi/180
r=[[0.0,0.0,0.0,0.0],[0.0,0.0,0.0,0.0],[0.0,0.0,0.0,0.0],[0.0,0.0,0.0,1.0]]
r[0][0],r[0][1],r[0][2]=cos(z)*cos(y), cos(z)*sin(y)*sin(x) - sin(z)*cos(x), cos(z)*sin(y)*cos(x) + sin(z)*sin(x)
r[1][0],r[1][1],r[1][2]=sin(z)*cos(y), sin(z)*sin(y)*sin(x) + cos(z)*cos(x), sin(z)*sin(y)*cos(x) - cos(z)*sin(x)
r[2][0],r[2][1],r[2][2]=-sin(y), cos(y)*sin(x), cos(y)*cos(x)
return r
# 输入分别为相机到物体的距离,相机的方位角以及俯仰角;输出为相机平移参数
def obj_centened_camera_pos(dist, azimuth_deg, elevation_deg):
phi = float(elevation_deg) / 180 * math.pi
theta = float(azimuth_deg) / 180 * math.pi
x = (dist * math.cos(theta) * math.cos(phi))
y = (dist * math.sin(theta) * math.cos(phi))
z = (dist * math.sin(phi))
return (x, y, z)
# 定义的欧拉角:分别为方位角、俯仰角和翻滚角
view_params=[[]for i in range(109)]
for i in range(72):
view_params[i]=[5*i,30,0]
for i in range(36):
view_params[i+72]=[10*i,60,0]
view_params[108]=[0,90,0]
dist=3 # 相机到物体的距离
fov=0.6911111611634243 # 手动计算的相机水平视场角,计算公式为2*math.atan(36/(2*50)),其中36为传感器尺寸,50为焦距
jsontext={'camera_angle_x':fov,'frames':[]} # 定义一个字典
# 获得每张图像的外参矩阵
i=0
for param in view_params:
azimuth_deg = param[0]
elevation_deg = param[1]
theta_deg = param[2]
r=xyz2r(azimuth_deg,elevation_deg,theta_deg)
r[0][3],r[1][3],r[2][3]=obj_centened_camera_pos(dist,azimuth_deg,elevation_deg)
jsontext['frames'].append({'file_path':'./train/%03d'%(round(i)),'transform_matrix':r})
i+=1
jsondata = json.dumps(jsontext,indent=4,separators=(',', ': '))# 生成json文件,后面的参数是格式控制
f = open('transform_train.json', 'w')
f.write(jsondata)
f.close()
最终得到的json文件如图所示(部分):