python实现obj模型的归一化

作者:石炜贤&曾翔钰

归一化,个人理解就是将数据范围映射到另一个更小的范围。比如,我们即将要说到的obj模型的归一化,原始的数据坐标范围是-100多到+100多,这里我要将它归一化到-1~1的范围。

具体步骤如下:

  1. 读取每个点的信息,计算这个模型的包围盒(好吧,其实就是找到这个模型最小的x,y,z和最大的x,y,z。注意,这里不是最小的点和最大的点的坐标。)
 def get_bounding_box(self, p):
        """
        获取物体的最小x,y,z和最大的x,y,z
        :param p:
        :return:
        """
        self.minP.x = p.x if p.x < self.minP.x else self.minP.x
        self.minP.y = p.y if p.y < self.minP.y else self.minP.y
        self.minP.z = p.z if p.z < self.minP.z else self.minP.z
        self.maxP.x = p.x if p.x > self.maxP.x else self.maxP.x
        self.maxP.y = p.y if p.y > self.maxP.y else self.maxP.y
        self.maxP.z = p.z if p.z > self.maxP.z else self.maxP.z
  1. 通过第一步的x,y,z我们得到(max_x-min_x),(max_y-min_y)和(max_z-min_z)三者中的最大值,取最大值作为包围盒的边长,构建一个正方体式的包围盒。现在,想象这个 obj模型被包围盒包着并处于包围盒的中心。
 def get_bounding_box_length(self):
        """
        获取包围盒的最大长度
        :return:
        """
        box_len = self.maxP.x - self.minP.x
        if box_len < (self.maxP.y - self.minP.y):
            box_len = self.maxP.y - self.minP.y
        if box_len < (self.maxP.z - self.minP.z):
            box_len = self.maxP.z - self.minP.z
        return box_len
  1. OK,我们剩下要做的就是简单的将坐标放缩一下而已了。
    def do_normalize(self, box_len, points):
        """
        归一化处理
        :param center_p: 物体的中心点
        :param box_len: 包围盒的一半
        :param points:要进行归一化处理的点
        :return:
        """
        new_points = []
        for point in points:
            x = (point.x - self.minP.x) * 2 / box_len - 1
            y = (point.y - self.minP.y) * 2 / box_len - 1
            z = (point.z - self.minP.z) * 2 / box_len - 1
            new_points.append(Point(x, y, z))
        return new_points

完整代码如下:
运行的话,只需写入存放obj文件的文件夹路径(或者父路径也行)。

# encoding:utf-8

import os


class Point(object):
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z


class MyFile(object):
    folders = []
    outFolders = []
    count = 0

    def get_folder_paths(self, base_path):
        """
        获取文件夹下所有含有obj文件的文件夹路径
        :param base_path:
        :return:
        """
        self.folders.append(base_path)
        files = os.listdir(base_path)
        contain_obj = False
        for file in files:
            file_path = os.path.join(base_path, file)
            if os.path.isdir(file_path):
                self.get_folder_paths(file_path)
                print(file_path)
                self.folders.append(file_path)
            elif os.path.isfile(base_path + file):
                contain_obj = True
        if contain_obj is not True:
            self.folders.remove(base_path)

    def get_obj_filenames(self, folder_path):
        filenames = []
        files = os.listdir(folder_path)
        for file in files:
            file_path = os.path.join(folder_path, file)
            if os.path.isfile(file_path) and file[-3:] == "obj":
                print("file:", file)
                filenames.append(file)
                self.count += 1
        return filenames

    def get_out_folders(self):
        for folder in self.folders:
            new_folder = str(folder).replace("Budata\\BU_3DFE", "NEW_NormalizeDataWithoutRound")
            self.outFolders.append(new_folder)
            # print(new_folder)
            if not os.path.exists(new_folder):
                os.makedirs(new_folder)


class MyNormalize(object):
    minP = Point(1000, 10000, 10000)
    maxP = Point(0, 0, 0)

    def get_bounding_box(self, p):
        """
        获取物体的最小x,y,z和最大的x,y,z
        :param p:
        :return:
        """
        self.minP.x = p.x if p.x < self.minP.x else self.minP.x
        self.minP.y = p.y if p.y < self.minP.y else self.minP.y
        self.minP.z = p.z if p.z < self.minP.z else self.minP.z
        self.maxP.x = p.x if p.x > self.maxP.x else self.maxP.x
        self.maxP.y = p.y if p.y > self.maxP.y else self.maxP.y
        self.maxP.z = p.z if p.z > self.maxP.z else self.maxP.z

    def get_bounding_box_length(self):
        """
        获取包围盒的最大长度
        :return:
        """
        box_len = self.maxP.x - self.minP.x
        if box_len < (self.maxP.y - self.minP.y):
            box_len = self.maxP.y - self.minP.y
        if box_len < (self.maxP.z - self.minP.z):
            box_len = self.maxP.z - self.minP.z
        return box_len

    def do_normalize(self, box_len, points):
        """
        归一化处理
        :param center_p: 物体的中心点
        :param box_len: 包围盒的一半
        :param points:要进行归一化处理的点
        :return:
        """
        new_points = []
        for point in points:
            x = (point.x - self.minP.x) * 2 / box_len - 1
            y = (point.y - self.minP.y) * 2 / box_len - 1
            z = (point.z - self.minP.z) * 2 / box_len - 1
            new_points.append(Point(x, y, z))
        return new_points

    def read_points(self, filename):
        """
        读取一个obj文件里的点
        :param filename:
        :return:
        """
        with open(filename) as file:
            points = []
            while 1:
                line = file.readline()
                if not line:
                    break
                strs = line.split(" ")
                if strs[0] == "v":
                    points.append(Point(float(strs[1]), float(strs[2]), float(strs[3])))
                if strs[0] == "vt":
                    break
        return points

    def write_points(self, points, src_filename, des_filename):
        """
        将归一化好的点保存到另一个文件
        :param points:
        :param src_filename:
        :param des_filename:
        :return:
        """
        point_lines = []
        for point in points:
            point_line = "v " + str(point.x) + " " + str(point.y) + " " + str(point.z) + "\n"
            point_lines.append(point_line)
        with open(src_filename, "r") as file:
            outFile = open(des_filename, "a")
            for point in points:
                point_line = "v " + str(point.x) + " " + str(point.y) + " " + str(point.z) + "\n"
                outFile.write(point_line)
                outFile.flush()
            while 1:
                line = file.readline()
                if not line:
                    break
                strs = line.split(" ")
                if strs[0] == "f":
                    outFile.write(line)
            outFile.flush()
            outFile.close()


if __name__ == "__main__":
    myFile = MyFile()
    myNormalize = MyNormalize()
    basePath = "H:\\Budata\\BU_3DFE\\"

    myFile.get_folder_paths(basePath)
    myFile.get_out_folders()
    count = 0
    for folder in myFile.folders:
        outFolderPath = myFile.outFolders.pop(0)
        print(outFolderPath)
        objFilenames = myFile.get_obj_filenames(folder)
        for objFilename in objFilenames:
            # 读取obj文件的坐标点
            objFilePath = os.path.join(folder, objFilename)
            points = myNormalize.read_points(objFilePath)
            for point in points:
                myNormalize.get_bounding_box(point)
            # 包围盒长度
            boxLength = myNormalize.get_bounding_box_length()
            # 归一化
            points = myNormalize.do_normalize(boxLength, points)
            # 将归一化的坐标点写入目标文件
            myNormalize.write_points(points, os.path.join(folder, objFilePath),
                                     os.path.join(outFolderPath, objFilename))
            count += 1
            print("第", count, "个执行完毕")

    print("执行结束")
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值