地表三维模型的构建与时序模型地表形变监测(四):CMVS测试与试验性尝试分析

发布于 2020-09-11  475 次阅读


一、背景与概要

在经过第三篇发现的CMVS开源项目的研究探索后,本文将针对CMVS与PMVS技术进行实验性探索,研究其相关的情况,如可行性、精度与可靠性等。

组件包遵循GPL开源协议,开源地址如下:GitHub,该版本适用于Windows/Linux等平台。

影像特征

CMVS采用C++进行开发,能快速处理大量高精度数据,可以通过CUDA进行模型加速重建与渲染。

而Python具有简洁简单可快速开发的优势,故采用Python+CMVS结合开发的方式,一方面即可扩展已有的GNSSAMS程序,加入三维形变监测功能,另一方面能够实现代码的精简与快速开发,使得研究注意力能够集中在三维模型的构建与处理上。

二、图像三维重建基本流程

  1. 多视角图像
  2. 图像特征提取匹配
  3. 稀疏重建Sfm
  4. 稠密重建MVS
  5. 点云模型化
  6. 生成三维模型

基于多视角图像的三维重建
三维重建

从上面流程来看,CMVS实现了稠密点云的重建,后续的数据提取等还需要进一步的编程处理。

三、基本流程试验

3.1 流程框架与重难点标记

序号 项目 备注
1 相机参数标定 两种方式
2 多角度原始目标影像获取与相片相机参数文件标定 采用影像自带参数或是棋盘法,通过Python提取
3 获取影像的特征提取与匹配 待研究如何制作
4 稀疏重建Sfm 待研究
5 稠密重建MVS 待研究
6 点云模型化 待研究
7 生成三维模型 待研究
8 模型控制点世界三维坐标获取与存档 需要模型结果与模型存储结构
9 时序模型控制点平面网型形变分析 通过不同时候获取的同一目标三维模型,确定控制点组成的表面网变化情况,得出形变数据

本次研究的风险在于CMVS是否可用,成果是否能进行二次开发。

记录一下:2020年9月11日,电脑注册表疑似遭到破坏,需要重装系统,重整所有系统问题。

3.2 相机参数标定与固定拍摄角度的参数确定

相机参数包含相机的内参与外参,相机参数确定了成像的相关情况。

3.2.1 从EXIF获取相机参数

相关的代码如下:

    def getPara_PICTURE_EXIF(self,standardPic):
        """
        从图像EXIF参数中获取相机参数
        :param standardPic: 标准像片
        :return:
        """
        # CMOS长宽信息
        w_c = 6.29
        h_c = 5.21

        # 读取图片的长宽信息
        img = cv2.imread(standardPic)
        h = img.shape[0]
        w = img.shape[1]
        print(w, h)

        # 从EXIF中读取焦距
        i = Image(standardPic)
        a, b = i.read_exif().get('Exif.Photo.FocalLength').split('/')
        fm = int(a) / int(b)
        f = w * fm / w_c

        # 计算内参信息
        K = np.zeros((3, 3))
        K[0][0] = f
        K[1][1] = f
        K[0][2] = w / 2
        K[1][2] = h / 2
        print(K)

3.2.2 采用棋盘法估计相机参数

棋盘法即张正友标定法,需要棋盘图,可以用摄影测量专业的相机标定板。

但网上有很多棋盘图相关的图像,但是为了保证高清与自适应需要,可以自己做一个。

棋盘生成的代码参见:地表三维模型的构建与时序模型地表形变监测(五):相机标定试验与研究

标定图片拍摄规范
在标定的整个过程中,不能调节相机的光圈、焦距,要保证在标定中摄像头进光量与焦距的一致
建议在摄像头视野内五个不同位置上(左上、右上、左下、右下、正中心)分别拍摄图片
拍摄的图片最好为摄像头视野的1/4左右
不要只拍摄标定板与镜头面平行的图片,也要拍摄一些有倾斜角度的图片
拍摄过程中可以对标定板适当的进行补光,调节标定板到镜头的距离,以便于排出清晰的图片
标定图片数量个人认为15张左右,太少的话标定的参数会不准确
标定时用的标定板最好选择x方向与y方向棋盘格不同的,便于标定程序识别标定板方向

相关的代码如下:

    def getPara_CHESE(self,standardPicDir):
        """
        棋盘法(张正友法)获取相机内参
        :param standardPicDir: 标准像片
        :return:
        """
        # 设置棋盘板长宽
        chessboard_size = (9, 6)

        # 定义数组存储检测到的点
        obj_points = []  # 真实世界中的三维坐标
        img_points = []  # 图片平面的二维坐标

        ####准备目标坐标 (0,0,0),(1,0,0)...(9,6,0)
        # 设置世界坐标下的坐标值
        # 假设棋盘正好在x-y平面上,z直接取零,从而简化初始步骤
        # objp包含的是10*7每一角点的坐标
        objp = np.zeros((np.prod(chessboard_size), 3), np.float32)  # 9*6个三维坐标
        objp[:, :2] = np.mgrid[0:chessboard_size[0], 0:chessboard_size[1]].T.reshape(-1, 2)

        # 读取图片,使用glob文件名管理工具
        calibration_paths = glob.glob(standardPicDir+"/*.JPG")

        # 对每张图片,识别出角点,记录世界物体坐标和图像坐标
        for img_path in tqdm(calibration_paths):
            # tqdm是进度条,以了解距离处理上一个图像多长时间,还剩多少图像没有处理
            # 加载图片
            img = cv2.imread(img_path)
            # 照片太大 缩小一半 (不能缩小!!!!内参会变!!像素变小了 并不是对原图像处理)
            # img = cv2.resize(img, None, fx=0.2, fy=0.2, interpolation=cv2.INTER_CUBIC)
            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            #
            # cv2.imshow('i',img)
            # cv2.waitKey(0)
            # 寻找角点将其存入corners(该图片9*6个角点坐标),ret是找到角点的标志(True/False)
            ret, corners = cv2.findChessboardCorners(gray, (9, 6), None)

            if ret == True:
                # 检测到角点执行以下操作(一般都能检测到角点,除非图片不是规定的棋盘格)
                # 定义角点精准化迭代过程的终止条件 (包括精度和迭代次数)
                criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.0010)
                # 执行亚像素级角点检测
                corners2 = cv2.cornerSubPix(gray, corners, (5, 5), (-1, -1), criteria)

                obj_points.append(objp)
                img_points.append(corners2)

            # 可视化角点
            # img = cv2.drawChessboardCorners(gray,(9,6),corners2,ret)
            # cv2.imshow('s',img)
            # cv2.waitKey(100)

        # 相机标定
        # 每张图片都有自己的旋转和平移矩阵 但是相机内参是畸变系数只有一组(因为相机没变,焦距和主心坐标是一样的)
        ret, K, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, gray.shape[::-1], None, None)
        # 保存参数
        np.save("./camera_params/ret", ret)
        np.save("./camera_params/K", K)
        np.save("./camera_params/dist", dist)
        np.save("./camera_params/rvecs", rvecs)
        np.save("./camera_params/tvecs", tvecs)

        print(K)

3.3 Python联合式开发与多源信息反馈

采用Python将exe、DLL库等各功能模块链接起来,实现信息数据采集-数据标记与处理-三维重建的工作。

3.3.1 CMVS开源建模软件

Python调用机制:

import subprocess

rootDir = "../source/exModule/"

# 设置命令
commend1 = rootDir + "\cmvs.exe" + " " + rootDir + "\pmvs\\"
commend2 = rootDir + "\genOption.exe" + " " + rootDir + "\pmvs\\"
commend3 = rootDir + "\pmvs2.exe" + " " + rootDir + "\pmvs\ option-0000"
# 执行CMVS
process = subprocess.Popen(commend1, shell=True)
process.wait()
process = subprocess.Popen(commend2, shell=True)
process.wait()
process = subprocess.Popen(commend3, shell=True)
process.wait()

3.3.2 Python脚本开发与GNSSAMS综合——数据处理与信息呈现

将处理的结果融入到前端PyQt5组件,将三维模型呈现出来。

3.4 模型数据处理(纠正/去噪等)与二次开发可行性评估

CMVS是否提供成熟的方案?

3.5 时序模型数据比对与形变提取

时序模型

3.6 形变提取误差与可靠性评估

评估评价

四、参考文献

[1]ayayayayo. 学习记录之三维重建相关论文阅读[OL]. CSDN. 2020-01-11.
[2]ayayayayo. 学习记录之相机内参估计[OL]. CSDN. 2020-01-19.
[3]ayayayayo. 学习记录之三维重建2:cmvs稠密点云重建的实现[OL]. CSDN. 2020-02-12.
[4]dongtuu. opencv-python 张正友相机标定法实现[OL]. CSDN. 2018-03-10.
[5]weixin_33795833. 生成黑白棋盘标定图和单目相机标定(一)(python+opencv实现)[OL]. CSDN. 2014-06-13.