Knowhow/Vision

COLMAP read bin/txt/ db files in python

침닦는수건 2022. 7. 13. 18:04
반응형

COLMAP을 사용하고 나면 나오는 결과 파일은 총 ( images, cameras, points3D )  3개다.

 

bin 파일이나 txt 파일로 저장되도록 설정이 되어있는데, 이를 주로 사용하는 python으로 가져오기가 매번 heuristic하기엔 번거로워 read function을 작성했다. 

 

아래 함수를 통해서, colmap의 결과값을 각 요소 별로 list 형태로 얻을 수 있다. 이후에 원하는 형태로 사용하면 된다.

 

먼저 bin 파일을 갖고 있을 경우, txt 파일로 변환하는 것으로 시작한다.

 

colmap model_converter --input_path /path/to/dir-of-bins/ --output_path /path/to/dir-of-txts/ --output_type TXT

 

1. cameras.txt 

def read_cam_txt(cam_txt_path):
    if not os.path.exists(cam_txt_path):
        raise Exception(f"No such file : {cam_txt_path}")

    with open(cam_txt_path, 'r') as f:
        lines = f.readlines()

    if len(lines) < 3:
        raise Exception(f"Invalid cameras.txt file : {cam_txt_path}")

    comments = lines[:3]
    contents = lines[3:]

    ids = []
    Ks = []
    dists = []
    img_dims = [] # (w, h)


    for cam_idx, content in enumerate(contents):
        content_items = content.split(' ')
        cam_id = content_items[0]
        cam_type = content_items[1]
        img_w, img_h = int(content_items[2]), int(content_items[3])

        if cam_type == "OPENCV":
            fx, fy = content_items[4], content_items[5]
            cx, cy = content_items[6], content_items[7]
            K = np.array([[fx, 0, cx], [0, fy, cy], [0, 0, 1]], dtype=np.float32)
            dist = content_items[8:] + [0] # k1 k2 p1 p2 + k3(0)
            dist = np.asarray(dist)
            ids.append(cam_id)
            Ks.append(K)
            dists.append(dist)
            img_dims.append((img_w, img_h))
        elif cam_type== "PINHOLE":
            fx, fy = content_items[4], content_items[5]
            cx, cy = content_items[6], content_items[7]
            K = np.array([[fx, 0, cx], [0, fy, cy], [0, 0, 1]], dtype=np.float32)
            dist = np.zeros([5], dtype=np.float32)
            ids.append(cam_id)
            Ks.append(K)
            dists.append(dist)
            img_dims.append((img_w, img_h))
        else:
            raise NotImplementedError(f"Only opencv/pinhole camera will be supported.")

    return ids, Ks, dists, img_dims

 

2. images.txt : pose는 T_cw 방향이다. (notation이 뭔지 모르면 https://jseobyun.tistory.com/31 참고)

def read_images_txt(images_path):
    if not os.path.exists(images_path):
        raise Exception(f"No such file : {images_path}")

    with open(images_path, 'r') as f:
        lines = f.readlines()

    if len(lines) < 2:
        raise Exception(f"Invalid cameras.txt file : {images_path}")

    comments = lines[:4]
    contents = lines[4:]

    img_ids = []
    cam_ids = []
    img_names = []
    poses = []
    for img_idx, content in enumerate(contents[::2]):
        content_items = content.split(' ')
        img_id = content_items[0]
        q_xyzw = np.array(content_items[2:5] + content_items[1:2], dtype=np.float32) # colmap uses wxyz
        t_xyz = np.array(content_items[5:8], dtype=np.float32)
        cam_id = content_items[8]
        img_name = content_items[9]

        R = Rot.from_quat(q_xyzw).as_matrix()
        T = np.eye(4)
        T[:3, :3] = R
        T[:3, -1] = t_xyz

        img_ids.append(img_id)
        cam_ids.append(cam_id)
        img_names.append(img_name)
        poses.append(T)

    return img_ids, cam_ids, img_names, poses

 

3. points3D.txt : PCD 이므로 point id는 무시했음.

def read_point3d_txt(point3d_path):
    if not os.path.exists(point3d_path):
        raise Exception(f"No such file : {point3d_path}")

    with open(point3d_path, 'r') as f:
        lines = f.readlines()

    if len(lines) < 2:
        raise Exception(f"Invalid cameras.txt file : {point3d_path}")

    comments = lines[:3]
    contents = lines[3:]

    XYZs = []
    RGBs = []
    candidate_ids = {}

    for pt_idx, content in enumerate(contents):
        content_items = content.split(' ')
        pt_id = content_items[0]
        XYZ = content_items[1:4]
        RGB = content_items[4:7]
        error = content_items[7],
        candidate_id = content_items[8::2]
        XYZs.append(np.array(XYZ, dtype=np.float32).reshape(1,3))
        RGBs.append(np.array(RGB, dtype=np.float32).reshape(1, 3) / 255.0)
        candidate_ids[pt_id] = candidate_id
    XYZs = np.concatenate(XYZs, axis=0)
    RGBs = np.concatenate(RGBs, axis=0)

    return XYZs, RGBs, candidate_ids
    
def inverse_relation(candidate_img_ids):
    candidate_point_ids = {}
    pt_ids = list(candidate_img_ids.keys())

    for pt_id in pt_ids:
        candidate_img_id = candidate_img_ids[pt_id]
        for img_id in candidate_img_id:
            if img_id in list(candidate_point_ids.keys()):
                candidate_point_ids[img_id].append(pt_id)
            else:
                candidate_point_ids[img_id] = [pt_id]
    return candidate_point_ids

 

(4. database.db )

 

Check failed: existing_image.Name() == image.second.Name()

 

위 에러가 나는 경우의 원인이, database.db 내 이미지 index와 images.txt 내 이미지 index가 다르기 때문인데 이를 해결하기 위해 database.db 파일을 열어봐야 하는 경우가 있다.

 

def read_db(db_path):
    conn = sqlite3.connect(db_path)
    c = conn.cursor()
    c.execute("SELECT * FROM images")
    images_tuples = c.fetchall()

    c.execute("SELECT * FROM cameras")
    cameras_tuples = c.fetchall()

    return cameras_tuples, images_tuples

 

직접 쓰는 일은 드물기 때문에 세부적으로 정리하진 않고 list of tuples이 return되도록 냅두었다. 

반응형