目的
在三维仿真的场景中,无人机影像的建模是一种快速可靠的对大场景地形三维重现的方式,但是无人机影像的建模无法对单个场景的属性进行分析,如对单个楼层、居民区进行属性分析查询。在GIS中,我们通过shp数据描述空间信息,这些数据通常以点线面的形式,即概括了地物的空间关系也包含地物的属性信息。通过shp数据和无人机影像建模的结合,可以将大范围三维场景的模型进行分割,使之成为一个个独立的单元并且赋予属性信息,本文通过一个小实验来实现shp面对obj模型中某个单个建筑的分割。
数据准备
采用网上公开无人机影像数据,通过contextcapture对影像进行建模,生成倾斜摄影数据和obj模型,本文采用obj模型进行切割。
由于模型场景很大,本文采用截取一个单体建筑的方式来对场景进行裁剪,后期需要裁剪所有建筑,则需要迭代计算并优化效率(黑框全选为需要裁剪的建筑)。
shp数据为建筑面数据
算法思路
由于obj模型包含顶点和纹理,而shp数据基本处于二维平面,那么需要考虑顶点落在shp面数据xy平面内的数据,同时,一个面最少由三个顶点构成,如果一个面的部分在shp范围内,部分在范围外,则需要重新分割,生成新的顶点并构建网格。
obj数据的格式
通常简单的obj数据包含顶点信息,纹理信息,面信息,有的obj还包含一个mtl文件
其中常见的obj数据的组成形式为:
- 首行:
mtllib *.mtl表示使用哪个mtl文件,以mtllib开头 - 顶点坐标:
v x y z表示一个顶点的坐标,以v开头 - 纹理坐标:
vt u v表示一个纹理的坐标,以vt开头 - 引用的材质:
usemtl *表示引用mtl文件的哪部分纹理,以usemtl开头 - 面索引:
f v_i1/u_i1 v_i2/u_i2 v_i3/u_i3,以f开头,分别记录 顶点的序号和纹理的序号,序号从1开始,一个面由三个顶点组成,所以有三个顶点序号和纹理序号
mtl文件的格式
mtl记录了纹理的一些配置信息,主要有:
newmtl *: 创建一个材质,材质名为*,对应obj中的usemtl *ka * * *: 环境颜色kd * * *:漫反射颜色d *: 透明度Ns: *: 高光指数illum: *: 光照模型map_kd: *.jpg:纹理图片的名称
以上可以得出一个结论,一个mtl可以创建多个材质,那么在obj的面索引前面,可以多次引入材质信息,这个材质信息下面对应的面索引的顶点和纹理坐标,使用该材质
解析obj数据
实验采用python语言,通过pywavefront库解析obj模型
model = pywavefront.Wavefront(r"D:\data\osgb\mec\Production_1 (3)\Data\Model\2.obj",collect_faces=True)
解析后的数据存在在model对象上,那么就需要知道model对象对应的成员有哪些:
materials: 记录了材质信息vertices: 一个坐标数组,五个数据一组,前两位表示纹理坐标,后三位表示顶点坐标
mtllibs: 模型使用的mtl文件名,以数组形式存储vertices: 顶点坐标数组meshes: 格网列表,表示面数据索引集合
由于每个材质对应了一部分三角格网数据,格网数据对应了相应的顶点和纹理坐标数据,因此在解析数据的时候以材质进行分类,先记录每个材质的相关信息,在根据材质读取对应的顶点坐标和纹理坐标,进行裁剪和划分。
for mesh_name in model.materials:
model_mesh = model.materials[mesh_name]
if model_mesh.texture == None:
continue
newmtl = model_mesh.name
#环境颜色
Ka = model_mesh.ambient
#漫反射颜色
Kd = model_mesh.diffuse
#透明度
d = model_mesh.transparency
#高光指数
Ns = model_mesh.specular
#光照模型
illum = model_mesh.illumination_model
#材质
map_Kd = model_mesh.texture.file_name
file_path = model_mesh.texture.path
vertices = model_mesh.vertices
new_faces = []
new_mtls = []
split_faces = []
split_mtls = []
flag = False
#获取顶点和纹理坐标
#检查面的顶点是否在多边形内
#实现面的分割逻辑
if flag:
materials = {
"Ka": Ka,
"Kd": Kd,
"d": d,
"Ns": Ns,
"illum": illum,
"map_Kd": map_Kd,
"file_path": file_path,
"new_faces": new_faces,
"new_mtls": new_mtls,
"split_faces": split_faces,
"split_mtls": split_mtls,
"newmtl":newmtl
}
newObj.append(materials)
如上所示,首先根据材质读取材质信息,然后检查面的顶点是否在多边形内,如果面的所有顶点都在多边形内,那么只需要保留这个面的顶点和纹理坐标就行;如果面的部分顶点在多边形内,部分不在,则需要进行分割。
由于顶点和纹理信息存储在model_mesh.vertices中,只需要遍历该数据,即可获取对应的顶点坐标和纹理坐标。
for index in range(int(len(vertices) /5/3) ):
#计算每一个面
projected_vertices = []
new_mtl = []
for i in range(3):
projected_vertices.append([vertices[15*index+2 + 5*i],vertices[15*index+3+5*i],vertices[15*index+4+5*i]])
new_mtl.append([vertices[15*index+0 + 5*i],vertices[15*index+1 + 5*i]])
#检查面的顶点是否在多边形内
#实现面的分割逻辑
进行分割和裁剪面的构建
多边形的构建采用shapely库,由于是对一个shp要素进行裁剪,因此可以直接通过shapely构建一个多边形要素作为该建筑轮廓,实际分割中也可以采取读取shp要素,依次构建的方式。
# 定义多边形
polygon = shapely.geometry.Polygon([(12730175.00000000 ,3571689.50000000),(12730175.00000000 ,3571656.25000000),(12730189.00000000,3571656.25000000),(12730189.00000000 ,3571625.25000000),(12730175.00000000,3571625.00000000),(12730175.00000000,3571628.75000000),(12730113.00000000,3571628.50000000),(12730113.00000000,3571626.00000000),(12730099.00000000,3571626.00000000),(12730099.00000000,3571651.25000000),(

5253

被折叠的 条评论
为什么被折叠?



