{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":91498,"databundleVersionId":11655853,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":3462364,"sourceType":"datasetVersion","datasetId":2069214},{"sourceId":11924468,"sourceType":"datasetVersion","datasetId":6988459},{"sourceId":237741314,"sourceType":"kernelVersion"}],"dockerImageVersionId":31041,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install umap-learn -q ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:06:30.484765Z","iopub.execute_input":"2025-06-24T12:06:30.484996Z","iopub.status.idle":"2025-06-24T12:06:34.481206Z","shell.execute_reply.started":"2025-06-24T12:06:30.484972Z","shell.execute_reply":"2025-06-24T12:06:34.480201Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install transformers torchvision -q ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:06:34.482164Z","iopub.execute_input":"2025-06-24T12:06:34.482455Z","iopub.status.idle":"2025-06-24T12:07:50.84442Z","shell.execute_reply.started":"2025-06-24T12:06:34.482428Z","shell.execute_reply":"2025-06-24T12:07:50.843725Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install hdbscan","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:07:50.846762Z","iopub.execute_input":"2025-06-24T12:07:50.846997Z","iopub.status.idle":"2025-06-24T12:07:53.865142Z","shell.execute_reply.started":"2025-06-24T12:07:50.846974Z","shell.execute_reply":"2025-06-24T12:07:53.864426Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from transformers import DetrImageProcessor, DetrForObjectDetection\nfrom PIL import Image\nfrom transformers import AutoFeatureExtractor,AutoModel\nimport matplotlib.pyplot as plt\nfrom sklearn.manifold import TSNE\nimport umap\nimport torch\nfrom kaggle_secrets import UserSecretsClient\nfrom transformers import AutoFeatureExtractor, AutoModel\nimport cv2\nimport random\nfrom collections import Counter, defaultdict\nfrom sklearn.cluster import KMeans\nimport numpy as np \nimport pandas as pd \nimport os\nfrom collections import defaultdict\nimport hdbscan\nimport sys\nimport os\nfrom tqdm import tqdm\nfrom time import time, sleep\nimport dataclasses\nfrom IPython.display import clear_output\nfrom copy import deepcopy\nfrom torchvision import transforms\nimport transformers\nfrom sklearn.cluster import KMeans\nfrom pathlib import Path\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:12:31.78702Z","iopub.execute_input":"2025-06-24T12:12:31.787646Z","iopub.status.idle":"2025-06-24T12:12:31.811796Z","shell.execute_reply.started":"2025-06-24T12:12:31.787625Z","shell.execute_reply":"2025-06-24T12:12:31.810834Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(\"Using device:\", device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:11:04.243008Z","iopub.execute_input":"2025-06-24T12:11:04.243287Z","iopub.status.idle":"2025-06-24T12:11:04.247974Z","shell.execute_reply.started":"2025-06-24T12:11:04.243268Z","shell.execute_reply":"2025-06-24T12:11:04.247272Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"user_secrets = UserSecretsClient()\nhf_token = user_secrets.get_secret(\"Em Brasa\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:11:04.527383Z","iopub.execute_input":"2025-06-24T12:11:04.527905Z","iopub.status.idle":"2025-06-24T12:11:04.629823Z","shell.execute_reply.started":"2025-06-24T12:11:04.527886Z","shell.execute_reply":"2025-06-24T12:11:04.629269Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_resolution = 840","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:11:05.398514Z","iopub.execute_input":"2025-06-24T12:11:05.399196Z","iopub.status.idle":"2025-06-24T12:11:05.402296Z","shell.execute_reply.started":"2025-06-24T12:11:05.39917Z","shell.execute_reply":"2025-06-24T12:11:05.401722Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"\n\n# DINOv2 expects images resized and normalized like this:\ndinov2_transform = transforms.Compose([\n    transforms.Resize(image_resolution, interpolation=transforms.InterpolationMode.BICUBIC),\n    transforms.CenterCrop(image_resolution),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.5, 0.5, 0.5],\n                         std=[0.5, 0.5, 0.5])\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:12:52.736613Z","iopub.execute_input":"2025-06-24T12:12:52.737287Z","iopub.status.idle":"2025-06-24T12:12:52.741337Z","shell.execute_reply.started":"2025-06-24T12:12:52.737263Z","shell.execute_reply":"2025-06-24T12:12:52.740522Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load model (you can use dinov2-small or dinov2-vitl14)\nmodel = AutoModel.from_pretrained(\"facebook/dinov2-small\",use_auth_token=hf_token)\nmodel.to(device)\n\nmodel.eval()\n\ndef extract_feature(image_path):\n    image = Image.open(image_path).convert(\"RGB\")\n    input_tensor = dinov2_transform(image).unsqueeze(0).to(device)  # Add batch dimension\n\n    with torch.no_grad():\n        output = model(input_tensor)\n        # CLS token is the first token in the sequence\n        cls_embedding = output.last_hidden_state[:, 0]  # Shape: [1, 1024] for vitl14\n        return cls_embedding.squeeze().cpu().numpy()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:13:38.285814Z","iopub.execute_input":"2025-06-24T12:13:38.286108Z","iopub.status.idle":"2025-06-24T12:13:38.503012Z","shell.execute_reply.started":"2025-06-24T12:13:38.286088Z","shell.execute_reply":"2025-06-24T12:13:38.502366Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"features = []\nimg_paths = ['/kaggle/input/image-matching-challenge-2025/train/ETs/another_et_another_et001.png',\n            '/kaggle/input/image-matching-challenge-2025/train/ETs/another_et_another_et002.png',\n            '/kaggle/input/image-matching-challenge-2025/train/ETs/another_et_another_et009.png',\n             '/kaggle/input/image-matching-challenge-2025/train/ETs/outliers_out_et001.png',\n             '/kaggle/input/image-matching-challenge-2025/train/ETs/outliers_out_et002.png',\n             '/kaggle/input/image-matching-challenge-2025/train/ETs/outliers_out_et003.png',\n            '/kaggle/input/image-matching-challenge-2025/train/ETs/et_et008.png',\n            '/kaggle/input/image-matching-challenge-2025/train/fbk_vineyard/vineyard_split_1_frame_0900.png',\n            '/kaggle/input/image-matching-challenge-2025/train/fbk_vineyard/vineyard_split_1_frame_0925.png',\n            '/kaggle/input/image-matching-challenge-2025/train/ETs/et_et005.png',\n            '/kaggle/input/image-matching-challenge-2025/train/pt_piazzasanmarco_grandplace/grand_place_brussels_00460368_4162644685.png',\n           '/kaggle/input/image-matching-challenge-2025/train/pt_piazzasanmarco_grandplace/grand_place_brussels_01587288_3907486251.png',\n             '/kaggle/input/image-matching-challenge-2025/train/imc2024_dioscuri_baalshamin/baalshamin_19144401003_5d0dee05f5_o.png',\n            '/kaggle/input/image-matching-challenge-2025/train/stairs/stairs_split_1_1710453576271.png'\n\n            ]\n\nfor path in img_paths:\n    features.append(extract_feature(path))\nfeatures = np.vstack(features)","metadata":{"trusted":true,"_kg_hide-input":true,"execution":{"iopub.status.busy":"2025-06-24T12:13:41.666355Z","iopub.execute_input":"2025-06-24T12:13:41.666841Z","iopub.status.idle":"2025-06-24T12:13:44.944311Z","shell.execute_reply.started":"2025-06-24T12:13:41.666819Z","shell.execute_reply":"2025-06-24T12:13:44.94372Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#features = np.vstack(features)  # Shape: (N_images, 1024)\nreducer = umap.UMAP(n_components=2, random_state=42)\nproj_2d = reducer.fit_transform(features)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:13:49.338334Z","iopub.execute_input":"2025-06-24T12:13:49.338929Z","iopub.status.idle":"2025-06-24T12:13:55.8627Z","shell.execute_reply.started":"2025-06-24T12:13:49.338905Z","shell.execute_reply":"2025-06-24T12:13:55.862109Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from matplotlib.offsetbox import OffsetImage, AnnotationBbox\n\ndef plot_scatter_with_images(proj_2d, image_paths, thumb_size=(64, 64)):\n    fig, ax = plt.subplots(figsize=(12, 12))\n    ax.set_facecolor(\"white\")\n\n    ax.set_xlim(proj_2d[:, 0].min() - 1, proj_2d[:, 0].max() + 1)\n    ax.set_ylim(proj_2d[:, 1].min() - 1, proj_2d[:, 1].max() + 1)\n\n    for (x, y), img_path in zip(proj_2d, image_paths):\n        full_path = img_path\n        try:\n            img = Image.open(full_path).convert(\"RGB\")\n            img.thumbnail(thumb_size)\n            imagebox = OffsetImage(img, zoom=0.8)\n            ab = AnnotationBbox(imagebox, (x, y), frameon=False)\n            ax.add_artist(ab)\n        except Exception as e:\n            print(f\"Could not display {img_path}: {e}\")\n            continue\n\n    plt.axis(\"off\")\n    plt.title(\"UMAP of DINOv2 Features\")\n    plt.show()\n\n# Example usage:\nplot_scatter_with_images(proj_2d, img_paths)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:13:55.863792Z","iopub.execute_input":"2025-06-24T12:13:55.864073Z","iopub.status.idle":"2025-06-24T12:13:56.391226Z","shell.execute_reply.started":"2025-06-24T12:13:55.864044Z","shell.execute_reply":"2025-06-24T12:13:56.390508Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# Choose number of clusters (adjust based on how many scenes you expect)\nn_clusters = 6\n\nkmeans = KMeans(n_clusters=n_clusters, random_state=42)\nlabels = kmeans.fit_predict(proj_2d)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.671896Z","iopub.status.idle":"2025-06-24T12:08:37.672146Z","shell.execute_reply.started":"2025-06-24T12:08:37.672041Z","shell.execute_reply":"2025-06-24T12:08:37.672052Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Set seed\nrandom.seed(42)\n\n# Update to your competition input path\ntrain_root = \"/kaggle/input/image-matching-challenge-2025/train/\"\n\n# 1. Get list of all image paths with folder labels\ndef load_image_paths(root):\n    image_paths = []\n    labels = []\n    folders = sorted(os.listdir(root))\n    for folder in folders:\n        folder_path = os.path.join(root, folder)\n        if not os.path.isdir(folder_path):\n            continue\n        for img_name in os.listdir(folder_path):\n            if img_name.lower().endswith(('.jpg', '.png', '.jpeg', '.bmp')):\n                image_paths.append(os.path.join(folder_path, img_name))\n                labels.append(folder)\n    return image_paths, labels\n\n# 2. Sample N images ensuring each folder appears\ndef sample_images_per_folder(root, samples_per_folder=5):\n    sampled_paths = []\n    sampled_labels = []\n    for folder in sorted(os.listdir(root)):\n        folder_path = os.path.join(root, folder)\n        if not os.path.isdir(folder_path):\n            continue\n        all_imgs = [os.path.join(folder_path, f) for f in os.listdir(folder_path) \n                    if f.lower().endswith(('.jpg', '.png', '.jpeg', '.bmp'))]\n        selected = random.sample(all_imgs, min(samples_per_folder, len(all_imgs)))\n        sampled_paths.extend(selected)\n        sampled_labels.extend([folder] * len(selected))\n    return sampled_paths, sampled_labels\n\n# 3. Compute feature matrix\ndef compute_features(image_paths):\n    feature_list = []\n    for path in tqdm(image_paths, desc=\"Extracting features\"):\n        try:\n            feat = extract_feature(path)\n            feature_list.append(feat)\n        except Exception as e:\n            print(f\"Error extracting {path}: {e}\")\n    return np.vstack(feature_list)\n\n# 4. Evaluate clustering\ndef evaluate_hdbscan(features, labels, min_cluster_size=5):\n    clusterer = hdbscan.HDBSCAN(min_cluster_size=min_cluster_size, prediction_data=True)\n    pred_clusters = clusterer.fit_predict(features)\n\n    # Filter out noise (-1 label means noise)\n    valid_indices = pred_clusters != -1\n    pred_clusters = pred_clusters[valid_indices]\n    filtered_labels = [labels[i] for i in range(len(labels)) if valid_indices[i]]\n\n    # Evaluate clustering using majority vote per cluster\n    cluster_to_gt = defaultdict(list)\n    for idx, cluster_id in enumerate(pred_clusters):\n        cluster_to_gt[cluster_id].append(filtered_labels[idx])\n\n    correct = 0\n    total = len(filtered_labels)\n\n    for cluster_id, label_list in cluster_to_gt.items():\n        most_common_label, count = Counter(label_list).most_common(1)[0]\n        correct += count\n        print(f\"Cluster {cluster_id}: predicted → {most_common_label} ({count}/{len(label_list)} correct)\")\n\n    accuracy = correct / total\n    print(f\"\\n🧮 HDBSCAN Clustering Accuracy (excluding noise): {accuracy:.2%}\")\n    return accuracy, pred_clusters\n\ndef evaluate_majority_vote(pred_clusters, true_labels):\n    cluster_to_gt = defaultdict(list)\n    for idx, cluster_id in enumerate(pred_clusters):\n        cluster_to_gt[cluster_id].append(true_labels[idx])\n\n    correct = 0\n    total = len(true_labels)\n\n    for cluster_id, label_list in cluster_to_gt.items():\n        if not label_list:\n            continue\n        most_common_label, count = Counter(label_list).most_common(1)[0]\n        correct += count\n        print(f\"Cluster {cluster_id}: predicted → {most_common_label} ({count}/{len(label_list)} correct)\")\n\n    accuracy = correct / total\n    print(f\"\\n🧮 Overall Clustering Accuracy: {accuracy:.2%}\")\n    return accuracy","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:14:03.555614Z","iopub.execute_input":"2025-06-24T12:14:03.556125Z","iopub.status.idle":"2025-06-24T12:14:03.571699Z","shell.execute_reply.started":"2025-06-24T12:14:03.556103Z","shell.execute_reply":"2025-06-24T12:14:03.570858Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sampled_paths, sampled_labels = sample_images_per_folder(train_root, samples_per_folder=50)\nfeatures = compute_features(sampled_paths)\nn_clusters = len(set(sampled_labels))\nacc,clusters=evaluate_hdbscan(features, sampled_labels, n_clusters)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:14:07.657586Z","iopub.execute_input":"2025-06-24T12:14:07.658316Z","iopub.status.idle":"2025-06-24T12:16:55.302543Z","shell.execute_reply.started":"2025-06-24T12:14:07.658291Z","shell.execute_reply":"2025-06-24T12:16:55.301892Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import optuna\nimport hdbscan\n\ndef objective(trial):\n    min_cluster_size = trial.suggest_int(\"min_cluster_size\", 2, 75)\n    min_samples = trial.suggest_int(\"min_samples\", 2, 30)\n    eps = trial.suggest_float(\"cluster_selection_epsilon\", 0.0, 1.0)\n\n    clusterer = hdbscan.HDBSCAN(\n        min_cluster_size=min_cluster_size,\n        min_samples=min_samples,\n        cluster_selection_epsilon=eps,\n        prediction_data=True\n    )\n    pred_clusters = clusterer.fit_predict(features)\n\n    # Remove noise\n    valid = pred_clusters != -1\n    if valid.sum() < 10:\n        return 0.0  # not enough data clustered, bad result\n\n    filtered_labels = [sampled_labels[i] for i in range(len(sampled_labels)) if valid[i]]\n    pred_clusters = pred_clusters[valid]\n\n    cluster_to_gt = defaultdict(list)\n    for idx, cluster_id in enumerate(pred_clusters):\n        cluster_to_gt[cluster_id].append(filtered_labels[idx])\n\n    correct = 0\n    total = len(filtered_labels)\n\n    for cluster_id, label_list in cluster_to_gt.items():\n        most_common_label, count = Counter(label_list).most_common(1)[0]\n        correct += count\n\n    return correct / total  # this is your accuracy\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.676186Z","iopub.status.idle":"2025-06-24T12:08:37.676395Z","shell.execute_reply.started":"2025-06-24T12:08:37.676289Z","shell.execute_reply":"2025-06-24T12:08:37.676298Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#study = optuna.create_study(direction=\"maximize\")\n#study.optimize(objective, n_trials=100)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.677352Z","iopub.status.idle":"2025-06-24T12:08:37.677655Z","shell.execute_reply.started":"2025-06-24T12:08:37.677484Z","shell.execute_reply":"2025-06-24T12:08:37.677494Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Best trial:\")\nprint(study.best_trial)\nprint(\"Best params:\", study.best_params)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.678997Z","iopub.status.idle":"2025-06-24T12:08:37.679226Z","shell.execute_reply.started":"2025-06-24T12:08:37.679124Z","shell.execute_reply":"2025-06-24T12:08:37.679133Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from optuna.visualization import plot_param_importances\n#plot_param_importances(study)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.679982Z","iopub.status.idle":"2025-06-24T12:08:37.680225Z","shell.execute_reply.started":"2025-06-24T12:08:37.680078Z","shell.execute_reply":"2025-06-24T12:08:37.680087Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\"\"\"from sklearn.cluster import KMeans, AgglomerativeClustering, SpectralClustering, DBSCAN\nfrom sklearn.metrics import silhouette_score\n\nclustering_methods = {\n    \"KMeans\": KMeans(n_clusters=n_clusters, random_state=42),\n    \"Agglomerative\": AgglomerativeClustering(n_clusters=n_clusters),\n    \"Spectral\": SpectralClustering(n_clusters=n_clusters, affinity='nearest_neighbors', assign_labels='kmeans'),\n    \"DBSCAN\": DBSCAN(eps=43.0, min_samples=2)  # You may need to tune this\n}\n\nfor name, clusterer in clustering_methods.items():\n    print(f\"\\n🔹 {name}\")\n    try:\n        preds = clusterer.fit_predict(features)\n        evaluate_majority_vote(preds, sampled_labels)\n    except Exception as e:\n        print(f\"{name} failed: {e}\")\n\"\"\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.681114Z","iopub.status.idle":"2025-06-24T12:08:37.681323Z","shell.execute_reply.started":"2025-06-24T12:08:37.681223Z","shell.execute_reply":"2025-06-24T12:08:37.681232Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.preprocessing import normalize\nimport numpy as np\nfrom collections import defaultdict\n\ndef rank_similar_images_per_cluster(image_paths, labels, features, top_k=5):\n    print(\"Normalizing features...\")\n    features_norm = normalize(features, axis=1)\n\n    print(\"Grouping features by label...\")\n    label_to_indices = defaultdict(list)\n    for idx, label in enumerate(labels):\n        label_to_indices[label].append(idx)\n\n    print(\"Ranking images per cluster...\")\n    cluster_rankings = {}\n\n    for label, indices in label_to_indices.items():\n        if len(indices) < 2:\n            continue  # skip clusters with only 1 image\n\n        # Subset features\n        cluster_feats = features_norm[indices]\n        similarity_matrix = cluster_feats @ cluster_feats.T\n        np.fill_diagonal(similarity_matrix, -1)\n\n        # Get top-k similar indices per image\n        topk = np.argsort(-similarity_matrix, axis=1)[:, :top_k]\n        image_names = [image_paths[i] for i in indices]\n\n        # Map results\n        cluster_rankings[label] = {\n            image_names[i]: [image_names[j] for j in topk[i]]\n            for i in range(len(indices))\n        }\n\n    print(\"✅ Ranking done per cluster.\")\n    return cluster_rankings\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:16:55.303785Z","iopub.execute_input":"2025-06-24T12:16:55.304171Z","iopub.status.idle":"2025-06-24T12:16:55.311074Z","shell.execute_reply.started":"2025-06-24T12:16:55.304152Z","shell.execute_reply":"2025-06-24T12:16:55.31035Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Call ranking function\nrankings = rank_similar_images_per_cluster(sampled_paths, sampled_labels, features, top_k=5)\n\n# Example: show ranking for one cluster\nfor label, image_rank in rankings.items():\n    print(f\"\\nCluster: {label}\")\n    for img, neighbors in image_rank.items():\n        print(f\"{os.path.basename(img)} → {[os.path.basename(n) for n in neighbors]}\")\n    break  # just show one cluster","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:16:55.311889Z","iopub.execute_input":"2025-06-24T12:16:55.312136Z","iopub.status.idle":"2025-06-24T12:16:55.356997Z","shell.execute_reply.started":"2025-06-24T12:16:55.312115Z","shell.execute_reply":"2025-06-24T12:16:55.356393Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install kornia -q\n!pip install kornia_moons -q","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:22:09.533063Z","iopub.execute_input":"2025-06-24T12:22:09.533896Z","iopub.status.idle":"2025-06-24T12:22:16.081284Z","shell.execute_reply.started":"2025-06-24T12:22:09.533863Z","shell.execute_reply":"2025-06-24T12:22:16.080222Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import kornia\nfrom kornia_moons.feature import *\nimport kornia as K\nimport kornia.feature as KF\nimport gc\nimport csv","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:22:16.083293Z","iopub.execute_input":"2025-06-24T12:22:16.083576Z","iopub.status.idle":"2025-06-24T12:22:17.076521Z","shell.execute_reply.started":"2025-06-24T12:22:16.083553Z","shell.execute_reply":"2025-06-24T12:22:17.075716Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Configure environment and grab LoFTR code.\n!rm -rf sample_data\n!pip install torch einops yacs kornia\n!git clone https://github.com/zju3dv/LoFTR --depth 1\n!mv LoFTR/* . && rm -rf LoFTR\n\n# Download pretrained weights\n!mkdir weights\n%cd weights/\n!gdown --id 1w1Qhea3WLRMS81Vod_k5rxS_GNRgIi-O  # indoor-ds\n!gdown --id 1M-VD35-qdB5Iw-AtbDBCKC7hPolFW9UY # outdoor-ds\n%cd ..","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:22:17.077353Z","iopub.execute_input":"2025-06-24T12:22:17.077559Z","iopub.status.idle":"2025-06-24T12:22:26.862373Z","shell.execute_reply.started":"2025-06-24T12:22:17.077543Z","shell.execute_reply":"2025-06-24T12:22:26.86158Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.cm as cm\n\nfrom src.utils.plotting import make_matching_figure\nfrom src.loftr import LoFTR, default_cfg\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:22:26.863987Z","iopub.execute_input":"2025-06-24T12:22:26.8642Z","iopub.status.idle":"2025-06-24T12:22:26.913209Z","shell.execute_reply.started":"2025-06-24T12:22:26.86418Z","shell.execute_reply":"2025-06-24T12:22:26.912523Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"matcher = LoFTR(config=default_cfg)\nimage_type='outdoor'\nif image_type == 'indoor':\n  matcher.load_state_dict(torch.load(\"weights/indoor_ds.ckpt\")['state_dict'])\nelif image_type == 'outdoor':\n  matcher.load_state_dict(torch.load(\"weights/outdoor_ds.ckpt\")['state_dict'])\nelse:\n  raise ValueError(\"Wrong image_type is given.\")\nmatcher = matcher.eval().cuda()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:22:26.91397Z","iopub.execute_input":"2025-06-24T12:22:26.914212Z","iopub.status.idle":"2025-06-24T12:22:27.239281Z","shell.execute_reply.started":"2025-06-24T12:22:26.914191Z","shell.execute_reply":"2025-06-24T12:22:27.238711Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def estimate_pose(mkpts0, mkpts1, image_shape):\n    h, w = image_shape[:2]\n\n    # Approximate focal length and principal point if unknown\n    focal_length = 0.8 * max(image_resolution, image_resolution)\n    cx = image_resolution / 2\n    cy = image_resolution / 2\n    K = np.array([[focal_length, 0, cx],\n                  [0, focal_length, cy],\n                  [0, 0, 1]])\n\n    # Convert keypoints to float32 if not already\n    pts1 = mkpts0.astype(np.float32)\n    pts2 = mkpts1.astype(np.float32)\n\n    # Find Essential matrix using RANSAC\n    E, mask = cv2.findEssentialMat(pts1, pts2, K, method=cv2.RANSAC, prob=0.9999999, threshold=1.0)\n    if E is None:\n        print(\"Essential matrix not found!\")\n        return None, None, None\n\n    # Recover pose from Essential matrix\n    _, R, t, mask_pose = cv2.recoverPose(E, pts1, pts2, K, mask=mask)\n\n    return R, t, mask_pose\n\n# Example usage:\n# mkpts0, mkpts1 = ...  # matched keypoints from LoFTR, Nx2 numpy arrays\n# image_shape = cv2.imread('path_to_img1.jpg').shape\n\n# R, t, inliers = estimate_pose(mkpts0, mkpts1, image_shape)\n# print(\"Rotation:\\n\", R)\n# print(\"Translation:\\n\", t)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:22:27.240091Z","iopub.execute_input":"2025-06-24T12:22:27.240279Z","iopub.status.idle":"2025-06-24T12:22:27.245828Z","shell.execute_reply.started":"2025-06-24T12:22:27.240265Z","shell.execute_reply":"2025-06-24T12:22:27.245127Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Group images by folder (cluster)\nfolder_to_images = defaultdict(list)\nfor path, label in zip(sampled_paths, sampled_labels):\n    folder_to_images[label].append(path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:22:27.247359Z","iopub.execute_input":"2025-06-24T12:22:27.247651Z","iopub.status.idle":"2025-06-24T12:22:27.2633Z","shell.execute_reply.started":"2025-06-24T12:22:27.247636Z","shell.execute_reply":"2025-06-24T12:22:27.262707Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_images_for_label(label, sampled_paths, sampled_labels):\n    return [p for p, l in zip(sampled_paths, sampled_labels) if l == label]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:22:29.273439Z","iopub.execute_input":"2025-06-24T12:22:29.274124Z","iopub.status.idle":"2025-06-24T12:22:29.27796Z","shell.execute_reply.started":"2025-06-24T12:22:29.274098Z","shell.execute_reply":"2025-06-24T12:22:29.277292Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def generate_pairs(image_list):\n    pairs = []\n    for i in range(len(image_list) - 1):\n        pairs.append((image_list[i], image_list[i+1]))\n    return pairs\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:22:30.469125Z","iopub.execute_input":"2025-06-24T12:22:30.469406Z","iopub.status.idle":"2025-06-24T12:22:30.473448Z","shell.execute_reply.started":"2025-06-24T12:22:30.469388Z","shell.execute_reply":"2025-06-24T12:22:30.472727Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def dual_matching(img0_raw, img1_raw, confi=0.15, device='cuda', matcher=None):\n    # img0_raw, img1_raw should be shape: [1, H, W]\n    assert img0_raw.ndim == 3 and img0_raw.shape[0] == 1, f\"Expected shape [1,H,W], got {img0_raw.shape}\"\n    assert img1_raw.ndim == 3 and img1_raw.shape[0] == 1, f\"Expected shape [1,H,W], got {img1_raw.shape}\"\n\n    img0 = img0_raw.unsqueeze(0)  # [1, 1, H, W]\n    img1 = img1_raw.unsqueeze(0)\n\n    assert img0.shape == img1.shape, f\"Mismatched image shapes: {img0.shape} vs {img1.shape}\"\n\n    batch = {'image0': img0.to(device), 'image1': img1.to(device)}\n\n    with torch.no_grad():\n        matcher(batch)\n    mkpts0 = batch['mkpts0_f'].cpu().numpy()\n    mkpts1 = batch['mkpts1_f'].cpu().numpy()\n    mconf = batch['mconf'].cpu().numpy()\n\n    mask = mconf >= confi\n    return mkpts0[mask], mkpts1[mask], mconf[mask]\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:22:33.042514Z","iopub.execute_input":"2025-06-24T12:22:33.04282Z","iopub.status.idle":"2025-06-24T12:22:33.048616Z","shell.execute_reply.started":"2025-06-24T12:22:33.042798Z","shell.execute_reply":"2025-06-24T12:22:33.047969Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def estimate_pose_from_raw_images(img0_raw, img1_raw, confi=0.15, device='cuda', matcher=None, resolution=(image_resolution,image_resolution)):\n    # Get matched points from LoFTR\n    mkpts0, mkpts1, _ = dual_matching(img0_raw, img1_raw, confi=confi, device=device, matcher=matcher)\n    if mkpts0.shape[0] < 5:  # not enough matches\n        print(\"Not enough matches for pose estimation\")\n        return None, None\n\n    # Convert resolution to h,w for K matrix\n    h, w = resolution\n\n    # Build camera intrinsics K (approximate)\n    focal_length = 0.8 * max(w, h)\n    cx = w / 2\n    cy = h / 2\n    K = np.array([[focal_length, 0, cx],\n                  [0, focal_length, cy],\n                  [0, 0, 1]])\n\n    # Convert to float32 for OpenCV\n    pts1 = mkpts0.astype(np.float32)\n    pts2 = mkpts1.astype(np.float32)\n\n    # Compute Essential matrix with RANSAC\n    E, mask = cv2.findEssentialMat(pts1, pts2, K, method=cv2.RANSAC, prob=0.999, threshold=1.0)\n    if E is None:\n        print(\"Essential matrix could not be computed\")\n        return None, None\n\n    # Recover pose\n    _, R, t, _ = cv2.recoverPose(E, pts1, pts2, K, mask=mask)\n    return R, t\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:22:34.488604Z","iopub.execute_input":"2025-06-24T12:22:34.488947Z","iopub.status.idle":"2025-06-24T12:22:34.495428Z","shell.execute_reply.started":"2025-06-24T12:22:34.488924Z","shell.execute_reply":"2025-06-24T12:22:34.494596Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load your images as raw tensors (same preprocessing as LoFTR expects)\n# Example (assuming grayscale and resized to resolution):\n\n\ndef load_and_preprocess(img_path, resolution=(840,840), device='cuda'):\n    img = Image.open(img_path).convert('L')\n    img = img.resize(resolution,Image.BILINEAR)\n    img_tensor = transforms.ToTensor()(img)  # shape (1,H,W), values [0,1]\n    return img_tensor.to(device)\n\n# For a pair:\n#img0_raw = load_and_preprocess(img_paths[2])\n#img1_raw = load_and_preprocess(img_paths[3])\n\n#R, t = estimate_pose_from_raw_images(img0_raw, img1_raw, confi=0.10, device=device, matcher=matcher)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:22:39.563436Z","iopub.execute_input":"2025-06-24T12:22:39.564018Z","iopub.status.idle":"2025-06-24T12:22:41.006539Z","shell.execute_reply.started":"2025-06-24T12:22:39.563994Z","shell.execute_reply":"2025-06-24T12:22:41.005719Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def estimate_relative_poses_for_cluster_rankings(\n    cluster_rankings,\n    matcher,\n    resolution=(840, 840),\n    device='cuda',\n    conf_thresh=0.15\n):\n    \"\"\"\n    Estimates relative poses for top-ranked image pairs in each cluster.\n\n    Args:\n        cluster_rankings: dict[str, dict[str, list[str]]]\n            e.g. {cluster: {img_path: [img_path1, img_path2, ...]}}\n        matcher: LoFTR matcher\n        resolution: tuple[int, int] — expected image resolution\n        device: 'cuda' or 'cpu'\n        conf_thresh: confidence threshold for LoFTR matches\n\n    Returns:\n        poses_dict: dict[(imgA, imgB)] -> (R, t)\n    \"\"\"\n    poses_dict = {}\n    seen_pairs = set()\n\n    for cluster_id, ranking in tqdm(cluster_rankings.items(), desc=\"Estimating poses\"):\n        for imgA, neighbors in ranking.items():\n            imgA_tensor = load_and_preprocess(imgA, resolution, device)\n\n            for imgB in neighbors:\n                pair_key = tuple(sorted([imgA, imgB]))\n                if pair_key in seen_pairs:\n                    continue\n\n                imgB_tensor = load_and_preprocess(imgB, resolution, device)\n\n                R, t = estimate_pose_from_raw_images(\n                    imgA_tensor, imgB,\n                    _tensor,\n                    confi=conf_thresh,\n                    device=device,\n                    matcher=matcher,\n                    resolution=resolution\n                )\n\n                if R is not None and t is not None:\n                    poses_dict[pair_key] = (R, t)\n\n                seen_pairs.add(pair_key)\n\n    return poses_dict\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:22:43.864317Z","iopub.execute_input":"2025-06-24T12:22:43.864605Z","iopub.status.idle":"2025-06-24T12:22:43.871105Z","shell.execute_reply.started":"2025-06-24T12:22:43.864584Z","shell.execute_reply":"2025-06-24T12:22:43.870461Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#relative_poses = estimate_relative_poses_for_cluster_rankings(\n#    cluster_rankings=rankings,  # from rank_similar_images_per_cluster\n#    matcher=matcher,                   # your LoFTR model\n#    resolution=(840, 840),\n#    device='cuda',\n#    conf_thresh=0.15\n#)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.706339Z","iopub.status.idle":"2025-06-24T12:08:37.706632Z","shell.execute_reply.started":"2025-06-24T12:08:37.706463Z","shell.execute_reply":"2025-06-24T12:08:37.706478Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pickle \n\n#with open('relative_poses.pkl', 'wb') as f:\n#    pickle.dump(relative_poses, f)\n        ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:22:47.741722Z","iopub.execute_input":"2025-06-24T12:22:47.742027Z","iopub.status.idle":"2025-06-24T12:22:47.835594Z","shell.execute_reply.started":"2025-06-24T12:22:47.742005Z","shell.execute_reply":"2025-06-24T12:22:47.834651Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\ndef load_and_preprocess(img_path, resolution=(840, 840), device='cuda'):\n    img = Image.open(img_path).convert('L')\n    img = img.resize(resolution, Image.BILINEAR)\n    img_tensor = transforms.ToTensor()(img)\n    return img_tensor.to(device)\n\ndef dual_matching(img0_raw, img1_raw, confi=0.05, device='cuda', matcher=None):#CONFIRIR DUAL MATCH\n    img0 = img0_raw.unsqueeze(0)  # [1, 1, H, W]\n    img1 = img1_raw.unsqueeze(0)\n    batch = {'image0': img0.to(device), 'image1': img1.to(device)}\n\n    with torch.no_grad():\n        matcher(batch)\n    mkpts0 = batch['mkpts0_f'].cpu().numpy()\n    mkpts1 = batch['mkpts1_f'].cpu().numpy()\n    mconf = batch['mconf'].cpu().numpy()\n    mask = mconf >= confi\n    return mkpts0[mask], mkpts1[mask]\n\ndef generate_colmap_loftr_matches(\n    cluster_rankings,\n    matcher,\n    output_dir,\n    resolution=(840, 840),\n    device='cuda',\n    conf_thresh=0.15\n):\n    \"\"\"\n    Generate COLMAP-compatible matches from LoFTR.\n    \"\"\"\n    output_dir = Path(output_dir)\n    matches_dir = output_dir / \"matches\"\n    matches_dir.mkdir(parents=True, exist_ok=True)\n    pairs_txt_path = output_dir / \"pairs.txt\"\n\n    seen_pairs = set()\n\n    with open(pairs_txt_path, 'w') as pairs_file:\n        for cluster_id, ranking in tqdm(cluster_rankings.items(), desc=\"Generating matches\"):#conferir essa função \n            for imgA, neighbors in ranking.items():\n                imgA_tensor = load_and_preprocess(imgA, resolution, device)\n                for imgB in neighbors:\n                    pair_key = tuple(sorted([imgA, imgB]))\n                    if pair_key in seen_pairs:\n                        continue\n\n                    imgB_tensor = load_and_preprocess(imgB, resolution, device)\n\n                    mkpts0, mkpts1 = dual_matching(\n                        imgA_tensor, imgB_tensor,\n                        confi=conf_thresh,\n                        device=device,\n                        matcher=matcher\n                    )\n\n                    if len(mkpts0) < 8:\n                        continue  # not enough matches\n\n                    # Save match file\n                    img1_name = Path(pair_key[0]).name\n                    img2_name = Path(pair_key[1]).name\n                    match_fname = matches_dir / f\"{img1_name}---{img2_name}.txt\"\n                    \n                    # Save Nx2 keypoint coords (image1 coords, one per line)\n                    np.savetxt(match_fname, np.hstack([mkpts0, mkpts1]), fmt=\"%.6f\")\n                    #print(np.hstack([mkpts0, mkpts1]))\n                    # Add to pairs.txt\n                    pairs_file.write(f\"{img1_name} {img2_name}\\n\")\n                    seen_pairs.add(pair_key)\n    print(f\"Saved matches to: {matches_dir}\")\n    print(f\"Saved pair list to: {pairs_txt_path}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T13:23:55.992782Z","iopub.execute_input":"2025-06-24T13:23:55.993344Z","iopub.status.idle":"2025-06-24T13:23:56.002731Z","shell.execute_reply.started":"2025-06-24T13:23:55.993311Z","shell.execute_reply":"2025-06-24T13:23:56.001973Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"generate_colmap_loftr_matches(\n    rankings,\n    matcher,\n    output_dir=\"/kaggle/working/\",\n    resolution=(840, 840),\n    device='cuda',\n    conf_thresh=0.2\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T13:23:57.492977Z","iopub.execute_input":"2025-06-24T13:23:57.493516Z","iopub.status.idle":"2025-06-24T13:48:56.573805Z","shell.execute_reply.started":"2025-06-24T13:23:57.493495Z","shell.execute_reply":"2025-06-24T13:48:56.57311Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!zip -r matches.zip /kaggle/working/matches","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T13:59:22.012034Z","iopub.execute_input":"2025-06-24T13:59:22.012644Z","iopub.status.idle":"2025-06-24T13:59:27.51589Z","shell.execute_reply.started":"2025-06-24T13:59:22.012621Z","shell.execute_reply":"2025-06-24T13:59:27.514998Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install pycolmap -q","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.714382Z","iopub.status.idle":"2025-06-24T12:08:37.714711Z","shell.execute_reply.started":"2025-06-24T12:08:37.714508Z","shell.execute_reply":"2025-06-24T12:08:37.714523Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!colmap database_creator \\ /kaggle/working","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.715618Z","iopub.status.idle":"2025-06-24T12:08:37.715991Z","shell.execute_reply.started":"2025-06-24T12:08:37.715771Z","shell.execute_reply":"2025-06-24T12:08:37.715785Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!export QT_QPA_PLATFORM=offscreen","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.71762Z","iopub.status.idle":"2025-06-24T12:08:37.718002Z","shell.execute_reply.started":"2025-06-24T12:08:37.71783Z","shell.execute_reply":"2025-06-24T12:08:37.717843Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!apt-get install libxcb-xinerama0","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.719206Z","iopub.status.idle":"2025-06-24T12:08:37.719469Z","shell.execute_reply.started":"2025-06-24T12:08:37.719346Z","shell.execute_reply":"2025-06-24T12:08:37.719359Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!colmap feature_extractor --database_path database.db --image_path images/\n!colmap exhaustive_matcher --database_path database.db\n!colmap mapper --database_path database.db --image_path images/ --output_path sparse/","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.720643Z","iopub.status.idle":"2025-06-24T12:08:37.72096Z","shell.execute_reply.started":"2025-06-24T12:08:37.720803Z","shell.execute_reply":"2025-06-24T12:08:37.720817Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!colmap feature_extractor \\\n    --database_path ./database.db \\\n    --image_path /kaggle/input/image-matching-challenge-2025/train/\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.722224Z","iopub.status.idle":"2025-06-24T12:08:37.722517Z","shell.execute_reply.started":"2025-06-24T12:08:37.722347Z","shell.execute_reply":"2025-06-24T12:08:37.722362Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pycolmap","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.723146Z","iopub.status.idle":"2025-06-24T12:08:37.723423Z","shell.execute_reply.started":"2025-06-24T12:08:37.723294Z","shell.execute_reply":"2025-06-24T12:08:37.723307Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def build_absolute_poses_with_pycolmap(relative_poses, clusters, image_resolution=(840, 840), device='cuda'):\n    h, w = image_resolution\n    fx = fy = 0.8 * max(w, h)\n    cx = w / 2\n    cy = h / 2\n\n    reconstruction = pycolmap.Reconstruction()\n\n    camera = pycolmap.Camera()\n    camera.model = \"PINHOLE\"\n    camera.width = w\n    camera.height = h\n    camera.params = [fx, fy, cx, cy]\n    cam_id = 42\n    reconstruction.add_camera(camera)\n\n    for cluster_id, img_list in clusters.items():\n        if len(img_list) == 0:\n            continue\n\n        # First image: identity pose\n        img0_path = img_list[0]\n        image0 = pycolmap.Image(name=os.path.basename(img0_path), camera_id=cam_id)\n        image0.set_pose(np.array([1.0, 0.0, 0.0, 0.0]), np.zeros(3))\n        reconstruction.add_image(image0)\n\n        # Relative poses for remaining images\n        for img1_path in img_list[1:]:\n            key = tuple(sorted([img0_path, img1_path]))\n            if key not in relative_poses:\n                continue\n            R, t = relative_poses[key]\n            qvec = pycolmap.rotation_matrix_to_quaternion(R)\n            image1 = pycolmap.Image(name=os.path.basename(img1_path), camera_id=cam_id)\n            image1.set_pose(qvec, t.flatten())\n            reconstruction.add_image(image1)\n\n    return reconstruction\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.731872Z","iopub.status.idle":"2025-06-24T12:08:37.732133Z","shell.execute_reply.started":"2025-06-24T12:08:37.732018Z","shell.execute_reply":"2025-06-24T12:08:37.732031Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\ndef build_clusters_dict(image_paths, cluster_labels):\n    clusters = defaultdict(list)\n    for img_path, cluster_id in zip(image_paths, cluster_labels):\n        if cluster_id != -1:  # optionally skip noise if using HDBSCAN\n            clusters[cluster_id].append(img_path)\n    return dict(clusters)\n\nclusters_dict = build_clusters_dict(img_paths , clusters)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.733101Z","iopub.status.idle":"2025-06-24T12:08:37.733387Z","shell.execute_reply.started":"2025-06-24T12:08:37.733223Z","shell.execute_reply":"2025-06-24T12:08:37.733236Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#recons = build_absolute_poses_with_pycolmap(relative_poses, clusters_dict, (840, 840))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.734612Z","iopub.status.idle":"2025-06-24T12:08:37.734909Z","shell.execute_reply.started":"2025-06-24T12:08:37.734748Z","shell.execute_reply":"2025-06-24T12:08:37.73476Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#!apt-get update\n#!apt-get install -y colmap","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.736039Z","iopub.status.idle":"2025-06-24T12:08:37.736319Z","shell.execute_reply.started":"2025-06-24T12:08:37.736184Z","shell.execute_reply":"2025-06-24T12:08:37.736196Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#!colmap version","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.737512Z","iopub.status.idle":"2025-06-24T12:08:37.737826Z","shell.execute_reply.started":"2025-06-24T12:08:37.737626Z","shell.execute_reply":"2025-06-24T12:08:37.737639Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#!colmap feature_extractor \\\n    --database_path ./database.db \\\n    --image_path /kaggle/input/image-matching-challenge-2025/train/","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.738857Z","iopub.status.idle":"2025-06-24T12:08:37.73913Z","shell.execute_reply.started":"2025-06-24T12:08:37.739011Z","shell.execute_reply":"2025-06-24T12:08:37.739023Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#!colmap exhaustive_matcher --database_path ./database.db\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T12:08:37.740481Z","iopub.status.idle":"2025-06-24T12:08:37.740785Z","shell.execute_reply.started":"2025-06-24T12:08:37.74062Z","shell.execute_reply":"2025-06-24T12:08:37.740633Z"}},"outputs":[],"execution_count":null}]}