@@ -121,3 +121,90 @@ def test_process_images_skip_colmap(tmp_path: Path):
121121 )
122122 dataparser_poses = np .linalg .inv (dataparser_poses )
123123 np .testing .assert_allclose (gt_poses , dataparser_poses , rtol = 0 , atol = 1e-5 )
124+
125+
126+ def test_process_images_recursively_skip_colmap (tmp_path : Path ):
127+ """
128+ Test ns-process-data images when images contains subdirectories"
129+ """
130+ # Mock a colmap sparse model
131+ width = 100
132+ height = 150
133+ sparse_path = tmp_path / "sparse" / "0"
134+ sparse_path .mkdir (exist_ok = True , parents = True )
135+ (tmp_path / "images" ).mkdir (exist_ok = True , parents = True )
136+ write_cameras_binary (
137+ {1 : Camera (1 , "OPENCV" , width , height , [110 , 110 , 50 , 75 , 0 , 0 , 0 , 0 , 0 , 0 ])},
138+ sparse_path / "cameras.bin" ,
139+ )
140+ write_points3D_binary (
141+ {
142+ 1 : Point3D (
143+ id = 1 ,
144+ xyz = np .array ([0 , 0 , 0 ]),
145+ rgb = np .array ([0 , 0 , 0 ]),
146+ error = np .array ([0 ]),
147+ image_ids = np .array ([1 ]),
148+ point2D_idxs = np .array ([0 ]),
149+ ),
150+ },
151+ sparse_path / "points3D.bin" ,
152+ )
153+ frames = {}
154+ num_frames = 9
155+ num_subdirs = 3
156+ qvecs = random_quaternion (num_frames )
157+ tvecs = np .random .uniform (size = (num_frames , 3 ))
158+ original_poses = np .concatenate (
159+ (
160+ np .concatenate (
161+ (
162+ np .stack (list (map (qvec2rotmat , qvecs ))),
163+ tvecs [:, :, None ],
164+ ),
165+ - 1 ,
166+ ),
167+ np .array ([[[0 , 0 , 0 , 1 ]]], dtype = qvecs .dtype ).repeat (num_frames , 0 ),
168+ ),
169+ - 2 ,
170+ )
171+ for i in range (num_frames ):
172+ subdir = f"subdir_{ num_frames // num_subdirs } "
173+ frames [i + 1 ] = ColmapImage (i + 1 , qvecs [i ], tvecs [i ], 1 , f"{ subdir } /image_{ i } .png" , [], [])
174+ (tmp_path / "images" / subdir ).mkdir (parents = True , exist_ok = True )
175+ Image .new ("RGB" , (width , height )).save (tmp_path / "images" / subdir / f"image_{ i } .png" )
176+ write_images_binary (frames , sparse_path / "images.bin" )
177+
178+ # Mock missing COLMAP and ffmpeg in the dev env
179+ old_path = os .environ .get ("PATH" , "" )
180+ os .environ ["PATH" ] = str (tmp_path / "mocked_bin" ) + f":{ old_path } "
181+ (tmp_path / "mocked_bin" ).mkdir ()
182+ (tmp_path / "mocked_bin" / "colmap" ).touch (mode = 0o777 )
183+ (tmp_path / "mocked_bin" / "ffmpeg" ).touch (mode = 0o777 )
184+
185+ # Convert images into a NerfStudio dataset
186+ cmd = ImagesToNerfstudioDataset (
187+ data = tmp_path / "images" , output_dir = tmp_path / "nerfstudio" , colmap_model_path = sparse_path , skip_colmap = True
188+ )
189+ cmd .main ()
190+ os .environ ["PATH" ] = old_path
191+
192+ assert (tmp_path / "nerfstudio" / "transforms.json" ).exists ()
193+ parser = NerfstudioDataParserConfig (
194+ data = tmp_path / "nerfstudio" ,
195+ downscale_factor = None ,
196+ orientation_method = "none" , # orientation_method,
197+ center_method = "none" ,
198+ auto_scale_poses = False ,
199+ ).setup ()
200+ outputs = parser .get_dataparser_outputs ("train" )
201+ assert len (outputs .image_filenames ) == 9
202+ assert torch .is_tensor (outputs .dataparser_transform )
203+
204+ # Test if the original poses can be obtained back
205+ dataparser_poses = outputs .transform_poses_to_original_space (outputs .cameras .camera_to_worlds , "opencv" ).numpy ()
206+ dataparser_poses = np .concatenate (
207+ (dataparser_poses , np .array ([[[0 , 0 , 0 , 1 ]]]).repeat (len (dataparser_poses ), 0 )), 1
208+ )
209+ dataparser_poses = np .linalg .inv (dataparser_poses )
210+ np .testing .assert_allclose (original_poses , dataparser_poses , rtol = 0 , atol = 1e-5 )
0 commit comments