{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceType":"competition","sourceId":127282,"databundleVersionId":15520105}],"dockerImageVersionId":31287,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install wildlife-datasets timm -q","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-20T16:58:43.400377Z","iopub.execute_input":"2026-02-20T16:58:43.400752Z","iopub.status.idle":"2026-02-20T16:58:47.140229Z","shell.execute_reply.started":"2026-02-20T16:58:43.400713Z","shell.execute_reply":"2026-02-20T16:58:47.139433Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport torch\nimport numpy as np\nimport pandas as pd\nfrom tqdm import tqdm\nfrom PIL import Image\nfrom sklearn.cluster import DBSCAN\nfrom sklearn.metrics.pairwise import cosine_similarity\nimport torchvision.transforms as T\n\nfrom wildlife_datasets.datasets import AnimalCLEF2026\nimport timm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-20T16:58:47.142219Z","iopub.execute_input":"2026-02-20T16:58:47.142533Z","iopub.status.idle":"2026-02-20T16:58:59.661567Z","shell.execute_reply.started":"2026-02-20T16:58:47.142504Z","shell.execute_reply":"2026-02-20T16:58:59.660807Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Config\n\nDEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nROOT = \"/kaggle/input/animal-clef-2026\"\n\nBATCH_SIZE = 32\nSIM_THRESHOLD = 0.70\nDBSCAN_EPS = 0.35\nMIN_SAMPLES = 2","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-20T16:58:59.662482Z","iopub.execute_input":"2026-02-20T16:58:59.663000Z","iopub.status.idle":"2026-02-20T16:58:59.719911Z","shell.execute_reply.started":"2026-02-20T16:58:59.662974Z","shell.execute_reply":"2026-02-20T16:58:59.719143Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Load Dataset\n\ndataset = AnimalCLEF2026(ROOT)\nmetadata = dataset.metadata.copy()\n\ntrain_meta = metadata[metadata.split == \"train\"].reset_index(drop=True)\ntest_meta = metadata[metadata.split == \"test\"].reset_index(drop=True)\n\nprint(\"Train Images: \", len(train_meta))\nprint(\"Test Images: \", len(test_meta))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-20T16:59:22.438124Z","iopub.execute_input":"2026-02-20T16:59:22.438455Z","iopub.status.idle":"2026-02-20T17:00:22.909482Z","shell.execute_reply.started":"2026-02-20T16:59:22.438430Z","shell.execute_reply":"2026-02-20T17:00:22.908754Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Load MegaDescriptor\n\nmodel = timm.create_model(\n    \"hf-hub:BVRA/MegaDescriptor-L-384\",\n    pretrained = True\n)\n\nmodel = model.to(DEVICE)\nmodel.eval()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-20T17:01:35.021920Z","iopub.execute_input":"2026-02-20T17:01:35.022605Z","iopub.status.idle":"2026-02-20T17:01:45.871789Z","shell.execute_reply.started":"2026-02-20T17:01:35.022576Z","shell.execute_reply":"2026-02-20T17:01:45.871045Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Transform\n\ntransform = T.Compose([\n    T.Resize((384, 384)),\n    T.ToTensor(),\n    T.Normalize(\n        [0.5, 0.5, 0.5],\n        [0.5, 0.5, 0.5]\n    )\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-20T17:02:04.246888Z","iopub.execute_input":"2026-02-20T17:02:04.247493Z","iopub.status.idle":"2026-02-20T17:02:04.251442Z","shell.execute_reply.started":"2026-02-20T17:02:04.247461Z","shell.execute_reply":"2026-02-20T17:02:04.250846Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"SPECIES_PREFIX = {\n    \"LynxID2025\" : \"cluster_LynxID2025_\",\n    \"SalamanderID2025\" : \"cluster_SalamandarID2025_\",\n    \"SeaTurtleID2022\" : \"cluster_SeaTurtleID2022_\",\n    \"TexasHornedLizards\" : \"cluster_TexasHornedLizards_\"\n}","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Embedding extraction\n\ndef extract_embedding(df):\n    embeddings = []\n    image_ids = []\n\n    with torch.no_grad():\n        for i in tqdm(range(0, len(df), BATCH_SIZE)):\n            batch_df = df.iloc[i:i+BATCH_SIZE]\n            images = []\n\n            for path in batch_df.path:\n                full_path = os.path.join(ROOT, path)\n                img = Image.open(full_path).convert(\"RGB\")\n                img = transform(img)\n                images.append(img)\n\n            images = torch.stack(images).to(DEVICE)\n\n            feats = model(images)\n            feats = torch.nn.functional.normalize(feats, p=2, dim=1)\n\n            embeddings.append(feats.cpu().numpy())\n            image_ids.extend(batch_df.image_id.values)\n\n    embeddings = np.vstack(embeddings)\n    return embeddings, image_ids","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-20T17:07:09.385793Z","iopub.execute_input":"2026-02-20T17:07:09.386171Z","iopub.status.idle":"2026-02-20T17:07:09.392158Z","shell.execute_reply.started":"2026-02-20T17:07:09.386142Z","shell.execute_reply":"2026-02-20T17:07:09.391458Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#train & Test Embeddings\n\ntrain_embeddings, train_ids = extract_embedding(train_meta)\ntest_embeddings, test_ids = extract_embedding(test_meta)\n\ntrain_meta[\"embedding_idx\"] = range(len(train_meta))\ntest_meta[\"embedding_idx\"] = range(len(test_meta))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-20T17:07:10.489974Z","iopub.execute_input":"2026-02-20T17:07:10.490300Z","iopub.status.idle":"2026-02-20T17:23:29.496526Z","shell.execute_reply.started":"2026-02-20T17:07:10.490274Z","shell.execute_reply":"2026-02-20T17:23:29.495786Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Species wise Matching and clustering\n\nsubmission = []\ncluster_counters = {}\n\nfor dataset_name in test_meta.dataset.unique():\n\n    print(f\"\\nProcessing {dataset_name}\")\n\n    species_test = test_meta[test_meta.dataset == dataset_name]\n    species_test_idx = species_test.embedding_idx.values\n    species_test_emb = test_embeddings[species_test_idx]\n\n    species_train = train_meta[train_meta.dataset == dataset_name]\n\n    if dataset_name not in cluster_counters:\n        cluster_counters[dataset_name] = 0\n\n    #Case = 1, no train identities (Texas Horned Lizrd)\n    #--------------------------------------------------\n    if len(species_train) == 0:\n\n        clustering = DBSCAN(\n            eps = DBSCAN_EPS,\n            min_samples = MIN_SAMPLES,\n            metric = \"cosine\"\n        ).fit(species_test_emb)\n\n        labels = clustering.labels_\n\n        for img_id, label in zip(species_test.image_id.values, labels):\n\n            if label == -1:\n                label = cluster_counters[dataset_name]\n                cluster_counters[dataset_name] += 1\n\n            prefix = SPECIES_PREFIX[dataset_name]\n            cluster_name = f\"{prefix}{label}\"\n            submission.append([img_id, cluster_name])\n\n        continue\n\n\n    #------------------------------------------------\n    #Case - 2: known identity matching\n\n    species_train_idx = species_train.embedding_idx.values\n    species_train_emb = train_embeddings[species_train_idx]\n    species_train_ids = species_train.identity.values\n\n    similarity = cosine_similarity(species_test_emb, species_train_emb)\n\n    unmatched_embeddings = []\n    unmatched_image_ids = []\n\n    for i in range(len(species_test)):\n        max_sim = similarity[i].max()\n        max_idx = similarity[i].argmax()\n\n        if max_sim >= SIM_THRESHOLD:\n            known_id = species_train_ids[max_idx]\n            prefix = SPECIES_PREFIX[dataset_name]\n            cluster_name = f\"{prefix}{known_id}\"\n            submission.append([species_test.image_id.values[i], cluster_name])\n        else:\n            unmatched_embeddings.append(species_test_emb[i])\n            unmatched_image_ids.append(species_test.image_id.values[i])\n\n\n    #---------------------------------------------\n    #Cluster unmatched images\n\n    if len(unmatched_embeddings) > 0:\n        unmatched_embeddings = np.vstack(unmatched_embeddings)\n\n        clustering = DBSCAN(\n            eps = DBSCAN_EPS,\n            min_samples = MIN_SAMPLES,\n            metric = \"cosine\",\n        ).fit(unmatched_embeddings)\n\n        labels = clustering.labels_\n\n        for img_id, label in zip(unmatched_image_ids, labels):\n\n            if label == -1:\n                label = cluster_counters[dataset_name]\n                cluster_counters[dataset_name] += 1\n\n            prefix = SPECIES_PREFIX[dataset_name]\n            cluster_name = f\"{prefix}{label}\"\n            submission.append([img_id, cluster_name])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-20T17:43:14.299430Z","iopub.execute_input":"2026-02-20T17:43:14.300128Z","iopub.status.idle":"2026-02-20T17:43:14.603387Z","shell.execute_reply.started":"2026-02-20T17:43:14.300096Z","shell.execute_reply":"2026-02-20T17:43:14.602720Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Generate submission\n\nsubmission_df = pd.DataFrame(submission, columns=[\"image_id\", 'cluster'])\n\nsample = pd.read_csv(\"/kaggle/input/animal-clef-2026/sample_submission.csv\")\n\nsubmission_df = submission_df.set_index(\"image_id\")\nsubmission_df = submission_df.loc[sample.image_id.values]\nsubmission_df = submission_df.reset_index()\n\nsubmission_df.to_csv(\"submission.csv\", index=False)\n\nprint(\"submission.csv Saved!\")\nsubmission_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-20T17:43:39.919886Z","iopub.execute_input":"2026-02-20T17:43:39.920267Z","iopub.status.idle":"2026-02-20T17:43:39.956364Z","shell.execute_reply.started":"2026-02-20T17:43:39.920240Z","shell.execute_reply":"2026-02-20T17:43:39.955415Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}