{"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"}],"dockerImageVersionId":31040,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Step 1: Setup & Imports\nimport os\nimport gc\nimport numpy as np\nimport pandas as pd\nfrom pathlib import Path\nfrom tqdm.notebook import tqdm\n\nimport cv2\nimport torch\nimport torchvision.models as models\nimport torchvision.transforms as transforms\nfrom sklearn.cluster import AgglomerativeClustering\nfrom sklearn.metrics.pairwise import cosine_similarity\n\n# Set device\ndevice = 'cuda' if torch.cuda.is_available() else 'cpu'\nprint(f\"Using device: {device}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-16T04:49:58.974516Z","iopub.execute_input":"2025-05-16T04:49:58.974799Z","iopub.status.idle":"2025-05-16T04:49:58.980458Z","shell.execute_reply.started":"2025-05-16T04:49:58.974777Z","shell.execute_reply":"2025-05-16T04:49:58.979779Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Step 2: Define Memory-Efficient Image Loader\n\ndef load_images_from_folder(folder_path, img_size=(224, 224)):\n    \"\"\"\n    Loads and resizes images from a folder.\n    Returns list of images and their filenames.\n    \"\"\"\n    image_files = [f for f in os.listdir(folder_path) if f.lower().endswith(('.png', '.jpg', '.jpeg'))]\n    images = []\n    filenames = []\n    for filename in image_files:\n        img = cv2.imread(os.path.join(folder_path, filename))\n        if img is not None:\n            img = cv2.resize(img, img_size)\n            images.append(img)\n            filenames.append(filename)\n        del filename, img\n        gc.collect()\n    return images, filenames","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-16T04:50:37.320476Z","iopub.execute_input":"2025-05-16T04:50:37.320764Z","iopub.status.idle":"2025-05-16T04:50:37.326636Z","shell.execute_reply.started":"2025-05-16T04:50:37.320742Z","shell.execute_reply":"2025-05-16T04:50:37.326051Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Step 3: Load Pretrained ResNet18 Model for Feature Extraction\n\nmodel = models.resnet18(pretrained=True)\nmodel = torch.nn.Sequential(*list(model.children())[:-1])  # Remove final pooling and FC layer\nmodel = model.to(device)\nmodel.eval()\n\npreprocess = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n])\n\nprint(\"Model loaded successfully.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-16T04:51:02.375873Z","iopub.execute_input":"2025-05-16T04:51:02.376178Z","iopub.status.idle":"2025-05-16T04:51:03.185522Z","shell.execute_reply.started":"2025-05-16T04:51:02.376159Z","shell.execute_reply":"2025-05-16T04:51:03.184692Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Step 4: Define Feature Extraction Function\n\ndef extract_features(images, model, preprocess):\n    \"\"\"\n    Extracts deep features from a list of images using a CNN.\n    Returns NumPy array of shape (num_images, feature_dim)\n    \"\"\"\n    features = []\n    for img in tqdm(images, desc=\"Extracting Features\"):\n        # Convert BGR (OpenCV) to RGB\n        img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        # Apply preprocessing and move to GPU\n        img_tensor = preprocess(img_rgb).unsqueeze(0).to(device)\n        with torch.no_grad():\n            feat = model(img_tensor).squeeze().cpu().numpy()\n        features.append(feat)\n        del img_rgb, img_tensor, feat\n        gc.collect()\n    return np.array(features)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-16T04:51:34.065548Z","iopub.execute_input":"2025-05-16T04:51:34.066384Z","iopub.status.idle":"2025-05-16T04:51:34.07217Z","shell.execute_reply.started":"2025-05-16T04:51:34.06635Z","shell.execute_reply":"2025-05-16T04:51:34.071459Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Step 5: Define Image Clustering Function\n\ndef cluster_images(features, distance_threshold=1.0):\n    \"\"\"\n    Clusters image features using Agglomerative Clustering.\n    Returns cluster labels for each image.\n    \"\"\"\n    clustering = AgglomerativeClustering(\n        n_clusters=None,\n        linkage='average',\n        distance_threshold=distance_threshold,\n        affinity='cosine'\n    )\n    clusters = clustering.fit_predict(features)\n    return clusters","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-16T04:51:54.849728Z","iopub.execute_input":"2025-05-16T04:51:54.850331Z","iopub.status.idle":"2025-05-16T04:51:54.854133Z","shell.execute_reply.started":"2025-05-16T04:51:54.850307Z","shell.execute_reply":"2025-05-16T04:51:54.853468Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Step 6: Dummy Pose Generator\n\ndef estimate_pose():\n    \"\"\"\n    Returns dummy rotation matrix and translation vector.\n    These will be replaced with real values in future improvements.\n    \"\"\"\n    R = np.eye(3).flatten().tolist()  # Identity rotation matrix (flattened)\n    t = [0.0, 0.0, 0.0]              # Zero translation vector\n    return ';'.join(map(str, R)), ';'.join(map(str, t))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-16T04:52:25.034459Z","iopub.execute_input":"2025-05-16T04:52:25.035054Z","iopub.status.idle":"2025-05-16T04:52:25.03923Z","shell.execute_reply.started":"2025-05-16T04:52:25.035029Z","shell.execute_reply":"2025-05-16T04:52:25.038504Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Step 7: Process Each Dataset Folder in Test Set\n\ntest_dir = Path('/kaggle/input/image-matching-challenge-2025/test')  # Adjust path based on your dataset location\n\nsubmission_data = []\n\nprint(\"Starting to process test datasets...\\n\")\n\nfor dataset_folder in test_dir.iterdir():\n    if not dataset_folder.is_dir():\n        continue\n\n    dataset_name = dataset_folder.name\n    print(f\"📂 Processing dataset: {dataset_name}\")\n\n    # Load images\n    images, filenames = load_images_from_folder(dataset_folder)\n    if not images:\n        print(f\"⚠️ No images found in {dataset_name}, skipping...\")\n        continue\n\n    # Extract features\n    features = extract_features(images, model, preprocess)\n\n    # Cluster images\n    clusters = cluster_images(features)\n\n    # Free memory\n    del features\n    gc.collect()\n\n    # Map cluster IDs to scene labels like 'cluster0', 'cluster1', etc.\n    unique_clusters = np.unique(clusters)\n    cluster_map = {cl: f\"cluster{i}\" for i, cl in enumerate(unique_clusters)}\n\n    # Build submission data\n    for idx, filename in enumerate(filenames):\n        cluster_id = clusters[idx]\n        if cluster_id == -1:\n            scene_label = \"outliers\"\n            R_str, t_str = \"nan;nan;nan;nan;nan;nan;nan;nan;nan\", \"nan;nan;nan\"\n        else:\n            scene_label = cluster_map[cluster_id]\n            R_str, t_str = estimate_pose()\n\n        submission_data.append({\n            'image_id': filename,\n            'dataset': dataset_name,\n            'scene': scene_label,\n            'image': filename,\n            'rotation_matrix': R_str,\n            'translation_vector': t_str\n        })\n\n    # Clean up before moving to next dataset\n    del images, clusters\n    gc.collect()\n    print(f\"✅ Finished processing {dataset_name}\\n\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-16T04:53:13.109795Z","iopub.execute_input":"2025-05-16T04:53:13.110093Z","iopub.status.idle":"2025-05-16T04:53:38.127417Z","shell.execute_reply.started":"2025-05-16T04:53:13.110069Z","shell.execute_reply":"2025-05-16T04:53:38.126602Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Step 8: Save Submission File\n\nsubmission_df = pd.DataFrame(submission_data)\nsubmission_df = submission_df[['image_id', 'dataset', 'scene', 'image', 'rotation_matrix', 'translation_vector']]\nsubmission_df.to_csv('submission.csv', index=False)\n\nprint(\"✅ Submission file saved successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-16T04:54:07.461131Z","iopub.execute_input":"2025-05-16T04:54:07.46179Z","iopub.status.idle":"2025-05-16T04:54:07.483511Z","shell.execute_reply.started":"2025-05-16T04:54:07.461764Z","shell.execute_reply":"2025-05-16T04:54:07.482782Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}