{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":71885,"databundleVersionId":8143495,"sourceType":"competition"}],"dockerImageVersionId":31260,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install pycolmap","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-29T03:57:42.320119Z","iopub.execute_input":"2026-01-29T03:57:42.320427Z","iopub.status.idle":"2026-01-29T03:57:48.471942Z","shell.execute_reply.started":"2026-01-29T03:57:42.320404Z","shell.execute_reply":"2026-01-29T03:57:48.470915Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# General utilities\nimport matplotlib.pyplot as plt\n\nimport os\nfrom tqdm import tqdm\nfrom pathlib import Path\nfrom time import time, sleep\nfrom fastprogress import progress_bar\nimport gc\nimport numpy as np\nimport h5py\nfrom IPython.display import clear_output\nfrom collections import defaultdict\nfrom copy import deepcopy\nfrom typing import Any\nimport itertools\nimport pandas as pd\n\n# CV/MLe\nimport cv2\nimport torch\nfrom torch import Tensor as T\nimport torch.nn.functional as F\nimport kornia as K\nimport kornia.feature as KF\nfrom PIL import Image\nfrom transformers import AutoImageProcessor, AutoModel\n\nimport torch\n\n# 3D reconstruction\nimport pycolmap\n\n# Data importing into colmap\nimport sys\nsys.path.append(\"/kaggle/input/colmap-db-import\")\n\n# Provided by organizers\n#from database import *\n#from h5_to_db import *\n\ndef arr_to_str(a):\n    \"\"\"Returns ;-separated string representing the input\"\"\"\n    return \";\".join([str(x) for x in a.reshape(-1)])\n\ndef load_torch_image(file_name: Path | str, device=torch.device(\"cpu\")):\n    \"\"\"Loads an image and adds batch dimension\"\"\"\n    img = K.io.load_image(file_name, K.io.ImageLoadType.RGB32, device=device)[None, ...]\n    return img\n\ndevice = K.utils.get_cuda_device_if_available(0)\nprint(device)\n\nDEBUG = len([p for p in Path(\"/kaggle/input/image-matching-challenge-2024/test/\").iterdir() if p.is_dir()]) == 2\nprint(\"DEBUG:\", DEBUG)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T03:57:48.474072Z","iopub.execute_input":"2026-01-29T03:57:48.474340Z","iopub.status.idle":"2026-01-29T03:58:26.343157Z","shell.execute_reply.started":"2026-01-29T03:57:48.474310Z","shell.execute_reply":"2026-01-29T03:58:26.342481Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!git clone --quiet --recursive https://github.com/cvg/Hierarchical-Localization/\n%cd Hierarchical-Localization\n!pip install --progress-bar off --quiet -e .\n!pip install --progress-bar off --quiet --upgrade plotly\n\nimport tqdm, tqdm.notebook\ntqdm.tqdm = tqdm.notebook.tqdm  # notebook-friendly progress bars\nfrom pathlib import Path\n\nfrom hloc import extract_features, match_features, reconstruction, visualization, pairs_from_exhaustive\nfrom hloc.visualization import plot_images, read_image\nfrom hloc.utils import viz_3d","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T03:58:26.344051Z","iopub.execute_input":"2026-01-29T03:58:26.344610Z","iopub.status.idle":"2026-01-29T03:59:18.235054Z","shell.execute_reply.started":"2026-01-29T03:58:26.344584Z","shell.execute_reply":"2026-01-29T03:59:18.234093Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"images = Path('/kaggle/input/image-matching-challenge-2024/train/church')\noutputs = Path('/kaggle/working/')\n!rm -rf $outputs\nsfm_pairs = outputs / 'pairs-sfm.txt'\nloc_pairs = outputs / 'pairs-loc.txt'\nsfm_dir = outputs / 'sfm'\nfeatures = outputs / 'features.h5'\nmatches = outputs / 'matches.h5'\n\nfeature_conf = extract_features.confs['disk']\nmatcher_conf = match_features.confs['disk+lightglue']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T03:59:18.236410Z","iopub.execute_input":"2026-01-29T03:59:18.237080Z","iopub.status.idle":"2026-01-29T03:59:18.444754Z","shell.execute_reply.started":"2026-01-29T03:59:18.237039Z","shell.execute_reply":"2026-01-29T03:59:18.443891Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\nfrom pprint import pformat\n\nfrom hloc import extract_features, match_features, visualization","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T03:59:18.446222Z","iopub.execute_input":"2026-01-29T03:59:18.446540Z","iopub.status.idle":"2026-01-29T03:59:18.450552Z","shell.execute_reply.started":"2026-01-29T03:59:18.446509Z","shell.execute_reply":"2026-01-29T03:59:18.449782Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%cd /kaggle/working\n!rm -rf Hierarchical-Localization\n!git clone --recursive https://github.com/cvg/Hierarchical-Localization.git\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T03:59:18.451571Z","iopub.execute_input":"2026-01-29T03:59:18.451981Z","iopub.status.idle":"2026-01-29T03:59:27.163527Z","shell.execute_reply.started":"2026-01-29T03:59:18.451949Z","shell.execute_reply":"2026-01-29T03:59:27.162759Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%cd Hierarchical-Localization\n!pip install --quiet -e .","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T03:59:27.166060Z","iopub.execute_input":"2026-01-29T03:59:27.166331Z","iopub.status.idle":"2026-01-29T03:59:40.105637Z","shell.execute_reply.started":"2026-01-29T03:59:27.166295Z","shell.execute_reply":"2026-01-29T03:59:40.104649Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import hloc\nprint(hloc.__file__)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T03:59:40.107009Z","iopub.execute_input":"2026-01-29T03:59:40.107305Z","iopub.status.idle":"2026-01-29T03:59:40.111768Z","shell.execute_reply.started":"2026-01-29T03:59:40.107268Z","shell.execute_reply":"2026-01-29T03:59:40.111061Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from hloc import extract_features, match_features, reconstruction, visualization, pairs_from_exhaustive, match_dense, pairs_from_retrieval","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T03:59:40.113471Z","iopub.execute_input":"2026-01-29T03:59:40.113718Z","iopub.status.idle":"2026-01-29T03:59:40.143417Z","shell.execute_reply.started":"2026-01-29T03:59:40.113696Z","shell.execute_reply":"2026-01-29T03:59:40.142692Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\n\nimages_dir = Path(\"/kaggle/input/image-matching-challenge-2024/train/church/images\")\noutputs = Path(\"outputs\")\n\nextract_features.main(\n    conf=extract_features.confs['netvlad'],\n    image_dir=images_dir,\n    export_dir=outputs\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T03:59:40.144344Z","iopub.execute_input":"2026-01-29T03:59:40.144975Z","iopub.status.idle":"2026-01-29T03:59:58.766518Z","shell.execute_reply.started":"2026-01-29T03:59:40.144952Z","shell.execute_reply":"2026-01-29T03:59:58.765883Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pairs_from_retrieval.main(\n    Path(\"outputs/global-feats-netvlad.h5\"),\n    Path(\"outputs/pairs.txt\"),\n    num_matched=20\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T04:01:30.343707Z","iopub.execute_input":"2026-01-29T04:01:30.344457Z","iopub.status.idle":"2026-01-29T04:01:30.865206Z","shell.execute_reply.started":"2026-01-29T04:01:30.344427Z","shell.execute_reply":"2026-01-29T04:01:30.864583Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from hloc import extract_features, match_features\n\nprint(extract_features.confs.keys())\nprint(match_features.confs.keys())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T04:02:36.889869Z","iopub.execute_input":"2026-01-29T04:02:36.890545Z","iopub.status.idle":"2026-01-29T04:02:36.894738Z","shell.execute_reply.started":"2026-01-29T04:02:36.890519Z","shell.execute_reply":"2026-01-29T04:02:36.894107Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"extract_features.main(\n    conf=extract_features.confs['aliked-n16'],\n    image_dir=images_dir,\n    export_dir=outputs\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T04:02:48.176650Z","iopub.execute_input":"2026-01-29T04:02:48.177289Z","iopub.status.idle":"2026-01-29T04:02:54.730271Z","shell.execute_reply.started":"2026-01-29T04:02:48.177262Z","shell.execute_reply":"2026-01-29T04:02:54.729610Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from hloc import match_features\nprint(match_features.confs.keys())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T04:03:58.734367Z","iopub.execute_input":"2026-01-29T04:03:58.734907Z","iopub.status.idle":"2026-01-29T04:03:58.738774Z","shell.execute_reply.started":"2026-01-29T04:03:58.734879Z","shell.execute_reply":"2026-01-29T04:03:58.738228Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"match_features.main(\n    match_features.confs['aliked+lightglue'],\n    Path(\"outputs/pairs.txt\"),\n    Path(\"outputs/feats-aliked-n16.h5\"),\n    Path(\"outputs\"),\n    matches=Path(\"outputs/matches-aliked-lightglue.h5\")\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T04:12:27.500454Z","iopub.execute_input":"2026-01-29T04:12:27.501036Z","iopub.status.idle":"2026-01-29T04:14:47.857368Z","shell.execute_reply.started":"2026-01-29T04:12:27.501010Z","shell.execute_reply":"2026-01-29T04:14:47.856560Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\n\nimage_list = Path(\"outputs/image_list.txt\")\nwith image_list.open(\"w\") as f:\n    for p in sorted(images_dir.glob(\"*.png\")):\n        f.write(p.name + \"\\n\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T04:18:22.156943Z","iopub.execute_input":"2026-01-29T04:18:22.157644Z","iopub.status.idle":"2026-01-29T04:18:22.166390Z","shell.execute_reply.started":"2026-01-29T04:18:22.157608Z","shell.execute_reply":"2026-01-29T04:18:22.165827Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"images_dir = Path(\"/kaggle/input/image-matching-challenge-2024/train/church/images\")\n\nreconstruction.main(\n    Path(\"outputs/sfm\"),\n    images_dir,\n    Path(\"outputs/pairs.txt\"),\n    Path(\"outputs/feats-aliked-n16.h5\"),\n    Path(\"outputs/matches-aliked-lightglue.h5\"),\n    camera_mode=\"PER_IMAGE\"   \n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T04:20:39.133250Z","iopub.execute_input":"2026-01-29T04:20:39.133886Z","iopub.status.idle":"2026-01-29T04:35:34.803398Z","shell.execute_reply.started":"2026-01-29T04:20:39.133848Z","shell.execute_reply":"2026-01-29T04:35:34.802861Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pycolmap\n\nrecon = pycolmap.Reconstruction(\"outputs/sfm\")\nprint(recon.summary())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T04:37:52.262773Z","iopub.execute_input":"2026-01-29T04:37:52.263555Z","iopub.status.idle":"2026-01-29T04:37:52.324428Z","shell.execute_reply.started":"2026-01-29T04:37:52.263524Z","shell.execute_reply":"2026-01-29T04:37:52.323603Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from hloc import visualization as viz\ndir(viz)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T04:38:47.581612Z","iopub.execute_input":"2026-01-29T04:38:47.581938Z","iopub.status.idle":"2026-01-29T04:38:47.587173Z","shell.execute_reply.started":"2026-01-29T04:38:47.581910Z","shell.execute_reply":"2026-01-29T04:38:47.586633Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pycolmap\nfrom pathlib import Path\nfrom hloc.visualization import visualize_sfm_2d\n\nmodel = pycolmap.Reconstruction(\"outputs/sfm\")\nimage_dir = Path(\"/kaggle/input/image-matching-challenge-2024/train/church/images\")\n\nvisualize_sfm_2d(\n    model,\n    image_dir,\n    color_by=\"track_length\",\n    n=5\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T04:44:37.546124Z","iopub.execute_input":"2026-01-29T04:44:37.546636Z","iopub.status.idle":"2026-01-29T04:44:38.831764Z","shell.execute_reply.started":"2026-01-29T04:44:37.546609Z","shell.execute_reply":"2026-01-29T04:44:38.831103Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pycolmap\nfrom hloc.utils import viz_3d\n\nmodel = pycolmap.Reconstruction(\"outputs/sfm\")\nfig = viz_3d.init_figure()\nviz_3d.plot_reconstruction(\n    fig,\n    model,\n    name=\"mapping\",\n    points_rgb=True,                  \n    cameras=True,                     \n    points=True,                      \n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T04:52:13.583390Z","iopub.execute_input":"2026-01-29T04:52:13.584218Z","iopub.status.idle":"2026-01-29T04:52:15.464725Z","shell.execute_reply.started":"2026-01-29T04:52:13.584187Z","shell.execute_reply":"2026-01-29T04:52:15.463940Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T04:52:18.296307Z","iopub.execute_input":"2026-01-29T04:52:18.297030Z","iopub.status.idle":"2026-01-29T04:52:18.405016Z","shell.execute_reply.started":"2026-01-29T04:52:18.297000Z","shell.execute_reply":"2026-01-29T04:52:18.404048Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pycolmap\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nrecon = pycolmap.Reconstruction(\"outputs/sfm\")\n\npts = np.array([p.xyz for p in recon.points3D.values()])\ncols = np.array([p.color for p in recon.points3D.values()]) / 255.0\n\n# subsample cho nhẹ\nidx = np.random.choice(len(pts), min(15000, len(pts)), replace=False)\n\nfig = plt.figure(figsize=(8, 8))\nax = fig.add_subplot(111, projection=\"3d\")\nax.scatter(pts[idx,0], pts[idx,1], pts[idx,2],\n           c=cols[idx], s=1)\n\nax.set_title(\"Sparse SfM reconstruction\")\nax.set_axis_off()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T04:45:41.410819Z","iopub.execute_input":"2026-01-29T04:45:41.411162Z","iopub.status.idle":"2026-01-29T04:45:42.078026Z","shell.execute_reply.started":"2026-01-29T04:45:41.411134Z","shell.execute_reply":"2026-01-29T04:45:42.077238Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from hloc.visualization import plot_matches\nfrom hloc.utils.io import read_image\nimport matplotlib.pyplot as plt\n\nimg0 = read_image(\"/kaggle/input/image-matching-challenge-2024/train/church/images/00021.png\")\nimg1 = read_image(\"/kaggle/input/image-matching-challenge-2024/train/church/images/00051.png\")\n\n# load matches từ h5\nfrom hloc.utils.io import read_matches_h5\nmatches = read_matches_h5(\"outputs/matches-aliked-lightglue.h5\")\n\nkpts0, kpts1, matches01 = matches[\"00021.png\", \"00051.png\"]\n\nplot_matches(\n    img0, img1,\n    kpts0, kpts1,\n    matches01,\n    max_points=100\n)\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T04:39:38.363001Z","iopub.execute_input":"2026-01-29T04:39:38.363479Z","iopub.status.idle":"2026-01-29T04:39:38.428385Z","shell.execute_reply.started":"2026-01-29T04:39:38.363452Z","shell.execute_reply":"2026-01-29T04:39:38.427529Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!head outputs/pairs.txt\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T04:17:48.135579Z","iopub.execute_input":"2026-01-29T04:17:48.136439Z","iopub.status.idle":"2026-01-29T04:17:48.301762Z","shell.execute_reply.started":"2026-01-29T04:17:48.136411Z","shell.execute_reply":"2026-01-29T04:17:48.300900Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!ls /kaggle/input/image-matching-challenge-2024/train/church/images | head\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-29T04:18:00.392930Z","iopub.execute_input":"2026-01-29T04:18:00.393580Z","iopub.status.idle":"2026-01-29T04:18:00.583081Z","shell.execute_reply.started":"2026-01-29T04:18:00.393546Z","shell.execute_reply":"2026-01-29T04:18:00.582389Z"}},"outputs":[],"execution_count":null}]}