{"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,"sourceType":"competition"},{"sourceId":11540696,"sourceType":"datasetVersion","datasetId":7237442},{"sourceId":11549455,"sourceType":"datasetVersion","datasetId":7242801},{"sourceId":4534,"sourceType":"modelInstanceVersion","modelInstanceId":3326,"modelId":986},{"sourceId":17191,"sourceType":"modelInstanceVersion","modelInstanceId":14317,"modelId":21716},{"sourceId":17555,"sourceType":"modelInstanceVersion","modelInstanceId":14611,"modelId":22086},{"sourceId":354674,"sourceType":"modelInstanceVersion","modelInstanceId":295840,"modelId":316447}],"dockerImageVersionId":31011,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# Install pyvolmap and transformers as wheel dependency package.\n!pip install /kaggle/input/pycolmap-v11-1/pycolmap-3.11.1-cp311-cp311-manylinux_2_28_x86_64.whl > /dev/null 2>&1\n!pip install /kaggle/input/transformers-4-51-3/transformers-4.51.3-py3-none-any.whl > /dev/null 2>&1","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:41:53.644865Z","iopub.execute_input":"2025-04-27T17:41:53.645198Z","iopub.status.idle":"2025-04-27T17:42:09.094298Z","shell.execute_reply.started":"2025-04-27T17:41:53.645160Z","shell.execute_reply":"2025-04-27T17:42:09.093280Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 📦 Imports\nimport os\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom sklearn.cluster import KMeans\nfrom sklearn.decomposition import PCA\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.metrics import pairwise_distances\nfrom PIL import Image, UnidentifiedImageError\nimport torchvision.transforms as T\nimport torchvision.models as models\nimport torch\nimport warnings\nfrom tqdm import tqdm\nimport kagglehub\nimport time\nfrom IPython.display import display\nfrom torchvision.io import read_image\nfrom torchvision.transforms.functional import convert_image_dtype\nfrom pathlib import Path\nfrom torchvision.models import vit_b_16\nfrom torchvision.models.feature_extraction import create_feature_extractor\nfrom transformers import AutoImageProcessor, AutoModel","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:42:09.096018Z","iopub.execute_input":"2025-04-27T17:42:09.096251Z","iopub.status.idle":"2025-04-27T17:42:30.928698Z","shell.execute_reply.started":"2025-04-27T17:42:09.096232Z","shell.execute_reply":"2025-04-27T17:42:30.927979Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"warnings.filterwarnings(\"ignore\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:42:30.929515Z","iopub.execute_input":"2025-04-27T17:42:30.929970Z","iopub.status.idle":"2025-04-27T17:42:30.933604Z","shell.execute_reply.started":"2025-04-27T17:42:30.929952Z","shell.execute_reply":"2025-04-27T17:42:30.932756Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:42:30.935518Z","iopub.execute_input":"2025-04-27T17:42:30.935740Z","iopub.status.idle":"2025-04-27T17:42:31.027408Z","shell.execute_reply.started":"2025-04-27T17:42:30.935725Z","shell.execute_reply":"2025-04-27T17:42:31.026596Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!cp -r /kaggle/input/m/nehadas55/lightglue/transformers/default/1/LightGlue-main /kaggle/working/\n%cd /kaggle/working/LightGlue-main\n!pip install -e . > /dev/null 2>&1 || true","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:42:31.028223Z","iopub.execute_input":"2025-04-27T17:42:31.028507Z","iopub.status.idle":"2025-04-27T17:45:53.383840Z","shell.execute_reply.started":"2025-04-27T17:42:31.028487Z","shell.execute_reply":"2025-04-27T17:45:53.383105Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"try:\n    # Download model checkpoints using KaggleHub\n    aliked_path = kagglehub.model_download(\"oldufo/aliked/pyTorch/aliked-n16\")\n    lightglue_path = kagglehub.model_download(\"oldufo/lightglue/pyTorch/aliked\")\n    dinov2_path = kagglehub.model_download(\"metaresearch/dinov2/pyTorch/base\")\n\n    # Create cache dir for torch hub compatibility\n    !mkdir -p /root/.cache/torch/hub/checkpoints\n    !cp $aliked_path/aliked-n16.pth /root/.cache/torch/hub/checkpoints/aliked-n16.pth\n    !cp $lightglue_path/aliked_lightglue.pth /root/.cache/torch/hub/checkpoints/aliked_lightglue.pth\n    !cp $lightglue_path/aliked_lightglue.pth /root/.cache/torch/hub/checkpoints/aliked_lightglue_v0-1_arxiv-pth\n    !cp -R /kaggle/input/lightglue/pytorch/aliked/1/aliked_lightglue.pth /root/.cache/torch/hub/checkpoints/aliked_lightglue_v0-1_arxiv.pth\n    print(\"✅ Models downloaded from KaggleHub and prepared.\")\nexcept Exception as e:\n    print(f\"⚠️ KaggleHub model setup failed: {e}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:45:53.384860Z","iopub.execute_input":"2025-04-27T17:45:53.385114Z","iopub.status.idle":"2025-04-27T17:45:57.245649Z","shell.execute_reply.started":"2025-04-27T17:45:53.385093Z","shell.execute_reply":"2025-04-27T17:45:57.244721Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from lightglue import LightGlue, ALIKED\nfrom lightglue.utils import load_image, match_pair","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:45:57.246755Z","iopub.execute_input":"2025-04-27T17:45:57.247027Z","iopub.status.idle":"2025-04-27T17:45:58.449321Z","shell.execute_reply.started":"2025-04-27T17:45:57.247003Z","shell.execute_reply":"2025-04-27T17:45:58.448723Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":" # Optional: pycolmap (COLMAP must be installed separately)\ntry:\n    import pycolmap\nexcept ImportError:\n    pycolmap = None","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:45:58.449972Z","iopub.execute_input":"2025-04-27T17:45:58.450211Z","iopub.status.idle":"2025-04-27T17:45:58.454027Z","shell.execute_reply.started":"2025-04-27T17:45:58.450191Z","shell.execute_reply":"2025-04-27T17:45:58.453340Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# data load csvs and images (optional)\ndef _load_data():\n    for dirname, _, filenames in os.walk('/kaggle/input'):\n        for filename in filenames:\n            print(os.path.join(dirname, filename))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:45:58.454837Z","iopub.execute_input":"2025-04-27T17:45:58.455114Z","iopub.status.idle":"2025-04-27T17:46:00.642561Z","shell.execute_reply.started":"2025-04-27T17:45:58.455092Z","shell.execute_reply":"2025-04-27T17:46:00.641764Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#_load_data()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#move into working directory\n%cd /kaggle/working/","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==============================\n# Data Loading\n# ==============================\n\n# Set the path for the Kaggle dataset\ndata_path = \"/kaggle/input/image-matching-challenge-2025/\"\ntrain_dir = os.path.join(data_path, \"train\")\ntest_dir = os.path.join(data_path, \"test\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:46:00.645041Z","iopub.execute_input":"2025-04-27T17:46:00.645270Z","iopub.status.idle":"2025-04-27T17:46:00.649387Z","shell.execute_reply.started":"2025-04-27T17:46:00.645252Z","shell.execute_reply":"2025-04-27T17:46:00.648625Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Function to safely read CSV files with error handling\ndef safe_read_csv(file_path):\n    try:\n        df = pd.read_csv(file_path)\n        print(f\"Loaded {file_path} with shape {df.shape}\")\n        return df\n    except FileNotFoundError:\n        print(f\"File not found: {file_path}\")\n        return None\n    except pd.errors.EmptyDataError:\n        print(f\"Empty file: {file_path}\")\n        return None\n    except Exception as e:\n        print(f\"Failed to load {file_path}: {e}\")\n        return None","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:46:00.650062Z","iopub.execute_input":"2025-04-27T17:46:00.650274Z","iopub.status.idle":"2025-04-27T17:46:00.660278Z","shell.execute_reply.started":"2025-04-27T17:46:00.650260Z","shell.execute_reply":"2025-04-27T17:46:00.659686Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load CSVs\ntrain_labels_df = safe_read_csv(os.path.join(data_path, \"train_labels.csv\"))\ntrain_thresholds_df = safe_read_csv(os.path.join(data_path, \"train_thresholds.csv\"))\nsample_submission = safe_read_csv(os.path.join(data_path, \"sample_submission.csv\"))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:46:00.661000Z","iopub.execute_input":"2025-04-27T17:46:00.661200Z","iopub.status.idle":"2025-04-27T17:46:00.720505Z","shell.execute_reply.started":"2025-04-27T17:46:00.661185Z","shell.execute_reply":"2025-04-27T17:46:00.719922Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create lookup for real poses\ntrain_pose_lookup = {}\nif train_labels_df is not None:\n    for _, row in train_labels_df.iterrows():\n        key = (row['dataset'], row['image'])\n        train_pose_lookup[key] = (row['rotation_matrix'], row['translation_vector'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:46:00.721259Z","iopub.execute_input":"2025-04-27T17:46:00.721579Z","iopub.status.idle":"2025-04-27T17:46:00.808429Z","shell.execute_reply.started":"2025-04-27T17:46:00.721560Z","shell.execute_reply":"2025-04-27T17:46:00.807696Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"aliked = ALIKED(pretrained='/kaggle/input/aliked/pytorch/aliked-n16/1/aliked-n16.pth').to(device)\nmatcher = LightGlue(\n    features='aliked',\n    weights='/kaggle/input/lightglue/pytorch/aliked/1/aliked_lightglue.pth'\n).to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:46:00.809160Z","iopub.execute_input":"2025-04-27T17:46:00.809342Z","iopub.status.idle":"2025-04-27T17:46:01.286048Z","shell.execute_reply.started":"2025-04-27T17:46:00.809329Z","shell.execute_reply":"2025-04-27T17:46:01.285456Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==============================\n# Load Dino Model\n# ==============================\ndef load_dino_model():\n    processor = AutoImageProcessor.from_pretrained(dinov2_path)\n    model = AutoModel.from_pretrained(dinov2_path).to(device)\n    model.eval()\n    return processor, model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:46:01.286762Z","iopub.execute_input":"2025-04-27T17:46:01.286993Z","iopub.status.idle":"2025-04-27T17:46:01.291228Z","shell.execute_reply.started":"2025-04-27T17:46:01.286977Z","shell.execute_reply":"2025-04-27T17:46:01.290438Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==============================\n# Extract Dino Features\n# ==============================\ndef extract_dino_features(image_paths):\n    processor, model = load_dino_model()\n    features = []\n    for path in tqdm(image_paths):\n        image = Image.open(path).convert(\"RGB\")\n        inputs = processor(images=image, return_tensors=\"pt\").to(device)\n        with torch.no_grad():\n            outputs = model(**inputs)\n            feat = outputs.last_hidden_state[:, 0, :].squeeze().cpu().numpy()\n        features.append(feat)\n    return np.array(features)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:46:01.292093Z","iopub.execute_input":"2025-04-27T17:46:01.292358Z","iopub.status.idle":"2025-04-27T17:46:01.303455Z","shell.execute_reply.started":"2025-04-27T17:46:01.292336Z","shell.execute_reply":"2025-04-27T17:46:01.302796Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 🧠 Feature Extraction \ndef build_feature_matrix_from_folder(folder_path):\n    print(f\"🧠 Matching features using ALIKED + LightGlue in {folder_path}\")\n    image_paths = sorted([f for f in os.listdir(folder_path) if f.endswith(\".png\")])\n    full_paths = [os.path.join(folder_path, f) for f in image_paths]\n    keypoints_list = []\n\n    for image_path in tqdm(full_paths):\n        image = load_image(image_path).to(device)\n        feats = aliked.extract(image)\n        keypoints_list.append(feats)\n\n    # Compute pairwise matching scores (number of matches)\n    n = len(full_paths)\n    match_scores = np.zeros((n, n))\n    for i in range(n):\n        for j in range(i + 1, n):\n            m = matcher({\"image0\": keypoints_list[i], \"image1\": keypoints_list[j]})\n            score = len(m[\"matches\"]) if m[\"matches\"] is not None else 0\n            match_scores[i, j] = match_scores[j, i] = score\n\n    return match_scores, image_paths","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:46:01.304249Z","iopub.execute_input":"2025-04-27T17:46:01.304485Z","iopub.status.idle":"2025-04-27T17:46:01.317356Z","shell.execute_reply.started":"2025-04-27T17:46:01.304461Z","shell.execute_reply":"2025-04-27T17:46:01.316624Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==============================\n# Clustering\n# ==============================\n\ndef cluster_features(features):\n    if features.ndim != 2 or features.shape[0] < 2:\n        raise ValueError(\"Insufficient data for clustering. Need at least 2 images with valid features.\")\n    print(f\"DEBUG: Clustering {features.shape[0]} feature vectors\")\n    n_components = min(50, features.shape[0], features.shape[1])\n    reduced = PCA(n_components=n_components).fit_transform(StandardScaler().fit_transform(features))\n    k = min(5, features.shape[0] // 2)\n    clustering = KMeans(n_clusters=k, random_state=42).fit(reduced)\n    print(f\"DEBUG: Cluster labels: {np.unique(clustering.labels_)}\")\n    return clustering.labels_","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:46:01.318222Z","iopub.execute_input":"2025-04-27T17:46:01.318444Z","iopub.status.idle":"2025-04-27T17:46:01.329207Z","shell.execute_reply.started":"2025-04-27T17:46:01.318421Z","shell.execute_reply":"2025-04-27T17:46:01.328538Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==============================\n# Visualization\n# ==============================\n\ndef visualize_clusters(features, labels):\n    if len(features) < 2:\n        print(\"Not enough data to visualize clusters.\")\n        return\n    reduced = PCA(n_components=2).fit_transform(features)\n    plt.figure(figsize=(8, 6))\n    scatter = plt.scatter(reduced[:, 0], reduced[:, 1], c=labels, cmap='tab10')\n    plt.title(\"Cluster Visualization\")\n    plt.colorbar(scatter)\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:46:01.329950Z","iopub.execute_input":"2025-04-27T17:46:01.330165Z","iopub.status.idle":"2025-04-27T17:46:01.342564Z","shell.execute_reply.started":"2025-04-27T17:46:01.330141Z","shell.execute_reply":"2025-04-27T17:46:01.341939Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==============================\n# Evaluate Pose Accuracy (mAA)\n# ==============================\n\ndef evaluate_pose_accuracy(submission_df, train_labels_df):\n    if train_labels_df is None:\n        print(\"Skipping mAA calculation: no ground truth loaded.\")\n        return\n\n    merged = submission_df.merge(train_labels_df, on=[\"dataset\", \"image\"], suffixes=(\"_pred\", \"_gt\"))\n    if merged.empty:\n        print(\"No overlap between submission and train labels for evaluation.\")\n        return\n\n    merged['scene_match'] = merged['scene_pred'] == merged['scene_gt']\n    maa = merged['scene_match'].sum() / len(merged)\n    print(f\"\\n🧪 mAA (mean Average Accuracy) Score: {maa:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:46:01.343207Z","iopub.execute_input":"2025-04-27T17:46:01.343393Z","iopub.status.idle":"2025-04-27T17:46:01.354045Z","shell.execute_reply.started":"2025-04-27T17:46:01.343380Z","shell.execute_reply":"2025-04-27T17:46:01.353352Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==============================\n# Combined Submission File Generator\n# ==============================\n\nsubmission_entries = []\n\ndef generate_submission(image_paths, labels, dataset_name):\n    print(\"DEBUG: Generating submission entries...\")\n    df = pd.DataFrame({\"image\": image_paths, \"cluster\": labels})\n    for _, row in df.iterrows():\n        image_name = row['image'].lower()\n        is_outlier = \"outlier\" in os.path.basename(image_name) or \"outlier\" in os.path.dirname(image_name)\n        cluster = \"outliers\" if is_outlier else f\"cluster{row['cluster']}\"\n\n        if cluster == \"outliers\":\n            rotation = \";\".join([\"nan\"] * 9)\n            translation = \";\".join([\"nan\"] * 3)\n        else:\n            pose_key = (dataset_name, row['image'])\n            if pose_key in train_pose_lookup:\n                rotation, translation = train_pose_lookup[pose_key]\n            else:\n                # Here you could replace dummy with COLMAP-derived pose if available\n                rotation = \";\".join([\"0\"] * 9)\n                translation = \";\".join([\"0\"] * 3)\n\n        submission_entries.append([dataset_name, cluster, row['image'], rotation, translation])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:46:01.354751Z","iopub.execute_input":"2025-04-27T17:46:01.355063Z","iopub.status.idle":"2025-04-27T17:46:01.365556Z","shell.execute_reply.started":"2025-04-27T17:46:01.355041Z","shell.execute_reply":"2025-04-27T17:46:01.364822Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==============================\n# 🚀 COLMAP SfM Integration for Test Set\n# ==============================\n\ndef estimate_pose_with_colmap(dataset_path, output_dir=\"colmap_workspace\"):\n    if pycolmap is None:\n        print(\"pycolmap is not installed. Please install it manually to use this feature.\")\n        return\n\n    os.makedirs(output_dir, exist_ok=True)\n    image_dir = os.path.abspath(dataset_path)\n    database_path = os.path.join(output_dir, \"database.db\")\n    sparse_dir = os.path.join(output_dir, \"sparse\")\n\n    print(f\"Running COLMAP SfM on: {dataset_path}\")\n    pycolmap.extract_features(database_path, image_dir)\n    pycolmap.match_exhaustive(database_path)\n    options = pycolmap.IncrementalPipelineOptions()\n    options.multiple_models = False\n    options.ignore_watermarks = True\n    reconstruction_manager = pycolmap.ReconstructionManager()\n    pipeline = pycolmap.IncrementalPipeline(\n        options,\n        image_dir,\n        database_path,\n        reconstruction_manager\n    )\n    pipeline.run()\n\n    print(f\"✅ COLMAP SfM completed. Results in: {sparse_dir}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:46:01.366297Z","iopub.execute_input":"2025-04-27T17:46:01.366518Z","iopub.status.idle":"2025-04-27T17:46:01.379087Z","shell.execute_reply.started":"2025-04-27T17:46:01.366495Z","shell.execute_reply":"2025-04-27T17:46:01.378483Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def run_pipeline(dataset_path, mode, use_dino=False):\n    image_paths = sorted([os.path.join(dataset_path, f) for f in os.listdir(dataset_path) if f.endswith(\".png\")])\n    if use_dino:\n        feats = extract_dino_features(image_paths)\n    else:\n        feats, image_paths = build_feature_matrix_from_folder(dataset_path)\n\n    labels = cluster_features(feats)\n    visualize_clusters(feats, labels)\n\n    if mode == \"test\":\n        estimate_pose_with_colmap(dataset_path)\n\n    generate_submission(image_paths, labels, os.path.basename(dataset_path))\n\n\ndef run_and_compare(dataset_path):\n\n    image_paths = sorted([os.path.join(dataset_path, f) for f in os.listdir(dataset_path) if f.endswith(\".png\")])\n    results = []\n\n    for method in [\"aliked\", \"dino\"]:\n        print(f\"🔄 Running pipeline with: {method.upper()} on {os.path.basename(dataset_path)}\")\n        start = time.time()\n\n        if method == \"dino\":\n            feats = extract_dino_features(image_paths)\n            used_paths = image_paths\n        else:\n            feats, used_paths = build_feature_matrix_from_folder(dataset_path)\n\n        labels = cluster_features(feats)\n        visualize_clusters(feats, labels)\n        duration = time.time() - start\n\n        results.append({\n            \"Method\": method.upper(),\n            \"Images\": len(used_paths),\n            \"Clusters\": len(set(labels)),\n            \"Time (s)\": round(duration, 2)\n        })\n\n    print(\"📊 Comparison Table\")\n    display(pd.DataFrame(results))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:46:01.379665Z","iopub.execute_input":"2025-04-27T17:46:01.379856Z","iopub.status.idle":"2025-04-27T17:46:01.389195Z","shell.execute_reply.started":"2025-04-27T17:46:01.379843Z","shell.execute_reply":"2025-04-27T17:46:01.388579Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==============================\n# Entry Point\n# ==============================\nif __name__ == \"__main__\":\n    try:\n        print(\"Processing both TRAIN and TEST folders...\")\n        for mode, directory in [(\"train\", train_dir), (\"test\", test_dir)]:\n            for dataset_name in sorted(os.listdir(directory)):\n                dataset_path = os.path.join(directory, dataset_name)\n                if not os.path.isdir(dataset_path):\n                    continue\n                print(f\"\\nProcessing {mode.upper()} dataset: {dataset_name}\")\n                feats, imgs = build_feature_matrix_from_folder(dataset_path)\n                labels = cluster_features(feats)\n                visualize_clusters(feats, labels)\n\n                if mode == \"test\":\n                    estimate_pose_with_colmap(dataset_path)\n\n                generate_submission(imgs, labels, dataset_name)\n\n        # Save combined submission\n        print(\"\\nSaving combined submission file...\")\n        submission_df = pd.DataFrame(submission_entries, columns=[\"dataset\", \"scene\", \"image\", \"rotation_matrix\", \"translation_vector\"])\n\n        # 🔍 Ensure valid formatting for submission\n        submission_df.dropna(subset=[\"dataset\", \"scene\", \"image\", \"rotation_matrix\", \"translation_vector\"], inplace=True)\n        submission_df = submission_df.astype(str)\n\n        # 🔧 Validate rotation and translation string format\n        def valid_rt_format(rt_str, expected_count):\n            parts = rt_str.split(\";\")\n            return len(parts) == expected_count and all(p.replace(\".\", \"\", 1).replace(\"-\", \"\", 1).isdigit() or p.lower() == \"nan\" for p in parts)\n\n        submission_df = submission_df[submission_df[\"rotation_matrix\"].apply(lambda x: valid_rt_format(x, 9))]\n        submission_df = submission_df[submission_df[\"translation_vector\"].apply(lambda x: valid_rt_format(x, 3))]\n        required_columns = [\"dataset\", \"scene\", \"image\", \"rotation_matrix\", \"translation_vector\"]\n        submission_df = submission_df[required_columns]  # Drop any extra columns\n        submission_df.dropna(inplace=True)               # Remove any rows with NaN\n        submission_df = submission_df.astype(str)        # Ensure all fields are strings\n        submission_df = submission_df.drop_duplicates()  # Remove duplicates\n        #submission_df = submission_df[~submission_df[\"image\"].str.contains(\"outliers_\")]\n        submission_df.to_csv(\"submission.csv\", index=False)\n        print(\"Saved submission.csv\")\n\n        # Evaluate mAA on training set\n        evaluate_pose_accuracy(submission_df, train_labels_df)\n\n        print(\"\\n🧪 Running clustering comparison on ETs (DINO vs ALIKED)...\")\n        run_and_compare(os.path.join(test_dir, \"ETs\"))\n\n    except (FileNotFoundError, ValueError) as e:\n        print(f\"❌ Error: {e}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:46:01.389897Z","iopub.execute_input":"2025-04-27T17:46:01.390127Z","execution_failed":"2025-04-27T17:49:28.132Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Optional Cleanup\n%rm -rf /kaggle/working/LightGlue-main","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}