{"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":"none","dataSources":[{"sourceType":"competition","sourceId":71885,"databundleVersionId":8143495}],"dockerImageVersionId":31328,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"pip install git+https://github.com/cvg/LightGlue.git","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-08T04:20:53.605945Z","iopub.execute_input":"2026-04-08T04:20:53.606446Z","iopub.status.idle":"2026-04-08T04:21:06.163288Z","shell.execute_reply.started":"2026-04-08T04:20:53.606398Z","shell.execute_reply":"2026-04-08T04:21:06.162241Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install -q pycolmap\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-08T04:24:59.887775Z","iopub.execute_input":"2026-04-08T04:24:59.888977Z","iopub.status.idle":"2026-04-08T04:25:11.465233Z","shell.execute_reply.started":"2026-04-08T04:24:59.888904Z","shell.execute_reply":"2026-04-08T04:25:11.464183Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pycolmap\nprint(f\"PyColmap Version: {pycolmap.__version__}\")\n\n# Fix: has_cuda is a property in 4.x\nif pycolmap.has_cuda:\n    print(\"✅ GPU Acceleration detected!\")\n    device = pycolmap.Device.cuda\nelse:\n    print(\"⚠️ running on CPU. This will be slow.\")\n    device = pycolmap.Device.cpu","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-08T04:26:09.211763Z","iopub.execute_input":"2026-04-08T04:26:09.212210Z","iopub.status.idle":"2026-04-08T04:26:09.218764Z","shell.execute_reply.started":"2026-04-08T04:26:09.212174Z","shell.execute_reply":"2026-04-08T04:26:09.217964Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\ndef check_paths(base):\n    print(f\"--- Checking Directory: {base} ---\")\n    if os.path.exists(base):\n        for root, dirs, files in os.walk(base):\n            # Only go 3 levels deep so we don't print 1000s of images\n            level = root.replace(str(base), '').count(os.sep)\n            if level < 3:\n                print(f\"{'  ' * level}📁 {os.path.basename(root)}/\")\n                for d in dirs[:3]: # show first 3 subfolders\n                    print(f\"{'  ' * (level + 1)}📂 {d}\")\n                if files:\n                    print(f\"{'  ' * (level + 1)}📄 {files[0]} (+ {len(files)-1} more files)\")\n    else:\n        print(f\"❌ Path does not exist: {base}\")\n\n# Try both common Kaggle competition paths\ncheck_paths(\"/kaggle/input/image-matching-challenge-2024\")\ncheck_paths(\"/kaggle/input/competitions/image-matching-challenge-2024\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-08T04:32:10.789713Z","iopub.execute_input":"2026-04-08T04:32:10.790197Z","iopub.status.idle":"2026-04-08T04:32:15.576850Z","shell.execute_reply.started":"2026-04-08T04:32:10.790159Z","shell.execute_reply":"2026-04-08T04:32:15.575800Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport torch\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom pathlib import Path\n\n# --- 1. ROBUST IMPORTS & VIZ ALIASING ---\ntry:\n    from lightglue import LightGlue, ALIKED\n    import lightglue.utils as utils\n    # Fix: Import from viz2d specifically for modern LightGlue\n    from lightglue import viz2d\n    plot_images = viz2d.plot_images\n    plot_matches = viz2d.plot_matches\nexcept ImportError:\n    print(\"Trying legacy visualization import...\")\n    from lightglue.visualize import plot_images, plot_matches\n\nimport pycolmap\n\n# --- 2. CONFIGURATION & DEVICE ---\n# Fixed Path based on your debug output\nINPUT_ROOT = Path(\"/kaggle/input/competitions/image-matching-challenge-2024\")\nOUTPUT_DIR = Path(\"/kaggle/working/reconstruction\")\nOUTPUT_DIR.mkdir(exist_ok=True, parents=True)\n\nDEVICE = torch.device(\"cuda\" if torch.torch.cuda.is_available() else \"cpu\")\nCOLMAP_DEVICE = \"cuda\" if pycolmap.has_cuda else \"cpu\"\n\n# --- 3. THE MATCHING MODEL ---\nclass MatchingModel:\n    def __init__(self):\n        # We use ALIKED + LightGlue as our 'Foundational Model'\n        self.extractor = ALIKED(max_num_keypoints=2048, detection_threshold=0.01).eval().to(DEVICE)\n        self.matcher = LightGlue(features='aliked').eval().to(DEVICE)\n\n    def match_pair(self, img_path0, img_path1):\n        image0 = utils.load_image(img_path0).to(DEVICE)\n        image1 = utils.load_image(img_path1).to(DEVICE)\n        \n        with torch.no_grad():\n            feats0 = self.extractor.extract(image0)\n            feats1 = self.extractor.extract(image1)\n            matches01 = self.matcher({'image0': feats0, 'image1': feats1})\n            \n        return utils.rbd(feats0), utils.rbd(feats1), utils.rbd(matches01)\n\n# --- 4. ACCURACY & VISUALIZATION ---\ndef get_accuracy_report(reconstruction, total_images):\n    if not reconstruction:\n        return 0.0\n    return (reconstruction.num_reg_images() / total_images) * 100\n\ndef plot_3d_graph(reconstruction):\n    \"\"\"Plots the Camera Pose Graph in 3D space.\"\"\"\n    fig = plt.figure(figsize=(10, 8))\n    ax = fig.add_subplot(111, projection='3d')\n    \n    centers = []\n    for _, image in reconstruction.images.items():\n        centers.append(image.projection_center())\n    \n    centers = np.array(centers)\n    if len(centers) > 0:\n        ax.scatter(centers[:, 0], centers[:, 1], centers[:, 2], c='crimson', s=50)\n        \n    ax.set_title(\"3D Scene Graph (Camera Centers)\")\n    plt.show()\n\n# --- 5. SfM ENGINE (Version 4.0.3 FIX) ---\ndef run_reconstruction(scene_path):\n    database_path = OUTPUT_DIR / \"database.db\"\n    if database_path.exists(): database_path.unlink()\n    \n    print(f\"-> Extracting & Matching on {COLMAP_DEVICE}...\")\n    pycolmap.extract_features(database_path, scene_path, device=COLMAP_DEVICE)\n    pycolmap.match_exhaustive(database_path, device=COLMAP_DEVICE)\n    \n    print(f\"-> Starting Incremental Mapping...\")\n    # Fix: 4.0.3 returns a dict of reconstructions\n    reconstructions = pycolmap.incremental_mapping(database_path, scene_path, OUTPUT_DIR)\n    \n    if reconstructions and len(reconstructions) > 0:\n        # Return the first (largest) reconstruction from the dictionary\n        return reconstructions[0]\n    return None\n\n# --- 6. MAIN EXECUTION ---\ndef main():\n    model = MatchingModel()\n    \n    # Path verified from your debug session\n    scene_path = INPUT_ROOT / \"test\" / \"church\" / \"images\"\n    \n    if not scene_path.exists():\n        print(f\"❌ Path not found: {scene_path}\")\n        return\n\n    print(f\"🚀 Processing Hexathlon Scene: {scene_path.parent.name}\")\n    \n    # A. Build 3D Map\n    recon = run_reconstruction(scene_path)\n    \n    # B. Performance Report\n    img_files = sorted(list(scene_path.glob(\"*.png\")) + list(scene_path.glob(\"*.jpg\")))\n    accuracy = get_accuracy_report(recon, len(img_files))\n    \n    print(f\"\\n\" + \"=\"*30)\n    print(f\"📊 ACCURACY SCORE: {accuracy:.2f}%\")\n    print(f\"✅ Registered {recon.num_reg_images() if recon else 0} / {len(img_files)} images\")\n    print(\"=\"*30 + \"\\n\")\n\n    # C. Display Visualization Graphs\n    if recon:\n        print(\"📈 Rendering 3D Camera Graph...\")\n        plot_3d_graph(recon)\n        \n        if len(img_files) >= 2:\n            print(\"🔗 Rendering 2D Matching Graph...\")\n            f0, f1, m01 = model.match_pair(img_files[0], img_files[1])\n            \n            # Use the aliased plot functions\n            plot_images([utils.load_image(img_files[0]), utils.load_image(img_files[1])])\n            plot_matches(f0['keypoints'][m01['matches'][..., 0]], \n                         f1['keypoints'][m01['matches'][..., 1]], color='lime', lw=0.2)\n            plt.show()\n\nif __name__ == \"__main__\":\n    main()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-08T04:37:00.738141Z","iopub.execute_input":"2026-04-08T04:37:00.738623Z","iopub.status.idle":"2026-04-08T04:38:56.456791Z","shell.execute_reply.started":"2026-04-08T04:37:00.738584Z","shell.execute_reply":"2026-04-08T04:38:56.455642Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}