From b8970eaf53fa873152cbb2609b3737c27bf305c3 Mon Sep 17 00:00:00 2001 From: Francesco Ioli Date: Sat, 13 Apr 2024 13:50:53 +0200 Subject: [PATCH] updated notebooks --- assets/example_cyprus/config_roma.yaml | 22 + georeference_by_gcp.ipynb | 537 ------------------------- notebooks/georeference_by_gcp.ipynb | 168 ++++---- notebooks/run_dense_pycolmap.ipynb | 492 +++++++++++++++++++--- src/deep_image_matching/io/h5_to_db.py | 2 +- 5 files changed, 561 insertions(+), 660 deletions(-) create mode 100644 assets/example_cyprus/config_roma.yaml delete mode 100644 georeference_by_gcp.ipynb diff --git a/assets/example_cyprus/config_roma.yaml b/assets/example_cyprus/config_roma.yaml new file mode 100644 index 0000000..046309b --- /dev/null +++ b/assets/example_cyprus/config_roma.yaml @@ -0,0 +1,22 @@ +# User configuration file + +general: + # tile_size: (3000, 2000) # This setting is overwriten by the the upsample_res setting + tile_overlap: 50 + geom_verification: none + gv_threshold: 3 + gv_confidence: 0.9999 + min_matches_per_tile: 3 + min_inliers_per_pair: 5 + min_inlier_ratio_per_pair: 0.2 + refine_intrinsics: False + preselection_pipeline: roma + +extractor: + name: no_extractor + +matcher: + name: roma + coarse_res: 560 + upsample_res: 860 + num_sampled_points: 10000 diff --git a/georeference_by_gcp.ipynb b/georeference_by_gcp.ipynb deleted file mode 100644 index 69e0d00..0000000 --- a/georeference_by_gcp.ipynb +++ /dev/null @@ -1,537 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Jupyter environment detected. Enabling Open3D WebVisualizer.\n", - "[Open3D INFO] WebRTC GUI backend enabled.\n", - "[Open3D INFO] WebRTCWindowSystem: HTTP handshake server disabled.\n", - "Deep Image Matching loaded in 2.726 seconds.\n" - ] - } - ], - "source": [ - "from pathlib import Path\n", - "\n", - "import h5py\n", - "import numpy as np\n", - "import open3d as o3d\n", - "import pandas as pd\n", - "import pycolmap\n", - "from deep_image_matching.thirdparty.transformations import (\n", - " affine_matrix_from_points,\n", - " decompose_matrix,\n", - ")\n", - "from deep_image_matching.triangulation import db_from_existing_poses\n", - "from deep_image_matching.utils import COLMAPDatabase, OutputCapture\n", - "\n", - "root_path = Path(\"datasets/belv_20230725\")\n", - "image_dir = root_path / \"images\"\n", - "\n", - "sfm_dir = root_path / \"results_superpoint+lightglue_bruteforce_quality_highest\"\n", - "sfm_rec_path = sfm_dir / \"reconstruction\"\n", - "\n", - "# Output path for the triangulated markers\n", - "output_path = root_path / \"marker_triang\"\n", - "db_path = output_path / \"database_markers.db\"\n", - "\n", - "# Image coordinates of the markers\n", - "markers_file = root_path / \"markers_image_20230725.csv\"\n", - "\n", - "# World coordinates of the markers\n", - "georef_points = root_path / \"markers_utm.csv\"\n", - "\n", - "output_path.mkdir(exist_ok=True, parents=True)\n", - "\n", - "# Get image list\n", - "images = sorted(image_dir.glob(\"*\"))\n", - "\n", - "# Define a subset of the markers to use (leve none to use all)\n", - "# markers_to_use = [\"D38\", \"T2\", \"F2\", \"F4\", \"F10\", \"F20\"]\n", - "markers_to_use = [\"D38\", \"F2\", \"F4\", \"F10\", \"F20\"]\n", - "\n", - "\n", - "# Dense reconstruction path to georeference\n", - "dense_rec_path = root_path / \"results_roma_bruteforce_quality_high/dense_model\"" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "p1_20230725_115953_IMG_1147.JPG :\n", - " [[4832.3403 1582.9143]\n", - " [2027.8405 3478.0388]\n", - " [ 725.7628 3845.6572]\n", - " [4092.3181 2256.6533]\n", - " [3416.7617 3300.4646]]\n", - "p2_20230725_120026_IMG_0885.JPG :\n", - " [[3194.6826 1729.3799]\n", - " [3573.5344 3204.1846]\n", - " [4105.1738 3556.3171]\n", - " [4139.4536 1980.674 ]\n", - " [5928.5713 2516.8994]]\n", - "matches idx:\n", - " [[0 0]\n", - " [1 1]\n", - " [2 2]\n", - " [3 3]\n", - " [4 4]]\n" - ] - } - ], - "source": [ - "# Read marker image coordinates and create keypoints dictionary with the form:\n", - "# {\"image_name\": keypoints_array}\n", - "# {\n", - "# \"image1.jpg\": np.array([[x1, y1], [x2, y2], ...]),\n", - "# \"image2.jpg\": np.array([[x1, y1], [x2, y2], ...]),\n", - "# ...\n", - "# }\n", - "markers_image = pd.read_csv(markers_file, header=None, names=[\"image\", \"marker\", \"x\", \"y\"])\n", - "markers_image.sort_values([\"image\", \"marker\"], inplace=True, ascending=True)\n", - "if markers_to_use:\n", - " markers_image = markers_image[markers_image[\"marker\"].isin(markers_to_use)]\n", - "\n", - "kpts = {}\n", - "for image, gr in markers_image.groupby(\"image\"):\n", - " image = image + \".JPG\"\n", - " kpts[image] = gr[[\"x\", \"y\"]].values\n", - "for k, v in kpts.items():\n", - " print(k, \":\\n\", v)\n", - "\n", - "# Manually create 1-to-1 matches array\n", - "ids = np.arange(0, len(kpts[images[0].name]))\n", - "matches_idx = np.array([ids, ids]).astype(np.int64).T\n", - "print(\"matches idx:\\n\", matches_idx)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "# # plot image with markers\n", - "# import cv2\n", - "# from matplotlib import pyplot as plt\n", - "\n", - "# image_id = 1\n", - "\n", - "# img = cv2.cvtColor(cv2.imread(str(images[image_id])), cv2.COLOR_BGR2RGB)\n", - "# plt.imshow(img)\n", - "# plt.scatter(\n", - "# kpts[images[image_id].name][:, 0],\n", - "# kpts[images[image_id].name][:, 1],\n", - "# c=\"r\",\n", - "# s=10,\n", - "# )" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "# Save features to h5 file\n", - "features_h5 = output_path / \"features.h5\"\n", - "with h5py.File(features_h5, \"w\") as f:\n", - " for image in images:\n", - " image_name = image.name\n", - " kp = kpts[image_name]\n", - " f.create_group(image_name)\n", - " f[image_name].create_dataset(\"keypoints\", data=kp, dtype=np.float32)\n", - "\n", - "# Save matches to h5 file\n", - "matches_h5 = output_path / \"matches.h5\"\n", - "with h5py.File(matches_h5, \"w\") as f:\n", - " image0, image1 = images[0].name, images[1].name\n", - " gr0 = f.create_group(image0)\n", - " gr0.create_dataset(image1, data=matches_idx, dtype=np.int64)\n", - "\n", - "pair_file = sfm_dir / \"pairs.txt\"\n", - "\n", - "# # print features_h5 content\n", - "# with h5py.File(features_h5, \"r\") as f:\n", - "# print(f.keys())\n", - "# print(f[images[0].name].keys())\n", - "# print(f[images[0].name][\"keypoints\"][:])\n", - "# print(f[images[1].name].keys())\n", - "# print(f[images[1].name][\"keypoints\"][:])\n", - "\n", - "# # Print matches.h5 content\n", - "# with h5py.File(matches_h5, \"r\") as f:\n", - "# print(f.keys())\n", - "# g0 = f[images[0].name]\n", - "# print(g0.keys())\n", - "# g1 = g0[images[1].name]\n", - "# print(g1.__array__())" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[1;33m2024-04-12 12:06:35 | [WARNING ] The database already exists, deleting it.\u001b[0m\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Importing keypoints: 100%|██████████| 2/2 [00:00<00:00, 1053.71it/s]\n", - "Importing matches: 100%|██████████| 1/1 [00:00<00:00, 1454.34it/s]\n" - ] - } - ], - "source": [ - "sfm_rec = pycolmap.Reconstruction(sfm_rec_path)\n", - "\n", - "# Create a new database with the dense features and the known camera poses\n", - "\n", - "db_from_existing_poses(\n", - " db_path,\n", - " features_h5,\n", - " matches_h5,\n", - " sfm_rec_path,\n", - " pair_file,\n", - " do_geometric_verification=False,\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "# Define the options for the triangulation according to the IncrementalPipelineOptions available in pycolmap\n", - "# print(pycolmap.IncrementalPipelineOptions().summary())\n", - "opt = dict(\n", - " min_num_matches=3,\n", - " triangulation=dict(\n", - " ignore_two_view_tracks=False,\n", - " ),\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time\n", - " 0 2.837421e+00 0.00e+00 1.32e+03 0.00e+00 0.00e+00 1.00e+04 0 3.00e-05 9.20e-05\n", - " 1 2.471128e+00 3.66e-01 1.42e-01 5.44e-04 1.00e+00 3.00e+04 0 4.39e-05 4.82e-04\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "I20240412 12:06:35.960026 3086923 misc.cc:198] \n", - "==============================================================================\n", - "Loading database\n", - "==============================================================================\n", - "I20240412 12:06:35.961102 3086923 database_cache.cc:54] Loading cameras...\n", - "I20240412 12:06:35.961148 3086923 database_cache.cc:64] 2 in 0.000s\n", - "I20240412 12:06:35.961160 3086923 database_cache.cc:72] Loading matches...\n", - "I20240412 12:06:35.961179 3086923 database_cache.cc:78] 1 in 0.000s\n", - "I20240412 12:06:35.961184 3086923 database_cache.cc:94] Loading images...\n", - "I20240412 12:06:35.961223 3086923 database_cache.cc:143] 2 in 0.000s (connected 2)\n", - "I20240412 12:06:35.961228 3086923 database_cache.cc:154] Building correspondence graph...\n", - "I20240412 12:06:35.961241 3086923 database_cache.cc:190] in 0.000s (ignored 0)\n", - "I20240412 12:06:35.961364 3086923 timer.cc:91] Elapsed time: 0.000 [minutes]\n", - "I20240412 12:06:35.961529 3086923 misc.cc:198] \n", - "==============================================================================\n", - "Triangulating image #1 (0)\n", - "==============================================================================\n", - "I20240412 12:06:35.961535 3086923 sfm.cc:473] => Image sees 0 / 5 points\n", - "I20240412 12:06:35.961606 3086923 sfm.cc:478] => Triangulated 5 points\n", - "I20240412 12:06:35.961611 3086923 misc.cc:198] \n", - "==============================================================================\n", - "Triangulating image #2 (1)\n", - "==============================================================================\n", - "I20240412 12:06:35.961614 3086923 sfm.cc:473] => Image sees 5 / 5 points\n", - "I20240412 12:06:35.961618 3086923 sfm.cc:478] => Triangulated 0 points\n", - "I20240412 12:06:35.961622 3086923 misc.cc:198] \n", - "==============================================================================\n", - "Retriangulation\n", - "==============================================================================\n", - "I20240412 12:06:35.961628 3086923 incremental_mapper.cc:175] => Completed observations: 0\n", - "I20240412 12:06:35.961632 3086923 incremental_mapper.cc:178] => Merged observations: 0\n", - "I20240412 12:06:35.961642 3086923 misc.cc:198] \n", - "==============================================================================\n", - "Bundle adjustment\n", - "==============================================================================\n", - "I20240412 12:06:35.962460 3086923 misc.cc:205] \n", - "Bundle adjustment report\n", - "------------------------\n", - "I20240412 12:06:35.962476 3086923 bundle_adjustment.cc:942] \n", - " Residuals : 20\n", - " Parameters : 15\n", - " Iterations : 2\n", - " Time : 0.000760794 [s]\n", - " Initial cost : 0.376658 [px]\n", - " Final cost : 0.351506 [px]\n", - " Termination : Convergence\n", - "\n", - "I20240412 12:06:35.962482 3086923 incremental_mapper.cc:175] => Completed observations: 0\n", - "I20240412 12:06:35.962486 3086923 incremental_mapper.cc:178] => Merged observations: 0\n", - "I20240412 12:06:35.962497 3086923 incremental_mapper.cc:160] => Filtered observations: 0\n", - "I20240412 12:06:35.962500 3086923 sfm.cc:521] => Changed observations: 0.000000\n", - "I20240412 12:06:35.962515 3086923 misc.cc:198] \n", - "==============================================================================\n", - "Extracting colors\n", - "==============================================================================\n" - ] - } - ], - "source": [ - "# Run the triangulation with the known camera poses\n", - "verbose = True\n", - "with OutputCapture(verbose):\n", - " with pycolmap.ostream():\n", - " reconstruction = pycolmap.triangulate_points(\n", - " sfm_rec,\n", - " db_path,\n", - " image_dir,\n", - " output_path,\n", - " options=opt,\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1490 Point3D(xyz=[6.80372, -1.44258, 14.1866], color=[143, 139, 86], error=0.0227758, track=Track(length=2))\n", - "1491 Point3D(xyz=[1.99842, 1.31624, 8.79684], color=[163, 107, 101], error=0.358146, track=Track(length=2))\n", - "1492 Point3D(xyz=[0.691425, 1.45129, 6.99773], color=[244, 228, 229], error=1.49442, track=Track(length=2))\n", - "1493 Point3D(xyz=[4.91828, -0.280677, 10.1311], color=[234, 242, 242], error=0.00433232, track=Track(length=2))\n", - "1494 Point3D(xyz=[3.78636, 0.790003, 6.84661], color=[254, 243, 251], error=0.143108, track=Track(length=2))\n" - ] - } - ], - "source": [ - "pts = reconstruction.points3D\n", - "for i, pt in sorted(pts.items()):\n", - " print(i, pt)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[Point2D(xy=[4832.84, 1583.41], point3D_id=1490),\n", - " Point2D(xy=[2028.34, 3478.54], point3D_id=1491),\n", - " Point2D(xy=[726.263, 3846.16], point3D_id=1492),\n", - " Point2D(xy=[4092.82, 2257.15], point3D_id=1493),\n", - " Point2D(xy=[3417.26, 3300.96], point3D_id=1494)]" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "img = reconstruction.images[1]\n", - "img.points2D" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[ 6.80371791 -1.44258202 14.18655572]\n", - " [ 1.99842367 1.31624029 8.79683948]\n", - " [ 0.69142514 1.45128844 6.99772945]\n", - " [ 4.91827547 -0.28067699 10.13106025]\n", - " [ 3.78635656 0.79000304 6.8466085 ]]\n" - ] - } - ], - "source": [ - "image_id = 1\n", - "local = []\n", - "for pt in reconstruction.images[image_id].points2D:\n", - " if not isinstance(pt.point3D_id, int) or pt.point3D_id < 0:\n", - " print(f\"Point {pt.point2D_idx} is not triangulated\")\n", - " continue\n", - " local.append(reconstruction.points3D[pt.point3D_id].xyz)\n", - "local = np.array(local)\n", - "print(local)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " X Y Z\n", - "Label \n", - "D38 416273.191792 5.091048e+06 1912.062781\n", - "F10 416458.548600 5.091086e+06 1841.996800\n", - "F2 416514.111700 5.091104e+06 1839.217200\n", - "F20 416376.149300 5.091104e+06 1883.523600\n", - "F4 416451.502700 5.091155e+06 1857.065600\n" - ] - } - ], - "source": [ - "# Read the georeferenced points\n", - "georef = pd.read_csv(georef_points)\n", - "georef.index = georef[\"Label\"]\n", - "georef.drop(columns=[\"Label\"], inplace=True)\n", - "georef.sort_values(\"Label\", inplace=True)\n", - "if markers_to_use:\n", - " georef = georef[georef.index.isin(markers_to_use)]\n", - "print(georef)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Translation: [4.16637855e+05 5.09123835e+06 1.88055817e+03] m\n", - "Angles: [-91.10481171 -0.31795999 143.15041448] deg\n", - "Scale: 25.1244%\n" - ] - } - ], - "source": [ - "# Estimate a Helmert transformation between the mean pcd and the ref.\n", - "T = affine_matrix_from_points(\n", - " local.T, georef.to_numpy().T, shear=False, scale=True, usesvd=True\n", - ")\n", - "scale, _, angles, translation, _ = decompose_matrix(T)\n", - "scale_percent = scale.mean() - 1\n", - "angles_deg = np.rad2deg(angles)\n", - "\n", - "print(f\"Translation: {translation} m\")\n", - "print(f\"Angles: {angles_deg} deg\")\n", - "print(f\"Scale: {scale_percent:.6}%\")" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "# Export the dense point cloud from roma to a ply file\n", - "dense_rec = pycolmap.Reconstruction(dense_rec_path)\n", - "dense_rec.export_PLY(dense_rec_path / \"dense.ply\")" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Read and transform the point cloud\n", - "pcd = o3d.io.read_point_cloud(str(dense_rec_path / \"dense_merged_clean.ply\"))\n", - "georef_pcd = pcd.transform(T)\n", - "o3d.io.write_point_cloud(str(dense_rec_path / \"dense_georef.ply\"), georef_pcd)" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "PosixPath('datasets/belv_20230725/results_roma_bruteforce_quality_high/dense_model')" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "dense_rec_path" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "deep-image-matching", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.13" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/georeference_by_gcp.ipynb b/notebooks/georeference_by_gcp.ipynb index 59b7842..69e0d00 100644 --- a/notebooks/georeference_by_gcp.ipynb +++ b/notebooks/georeference_by_gcp.ipynb @@ -2,17 +2,34 @@ "cells": [ { "cell_type": "code", - "execution_count": 9, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Jupyter environment detected. Enabling Open3D WebVisualizer.\n", + "[Open3D INFO] WebRTC GUI backend enabled.\n", + "[Open3D INFO] WebRTCWindowSystem: HTTP handshake server disabled.\n", + "Deep Image Matching loaded in 2.726 seconds.\n" + ] + } + ], "source": [ "from pathlib import Path\n", "\n", "import h5py\n", "import numpy as np\n", + "import open3d as o3d\n", "import pandas as pd\n", "import pycolmap\n", - "from deep_image_matching.utils import COLMAPDatabase\n", + "from deep_image_matching.thirdparty.transformations import (\n", + " affine_matrix_from_points,\n", + " decompose_matrix,\n", + ")\n", + "from deep_image_matching.triangulation import db_from_existing_poses\n", + "from deep_image_matching.utils import COLMAPDatabase, OutputCapture\n", "\n", "root_path = Path(\"datasets/belv_20230725\")\n", "image_dir = root_path / \"images\"\n", @@ -20,11 +37,16 @@ "sfm_dir = root_path / \"results_superpoint+lightglue_bruteforce_quality_highest\"\n", "sfm_rec_path = sfm_dir / \"reconstruction\"\n", "\n", + "# Output path for the triangulated markers\n", "output_path = root_path / \"marker_triang\"\n", "db_path = output_path / \"database_markers.db\"\n", "\n", + "# Image coordinates of the markers\n", "markers_file = root_path / \"markers_image_20230725.csv\"\n", "\n", + "# World coordinates of the markers\n", + "georef_points = root_path / \"markers_utm.csv\"\n", + "\n", "output_path.mkdir(exist_ok=True, parents=True)\n", "\n", "# Get image list\n", @@ -32,12 +54,16 @@ "\n", "# Define a subset of the markers to use (leve none to use all)\n", "# markers_to_use = [\"D38\", \"T2\", \"F2\", \"F4\", \"F10\", \"F20\"]\n", - "markers_to_use = [\"D38\", \"F2\", \"F4\", \"F10\", \"F20\"]" + "markers_to_use = [\"D38\", \"F2\", \"F4\", \"F10\", \"F20\"]\n", + "\n", + "\n", + "# Dense reconstruction path to georeference\n", + "dense_rec_path = root_path / \"results_roma_bruteforce_quality_high/dense_model\"" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -93,7 +119,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -115,7 +141,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -156,30 +182,26 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "\u001b[1;33m2024-04-10 17:40:41 | [WARNING ] The database already exists, deleting it.\u001b[0m\n" + "\u001b[1;33m2024-04-12 12:06:35 | [WARNING ] The database already exists, deleting it.\u001b[0m\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "Importing keypoints: 100%|██████████| 2/2 [00:00<00:00, 1077.95it/s]\n", - "Importing matches: 100%|██████████| 1/1 [00:00<00:00, 862.67it/s]\n" + "Importing keypoints: 100%|██████████| 2/2 [00:00<00:00, 1053.71it/s]\n", + "Importing matches: 100%|██████████| 1/1 [00:00<00:00, 1454.34it/s]\n" ] } ], "source": [ - "import pycolmap\n", - "from deep_image_matching.triangulation import db_from_existing_poses\n", - "from deep_image_matching.utils import OutputCapture\n", - "\n", "sfm_rec = pycolmap.Reconstruction(sfm_rec_path)\n", "\n", "# Create a new database with the dense features and the known camera poses\n", @@ -196,7 +218,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -212,7 +234,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -220,66 +242,66 @@ "output_type": "stream", "text": [ "iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time\n", - " 0 2.837421e+00 0.00e+00 1.32e+03 0.00e+00 0.00e+00 1.00e+04 0 7.82e-04 2.60e-03\n", - " 1 2.471128e+00 3.66e-01 1.42e-01 5.44e-04 1.00e+00 3.00e+04 0 1.63e-03 4.84e-03\n" + " 0 2.837421e+00 0.00e+00 1.32e+03 0.00e+00 0.00e+00 1.00e+04 0 3.00e-05 9.20e-05\n", + " 1 2.471128e+00 3.66e-01 1.42e-01 5.44e-04 1.00e+00 3.00e+04 0 4.39e-05 4.82e-04\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "I20240410 17:40:41.086529 1134638 misc.cc:198] \n", + "I20240412 12:06:35.960026 3086923 misc.cc:198] \n", "==============================================================================\n", "Loading database\n", "==============================================================================\n", - "I20240410 17:40:41.091778 1134638 database_cache.cc:54] Loading cameras...\n", - "I20240410 17:40:41.091833 1134638 database_cache.cc:64] 2 in 0.000s\n", - "I20240410 17:40:41.091852 1134638 database_cache.cc:72] Loading matches...\n", - "I20240410 17:40:41.092137 1134638 database_cache.cc:78] 1 in 0.000s\n", - "I20240410 17:40:41.092149 1134638 database_cache.cc:94] Loading images...\n", - "I20240410 17:40:41.092454 1134638 database_cache.cc:143] 2 in 0.000s (connected 2)\n", - "I20240410 17:40:41.092463 1134638 database_cache.cc:154] Building correspondence graph...\n", - "I20240410 17:40:41.092716 1134638 database_cache.cc:190] in 0.000s (ignored 0)\n", - "I20240410 17:40:41.093197 1134638 timer.cc:91] Elapsed time: 0.000 [minutes]\n", - "I20240410 17:40:41.093751 1134638 misc.cc:198] \n", + "I20240412 12:06:35.961102 3086923 database_cache.cc:54] Loading cameras...\n", + "I20240412 12:06:35.961148 3086923 database_cache.cc:64] 2 in 0.000s\n", + "I20240412 12:06:35.961160 3086923 database_cache.cc:72] Loading matches...\n", + "I20240412 12:06:35.961179 3086923 database_cache.cc:78] 1 in 0.000s\n", + "I20240412 12:06:35.961184 3086923 database_cache.cc:94] Loading images...\n", + "I20240412 12:06:35.961223 3086923 database_cache.cc:143] 2 in 0.000s (connected 2)\n", + "I20240412 12:06:35.961228 3086923 database_cache.cc:154] Building correspondence graph...\n", + "I20240412 12:06:35.961241 3086923 database_cache.cc:190] in 0.000s (ignored 0)\n", + "I20240412 12:06:35.961364 3086923 timer.cc:91] Elapsed time: 0.000 [minutes]\n", + "I20240412 12:06:35.961529 3086923 misc.cc:198] \n", "==============================================================================\n", "Triangulating image #1 (0)\n", "==============================================================================\n", - "I20240410 17:40:41.093762 1134638 sfm.cc:473] => Image sees 0 / 5 points\n", - "I20240410 17:40:41.095908 1134638 sfm.cc:478] => Triangulated 5 points\n", - "I20240410 17:40:41.095934 1134638 misc.cc:198] \n", + "I20240412 12:06:35.961535 3086923 sfm.cc:473] => Image sees 0 / 5 points\n", + "I20240412 12:06:35.961606 3086923 sfm.cc:478] => Triangulated 5 points\n", + "I20240412 12:06:35.961611 3086923 misc.cc:198] \n", "==============================================================================\n", "Triangulating image #2 (1)\n", "==============================================================================\n", - "I20240410 17:40:41.095938 1134638 sfm.cc:473] => Image sees 5 / 5 points\n", - "I20240410 17:40:41.095944 1134638 sfm.cc:478] => Triangulated 0 points\n", - "I20240410 17:40:41.095949 1134638 misc.cc:198] \n", + "I20240412 12:06:35.961614 3086923 sfm.cc:473] => Image sees 5 / 5 points\n", + "I20240412 12:06:35.961618 3086923 sfm.cc:478] => Triangulated 0 points\n", + "I20240412 12:06:35.961622 3086923 misc.cc:198] \n", "==============================================================================\n", "Retriangulation\n", "==============================================================================\n", - "I20240410 17:40:41.095957 1134638 incremental_mapper.cc:175] => Completed observations: 0\n", - "I20240410 17:40:41.095961 1134638 incremental_mapper.cc:178] => Merged observations: 0\n", - "I20240410 17:40:41.096284 1134638 misc.cc:198] \n", + "I20240412 12:06:35.961628 3086923 incremental_mapper.cc:175] => Completed observations: 0\n", + "I20240412 12:06:35.961632 3086923 incremental_mapper.cc:178] => Merged observations: 0\n", + "I20240412 12:06:35.961642 3086923 misc.cc:198] \n", "==============================================================================\n", "Bundle adjustment\n", "==============================================================================\n", - "I20240410 17:40:41.103292 1134638 misc.cc:205] \n", + "I20240412 12:06:35.962460 3086923 misc.cc:205] \n", "Bundle adjustment report\n", "------------------------\n", - "I20240410 17:40:41.103349 1134638 bundle_adjustment.cc:942] \n", + "I20240412 12:06:35.962476 3086923 bundle_adjustment.cc:942] \n", " Residuals : 20\n", " Parameters : 15\n", " Iterations : 2\n", - " Time : 0.005234 [s]\n", + " Time : 0.000760794 [s]\n", " Initial cost : 0.376658 [px]\n", " Final cost : 0.351506 [px]\n", " Termination : Convergence\n", "\n", - "I20240410 17:40:41.103365 1134638 incremental_mapper.cc:175] => Completed observations: 0\n", - "I20240410 17:40:41.103372 1134638 incremental_mapper.cc:178] => Merged observations: 0\n", - "I20240410 17:40:41.103386 1134638 incremental_mapper.cc:160] => Filtered observations: 0\n", - "I20240410 17:40:41.103390 1134638 sfm.cc:521] => Changed observations: 0.000000\n", - "I20240410 17:40:41.103744 1134638 misc.cc:198] \n", + "I20240412 12:06:35.962482 3086923 incremental_mapper.cc:175] => Completed observations: 0\n", + "I20240412 12:06:35.962486 3086923 incremental_mapper.cc:178] => Merged observations: 0\n", + "I20240412 12:06:35.962497 3086923 incremental_mapper.cc:160] => Filtered observations: 0\n", + "I20240412 12:06:35.962500 3086923 sfm.cc:521] => Changed observations: 0.000000\n", + "I20240412 12:06:35.962515 3086923 misc.cc:198] \n", "==============================================================================\n", "Extracting colors\n", "==============================================================================\n" @@ -302,7 +324,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -325,7 +347,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -338,7 +360,7 @@ " Point2D(xy=[3417.26, 3300.96], point3D_id=1494)]" ] }, - "execution_count": 17, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -350,7 +372,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -379,7 +401,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -398,7 +420,6 @@ ], "source": [ "# Read the georeferenced points\n", - "georef_points = root_path / \"markers_utm.csv\"\n", "georef = pd.read_csv(georef_points)\n", "georef.index = georef[\"Label\"]\n", "georef.drop(columns=[\"Label\"], inplace=True)\n", @@ -410,16 +431,13 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Jupyter environment detected. Enabling Open3D WebVisualizer.\n", - "[Open3D INFO] WebRTC GUI backend enabled.\n", - "[Open3D INFO] WebRTCWindowSystem: HTTP handshake server disabled.\n", "Translation: [4.16637855e+05 5.09123835e+06 1.88055817e+03] m\n", "Angles: [-91.10481171 -0.31795999 143.15041448] deg\n", "Scale: 25.1244%\n" @@ -427,13 +445,6 @@ } ], "source": [ - "# Estimate affine transformation\n", - "import open3d as o3d\n", - "from deep_image_matching.thirdparty.transformations import (\n", - " affine_matrix_from_points,\n", - " decompose_matrix,\n", - ")\n", - "\n", "# Estimate a Helmert transformation between the mean pcd and the ref.\n", "T = affine_matrix_from_points(\n", " local.T, georef.to_numpy().T, shear=False, scale=True, usesvd=True\n", @@ -449,19 +460,18 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "# Export the dense point cloud from roma to a ply file\n", - "dense_rec_path = root_path / \"results_roma_bruteforce_quality_high/dense_model\"\n", "dense_rec = pycolmap.Reconstruction(dense_rec_path)\n", "dense_rec.export_PLY(dense_rec_path / \"dense.ply\")" ] }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -470,17 +480,37 @@ "True" ] }, - "execution_count": 23, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Read and transform the point cloud\n", - "pcd = o3d.io.read_point_cloud(str(dense_rec_path / \"dense.ply\"))\n", + "pcd = o3d.io.read_point_cloud(str(dense_rec_path / \"dense_merged_clean.ply\"))\n", "georef_pcd = pcd.transform(T)\n", "o3d.io.write_point_cloud(str(dense_rec_path / \"dense_georef.ply\"), georef_pcd)" ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "PosixPath('datasets/belv_20230725/results_roma_bruteforce_quality_high/dense_model')" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dense_rec_path" + ] } ], "metadata": { diff --git a/notebooks/run_dense_pycolmap.ipynb b/notebooks/run_dense_pycolmap.ipynb index 825028e..879f31c 100644 --- a/notebooks/run_dense_pycolmap.ipynb +++ b/notebooks/run_dense_pycolmap.ipynb @@ -8,7 +8,7 @@ "\n", "This notebook will guide you for running a dense reconstruction with LOFTR or RoMa starting from an existing reconstruction (e.g., carried out with SuperPoint+LightGlue that are more robust for image orientation).\n", "\n", - "This notebook will perform the dense matching with RoMa and it uses pycolmap to triangulate the points from the existing camera poses using pycolmap.\n" + "This notebook will perform the dense matching with RoMa and it uses pycolmap to triangulate the points from the existing camera poses using pycolmap. To build a sparse reconstruction with SuperPoint+LightGlue, you can use the notebook `sfm_pipeline.ipynb`.\n" ] }, { @@ -20,19 +20,50 @@ "name": "stdout", "output_type": "stream", "text": [ - "Deep Image Matching loaded in 3.552 seconds.\n" - ] - }, - { - "ename": "FileNotFoundError", - "evalue": "Configuration file /home/francesco/phd/deep-image-matching/assets/example_cyprus/config_roma.yaml not found.", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mFileNotFoundError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[1], line 24\u001b[0m\n\u001b[1;32m 9\u001b[0m params \u001b[38;5;241m=\u001b[39m {\n\u001b[1;32m 10\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mdir\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m../assets/example_cyprus\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 11\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mpipeline\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mroma\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mverbose\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[1;32m 21\u001b[0m }\n\u001b[1;32m 23\u001b[0m \u001b[38;5;66;03m# Build configuration\u001b[39;00m\n\u001b[0;32m---> 24\u001b[0m config \u001b[38;5;241m=\u001b[39m \u001b[43mdim\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mConfig\u001b[49m\u001b[43m(\u001b[49m\u001b[43mparams\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 25\u001b[0m imgs_dir \u001b[38;5;241m=\u001b[39m config\u001b[38;5;241m.\u001b[39mgeneral[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mimage_dir\u001b[39m\u001b[38;5;124m\"\u001b[39m]\n\u001b[1;32m 26\u001b[0m output_dir \u001b[38;5;241m=\u001b[39m config\u001b[38;5;241m.\u001b[39mgeneral[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124moutput_dir\u001b[39m\u001b[38;5;124m\"\u001b[39m]\n", - "File \u001b[0;32m~/phd/deep-image-matching/src/deep_image_matching/config.py:303\u001b[0m, in \u001b[0;36mConfig.__init__\u001b[0;34m(self, args)\u001b[0m\n\u001b[1;32m 301\u001b[0m config_file \u001b[38;5;241m=\u001b[39m Path(args[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mconfig_file\u001b[39m\u001b[38;5;124m\"\u001b[39m])\u001b[38;5;241m.\u001b[39mresolve()\n\u001b[1;32m 302\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m config_file\u001b[38;5;241m.\u001b[39mexists():\n\u001b[0;32m--> 303\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mFileNotFoundError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mConfiguration file \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mconfig_file\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m not found.\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 304\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mupdate_from_yaml(config_file)\n\u001b[1;32m 305\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprint()\n", - "\u001b[0;31mFileNotFoundError\u001b[0m: Configuration file /home/francesco/phd/deep-image-matching/assets/example_cyprus/config_roma.yaml not found." + "Deep Image Matching loaded in 3.127 seconds.\n", + "Using a custom configuration file: /home/francesco/phd/deep-image-matching/assets/example_cyprus/config_roma.yaml\n", + "Config general:\n", + "{'camera_options': '../assets/example_cyprus/cameras.yaml',\n", + " 'db_path': None,\n", + " 'geom_verification': ,\n", + " 'graph': True,\n", + " 'gv_confidence': 0.9999,\n", + " 'gv_threshold': 3,\n", + " 'image_dir': PosixPath('../assets/example_cyprus/images'),\n", + " 'matching_strategy': 'matching_lowres',\n", + " 'min_inlier_ratio_per_pair': 0.2,\n", + " 'min_inliers_per_pair': 5,\n", + " 'min_matches_per_tile': 3,\n", + " 'openmvg_conf': None,\n", + " 'output_dir': PosixPath('../assets/example_cyprus/results_roma_matching_lowres_quality_medium'),\n", + " 'overlap': None,\n", + " 'pair_file': PosixPath('../assets/example_cyprus/results_roma_matching_lowres_quality_medium/pairs.txt'),\n", + " 'preselection_pipeline': 'roma',\n", + " 'quality': ,\n", + " 'refine_intrinsics': False,\n", + " 'retrieval': None,\n", + " 'skip_reconstruction': True,\n", + " 'tile_overlap': 50,\n", + " 'tile_preselection_size': 1000,\n", + " 'tile_selection': ,\n", + " 'tile_size': (2400, 2000),\n", + " 'try_match_full_images': False,\n", + " 'upright': False,\n", + " 'verbose': False}\n", + "\n", + "\n", + "Config extractor:\n", + "{'name': 'no_extractor'}\n", + "\n", + "\n", + "Config matcher:\n", + "{'coarse_res': 560,\n", + " 'name': 'roma',\n", + " 'num_sampled_points': 10000,\n", + " 'pretrained': 'outdoor',\n", + " 'upsample_res': 860}\n", + "\n", + "\n" ] } ], @@ -45,57 +76,34 @@ "\n", "logger = dim.setup_logger(\"dim\")\n", "\n", + "# Define the paraemters for the dense matching\n", "params = {\n", " \"dir\": \"../assets/example_cyprus\",\n", " \"pipeline\": \"roma\",\n", " \"config_file\": \"../assets/example_cyprus/config_roma.yaml\",\n", - " \"strategy\": \"bruteforce\",\n", - " \"quality\": \"high\",\n", - " \"tiling\": \"preselection\",\n", + " \"strategy\": \"matching_lowres\",\n", + " \"quality\": \"medium\",\n", + " \"tiling\": \"none\",\n", " \"skip_reconstruction\": True,\n", " \"force\": True,\n", " \"camera_options\": \"../assets/example_cyprus/cameras.yaml\",\n", " \"openmvg\": None,\n", - " \"verbose\": True,\n", "}\n", "\n", - "# Build configuration\n", - "config = dim.Config(params)\n", - "imgs_dir = config.general[\"image_dir\"]\n", - "output_dir = config.general[\"output_dir\"]\n", - "config.save()" + "# Build the configuration object\n", + "config = dim.Config(params)" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "# Initialize ImageMatcher class\n", - "matcher = dim.ImageMatcher(config)\n", - "\n", - "# Run image matching\n", - "feature_path, match_path = matcher.run()\n", - "\n", - "# Read camera options\n", - "with open(config.general[\"camera_options\"], \"r\") as file:\n", - " camera_options = yaml.safe_load(file)\n", - "\n", - "# Export in colmap format\n", - "database_path = output_dir / \"database.db\"\n", - "dim.io.export_to_colmap(\n", - " img_dir=imgs_dir,\n", - " feature_path=feature_path,\n", - " match_path=match_path,\n", - " database_path=database_path,\n", - " camera_options=camera_options,\n", - ")" + "Define the parameters for building the dense reconstruction starting from the known camera poses computed previously.\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -103,8 +111,8 @@ "root_path = config.general[\"output_dir\"].parent\n", "image_dir = config.general[\"image_dir\"]\n", "\n", - "# Path to the pre-computed COLMAP reconstuction with the knwon poses\n", - "sfm_path = root_path / \"results_superpoint+lightglue_bruteforce_quality_highest\"\n", + "# Path to the pre-computed COLMAP reconstuction with the knwon poses (set your own path)\n", + "sfm_path = root_path / \"results_superpoint+lightglue_matching_lowres_quality_medium\"\n", "sfm_db_path = sfm_path / \"database.db\"\n", "sfm_rec_path = sfm_path / \"reconstruction\"\n", "\n", @@ -119,17 +127,223 @@ "model_path = dense_path / \"dense_model\"\n", "model_path.mkdir(exist_ok=True, parents=True)\n", "\n", - "# Do geometric verification of the dense features (with known camera poses)\n", + "# Do geometric verification of the dense features (computing the epipolar error)\n", "do_geometric_verification = True\n", "max_error = 6\n", + "\n", + "# Define trinagulation parameters\n", "triang_min_angle = 0.1" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[0;37m2024-04-13 13:38:19 | [INFO ] RoMa always use a coarse resolution of 860 pixels, regardless of the quality parameter resolution.\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/francesco/miniforge3/envs/deep-image-matching/lib/python3.10/site-packages/torchvision/models/_utils.py:208: UserWarning: The parameter 'pretrained' is deprecated since 0.13 and may be removed in the future, please use 'weights' instead.\n", + " warnings.warn(\n", + "/home/francesco/miniforge3/envs/deep-image-matching/lib/python3.10/site-packages/torchvision/models/_utils.py:223: UserWarning: Arguments other than a weight enum or `None` for 'weights' are deprecated since 0.13 and may be removed in the future. The current behavior is equivalent to passing `weights=None`.\n", + " warnings.warn(msg)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using coarse resolution (560, 560), and upsample res (860, 860)\n", + "\u001b[0;37m2024-04-13 13:38:25 | [INFO ] Running image matching with the following configuration:\u001b[0m\n", + "\u001b[0;37m2024-04-13 13:38:25 | [INFO ] Image folder: ../assets/example_cyprus/images\u001b[0m\n", + "\u001b[0;37m2024-04-13 13:38:25 | [INFO ] Output folder: ../assets/example_cyprus/results_roma_matching_lowres_quality_medium\u001b[0m\n", + "\u001b[0;37m2024-04-13 13:38:25 | [INFO ] Number of images: 10\u001b[0m\n", + "\u001b[0;37m2024-04-13 13:38:25 | [INFO ] Matching strategy: matching_lowres\u001b[0m\n", + "\u001b[0;37m2024-04-13 13:38:25 | [INFO ] Image quality: MEDIUM\u001b[0m\n", + "\u001b[0;37m2024-04-13 13:38:25 | [INFO ] Tile selection: NONE\u001b[0m\n", + "\u001b[0;37m2024-04-13 13:38:25 | [INFO ] Feature extraction method: no_extractor\u001b[0m\n", + "\u001b[0;37m2024-04-13 13:38:25 | [INFO ] Matching method: roma\u001b[0m\n", + "\u001b[0;37m2024-04-13 13:38:25 | [INFO ] Geometric verification: NONE\u001b[0m\n", + "\u001b[0;37m2024-04-13 13:38:25 | [INFO ] CUDA available: True\u001b[0m\n", + "\u001b[0;37m2024-04-13 13:38:25 | [INFO ] Low resolution matching, generating pairs ..\u001b[0m\n", + "Loaded SuperPoint model\n", + "\u001b[0;37m2024-04-13 13:38:25 | [INFO ] Extracting features from downsampled images...\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 10/10 [00:02<00:00, 4.64it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[0;37m2024-04-13 13:38:28 | [INFO ] Matching downsampled images...\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "100%|██████████| 45/45 [00:01<00:00, 43.27it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[0;37m2024-04-13 13:38:29 | [INFO ] Found 28 pairs.\u001b[0m\n", + "\u001b[0;37m2024-04-13 13:38:29 | [INFO ] Extracting features with no_extractor...\u001b[0m\n", + "\u001b[0;37m2024-04-13 13:38:29 | [INFO ] no_extractor configuration: \u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'name': 'no_extractor'}\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 10/10 [00:00<00:00, 738.36it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[0;37m2024-04-13 13:38:29 | [INFO ] Features extracted!\u001b[0m\n", + "\u001b[0;37m2024-04-13 13:38:29 | [INFO ] Matching features with roma...\u001b[0m\n", + "\u001b[0;37m2024-04-13 13:38:29 | [INFO ] roma configuration: \u001b[0m\n", + "{'coarse_res': 560,\n", + " 'name': 'roma',\n", + " 'num_sampled_points': 10000,\n", + " 'pretrained': 'outdoor',\n", + " 'upsample_res': 860}\n", + "\u001b[0;37m2024-04-13 13:38:29 | [INFO ] Matching features...\u001b[0m\n", + "\u001b[0;37m2024-04-13 13:38:29 | [INFO ] \u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + " 0%| | 0/28 [00:00 Image sees 0 / 36298 points\n", + "I20240413 13:40:12.061582 3611342 sfm.cc:478] => Triangulated 35733 points\n", + "I20240413 13:40:12.061640 3611342 misc.cc:198] \n", + "==============================================================================\n", + "Triangulating image #2 (1)\n", + "==============================================================================\n", + "I20240413 13:40:12.061645 3611342 sfm.cc:473] => Image sees 4106 / 34289 points\n", + "I20240413 13:40:12.132586 3611342 sfm.cc:478] => Triangulated 29618 points\n", + "I20240413 13:40:12.132617 3611342 misc.cc:198] \n", + "==============================================================================\n", + "Triangulating image #4 (2)\n", + "==============================================================================\n", + "I20240413 13:40:12.132622 3611342 sfm.cc:473] => Image sees 11307 / 45619 points\n", + "I20240413 13:40:12.213624 3611342 sfm.cc:478] => Triangulated 34312 points\n", + "I20240413 13:40:12.213681 3611342 misc.cc:198] \n", + "==============================================================================\n", + "Triangulating image #5 (3)\n", + "==============================================================================\n", + "I20240413 13:40:12.213687 3611342 sfm.cc:473] => Image sees 18114 / 47663 points\n", + "I20240413 13:40:12.276453 3611342 sfm.cc:478] => Triangulated 29549 points\n", + "I20240413 13:40:12.276499 3611342 misc.cc:198] \n", + "==============================================================================\n", + "Triangulating image #7 (4)\n", + "==============================================================================\n", + "I20240413 13:40:12.276504 3611342 sfm.cc:473] => Image sees 27382 / 48279 points\n", + "I20240413 13:40:12.320092 3611342 sfm.cc:478] => Triangulated 20368 points\n", + "I20240413 13:40:12.320122 3611342 misc.cc:198] \n", + "==============================================================================\n", + "Triangulating image #8 (5)\n", + "==============================================================================\n", + "I20240413 13:40:12.320127 3611342 sfm.cc:473] => Image sees 24236 / 35818 points\n", + "I20240413 13:40:12.344905 3611342 sfm.cc:478] => Triangulated 11054 points\n", + "I20240413 13:40:12.344945 3611342 misc.cc:198] \n", + "==============================================================================\n", + "Triangulating image #9 (6)\n", + "==============================================================================\n", + "I20240413 13:40:12.344950 3611342 sfm.cc:473] => Image sees 41536 / 47190 points\n", + "I20240413 13:40:12.358630 3611342 sfm.cc:478] => Triangulated 5362 points\n", + "I20240413 13:40:12.358664 3611342 misc.cc:198] \n", + "==============================================================================\n", + "Triangulating image #10 (7)\n", + "==============================================================================\n", + "I20240413 13:40:12.358668 3611342 sfm.cc:473] => Image sees 39315 / 39608 points\n", + "I20240413 13:40:12.361487 3611342 sfm.cc:478] => Triangulated 1 points\n", + "I20240413 13:40:12.361502 3611342 misc.cc:198] \n", + "==============================================================================\n", + "Retriangulation\n", + "==============================================================================\n", + "I20240413 13:40:12.378624 3611342 incremental_mapper.cc:175] => Completed observations: 0\n", + "I20240413 13:40:12.392472 3611342 incremental_mapper.cc:178] => Merged observations: 0\n", + "I20240413 13:40:12.400408 3611342 misc.cc:198] \n", + "==============================================================================\n", + "Bundle adjustment\n", + "==============================================================================\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time\n", + " 0 3.111451e+05 0.00e+00 2.87e+02 0.00e+00 0.00e+00 1.00e+04 0 1.15e-01 4.58e-01\n", + " 1 3.078114e+05 3.33e+03 1.10e+00 2.16e+00 1.00e+00 3.00e+04 0 1.64e-01 6.23e-01\n", + " 2 3.078098e+05 1.58e+00 1.23e-01 3.82e+00 1.00e+00 9.00e+04 0 1.53e-01 7.76e-01\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "I20240413 13:40:13.397854 3611342 misc.cc:205] \n", + "Bundle adjustment report\n", + "------------------------\n", + "I20240413 13:40:13.397927 3611342 bundle_adjustment.cc:942] \n", + " Residuals : 663988\n", + " Parameters : 497991\n", + " Iterations : 3\n", + " Time : 0.811643 [s]\n", + " Initial cost : 0.684544 [px]\n", + " Final cost : 0.680865 [px]\n", + " Termination : Convergence\n", + "\n", + "I20240413 13:40:13.417588 3611342 incremental_mapper.cc:175] => Completed observations: 0\n", + "I20240413 13:40:13.432924 3611342 incremental_mapper.cc:178] => Merged observations: 0\n", + "I20240413 13:40:13.469981 3611342 incremental_mapper.cc:160] => Filtered observations: 13909\n", + "I20240413 13:40:13.470013 3611342 sfm.cc:521] => Changed observations: 0.041895\n", + "I20240413 13:40:13.609081 3611342 misc.cc:198] \n", + "==============================================================================\n", + "Bundle adjustment\n", + "==============================================================================\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time\n", + " 0 2.766719e+05 0.00e+00 2.77e-04 0.00e+00 0.00e+00 1.00e+04 0 9.16e-02 3.94e-01\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "I20240413 13:40:14.188024 3611342 misc.cc:205] \n", + "Bundle adjustment report\n", + "------------------------\n", + "I20240413 13:40:14.188091 3611342 bundle_adjustment.cc:942] \n", + " Residuals : 608352\n", + " Parameters : 456264\n", + " Iterations : 1\n", + " Time : 0.423789 [s]\n", + " Initial cost : 0.674381 [px]\n", + " Final cost : 0.674381 [px]\n", + " Termination : Convergence\n", + "\n", + "I20240413 13:40:14.205947 3611342 incremental_mapper.cc:175] => Completed observations: 0\n", + "I20240413 13:40:14.218333 3611342 incremental_mapper.cc:178] => Merged observations: 0\n", + "I20240413 13:40:14.247723 3611342 incremental_mapper.cc:160] => Filtered observations: 0\n", + "I20240413 13:40:14.247749 3611342 sfm.cc:521] => Changed observations: 0.000000\n", + "I20240413 13:40:14.362433 3611342 misc.cc:198] \n", + "==============================================================================\n", + "Extracting colors\n", + "==============================================================================\n" + ] + } + ], "source": [ "# Run the triangulation with the known camera poses\n", "\n", @@ -174,6 +536,30 @@ " options=opt,\n", " )" ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# Export the reconstruction in ply (to be opened in CloudCompare or Meshlab)\n", + "reconstruction.export_PLY(model_path / \"model.ply\")\n", + "\n", + "# Export the reconstruction in bundler format (to be imported in Metashape)\n", + "reconstruction.export_bundler(\n", + " model_path / \"bundler.out\",\n", + " model_path / \"bundler_list.txt\",\n", + " skip_distortion=True,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now you can open the dense reconstruction also with the COLMAP GUI.\n" + ] } ], "metadata": { diff --git a/src/deep_image_matching/io/h5_to_db.py b/src/deep_image_matching/io/h5_to_db.py index abaed1f..28ef75e 100644 --- a/src/deep_image_matching/io/h5_to_db.py +++ b/src/deep_image_matching/io/h5_to_db.py @@ -55,7 +55,7 @@ def export_to_colmap( feature_path (Path): Path to the feature file (in HDF5 format) containing the extracted keypoints. match_path (Path): Path to the match file (in HDF5 format) containing the matches between keypoints. database_path (str, optional): Path to the COLMAP database file. Defaults to "colmap.db". - # camera_options (dict, optional): Flag indicating whether to use camera options. Defaults to default_camera_options. + camera_config_path (Path, optional): Path to the camera options yaml file. If none is passesed, the default camera configuration is used. Returns: None