run.sh
参数说明
1指定内参
1 | - - ImageReader.camera_params "1451.7946523730436,1438.2609968095967,960,540,0,0,0,0" \内参数据 |
1 2 3 4 5 6 7 8 9 10 11 | Camera.fx: 355.0974745605948 Camera.fy: 355.47832693317105 Camera.cx: 357.7074039567714 Camera.cy: 351.0244037313849 Camera.k1: -0.023790306606729556 Camera.k2: -0.0007571494794293715 Camera.p1: 0.00016452517056601848 Camera.p2: -0.0005743824914513448 Camera.k3: 0 Camera.k4: 0 |
1 |
2colmap重建代码
1 | calibDataRoot 文件根目录 |
1 | imagePath 图像地址 |
1 | ImageReader.camera_model PINHOLE 相机模型选择<br><strong>相机内参<br><br>无畸变模型</strong> |
1 | ImageReader.camera_params "1329.72479114946,1329.65375225699,961.340052491983,542.650028093206" \ |
1 2 | --ImageReader.camera_model PINHOLE --ImageReader.camera_params "fx, fy, cx, cy" |
1 | <strong>有畸变模型</strong><br><strong>k1, k2, p1, p2, k3</strong>:径向和切向畸变系数。 |
1 2 | --ImageReader.camera_model OPENCV --ImageReader.camera_params "fx, fy, cx, cy, k1, k2, p1, p2, k3" |
脚本指令参考
脚本1 给定内参 真孔相机
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | #!/bin/bash calibDataRoot= "/home/dongdong/2project/0data/house4/70/colmap/" imagePath= "/home/dongdong/2project/0data/house4/70/colmap/images/" mkdir -p ${calibDataRoot}/input colmap feature_extractor --database_path $calibDataRoot/input/database.db \ --image_path ${imagePath} \ --ImageReader.single_camera true \ --ImageReader.camera_model PINHOLE \ --ImageReader.camera_params "1329.72479114946,1329.65375225699,961.340052491983,542.650028093206" \ --SiftExtraction.estimate_affine_shape true \ --SiftExtraction.domain_size_pooling true \ --SiftExtraction.num_threads -1 \ --SiftExtraction.gpu_index 0 #sequential_matcher colmap exhaustive_matcher --database_path $calibDataRoot/input/database.db \ --SiftMatching.guided_matching true \ --SiftMatching.num_threads -1 --SiftMatching.gpu_index 0 # mkdir -p ${calibDataRoot}/input colmap mapper --database_path $calibDataRoot/input/database.db \ --image_path ${imagePath} \ --output_path $calibDataRoot/input \ --Mapper.ba_local_max_refinements 3 \ --Mapper.ba_local_max_num_iterations 100 \ --Mapper.max_extra_param 99999 \ --Mapper.ba_refine_principal_point 0 \ --Mapper.ba_refine_focal_length 0 \ --Mapper.ba_refine_extra_params 0 \ --Mapper.tri_ignore_two_view_tracks false \ --Mapper.num_threads -1 colmap model_converter --input_path ${calibDataRoot}/input/0 \ --output_type txt --output_path ${calibDataRoot}/input/0 |
畸变矫正参考
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | ### Image undistortion ## We need to undistort our images into ideal pinhole intrinsics. img_undist_cmd = (colmap_command + " image_undistorter \ --image_path " + args.source_path + " /input \ --input_path " + args.source_path + " /distorted/sparse/0 \ --output_path " + args.source_path + " \ --output_type COLMAP") exit_code = os.system(img_undist_cmd) if exit_code != 0: logging.error(f "Mapper failed with code {exit_code}. Exiting." ) exit(exit_code) files = os.listdir(args.source_path + "/sparse" ) os.makedirs(args.source_path + "/sparse/0" , exist_ok=True) # Copy each file from the source directory to the destination directory for file in files: if file == '0' : continue source_file = os.path. join (args.source_path, "sparse" , file) destination_file = os.path. join (args.source_path, "sparse" , "0" , file) shutil.move(source_file, destination_file) if (args.resize): print( "Copying and resizing..." ) |
python代码
程序1-1 单纯畸变矫正图像重新保存 图像
1 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | import os import cv2 import yaml import glob def load_camera_parameters(yaml_file): with open (yaml_file, 'r' ) as file : params = yaml.safe_load( file ) return params def undistort_images(input_folder, output_folder, camera_params): # 获取所有图像文件,并按时间戳排序 image_files = sorted (glob.glob(os.path.join(input_folder, '*.jpg' ))) # 提取相机内参 fx = camera_params[ 'Camera.fx' ] fy = camera_params[ 'Camera.fy' ] cx = camera_params[ 'Camera.cx' ] cy = camera_params[ 'Camera.cy' ] # 提取畸变系数 k1 = camera_params[ 'Camera.k1' ] k2 = camera_params[ 'Camera.k2' ] p1 = camera_params[ 'Camera.p1' ] p2 = camera_params[ 'Camera.p2' ] k3 = camera_params[ 'Camera.k3' ] k4 = camera_params[ 'Camera.k4' ] # 构造相机内参矩阵和畸变系数数组 camera_matrix = np.array([[fx, 0 , cx], [ 0 , fy, cy], [ 0 , 0 , 1 ]], dtype = np.float64) dist_coeffs = np.array([k1, k2, p1, p2, k3, k4], dtype = np.float64) for image_file in image_files: # 读取图像 image = cv2.imread(image_file) # 畸变校正 undistorted_image = cv2.undistort(image, camera_matrix, dist_coeffs) # 构造输出文件名 output_file = os.path.join(output_folder, os.path.basename(image_file)) # 保存校正后的图像 cv2.imwrite(output_file, undistorted_image) print (f "Saved undistorted image: {output_file}" ) if __name__ = = '__main__' : input_folder = 'path/to/input_folder' # 输入文件夹路径 output_folder = 'path/to/output_folder' # 输出文件夹路径 yaml_file = 'path/to/camera_parameters.yaml' # YAML 文件路径 # 加载相机参数 camera_params = load_camera_parameters(yaml_file) # 创建输出文件夹(如果不存在) os.makedirs(output_folder, exist_ok = True ) # 进行畸变校正 undistort_images(input_folder, output_folder, camera_params) |
1 |
程序1-2 单纯畸变矫正参考
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | import os import cv2 import yaml import glob import numpy as np def load_camera_parameters(yaml_file): with open (yaml_file, 'r' ) as file : params = yaml.safe_load( file ) return params def undistort_images(input_folder, output_folder, camera_params,img_type = 'png' ): # 获取所有图像文件,并按时间戳排序 image_files = sorted (glob.glob(os.path.join(input_folder, '*.' + img_type))) # 提取相机内参 fx = camera_params[ 'Camera.fx' ] fy = camera_params[ 'Camera.fy' ] cx = camera_params[ 'Camera.cx' ] cy = camera_params[ 'Camera.cy' ] # 提取畸变系数 k1 = camera_params[ 'Camera.k1' ] k2 = camera_params[ 'Camera.k2' ] p1 = camera_params[ 'Camera.p1' ] p2 = camera_params[ 'Camera.p2' ] k3 = camera_params[ 'Camera.k3' ] k4 = camera_params[ 'Camera.k4' ] #print("fx",fx,'fy',fy,'cx',cx,'cy',cy,'k1',k1,'k2',k2,'p1',p1,'p2',p2) # 构造相机内参矩阵和畸变系数数组 camera_matrix = np.array([[fx, 0 , cx], [ 0 , fy, cy], [ 0 , 0 , 1 ]], dtype = np.float64) #dist_coeffs = np.array([k1, k2, p1, p2, k3, k4], dtype=np.float64) dist_coeffs = np.array([k1, k2, p1, p2,k3], dtype = np.float64) for image_file in image_files: # 读取图像 image = cv2.imread(image_file) # 畸变校正 #undistorted_image = cv2.undistort(image, camera_matrix, dist_coeffs) h, w = image.shape[: 2 ] mapx, mapy = cv2.initUndistortRectifyMap(camera_matrix, dist_coeffs, None , camera_matrix, (w, h), 5 ) undistorted_image = cv2.remap(image, mapx, mapy, cv2.INTER_LINEAR) # 构造输出文件名 output_file = os.path.join(output_folder, os.path.basename(image_file)) # 保存校正后的图像 cv2.imwrite(output_file, undistorted_image) print (f "Saved undistorted image: {output_file}" ) if __name__ = = '__main__' : data_path = "/home/dongdong/2project/0data/ehang/ehang2_test/" input_folder = data_path + 'colmap/in_images/' # 输入文件夹路径 output_folder = data_path + 'colmap/images/' # 输出文件夹路径 yaml_file = data_path + 'FHY_config.yaml' # YAML 文件路径 # 加载相机参数 camera_params = load_camera_parameters(yaml_file) # 创建输出文件夹(如果不存在) os.makedirs(output_folder, exist_ok = True ) # 进行畸变校正 undistort_images(input_folder, output_folder, camera_params) |
程序2 不指定内参 自动估计重建且畸变矫正图像重新输出文件夹和结果
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 | ''' --source_path 输入文件夹 下面有一个input文件夹 存放原始没有畸变矫正的图片 --camera 相机模型 PINHOLE OPENCV --output_path 输出文件夹 images 输出畸变矫正后的图像 spase输出点云结果 python convert.py \ --source_path /home/dongdong/2project/0data/ehang/ehang2_test/ehang2 \ --camera OPENCV \ --output_path /home/dongdong/2project/0data/ehang/ehang2_test/ehang2_output 没有指定 --no_gpu 和 --skip_matching,所以默认会调用 GPU 并进行匹配。--resize 也没有被设置,因此默认值为 False。 ''' |
input输入未校准的图像位置
第一次未校准的结果存放在distorted
输出畸变矫正后结果
具体命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | # # Copyright (C) 2023, Inria # GRAPHDECO research group, https://team.inria.fr/graphdeco # All rights reserved. # # This software is free for non-commercial, research and evaluation use # under the terms of the LICENSE.md file. # # For inquiries contact george.drettakis@inria.fr # import time import os import logging from argparse import ArgumentParser import shutil ''' python convert.py -s /home/dongdong/2project/0data/ehang/ehang2_test/ehang2 --camera PINHOLE python convert.py -s /home/dongdong/2project/0data/ehang/ehang2_test/npu --camera OPENCV python convert.py \ --source_path /home/dongdong/2project/0data/ehang/ehang2_test/ehang2 \ --camera OPENCV \ --output_path /home/dongdong/2project/0data/ehang/ehang2_test/ehang2_output 没有指定 --no_gpu 和 --skip_matching,所以默认会调用 GPU 并进行匹配。--resize 也没有被设置,因此默认值为 False。 ''' # This Python script is based on the shell converter script provided in the MipNerF 360 repository. parser = ArgumentParser( "Colmap converter" ) parser.add_argument( "--no_gpu" , action = 'store_true' ) parser.add_argument( "--skip_matching" , action = 'store_true' ) parser.add_argument( "--source_path" , "-s" , required = True , type = str ) parser.add_argument( "--output_path" , "-o" , required = True , type = str ) parser.add_argument( "--camera" , default = "OPENCV" , type = str ) parser.add_argument( "--colmap_executable" , default = "", type = str ) parser.add_argument( "--resize" , action = "store_true" ) parser.add_argument( "--magick_executable" , default = "", type = str ) args = parser.parse_args() colmap_command = '"{}"' . format (args.colmap_executable) if len (args.colmap_executable) > 0 else "colmap" magick_command = '"{}"' . format (args.magick_executable) if len (args.magick_executable) > 0 else "magick" use_gpu = 1 if not args.no_gpu else 0 # 默认使用gpu # 默认不跳过skip_matching if not args.skip_matching: os.makedirs(args.source_path + "/distorted/sparse" , exist_ok = True ) ## Feature extraction 特征提取 =================== feat_extracton_cmd = colmap_command + " feature_extractor " \ "--database_path " + args.source_path + " / distorted / database.db \ - - image_path " + args.source_path + " / input \ - - ImageReader.single_camera 1 \ - - ImageReader.camera_model " + args.camera + " \ - - SiftExtraction.use_gpu " + str (use_gpu) exit_code = os.system(feat_extracton_cmd) if exit_code ! = 0 : logging.error(f "Feature extraction failed with code {exit_code}. Exiting." ) exit(exit_code) ## Feature matching 特征匹配 =================== feat_matching_cmd = colmap_command + " exhaustive_matcher \ - - database_path " + args.source_path + " / distorted / database.db \ - - SiftMatching.use_gpu " + str(use_gpu) +" \ - - SiftMatching.guided_matching true" exit_code = os.system(feat_matching_cmd) if exit_code ! = 0 : logging.error(f "Feature matching failed with code {exit_code}. Exiting." ) exit(exit_code) ### Bundle adjustment 优化重建 ================== # The default Mapper tolerance is unnecessarily large, # decreasing it speeds up bundle adjustment steps. mapper_cmd = (colmap_command + " mapper \ - - database_path " + args.source_path + " / distorted / database.db \ - - image_path " + args.source_path + " / input \ - - output_path " + args.source_path + " / distorted / sparse \ - - Mapper.ba_global_function_tolerance = 0.000001 ") exit_code = os.system(mapper_cmd) if exit_code ! = 0 : logging.error(f "Mapper failed with code {exit_code}. Exiting." ) exit(exit_code) # 输出转换为txt mapper_cmd = (colmap_command + " model_converter \ - - input_path " + args.source_path + " / distorted / sparse / 0 \ - - output_type " + " txt \ - - output_path " + args.source_path + " / distorted / sparse / 0 " ) exit_code = os.system(mapper_cmd) if exit_code ! = 0 : logging.error(f "bin转换txt失败{exit_code}. Exiting." ) exit(exit_code) ### Image undistortion ## We need to undistort our images into ideal pinhole intrinsics. ''' --image_path: 输入图像的路径。可以是单个图像文件或包含多个图像的目录。 --output_path: 默认images 去畸变后的图像输出路径。可以是一个目录,COLMAP 将在该目录下生成去畸变后的图像。 --input_path: 输入相机参数和姿态文件的路径,通常是 COLMAP 数据库或导出的模型文件。 --output_type: 指定输出图像的格式,如 PNG、JPEG 等。 --max_image_size: 指定输出图像的最大尺寸。COLMAP 会根据原图像的比例缩放图像。 --remove_uncovered: 选择是否移除未被覆盖的区域,这在某些情况下可以用来减少输出图像的数量。 --image_list_path: 包含要去畸变的图像列表的文件路径。 ''' print ( "===============准备开始矫正================" ) time.sleep( 5 ) img_undist_cmd = (colmap_command + " image_undistorter \ - - image_path " + args.source_path + " / input \ - - input_path " + args.source_path + " / distorted / sparse / 0 \ - - output_path " + args.output_path + " \ - - output_type COLMAP") exit_code = os.system(img_undist_cmd) if exit_code ! = 0 : logging.error(f "Mapper failed with code {exit_code}. Exiting." ) exit(exit_code) # 创建sparse文件夹 files = os.listdir(args.output_path + "/sparse" ) os.makedirs(args.output_path + "/sparse/0" , exist_ok = True ) # Copy each file from the source directory to the destination directory for file in files: if file = = '0' : continue source_file = os.path.join(args.output_path, "sparse" , file ) destination_file = os.path.join(args.output_path, "sparse" , "0" , file ) shutil.move(source_file, destination_file) # 输出转换为txt mapper_cmd = (colmap_command + " model_converter \ - - input_path " + args.output_path + " / sparse / 0 \ - - output_type " + " txt \ - - output_path " + args.output_path + " / sparse / 0 " ) exit_code = os.system(mapper_cmd) if exit_code ! = 0 : logging.error(f "bin转换txt失败{exit_code}. Exiting." ) exit(exit_code) if (args.resize): print ( "Copying and resizing..." ) # Resize images. os.makedirs(args.source_path + "/images_2" , exist_ok = True ) os.makedirs(args.source_path + "/images_4" , exist_ok = True ) os.makedirs(args.source_path + "/images_8" , exist_ok = True ) # Get the list of files in the source directory files = os.listdir(args.source_path + "/images" ) # Copy each file from the source directory to the destination directory for file in files: source_file = os.path.join(args.source_path, "images" , file ) destination_file = os.path.join(args.source_path, "images_2" , file ) shutil.copy2(source_file, destination_file) exit_code = os.system(magick_command + " mogrify -resize 50% " + destination_file) if exit_code ! = 0 : logging.error(f "50% resize failed with code {exit_code}. Exiting." ) exit(exit_code) destination_file = os.path.join(args.source_path, "images_4" , file ) shutil.copy2(source_file, destination_file) exit_code = os.system(magick_command + " mogrify -resize 25% " + destination_file) if exit_code ! = 0 : logging.error(f "25% resize failed with code {exit_code}. Exiting." ) exit(exit_code) destination_file = os.path.join(args.source_path, "images_8" , file ) shutil.copy2(source_file, destination_file) exit_code = os.system(magick_command + " mogrify -resize 12.5% " + destination_file) if exit_code ! = 0 : logging.error(f "12.5% resize failed with code {exit_code}. Exiting." ) exit(exit_code) print ( "Done." ) |
程序3 指定内参 重建且畸变矫正图像重新输出文件夹和结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | # 教程地址 https://blog.csdn.net/a40850273/article/details/127976111 # import time import os import logging from argparse import ArgumentParser import shutil import yaml import numpy as np def load_camera_parameters(yaml_file): with open (yaml_file, 'r' ) as file : params = yaml.safe_load( file ) return params ''' 输入参数: --source_path 输入文件夹 下面有一个input文件夹 存放原始没有畸变矫正的图片 --camera 相机模型 PINHOLE OPENCV --output_path 输出文件夹 images 输出畸变矫正后的图像 spase输出点云结果 --yaml_file 相机内参 格式 Camera.fx: 355.0974745605948 Camera.fy: 355.47832693317105 Camera.cx: 357.7074039567714 Camera.cy: 351.0244037313849 Camera.k1: -0.023790306606729556 Camera.k2: -0.0007571494794293715 Camera.p1: 0.00016452517056601848 Camera.p2: -0.0005743824914513448 Camera.k3: 0 Camera.k4: 0 执行命令: python convert_yaml.py \ --source_path /media/dongdong/Elements1/项目/fjx/2TestData/2TestData/ehang2 \ --output_path /home/dongdong/2project/0data/ehang/ehang2/ehang2_output_yaml \ --camera OPENCV \ --yaml_file /home/dongdong/2project/0data/ehang/ehang2/FHY_config.yaml 没有指定 --no_gpu 和 --skip_matching,所以默认会调用 GPU 并进行匹配。--resize 也没有被设置,因此默认值为 False。 ''' # This Python script is based on the shell converter script provided in the MipNerF 360 repository. parser = ArgumentParser( "Colmap converter" ) parser.add_argument( "--no_gpu" , action = 'store_true' ) parser.add_argument( "--skip_matching" , action = 'store_true' ) parser.add_argument( "--source_path" , "-s" , required = True , type = str ) parser.add_argument( "--output_path" , "-o" , required = True , type = str ) parser.add_argument( "--yaml_file" , "-y" , required = True , type = str ) parser.add_argument( "--camera" , default = "OPENCV" , type = str ) parser.add_argument( "--colmap_executable" , default = "", type = str ) parser.add_argument( "--resize" , action = "store_true" ) parser.add_argument( "--magick_executable" , default = "", type = str ) args = parser.parse_args() colmap_command = '"{}"' . format (args.colmap_executable) if len (args.colmap_executable) > 0 else "colmap" magick_command = '"{}"' . format (args.magick_executable) if len (args.magick_executable) > 0 else "magick" use_gpu = 1 if not args.no_gpu else 0 # 默认使用gpu # 加载相机参数 camera_params = load_camera_parameters(args.yaml_file) # 提取相机内参 fx = camera_params[ 'Camera.fx' ] fy = camera_params[ 'Camera.fy' ] cx = camera_params[ 'Camera.cx' ] cy = camera_params[ 'Camera.cy' ] # 提取畸变系数 k1 = camera_params[ 'Camera.k1' ] k2 = camera_params[ 'Camera.k2' ] p1 = camera_params[ 'Camera.p1' ] p2 = camera_params[ 'Camera.p2' ] # k3 = camera_params['Camera.k3'] # k4 = camera_params['Camera.k4'] camera_matrix = str (fx) + "," + str (fy) + "," + str (cx) + "," + str (cx) dist_coeffs = str (k1) + "," + str (k2) + "," + str (p1) + "," + str (p2) all_camInfo = "\"" + camera_matrix + "," + dist_coeffs + "\"" print ( "相机内参" , "fx" ,fx, 'fy' ,fy, 'cx' ,cx, 'cy' ,cy, 'k1' ,k1, 'k2' ,k2, 'p1' ,p1, 'p2' ,p2) print ( "相机内参" ,all_camInfo) # # 构造相机内参矩阵和畸变系数数组 # camera_matrix = np.array([[fx, 0, cx], # [0, fy, cy], # [0, 0, 1]], dtype=np.float64) # #dist_coeffs = np.array([k1, k2, p1, p2, k3, k4], dtype=np.float64) # dist_coeffs = np.array([k1, k2, p1, p2,k3], dtype=np.float64) # 默认不跳过skip_matching if not args.skip_matching: os.makedirs(args.source_path + "/distorted/sparse" , exist_ok = True ) ## Feature extraction 特征提取 =================== # --ImageReader.camera_params "fx, fy, cx, cy, k1, k2, p1, p2, k3" feat_extracton_cmd = colmap_command + " feature_extractor " \ "--database_path " + args.source_path + " / distorted / database.db \ - - image_path " + args.source_path + " / input \ - - ImageReader.single_camera true \ - - ImageReader.camera_params "+ all_camInfo + " \ - - ImageReader.camera_model " + args.camera + " \ - - SiftExtraction.use_gpu " + str(use_gpu) + " \ - - SiftExtraction.estimate_affine_shape true \ - - SiftExtraction.domain_size_pooling true \ - - SiftExtraction.num_threads - 1 " exit_code = os.system(feat_extracton_cmd) if exit_code ! = 0 : logging.error(f "Feature extraction failed with code {exit_code}. Exiting." ) exit(exit_code) ''' exhaustive_matcher:针对少量图像(几百张量级),可以获得足够快且最好的重建结果。它将每张图像与其余所有图像进行匹配,不过 block size 可能限制同时加载到内存中的图像数量。 sequential_matcher:针对顺序采集的视频图像,由于相邻帧存在视觉上的重叠且没有必要进行完全匹配,它只匹配视频流中的相邻帧。同时,这种匹配方式能够基于 vocabulary tree 进行回环检测。最后,帧之间的前后关系由图像文件名给定,与数据集中的存储顺序无关。 vocab_tree_matcher:针对大量图像(几千帧量级),可以通过提供 vocabulary tree 从而快速检索视觉上最相近的图像进行匹配。 spatial_matcher:针对能够提供准确定位信息的图像,可以通过对应图像采集时的 GPS 信息从而仅匹配空间位置上相近的图像。 transitive_matcher:基于传递规则使用已有的特征匹配关系确定更完全的匹配图,即 A 与 B 匹配,B 与 C 匹配,那将直接匹配 A 和 C。 Custom Matching:通过 text 文件指定图像的匹配关系,如果是导入的特征可以进一步指定两张图像之间特征的匹配关系。 ''' ## Feature matching 特征匹配 =================== feat_matching_cmd = colmap_command + " sequential_matcher \ - - database_path " + args.source_path + " / distorted / database.db \ - - SiftMatching.use_gpu " + str(use_gpu) +" \ - - SiftMatching.guided_matching true \ - - SiftMatching.num_threads - 1 " exit_code = os.system(feat_matching_cmd) if exit_code ! = 0 : logging.error(f "Feature matching failed with code {exit_code}. Exiting." ) exit(exit_code) ### Bundle adjustment 优化重建 ================== #在执行特征提取与匹配后通过 sfM 进行稀疏三维重建与构图。 # The default Mapper tolerance is unnecessarily large, # decreasing it speeds up bundle adjustment steps. ''' --database_path: 包含匹配特征的数据库路径。 --image_path: 用于建图的输入图像路径。 --output_path: 存储重建结果的目录。 --Mapper.ba_local_max_refinements: 本地束调整的最大精细调整次数。 --Mapper.ba_local_max_num_iterations: 本地束调整的最大迭代次数。 --Mapper.max_extra_param: 优化中额外参数的最大值。 --Mapper.ba_refine_principal_point: 是否对主点进行精细调整(0 表示不调整)。 --Mapper.ba_refine_focal_length: 是否对焦距进行精细调整(0 表示不调整)。 --Mapper.ba_refine_extra_params: 是否对其他相机参数进行精细调整(0 表示不调整)。 --Mapper.tri_ignore_two_view_tracks: 在三角测量中忽略两视图轨迹(false/true)。 --Mapper.num_threads: 建图时使用的线程数(-1 表示使用所有可用线程)。 ''' mapper_cmd = colmap_command + " mapper " \ "--database_path " + args.source_path + "/distorted/database.db " \ "--image_path " + args.source_path + "/input " \ "--output_path " + args.source_path + "/distorted/sparse " + \ "--Mapper.ba_global_function_tolerance=0.000001 " + \ "--Mapper.ba_local_max_num_iterations 100 " + \ "--Mapper.max_extra_param 99999 " + \ "--Mapper.ba_refine_principal_point 0 " + \ "--Mapper.ba_refine_focal_length 0 " + \ "--Mapper.ba_refine_extra_params 0 " + \ "--Mapper.tri_ignore_two_view_tracks false " + \ "--Mapper.num_threads -1 " exit_code = os.system(mapper_cmd) if exit_code ! = 0 : logging.error(f "Mapper failed with code {exit_code}. Exiting." ) exit(exit_code) # 输出转换为txt mapper_cmd = (colmap_command + " model_converter \ - - input_path " + args.source_path + " / distorted / sparse / 0 \ - - output_type " + " txt \ - - output_path " + args.source_path + " / distorted / sparse / 0 " ) exit_code = os.system(mapper_cmd) if exit_code ! = 0 : logging.error(f "bin转换txt失败{exit_code}. Exiting." ) exit(exit_code) ### Image undistortion ## We need to undistort our images into ideal pinhole intrinsics. ''' --image_path: 输入图像的路径。可以是单个图像文件或包含多个图像的目录。 --output_path: 默认images 去畸变后的图像输出路径。可以是一个目录,COLMAP 将在该目录下生成去畸变后的图像。 --input_path: 输入相机参数和姿态文件的路径,通常是 COLMAP 数据库或导出的模型文件。 --output_type: 指定输出图像的格式,如 PNG、JPEG 等。 --max_image_size: 指定输出图像的最大尺寸。COLMAP 会根据原图像的比例缩放图像。 --remove_uncovered: 选择是否移除未被覆盖的区域,这在某些情况下可以用来减少输出图像的数量。 --image_list_path: 包含要去畸变的图像列表的文件路径。 ''' print ( "===============准备开始矫正================" ) time.sleep( 5 ) img_undist_cmd = (colmap_command + " image_undistorter \ - - image_path " + args.source_path + " / input \ - - input_path " + args.source_path + " / distorted / sparse / 0 \ - - output_path " + args.output_path + " \ - - output_type COLMAP") exit_code = os.system(img_undist_cmd) if exit_code ! = 0 : logging.error(f "Mapper failed with code {exit_code}. Exiting." ) exit(exit_code) # 创建sparse文件夹 files = os.listdir(args.output_path + "/sparse" ) os.makedirs(args.output_path + "/sparse/0" , exist_ok = True ) # Copy each file from the source directory to the destination directory for file in files: if file = = '0' : continue source_file = os.path.join(args.output_path, "sparse" , file ) destination_file = os.path.join(args.output_path, "sparse" , "0" , file ) shutil.move(source_file, destination_file) # 输出转换为txt mapper_cmd = (colmap_command + " model_converter \ - - input_path " + args.output_path + " / sparse / 0 \ - - output_type " + " txt \ - - output_path " + args.output_path + " / sparse / 0 " ) exit_code = os.system(mapper_cmd) if exit_code ! = 0 : logging.error(f "bin转换txt失败{exit_code}. Exiting." ) exit(exit_code) if (args.resize): print ( "Copying and resizing..." ) # Resize images. os.makedirs(args.source_path + "/images_2" , exist_ok = True ) os.makedirs(args.source_path + "/images_4" , exist_ok = True ) os.makedirs(args.source_path + "/images_8" , exist_ok = True ) # Get the list of files in the source directory files = os.listdir(args.source_path + "/images" ) # Copy each file from the source directory to the destination directory for file in files: source_file = os.path.join(args.source_path, "images" , file ) destination_file = os.path.join(args.source_path, "images_2" , file ) shutil.copy2(source_file, destination_file) exit_code = os.system(magick_command + " mogrify -resize 50% " + destination_file) if exit_code ! = 0 : logging.error(f "50% resize failed with code {exit_code}. Exiting." ) exit(exit_code) destination_file = os.path.join(args.source_path, "images_4" , file ) shutil.copy2(source_file, destination_file) exit_code = os.system(magick_command + " mogrify -resize 25% " + destination_file) if exit_code ! = 0 : logging.error(f "25% resize failed with code {exit_code}. Exiting." ) exit(exit_code) destination_file = os.path.join(args.source_path, "images_8" , file ) shutil.copy2(source_file, destination_file) exit_code = os.system(magick_command + " mogrify -resize 12.5% " + destination_file) if exit_code ! = 0 : logging.error(f "12.5% resize failed with code {exit_code}. Exiting." ) exit(exit_code) print ( "Done." ) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
2018-06-19 第一行代码 3-2-2 软件也要拼脸蛋-UI界面-更强大的滚动条- 卡片
2018-06-19 第一行代码 3-5 软件也要拼脸蛋-UI界面-更强大的滚动条- 聊天室
2018-06-19 第一行代码 3-3 软件也要拼脸蛋-UI界面-更强大的滚动条- 网格模式和瀑布流模式
2018-06-19 第一行代码 3-3 软件也要拼脸蛋-UI界面-更强大的滚动条- 可设置Item操作的动画(删除或者添加等) -可设置Item的间隔样式(可绘制)