{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":91451,"databundleVersionId":11223220,"sourceType":"competition"}],"dockerImageVersionId":30919,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# === Imports ====\nimport os\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nimport torch\nimport torchvision\nimport timm\nimport torch.nn.functional as F\nfrom torchvision import transforms\nfrom PIL import Image\nfrom transformers import AutoFeatureExtractor, AutoModel\n\nsns.set(style='whitegrid')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-04-08T16:24:22.928771Z","iopub.execute_input":"2025-04-08T16:24:22.929047Z","iopub.status.idle":"2025-04-08T16:24:37.352379Z","shell.execute_reply.started":"2025-04-08T16:24:22.929018Z","shell.execute_reply":"2025-04-08T16:24:37.351672Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"root_dir =\"/kaggle/input/animal-clef-2025\"\nmetadata_path = \"/kaggle/input/animal-clef-2025/metadata.csv\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T16:25:37.513844Z","iopub.execute_input":"2025-04-08T16:25:37.514228Z","iopub.status.idle":"2025-04-08T16:25:37.517809Z","shell.execute_reply.started":"2025-04-08T16:25:37.514198Z","shell.execute_reply":"2025-04-08T16:25:37.516870Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# === Visualizing Data ===","metadata":{}},{"cell_type":"code","source":"metadata_df = pd.read_csv(metadata_path)\nmetadata_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T16:25:54.725974Z","iopub.execute_input":"2025-04-08T16:25:54.726293Z","iopub.status.idle":"2025-04-08T16:25:54.822358Z","shell.execute_reply.started":"2025-04-08T16:25:54.726272Z","shell.execute_reply":"2025-04-08T16:25:54.821545Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==== Counting the no of images v/s identity in database\n\nidentity_counts = metadata_df[metadata_df['split'] == 'database']['identity'].value_counts().reset_index()\nidentity_counts.columns = ['identity', 'num_images']\nidentity_counts","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T16:25:55.087531Z","iopub.execute_input":"2025-04-08T16:25:55.087800Z","iopub.status.idle":"2025-04-08T16:25:55.106633Z","shell.execute_reply.started":"2025-04-08T16:25:55.087779Z","shell.execute_reply":"2025-04-08T16:25:55.105987Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# === Exploratory Data Analysis ====","metadata":{}},{"cell_type":"code","source":"metadata_df.isna().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T16:25:58.271654Z","iopub.execute_input":"2025-04-08T16:25:58.271948Z","iopub.status.idle":"2025-04-08T16:25:58.283610Z","shell.execute_reply.started":"2025-04-08T16:25:58.271918Z","shell.execute_reply":"2025-04-08T16:25:58.282798Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"metadata_df[metadata_df[\"split\"] == \"database\"].isna().sum()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T16:25:59.527822Z","iopub.execute_input":"2025-04-08T16:25:59.528125Z","iopub.status.idle":"2025-04-08T16:25:59.541387Z","shell.execute_reply.started":"2025-04-08T16:25:59.528103Z","shell.execute_reply":"2025-04-08T16:25:59.540698Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"metadata_df[metadata_df[\"split\"] == \"query\"].isna().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T16:26:00.795974Z","iopub.execute_input":"2025-04-08T16:26:00.796320Z","iopub.status.idle":"2025-04-08T16:26:00.805508Z","shell.execute_reply.started":"2025-04-08T16:26:00.796292Z","shell.execute_reply":"2025-04-08T16:26:00.804796Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Count images per class and split\nclass_distribution = metadata_df.groupby([\"dataset\", \"split\"]).size().unstack(fill_value=0)\n\n# Plot\nplt.figure(figsize=(12, 6))\nax = class_distribution.plot(kind=\"bar\", colormap=\"coolwarm\", figsize=(12, 6), edgecolor=\"black\")\n\n# Add value labels on bars\nfor p in ax.patches:\n    ax.annotate(f'{int(p.get_height())}', (p.get_x() + p.get_width() / 2, p.get_height()),  \n                ha='center', va='bottom', fontsize=10, fontweight='bold', color='black')\n\n# Labels and title\nplt.xlabel(\"Class (Dataset Column)\")\nplt.ylabel(\"Number of Images\")\nplt.title(\"Number of Images in Database vs Query per Class\")\nplt.legend(title=\"Split\")\nplt.xticks(rotation=45)\nplt.grid(axis=\"y\", linestyle=\"--\", alpha=0.7)\n\n# Show plot\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T16:26:02.111924Z","iopub.execute_input":"2025-04-08T16:26:02.112272Z","iopub.status.idle":"2025-04-08T16:26:02.504996Z","shell.execute_reply.started":"2025-04-08T16:26:02.112246Z","shell.execute_reply":"2025-04-08T16:26:02.504202Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Visualizing the count of images v/s orientation\n\nplt.figure(figsize=(8, 5))\nsns.countplot(data=metadata_df, x=\"orientation\", order=metadata_df[\"orientation\"].value_counts().index)\nplt.xlabel(\"Orientation\")\nplt.ylabel(\"Count\")\nplt.title(\"Image Count per Orientation\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T16:26:03.783072Z","iopub.execute_input":"2025-04-08T16:26:03.783419Z","iopub.status.idle":"2025-04-08T16:26:04.033961Z","shell.execute_reply.started":"2025-04-08T16:26:03.783392Z","shell.execute_reply":"2025-04-08T16:26:04.033056Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Visualizing no of images in database v/s query\n\nplt.figure(figsize=(6, 5))\nsns.countplot(data=metadata_df, x=\"split\")\nplt.xlabel(\"Dataset Split\")\nplt.ylabel(\"Count\")\nplt.title(\"Number of Images in Database vs Query\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T16:26:05.428039Z","iopub.execute_input":"2025-04-08T16:26:05.428373Z","iopub.status.idle":"2025-04-08T16:26:05.580877Z","shell.execute_reply.started":"2025-04-08T16:26:05.428349Z","shell.execute_reply":"2025-04-08T16:26:05.580069Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# == Loading Pretrained Model for Embeddings ==","metadata":{}},{"cell_type":"code","source":"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\ndevice","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T16:26:09.249932Z","iopub.execute_input":"2025-04-08T16:26:09.250286Z","iopub.status.idle":"2025-04-08T16:26:09.311415Z","shell.execute_reply.started":"2025-04-08T16:26:09.250260Z","shell.execute_reply":"2025-04-08T16:26:09.310510Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==== Loading model and transformations ====\nmodel = timm.create_model(\"hf-hub:BVRA/MegaDescriptor-L-384\", pretrained=True)\nmodel = model.to(device)\nmodel = model.eval()\n\ntransform = transforms.Compose([\n    transforms.Resize(size=(384, 384)),\n    transforms.ToTensor(), \n    transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T16:26:12.372141Z","iopub.execute_input":"2025-04-08T16:26:12.372520Z","iopub.status.idle":"2025-04-08T16:26:28.074086Z","shell.execute_reply.started":"2025-04-08T16:26:12.372494Z","shell.execute_reply":"2025-04-08T16:26:28.073416Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === Function to extract embeddings ===\ndef extract_embedding(image_path):\n    image = Image.open(image_path).convert(\"RGB\")\n    image_tensor = transform(image).unsqueeze(0).to(device)\n\n    with torch.no_grad():\n        output = model(image_tensor)\n\n    # Normalize embedding\n    embedding = F.normalize(output, p=2, dim=1).squeeze(0)\n    return embedding","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T16:31:32.360266Z","iopub.execute_input":"2025-04-08T16:31:32.360611Z","iopub.status.idle":"2025-04-08T16:31:32.365136Z","shell.execute_reply.started":"2025-04-08T16:31:32.360586Z","shell.execute_reply":"2025-04-08T16:31:32.364222Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === Looping over database and storing embeddings and metadata\ndatabase_embeddings = []\n\nfor _, row in metadata_df.iterrows():\n    if row[\"split\"] == \"database\":\n        img_path = os.path.join(root_dir, row[\"path\"])\n        embed = extract_embedding(img_path)\n        # print(len(embed))\n        # break\n\n        database_embeddings.append({\n            \"embedding\" : embed,\n            \"identity\" : row[\"identity\"],\n            \"orientation\" : row[\"orientation\"]\n        })","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T16:32:48.600022Z","iopub.execute_input":"2025-04-08T16:32:48.600372Z","iopub.status.idle":"2025-04-08T16:44:09.584907Z","shell.execute_reply.started":"2025-04-08T16:32:48.600345Z","shell.execute_reply":"2025-04-08T16:44:09.584229Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(database_embeddings)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T16:49:32.450437Z","iopub.execute_input":"2025-04-08T16:49:32.450806Z","iopub.status.idle":"2025-04-08T16:49:32.455697Z","shell.execute_reply.started":"2025-04-08T16:49:32.450779Z","shell.execute_reply":"2025-04-08T16:49:32.454839Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# identifying similar patterns in query images \n\nthreshold = 0.68\nquery_results = []\nfor _, row in metadata_df.iterrows():\n    if row[\"split\"] == \"query\":\n        img_path = os.path.join(root_dir, row[\"path\"])\n        query_embed = extract_embedding(img_path)\n        query_orientation = row[\"orientation\"]\n\n        if pd.isna(query_orientation):\n            filtered_embeddings = database_embeddings\n        else:\n            filtered_embeddings = [\n                e for e in database_embeddings if e[\"orientation\"] == query_orientation\n            ]\n            if not filtered_embeddings:\n                filtered_embeddings = database_embeddings\n\n        if not filtered_embeddings:\n            query_results.append([row[\"image_id\"], \"new_individual\"])\n            continue\n\n        embed_matrix = torch.stack([e[\"embedding\"] for e in filtered_embeddings]).to(device)\n        img_identities = [e[\"identity\"] for e in filtered_embeddings]\n\n        # compute similarity scores\n        similarity_scores = torch.nn.functional.cosine_similarity(query_embed.unsqueeze(0), embed_matrix, dim=1)\n        best_indx = torch.argmax(similarity_scores)\n\n        print(f\"Image: {row['path']} | Best Similarity: {similarity_scores[best_indx].item():.4f}\")\n\n        if similarity_scores[best_indx].item() > threshold:\n            query_results.append([row[\"image_id\"], img_identities[best_indx]])\n        else:\n            query_results.append([row[\"image_id\"], \"new_individual\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T17:26:46.059202Z","iopub.execute_input":"2025-04-08T17:26:46.059535Z","iopub.status.idle":"2025-04-08T17:29:46.008132Z","shell.execute_reply.started":"2025-04-08T17:26:46.059510Z","shell.execute_reply":"2025-04-08T17:29:46.007236Z"},"collapsed":true,"jupyter":{"outputs_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"output_df = pd.DataFrame(query_results, columns=['image_id', 'identity'])\noutput_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T17:33:57.326683Z","iopub.execute_input":"2025-04-08T17:33:57.327008Z","iopub.status.idle":"2025-04-08T17:33:57.338046Z","shell.execute_reply.started":"2025-04-08T17:33:57.326988Z","shell.execute_reply":"2025-04-08T17:33:57.337073Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"output_df.to_csv(\"submission.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T17:35:14.228855Z","iopub.execute_input":"2025-04-08T17:35:14.229210Z","iopub.status.idle":"2025-04-08T17:35:14.239985Z","shell.execute_reply.started":"2025-04-08T17:35:14.229184Z","shell.execute_reply":"2025-04-08T17:35:14.239100Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}