Knowhow/Vision

COLMAP[CLI] SfM/MVS with known camera parameters and poses

침닦는수건 2022. 7. 14. 10:54
반응형

2022.07.07 - [Knowhow] - COLMAP[GUI] multi-camera setting 글에서 간접적으로 알 수 있듯이, COLMAP을 사용할 때 미리 알고 있는 카메라 파라미터가 있을 때, SfM 및 MVS의 최적화 과정에서 해당 파라미터를 제외할 수 있다.

 

앞선 글이 카메라 intrinsic parameter를 알고 있을 때 어떻게 반영하는지 포함했다면, 이 글에서는 카메라 intrinsic parameter와 더불어 카메라 자세까지 주어졌을 때 이를 어떻게 반영하는지 정리하고자 한다.

 

하나 참고할 점은, GUI를 사용하지 않고 CLI로 사용하는 방법을 정리한다. 왜냐하면, 

 

카메라 파라미터 중 intrinsic parameters를 알고 있는 경우, 카메라는 보통 수 대에 그치기 때문에 Processing > Database management > Cameras 에 수기 입력을 하는 것이 그리 번거롭지 않고, 최적화 과정에서 제외하는 것 또한 Reconstruction > Reconstruction options > Bundle 에서 refine_focal_length, refine_principal_point, refine_extra_params를 체크 해제하는 것만으로 바로 반영이 되기 때문에 GUI를 쓰는 것이 수월하고 확인도 쉽다.

 

반면, 카메라 파라미터 중 카메라의 자세를 알고 있는 경우, Processing > Database management > Images에 각 이미지 마다 quaternion(그것도 colmap format인 wxyz로 바꾼)과 translation을 수기 입력하는 것은 도통 번거로운 일이 아니다. 이미지 장 수가 보통 100~200장은 기본으로 되다 보니 수작업으로 할 분량은 아니다. 

 

따라서 자동화를 위해 약간의 코드 작업이 필요한데, command line에서 모든 걸 해결하기 위해서 GUI를 사용하지 않았다.

 

참고 링크 : https://colmap.github.io/faq.html#reconstruct-sparse-dense-model-from-known-camera-poses

 

 

1.  데이터 준비

+── path/to/manually/created/sparse/model
│   +── cameras.txt
│   +── images.txt
│   +── points3D.txt

 

위와 같이 cameras.txt, images.txt, points3D.txt가 필요하다.

 

1-1. cameras.txt는 다음과 같이 카메라의 intrinsic parameter 들이 정리되어 있는 txt 파일이다.

# Camera list with one line of data per camera:
#   CAMERA_ID, MODEL, WIDTH, HEIGHT, PARAMS[]
# Number of cameras: 5
5 OPENCV 6720 4480 9402.87 9586.74 3709.54 2246.37 0.0749967 -1.00472 -0.00952685 0.0123831
4 OPENCV 6720 4480 9436.4 9532.07 3295.49 2165.54 0.0835728 -1.18352 -0.00798109 -0.00221607
3 OPENCV 6720 4480 9883.57 9903.06 3354.33 2104.09 -0.00329978 0.710137 -0.0069645 -0.000568668
2 OPENCV 6720 4480 9574.61 9620.5 3309.01 2096.81 0.101227 -1.76723 -0.00832682 -0.00115585
1 OPENCV 6720 4480 9940.2 9941.26 3326.07 2203.65 -0.0188752 0.317263 -0.000803991 -0.00135296

 

1-2. images.txt는 다음과 같이 이미지 별로 자세 (quarternion in wxyz, translation)과 카메라 id, 이미지 이름이 정리된 txt 파일이다.

# Image list with two lines of data per image:
#   IMAGE_ID, QW, QX, QY, QZ, TX, TY, TZ, CAMERA_ID, NAME
#   POINTS2D[] as (X, Y, POINT3D_ID)
# Number of images: 120, mean observations per image: 1246.6333333333334
59 0.0439156 0.264904 0.944718 -0.188164 0.227762 0.0415339 1.36323 3 cam2/00011.jpg
4128.57 1012.77 16115 4095.94 1143.69 -1 4095.94 1143.69 -1 4085.47 1188.33 -1 ...

관찰해보면, 홀수 번째 줄은 정보가, 짝수 번째 줄은 해당 이미지에서 추출된 2D feature point가 연관된 3D point의 ID와 함께 (없으면 -1)과 함께 3자리씩 반복되어 저장되어 있다. 

 

준비 단계에서는, 2D feature point를 모르기 때문에 짝수 번째 줄은 비워두면 된다. 예를 들면, 다음과 같은 모양이다.

# Image list with two lines of data per image:
#   IMAGE_ID, QW, QX, QY, QZ, TX, TY, TZ, CAMERA_ID, NAME
#   POINTS2D[] as (X, Y, POINT3D_ID)
# Number of images: ?, mean observations per image: ?]
1 -0.0008815121783023931 0.9999682257570891 -0.00567545420457353 -0.005528076706286666 -0.18054126817199825 -0.10585283052378924 1.362915727765214 1 cam0/00001.jpg

2 -0.0016912216679944017 0.9921540068945645 0.12487386490608784 -0.0058381705821727 -0.1470779173627407 -0.1509056816296901 1.3626140235328155 1 cam0/00002.jpg

3 -0.002089311623550773 0.9672960589626577 0.2535749679545565 -0.005805576551552125 -0.1029790613349812 -0.18586650017477577 1.3623088921701507 1 cam0/00003.jpg

4 -0.002686913783124583 0.9258285745968341 0.3778913303410067 -0.005689763388806045 -0.0513340810368599 -0.20811664371348176 1.3617913198132607 1 cam0/00004.jpg

 

1-3. points3D.txt는 다음과 같이 3D point들의 id와 좌표가 정리된 txt 파일이다. 

# 3D point list with one line of data per point:
#   POINT3D_ID, X, Y, Z, R, G, B, ERROR, TRACK[] as (IMAGE_ID, POINT2D_IDX)
# Number of points: 9490, mean track length: 15.763540569020021
10265 0.22674129548586777 -0.11904128506180081 0.00020592211222249522 57 55 62 0.33639431788573776 14 2475 59 2041 8 2584
10245 0.33653512311845757 0.035499410484332736 0.00036374948246980479 232 236 249 0.95656726046426266 13 2327 17 2053 53 1996 59 1967 62 2118 68 2110 8 2480 58 2253 61 2073 56 2188
10225 0.26042051175618225 -0.20055126311644147 3.1959851315655093e-05 60 59 66 0.49725513217777861 13 2144 54 2220 56 2106 62 1959 65 1744 68 2179 8 2376 63 1733
10222 0.24222186138066554 -0.21868333321557915 0.00029583894999017901 74 74 78 0.6973140447120838 21 2090 64 1730 69 2051 8 2366
10213 0.19776815616832993 -0.20565534025528773 0.00010473873018725297 228 231 245 0.78689204883781394 10 1899 50 2153 62 1971 70 2073 8 2329 64 1755 69 2080 63 1751
10205 -0.023569412017420906 0.044370752496549516 -0.00028930060310327781 232 237 247 1.7565914276763619 18 2499 19 2272 22 2112 62 2164 71 1956 8 2272 16 2144 23 2077

준비 단계에서는, 3D point를 역시나 모르기 때문에 비워두면 된다. 그냥 points3D.txt 이름의 파일만 만들어 두면 된다.

 

각 파일은 포맷이 띄어쓰기 ' '로 구분된 나열형 string이기 때문에 만들기가 번거로울 뿐 어렵지 않다.

 

내가 작성해둔 위 파일들을 생성하는 코드는 다음 링크에 있다.

 

2022.07.13 - [Knowhow] - COLMAP write txt files in python

 

 

2. SfM (sparse model)

colmap feature_extractor \
    --database_path ./database.db \
    --image_path ./images \
    --ImageReader.camera_model OPNECV \
    --ImageReader.single_camera_per_folder 1
    
    
colmap exhaustive_matcher \ # or alternatively any other matcher
    --database_path ./database.db
    

colmap point_triangulator \
    --database_path ./database.db \
    --image_path ./images
    --input_path path/to/your/prepared/txt/ \
    --output_path ./output/sparse/bin/ \
    --Mapper.ba_refine_focal_length 0 \
    --Mapper.ba_refine_principal_point 0 \
    --Mapper.ba_refine_extra_params 0
    --clear_points # optional, if you got error about image index mismatch.
    		   # I recommend you to revise img_idx of images.txt according to database.db

* intrinsic parameters를 제외하기 위한 옵션을 꼭 확인할 것.

 

 

3. MVS (dense model)

colmap image_undistorter \
    --image_path ./images \
    --input_path ./output/sparse/bin \
    --output_path ./output/dense/

colmap patch_match_stereo \
    --workspace_path ./output/dense/
    --PatchMatchStereo.depth_min 0.5 \
    --PatchMatchStereo.depth_max 3 \ # m scale

colmap stereo_fusion \
    --workspace_path ./output/dense/
    --output_path .output/dense/fused.ply

 

 

Tips

 

SfM을 돌리고 나서 나온 sparse 결과들이 MVS의 입력으로 들어가기 때문에, SfM으로 계산된 값이 MVS에 사용되는구나 라고 생각할 수 있는데 굳이 그런건 아니다.

 

그럼 왜 굳이 넣어주는가? 그것은 MVS가 image pair를 찾을 때, SfM에서 나온 3D point를 참조하기 때문이다. 

 

points3D.txt를 관찰해보면 3D point 마다 어떤 2D feature들이 triangulation되어 복원됐는지 적혀있다. 어떤 이미지 pair 끼리 matching이 되었는지가 다 기록되어 있다는 것이다. 

 

MVS가 돌 때, 이를 기반으로 어떤 이미지 pair가 가장 overlap이 많이 되는지 판단해서 neighbor를 정의하고 patch-match를 수행한다. 

 

image_undistorter까지 수행한 뒤에, 결과 폴더의 stereo 폴더에 들어가보면, patch-match.cfg 파일이 있는데 내부에 __auto__, 20과 같이 적혀있는 것을 볼 수 있다.

 

이 뜻이 SfM에서 나온 3D point들의 기반으로 20장의 이미지를 선별해서 patch-match를 하겠다는 뜻이다.

 

간혹 가다가, SfM에서 3D point가 너무 적게 뽑혀서 MVS가 이미지 pair를 선정하기에 부족할 경우가 있는데, 이럴 경우 manual하게 patch-match.cfg 파일을 수정해주면 된다.

 

(물론 어떤 이미지들이 서로 화각이 많이 겹칠지 사용자가 알고 있어야 한다. SfM을 안하고 MVS 바로 하겠다면 반드시 작성해야함.)

 

간단히 내가 patch-match.cfg 파일을 수정한 코드는 다음과 같다.

def update_neighbors(cfg_path, neighbor_type="__all__"):
    if not os.path.exists(cfg_path):
        raise Exception(f"No such file : {cfg_path}.")

    with open(cfg_path, 'r') as f:
        original_lines = f.readlines()

    open(cfg_path, 'w').close() # for clear

    if neighbor_type=="__all__" or neighbor_type.startswith("__auto__"):
        neighbor_str = neighbor_type
        def neighbor_func(ref_img_name):
            return neighbor_str
    elif neighbor_type == "custom":
        def neighbor_func(ref_img_name):       
            neighbors = []
            '''
            your neighbor define rule here
            '''     
       
            neighbors = ', '.join(neighbors) +'\n'
            return neighbors


    new_lines = original_lines.copy()
    for i, line in enumerate(original_lines):
        if i % 2 == 0:
            ref_image_name = line
            continue
        if i % 2 == 1:
            prev_line = line
            cur_line =  neighbor_func(ref_image_name)
            new_lines[i] = cur_line

    with open(cfg_path, 'w') as f:
        f.writelines(new_lines)

 

Note

python을 이용해 COLMAP을 쓰고 싶을 경우, 다음 글 참고

 

2023.12.07 - [Knowhow] - COLMAP[python] pycolmap 보다 편하게 colmap 사용하기

반응형