1+ # 先頭付近に追記(ファイル先頭でもOK)
2+ # import logging, unicodedata, os, difflib
3+ # logger = logging.getLogger("colmap_debug")
4+ # if not logger.handlers:
5+ # h = logging.FileHandler("colmap_debug.txt", encoding="utf-8")
6+ # fmt = logging.Formatter("%(asctime)s %(levelname)s: %(message)s")
7+ # h.setFormatter(fmt)
8+ # logger.addHandler(h)
9+ # logger.setLevel(logging.DEBUG)
10+
11+ # def _norm_key(s: str) -> str:
12+ # # キーの正規化: 区切り・先頭 ./ ・大文字小文字・Unicode などを吸収
13+ # s = unicodedata.normalize("NFC", s.strip())
14+ # s = s.replace("\\", "/")
15+ # if s.startswith("./"):
16+ # s = s[2:]
17+ # return s.lower()
18+
19+ # def _peek(head, n=5):
20+ # head = list(head)
21+ # return head[:n]
22+
23+ # def _summarize_list(name, arr, n=5):
24+ # logger.debug("%s: count=%d, sample=%s", name, len(arr), _peek(arr, n))
25+ # -----debug
26+ # 既に import 済みなら重複不要
27+
28+ from collections import defaultdict
129import json
230import os
331from typing import Any , Dict , List , Optional
634import imageio .v2 as imageio
735import numpy as np
836import torch
37+
938from PIL import Image
10- from pycolmap import SceneManager
39+ from pycolmap import Reconstruction , CameraModelId
1140from tqdm import tqdm
1241from typing_extensions import assert_never
1342
1847 transform_points ,
1948)
2049
50+ # path patch
51+ import unicodedata
52+ def _norm_key (s : str ) -> str :
53+ # 区切り/先頭.//大文字小文字/Unicode揺れを吸収
54+ s = unicodedata .normalize ("NFC" , s .strip ())
55+ s = s .replace ("\\ " , "/" )
56+ if s .startswith ("./" ):
57+ s = s [2 :]
58+ return s .lower ()
59+ # -----
60+
2161
2262def _get_rel_paths (path_dir : str ) -> List [str ]:
2363 """Recursively get relative paths of files in a directory."""
@@ -75,25 +115,28 @@ def __init__(
75115 colmap_dir
76116 ), f"COLMAP directory { colmap_dir } does not exist."
77117
78- manager = SceneManager (colmap_dir )
79- manager .load_cameras ()
80- manager .load_images ()
81- manager .load_points3D ()
118+ manager = Reconstruction (colmap_dir )
119+
120+ # point_id -> point3D_id_contiguous
121+ point3D_id_contiguous = dict ()
122+ for i , point_id in enumerate (manager .points3D .keys ()):
123+ point3D_id_contiguous [point_id ] = i
82124
83125 # Extract extrinsic matrices in world-to-camera format.
84126 imdata = manager .images
85127 w2c_mats = []
86128 camera_ids = []
87129 Ks_dict = dict ()
130+ point_indices = defaultdict (list ) # image_name -> [point_idx]
88131 params_dict = dict ()
89132 imsize_dict = dict () # width, height
90133 mask_dict = dict ()
91134 bottom = np .array ([0 , 0 , 0 , 1 ]).reshape (1 , 4 )
92135 for k in imdata :
93136 im = imdata [k ]
94- rot = im .R ()
95- trans = im . tvec . reshape ( 3 , 1 )
96- w2c = np . concatenate ([ np . concatenate ([ rot , trans ], 1 ), bottom ], axis = 0 )
137+ w2c = im .cam_from_world (). matrix ()
138+ w2c = np . concatenate ([ w2c , bottom ], axis = 0 )
139+
97140 w2c_mats .append (w2c )
98141
99142 # support different camera intrinsics
@@ -102,30 +145,40 @@ def __init__(
102145
103146 # camera intrinsics
104147 cam = manager .cameras [camera_id ]
105- fx , fy , cx , cy = cam . fx , cam . fy , cam . cx , cam . cy
106- K = np . array ([[ fx , 0 , cx ], [ 0 , fy , cy ], [ 0 , 0 , 1 ]] )
148+
149+ K = cam . calibration_matrix ( )
107150 K [:2 , :] /= factor
108151 Ks_dict [camera_id ] = K
109152
153+ # get image_name -> [point_idx] dict
154+ for obs_point2d in im .get_observation_points2D ():
155+ point_indices [im .name ].append (point3D_id_contiguous [obs_point2d .point3D_id ])
156+
110157 # Get distortion parameters.
111- type_ = cam .camera_type
112- if type_ == 0 or type_ == "SIMPLE_PINHOLE" :
158+ type_ = cam .model
159+ # SIMPLE_PINHOLE: f, cx, cy
160+ if type_ == CameraModelId .SIMPLE_PINHOLE :
113161 params = np .empty (0 , dtype = np .float32 )
114162 camtype = "perspective"
115- elif type_ == 1 or type_ == "PINHOLE" :
163+ # PINHOLE: fx, fy, cx, cy
164+ elif type_ == CameraModelId .PINHOLE :
116165 params = np .empty (0 , dtype = np .float32 )
117166 camtype = "perspective"
118- if type_ == 2 or type_ == "SIMPLE_RADIAL" :
119- params = np .array ([cam .k1 , 0.0 , 0.0 , 0.0 ], dtype = np .float32 )
167+ # SIMPLE_RADIAL: f, cx, cy, k
168+ if type_ == CameraModelId .SIMPLE_RADIAL :
169+ params = np .array ([cam .params [3 ], 0.0 , 0.0 , 0.0 ], dtype = np .float32 )
120170 camtype = "perspective"
121- elif type_ == 3 or type_ == "RADIAL" :
122- params = np .array ([cam .k1 , cam .k2 , 0.0 , 0.0 ], dtype = np .float32 )
171+ # RADIAL: f, cx, cy, k1, k2
172+ elif type_ == CameraModelId .RADIAL :
173+ params = np .array ([cam .params [3 ], cam .params [4 ], 0.0 , 0.0 ], dtype = np .float32 )
123174 camtype = "perspective"
124- elif type_ == 4 or type_ == "OPENCV" :
125- params = np .array ([cam .k1 , cam .k2 , cam .p1 , cam .p2 ], dtype = np .float32 )
175+ # OPENCV: fx, fy, cx, cy, k1, k2, p1, p2
176+ elif type_ == CameraModelId .OPENCV :
177+ params = np .array ([cam .params [4 ], cam .params [5 ], cam .params [6 ], cam .params [7 ]], dtype = np .float32 )
126178 camtype = "perspective"
127- elif type_ == 5 or type_ == "OPENCV_FISHEYE" :
128- params = np .array ([cam .k1 , cam .k2 , cam .k3 , cam .k4 ], dtype = np .float32 )
179+ # OPENCV_FISHEYE: fx, fy, cx, cy, k1, k2, k3, k4
180+ elif type_ == CameraModelId .OPENCV_FISHEYE :
181+ params = np .array ([cam .params [4 ], cam .params [5 ], cam .params [6 ], cam .params [7 ]], dtype = np .float32 )
129182 camtype = "fisheye"
130183 assert (
131184 camtype == "perspective" or camtype == "fisheye"
@@ -140,7 +193,7 @@ def __init__(
140193
141194 if len (imdata ) == 0 :
142195 raise ValueError ("No images found in COLMAP." )
143- if not (type_ == 0 or type_ == 1 ):
196+ if not (type_ == CameraModelId . PINHOLE or type_ == CameraModelId . SIMPLE_PINHOLE ):
144197 print ("Warning: COLMAP Camera is not PINHOLE. Images have distortion." )
145198
146199 w2c_mats = np .stack (w2c_mats , axis = 0 )
@@ -195,21 +248,27 @@ def __init__(
195248 colmap_image_dir , image_dir + "_png" , factor = factor
196249 )
197250 image_files = sorted (_get_rel_paths (image_dir ))
198- colmap_to_image = dict (zip (colmap_files , image_files ))
199- image_paths = [os .path .join (image_dir , colmap_to_image [f ]) for f in image_names ]
200-
201- # 3D points and {image_name -> [point_idx]}
202- points = manager .points3D .astype (np .float32 )
203- points_err = manager .point3D_errors .astype (np .float32 )
204- points_rgb = manager .point3D_colors .astype (np .uint8 )
205- point_indices = dict ()
206-
207- image_id_to_name = {v : k for k , v in manager .name_to_image_id .items ()}
208- for point_id , data in manager .point3D_id_to_images .items ():
209- for image_id , _ in data :
210- image_name = image_id_to_name [image_id ]
211- point_idx = manager .point3D_id_to_point3D_idx [point_id ]
212- point_indices .setdefault (image_name , []).append (point_idx )
251+
252+ # --- fix ---
253+ # colmap_to_image = dict(zip(colmap_files, image_files))
254+ # image_paths = [os.path.join(image_dir, colmap_to_image[f]) for f in image_names]
255+
256+ # キーは“正規化した colmap_files”、値はディスク上の相対パス(そのまま)
257+ colmap_to_image = { _norm_key (k ): v for k , v in zip (colmap_files , image_files ) }
258+
259+ # 参照側(COLMAP名)も正規化して引く。最後に normpath でOS向けに整形。
260+ image_paths = [
261+ os .path .normpath (os .path .join (image_dir , colmap_to_image [_norm_key (f )]))
262+ for f in image_names
263+ # --- fix
264+ ]
265+
266+ # 3D points
267+ points3D = manager .points3D .values ()
268+ points_err = np .array ([p .error for p in points3D ], dtype = np .float32 )
269+ points_rgb = np .array ([p .color for p in points3D ], dtype = np .uint8 )
270+ points = np .array ([p .xyz for p in points3D ], dtype = np .float32 )
271+
213272 point_indices = {
214273 k : np .array (v ).astype (np .int32 ) for k , v in point_indices .items ()
215274 }
0 commit comments