{"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":"none","dataSources":[{"sourceId":91498,"databundleVersionId":11655853,"sourceType":"competition"}],"dockerImageVersionId":31040,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"<div style=\"display: flex; justify-content: space-between; align-items: flex-start;\">\n    <div style=\"text-align: left;\">\n        <p style=\"color:#FFD700; font-size: 15px; font-weight: bold; margin-bottom: 1px; text-align: left;\">Published on  May 16, 2025</p>\n        <h4 style=\"color:#4B0082; font-weight: bold; text-align: left; margin-top: 6px;\">Author: Jocelyn C. Dumlao</h4>\n        <p style=\"font-size: 17px; line-height: 1.7; color: #333; text-align: center; margin-top: 20px;\"></p>\n        <a href=\"https://www.linkedin.com/in/jocelyn-dumlao-168921a8/\" target=\"_blank\" style=\"display: inline-block; background-color: #003f88; color: #fff; text-decoration: none; padding: 5px 10px; border-radius: 10px; margin: 15px;\">LinkedIn</a>\n        <a href=\"https://github.com/jcdumlao14\" target=\"_blank\" style=\"display: inline-block; background-color: transparent; color: #059c99; text-decoration: none; padding: 5px 10px; border-radius: 10px; margin: 15px; border: 2px solid #007bff;\">GitHub</a>\n        <a href=\"https://www.youtube.com/@CogniCraftedMinds\" target=\"_blank\" style=\"display: inline-block; background-color: #ff0054; color: #fff; text-decoration: none; padding: 5px 10px; border-radius: 10px; margin: 15px;\">YouTube</a>\n        <a href=\"https://www.kaggle.com/jocelyndumlao\" target=\"_blank\" style=\"display: inline-block; background-color: #3a86ff; color: #fff; text-decoration: none; padding: 5px 10px; border-radius: 10px; margin: 15px;\">Kaggle</a>\n    </div>\n</div>","metadata":{}},{"cell_type":"markdown","source":"# <p style=\"padding:10px;background-color:#c8b8dc;margin:0;color:#102d02;font-family:newtimeroman;font-size:100%;text-align:center;border-radius:15px 50px;overflow:hidden;font-weight:500;border: 6px groove #6a7ba2;\">Import Libraries</p>","metadata":{}},{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom tqdm.notebook import tqdm  # For progress bars\nfrom skimage import feature  # For Canny edge detection\nimport logging\nimport traceback\nfrom collections import defaultdict\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-16T06:14:09.487786Z","iopub.execute_input":"2025-05-16T06:14:09.488076Z","iopub.status.idle":"2025-05-16T06:14:10.530268Z","shell.execute_reply.started":"2025-05-16T06:14:09.488050Z","shell.execute_reply":"2025-05-16T06:14:10.529163Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# <p style=\"padding:10px;background-color:#c8b8dc;margin:0;color:#102d02;font-family:newtimeroman;font-size:100%;text-align:center;border-radius:15px 50px;overflow:hidden;font-weight:500;border: 6px groove #6a7ba2;\">Data Loading and Inspection</p>","metadata":{}},{"cell_type":"code","source":"# --- Setup Logging ---\nlogging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-16T06:14:20.735496Z","iopub.execute_input":"2025-05-16T06:14:20.735912Z","iopub.status.idle":"2025-05-16T06:14:20.740446Z","shell.execute_reply.started":"2025-05-16T06:14:20.735886Z","shell.execute_reply":"2025-05-16T06:14:20.739229Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Data Loading and Inspection \ntry:\n    train_labels = pd.read_csv('/kaggle/input/image-matching-challenge-2025/train_labels.csv')\n    train_thresholds = pd.read_csv('/kaggle/input/image-matching-challenge-2025/train_thresholds.csv')\n    submission_df = pd.read_csv('/kaggle/input/image-matching-challenge-2025/sample_submission.csv')\n    logging.info(\"Data loaded successfully.\")\n\n    # --- Data Inspection Limit ---\n    INSPECTION_LIMIT = 5  # Limit the number of rows printed for inspection\n\n    print(\"Train Labels Head:\")\n    print(train_labels.head(INSPECTION_LIMIT))\n    print(\"\\nTrain Thresholds Head:\")\n    print(train_thresholds.head(INSPECTION_LIMIT))\n    print(\"\\nSubmission DataFrame Head:\")\n    print(submission_df.head(INSPECTION_LIMIT))\n\nexcept FileNotFoundError as e:\n    logging.error(f\"Error loading data: {e}\")\n    raise  # Re-raise the exception to halt execution\nexcept Exception as e:\n    logging.error(f\"An unexpected error occurred during data loading: {e}\")\n    logging.error(traceback.format_exc())  # Log the traceback for debugging\n    raise\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-16T06:14:25.152788Z","iopub.execute_input":"2025-05-16T06:14:25.153515Z","iopub.status.idle":"2025-05-16T06:14:25.239216Z","shell.execute_reply.started":"2025-05-16T06:14:25.153483Z","shell.execute_reply":"2025-05-16T06:14:25.238333Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# <p style=\"padding:10px;background-color:#c8b8dc;margin:0;color:#102d02;font-family:newtimeroman;font-size:100%;text-align:center;border-radius:15px 50px;overflow:hidden;font-weight:500;border: 6px groove #6a7ba2;\">Visualization Functions</p>\n- This section defines several functions to help visualize images:\n  - **`visualize_image()`:** Displays the original color image.\n  - **`visualize_grayscale()`:** Displays the image in grayscale. Grayscale images are often used in image processing because they reduce complexity.\n  - **`visualize_canny_edges()`:** Applies Canny edge detection and displays the edges. Canny edge detection highlights the important outlines in the image, which can be useful for feature extraction. The sigma parameter controls the amount of blurring applied before edge detection.\n  - **`visualize_all()`:** Combines the previous three visualizations (original, grayscale, and Canny edges) into a single figure for easy comparison.","metadata":{}},{"cell_type":"code","source":"# Visualization Functions \ndef visualize_image(image_path, title=\"Original\"):\n    \"\"\"Displays an image.\"\"\"\n    try:\n        img = cv2.imread(image_path)\n        if img is None:\n            raise ValueError(f\"Could not read image at path: {image_path}\")\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # Convert to RGB for matplotlib\n        plt.imshow(img)\n        plt.title(title)\n        plt.axis('off')\n        plt.show()\n    except Exception as e:\n        logging.error(f\"Error visualizing image {image_path}: {e}\")\n\ndef visualize_grayscale(image_path):\n    \"\"\"Displays the grayscale version of an image.\"\"\"\n    try:\n        img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)\n        if img is None:\n            raise ValueError(f\"Could not read image at path: {image_path}\")\n        plt.imshow(img, cmap='gray')\n        plt.title(\"Grayscale\")\n        plt.axis('off')\n        plt.show()\n    except Exception as e:\n        logging.error(f\"Error visualizing grayscale image {image_path}: {e}\")\n\ndef visualize_canny_edges(image_path, sigma=1):\n    \"\"\"Displays Canny edge detection result.\"\"\"\n    try:\n        img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)\n        if img is None:\n            raise ValueError(f\"Could not read image at path: {image_path}\")\n        edges = feature.canny(img, sigma=sigma)  # Apply Canny edge detection\n        plt.imshow(edges, cmap='gray')\n        plt.title(f\"Canny Edges (Sigma={sigma})\")\n        plt.axis('off')\n        plt.show()\n    except Exception as e:\n        logging.error(f\"Error visualizing Canny edges for image {image_path}: {e}\")\n\ndef visualize_all(image_path, sigma=1):\n    \"\"\"Visualizes original, grayscale, and Canny edges of an image.\"\"\"\n    try:\n        plt.figure(figsize=(15, 5))  # Adjust figure size\n\n        # Original Image\n        plt.subplot(1, 3, 1)\n        img = cv2.imread(image_path)\n        if img is None:\n            raise ValueError(f\"Could not read image at path: {image_path}\")\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        plt.imshow(img)\n        plt.title(\"Original\")\n        plt.axis('off')\n\n        # Grayscale Image\n        plt.subplot(1, 3, 2)\n        img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)\n        plt.imshow(img, cmap='gray')\n        plt.title(\"Grayscale\")\n        plt.axis('off')\n\n        # Canny Edges\n        plt.subplot(1, 3, 3)\n        edges = feature.canny(img, sigma=sigma)\n        plt.imshow(edges, cmap='gray')\n        plt.title(f\"Canny Edges (Sigma={sigma})\")\n        plt.axis('off')\n\n        plt.tight_layout()  # Adjust subplot parameters for a tight layout.\n        plt.show()\n    except Exception as e:\n        logging.error(f\"Error visualizing all images for {image_path}: {e}\")\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-16T06:14:31.886946Z","iopub.execute_input":"2025-05-16T06:14:31.887823Z","iopub.status.idle":"2025-05-16T06:14:31.899165Z","shell.execute_reply.started":"2025-05-16T06:14:31.887793Z","shell.execute_reply":"2025-05-16T06:14:31.898196Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Example Visualizations\n# Choose an example image from the training data.\ntry:\n    example_image_path = '/kaggle/input/image-matching-challenge-2025/train/amy_gardens/peach_0001.png'\n    # Visualize the example image\n    visualize_all(example_image_path, sigma=1.5)\n\n    example_image_path2 = '/kaggle/input/image-matching-challenge-2025/train/fbk_vineyard/vineyard_split_1_frame_0905.png'\n    visualize_all(example_image_path2, sigma=1.5)\n\n    example_image_path3 = '/kaggle/input/image-matching-challenge-2025/train/imc2023_heritage/cyprus_dsc_6496.png'\n    visualize_all(example_image_path3, sigma=1.5)\nexcept FileNotFoundError as e:\n    logging.warning(f\"Example image file not found: {e}. Skipping example visualizations.\")\nexcept Exception as e:\n    logging.error(f\"Error during example visualizations: {e}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-16T06:14:38.976554Z","iopub.execute_input":"2025-05-16T06:14:38.976860Z","iopub.status.idle":"2025-05-16T06:14:53.847048Z","shell.execute_reply.started":"2025-05-16T06:14:38.976835Z","shell.execute_reply":"2025-05-16T06:14:53.846114Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# <p style=\"padding:10px;background-color:#c8b8dc;margin:0;color:#102d02;font-family:newtimeroman;font-size:100%;text-align:center;border-radius:15px 50px;overflow:hidden;font-weight:500;border: 6px groove #6a7ba2;\">Keypoint Detection and Matching (Simplified)</p>\n- This is a placeholder for a more complex image matching pipeline. It uses the ORB (Oriented FAST and Rotated BRIEF) algorithm, a relatively simple feature detector and descriptor.\n- `detect_and_match_orb()`:\n  - Takes two image paths as input.\n  - Reads the images in grayscale.\n  - Uses ORB to find keypoints (distinctive points) and descriptors (numerical representations of those points) in each image.\n  - Uses a Brute-Force Matcher to find the best matches between the descriptors in the two images.\n  - Sorts the matches by distance (lower distance means a better match).\n  - Draws the top 20 matches on a combined image and displays it.\n- **Important**: The code emphasizes that this ORB approach is simplified and that a pre-trained model like SuperPoint would give significantly better results for real-world image matching. ORB is used here for demonstration purposes.\n- Keypoint matching is a technique where we try to find similar features in two different images. If we find enough matching features, it suggests that the images might be of the same scene or object.","metadata":{}},{"cell_type":"code","source":"# Keypoint Detection and Matching\n\ndef detect_and_match_orb(image_path1, image_path2):\n    \"\"\"Detects ORB keypoints and matches them between two images.\"\"\"\n    try:\n        img1 = cv2.imread(image_path1, cv2.IMREAD_GRAYSCALE)\n        img2 = cv2.imread(image_path2, cv2.IMREAD_GRAYSCALE)\n\n        if img1 is None or img2 is None:\n            raise ValueError(f\"Could not read one or both images: {image_path1}, {image_path2}\")\n\n        # Initiate ORB detector\n        orb = cv2.ORB_create()\n\n        # Find the keypoints and descriptors with ORB\n        kp1, des1 = orb.detectAndCompute(img1, None)\n        kp2, des2 = orb.detectAndCompute(img2, None)\n\n        if kp1 is None or kp2 is None or len(kp1) == 0 or len(kp2) == 0:\n            logging.warning(f\"No keypoints found in one or both images: {image_path1}, {image_path2}. Skipping matching.\")\n            return\n\n        # Create BFMatcher object (Brute-Force Matcher)\n        bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)\n\n        # Match descriptors\n        matches = bf.match(des1, des2)\n\n        # Sort them in the order of their distance\n        matches = sorted(matches, key=lambda x: x.distance)\n\n        # Draw first 20 matches.\n        img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:20], None,\n                               flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)\n\n        plt.imshow(img3)\n        plt.title(\"ORB Feature Matching (Top 20)\")\n        plt.axis('off')\n        plt.show()\n\n    except Exception as e:\n        logging.error(f\"Error detecting and matching ORB features: {e}\")\n\n\n# Example usage of keypoint matching\ntry:\n    example_image_path1 = '/kaggle/input/image-matching-challenge-2025/train/amy_gardens/peach_0001.png'\n    example_image_path2 = '/kaggle/input/image-matching-challenge-2025/train/amy_gardens/peach_0002.png'  # Assuming a second image exists\n    detect_and_match_orb(example_image_path1, example_image_path2)\nexcept FileNotFoundError as e:\n    logging.warning(f\"Example image file not found for ORB matching: {e}. Skipping ORB matching.\")\nexcept Exception as e:\n    logging.error(f\"Error during ORB feature matching example: {e}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-16T06:14:59.168998Z","iopub.execute_input":"2025-05-16T06:14:59.169495Z","iopub.status.idle":"2025-05-16T06:14:59.612798Z","shell.execute_reply.started":"2025-05-16T06:14:59.169469Z","shell.execute_reply":"2025-05-16T06:14:59.611710Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# <p style=\"padding:10px;background-color:#c8b8dc;margin:0;color:#102d02;font-family:newtimeroman;font-size:100%;text-align:center;border-radius:15px 50px;overflow:hidden;font-weight:500;border: 6px groove #6a7ba2;\">Pose Prediction (Placeholder)</p>\n- `predict_pose()`: This is a crucial placeholder. In a real image matching pipeline, this function would contain your Structure from Motion (SfM) or pose estimation algorithm (like integration with COLMAP).\n- The goal of pose prediction is to determine the camera's position and orientation (its pose) when the image was taken.\n- Currently, the `predict_pose()` function just returns a dummy pose (no rotation or translation). ","metadata":{}},{"cell_type":"code","source":"# --- 5.  Pose Prediction (Placeholder) ---\n\ndef predict_pose(image_path):\n    \"\"\"Placeholder function for pose prediction.\"\"\"\n    # This is where you would integrate we SfM/COLMAP pipeline.\n    # For now, let's return a dummy pose.\n    rotation_matrix = np.eye(3).flatten()  # Identity matrix (no rotation)\n    translation_vector = np.zeros(3)  # No translation\n    return rotation_matrix, translation_vector","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-16T06:15:05.600528Z","iopub.execute_input":"2025-05-16T06:15:05.600864Z","iopub.status.idle":"2025-05-16T06:15:05.606504Z","shell.execute_reply.started":"2025-05-16T06:15:05.600839Z","shell.execute_reply":"2025-05-16T06:15:05.605436Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# <p style=\"padding:10px;background-color:#c8b8dc;margin:0;color:#102d02;font-family:newtimeroman;font-size:100%;text-align:center;border-radius:15px 50px;overflow:hidden;font-weight:500;border: 6px groove #6a7ba2;\">Submission File</p>","metadata":{}},{"cell_type":"code","source":"# Submission File Generation \n\ndef generate_submission(submission_df, output_csv=\"submission.csv\"):\n    \"\"\"Generates a submission file with dummy pose predictions.\"\"\"\n    submission_data = []\n    failed_images = []\n    dataset_scene_counts = defaultdict(int)  # Track scene counts for each dataset\n\n    for index, row in tqdm(submission_df.iterrows(), total=len(submission_df), desc=\"Generating Submission\"):\n        image_id = row['image_id']\n        dataset = row['dataset']\n        scene = row['scene']\n        image_name = row['image']  # Extract the image name from the 'image' column.  \n\n        image_path = os.path.join('/kaggle/input/image-matching-challenge-2025/test', dataset, scene, image_name)\n\n        try:\n            # Predict the pose using the placeholder function.\n            rotation_matrix, translation_vector = predict_pose(image_path)\n\n            # Format the pose as strings.\n            rotation_string = \";\".join(map(str, rotation_matrix))\n            translation_string = \";\".join(map(str, translation_vector))\n\n            # Append the data to the list.\n            submission_data.append({\n                'image_id': image_id,\n                'dataset': dataset,\n                'scene': scene,\n                'image': image_name,  # Keep the image name as is.\n                'rotation_matrix': rotation_string,\n                'translation_vector': translation_string\n            })\n            dataset_scene_counts[dataset] += 1 # increment the image count per dataset\n        except FileNotFoundError:\n            logging.error(f\"Image file not found: {image_path}.  Setting pose to NaN.\")\n            rotation_string = \";\".join(['nan'] * 9)\n            translation_string = \";\".join(['nan'] * 3)\n            submission_data.append({\n                'image_id': image_id,\n                'dataset': dataset,\n                'scene': 'outliers',\n                'image': image_name,  # Keep the image name as is.\n                'rotation_matrix': rotation_string,\n                'translation_vector': translation_string\n            })\n            failed_images.append(image_id) #keep track of failed images\n        except Exception as e:\n            logging.error(f\"Error processing image {image_path}: {e}\")\n            logging.error(traceback.format_exc()) # Log the traceback for more details\n\n            #In case of an error, put it into outliers\n            rotation_string = \";\".join(['nan'] * 9)\n            translation_string = \";\".join(['nan'] * 3)\n            submission_data.append({\n                'image_id': image_id,\n                'dataset': dataset,\n                'scene': 'outliers',\n                'image': image_name,  # Keep the image name as is.\n                'rotation_matrix': rotation_string,\n                'translation_vector': translation_string\n            })\n\n            failed_images.append(image_id)\n\n    # Create a new DataFrame from the submission data.\n    submission_df = pd.DataFrame(submission_data)\n\n    # Save the DataFrame to a CSV file.\n    submission_df.to_csv(output_csv, index=False)\n    logging.info(f\"Submission file saved to {output_csv}\")\n\n    if failed_images:\n        logging.warning(f\"Failed to process {len(failed_images)} images.  They were assigned to outliers.\")\n        logging.warning(f\"Failed image IDs: {failed_images}\")\n\n    # Log number of images per dataset to check the data distribution\n    for dataset, count in dataset_scene_counts.items():\n        logging.info(f\"Number of images processed for dataset {dataset}: {count}\")\n\n\ntry:\n    #Ensure the submission_df exists\n    if 'submission_df' not in locals():\n        submission_df = pd.read_csv('/kaggle/input/image-matching-challenge-2025/sample_submission.csv') #or wherever it is\n    generate_submission(submission_df)\nexcept Exception as e:\n    logging.critical(f\"Critical error during submission generation: {e}\")\n    logging.critical(traceback.format_exc())  # Log the traceback for debugging","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-16T06:15:12.505970Z","iopub.execute_input":"2025-05-16T06:15:12.506327Z","iopub.status.idle":"2025-05-16T06:15:12.776317Z","shell.execute_reply.started":"2025-05-16T06:15:12.506300Z","shell.execute_reply":"2025-05-16T06:15:12.775381Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-16T06:15:18.871111Z","iopub.execute_input":"2025-05-16T06:15:18.871466Z","iopub.status.idle":"2025-05-16T06:15:18.890197Z","shell.execute_reply.started":"2025-05-16T06:15:18.871442Z","shell.execute_reply":"2025-05-16T06:15:18.889209Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}