{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"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":"gpu","dataSources":[{"sourceId":99249,"databundleVersionId":11859885,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":11425807,"sourceType":"datasetVersion","datasetId":7154629},{"sourceId":12250516,"sourceType":"datasetVersion","datasetId":7718919}],"dockerImageVersionId":31012,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install ultralytics","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-22T23:03:04.102620Z","iopub.execute_input":"2025-06-22T23:03:04.102877Z","iopub.status.idle":"2025-06-22T23:04:18.881417Z","shell.execute_reply.started":"2025-06-22T23:03:04.102856Z","shell.execute_reply":"2025-06-22T23:04:18.880704Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nfrom ultralytics import YOLO\nimport joblib\nfrom typing import List\nfrom sklearn.metrics import f1_score\nimport pandas as pd\n\nDATASET_ROOT = \"/kaggle/input/cupybara/dataset/\" \nEVAL_DIR = os.path.join(DATASET_ROOT, \"test\")\n\nCLASS_NAMES = {\n    0: 'armadillo',\n    1: 'bird',\n    2: 'capybara',\n    3: 'cow',\n    4: 'dusky_legged_guan',\n    5: 'gray_brocket',\n    6: 'hare',\n    7: 'human',\n    8: 'margay',\n    9: 'skunk',\n    10: 'unknown_animal',\n    11: 'wild_boar'\n}\n\ndef extract_16_frames(video_path: str) -> List[np.ndarray]:\n    print(f\"🔍 Extracting frames from: {video_path}\")\n    cap = cv2.VideoCapture(video_path)\n    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))\n    frame_indices = np.linspace(0, total_frames - 1, num=16, dtype=int)\n\n    frames = []\n    for idx in frame_indices:\n        cap.set(cv2.CAP_PROP_POS_FRAMES, idx)\n        ret, frame = cap.read()\n        if ret:\n            frames.append(frame)\n    cap.release()\n\n    if not frames:\n        height, width = 480, 640\n        frames = [np.zeros((height, width, 3), dtype=np.uint8)] * 16\n    elif len(frames) < 16:\n        frames += [frames[-1]] * (16 - len(frames))\n\n    print(f\"✅ Extracted {len(frames)} frames\")\n    return frames\n\ndef aggregate_predictions(preds):\n    print(\"🔎 Aggregating predictions...\")\n    class_counts = {}\n    for p in preds:\n        for d in p.boxes.data.cpu().numpy():\n            cls = int(d[5])\n            name = CLASS_NAMES.get(cls)\n            if name:\n                class_counts[name] = class_counts.get(name, 0) + 1\n    if not class_counts:\n        print(\"⚠️ No detections found, returning 'no_animal'\")\n        return \"'no_animal'\"\n    best_class = max(class_counts, key=class_counts.get)\n    print(f\"🏷️ Final prediction: {best_class}\")\n    return f\"'{best_class}'\"\n\nclass BaseModel:\n    def __init__(self):\n        print(\"🚀 Loading model...\")\n        self._load_model()\n        print(\"✅ Model loaded.\")\n\n    def _load_model(self) -> None:\n        raise NotImplementedError\n\n    def _predict(self, video_path: str) -> str:\n        raise NotImplementedError\n\n    def predict(self, video_path: str) -> str:\n        return self._predict(video_path)\n\n    def generate_submission(self, eval_dir: str) -> None:\n        output_path = \"submission.csv\"\n        filenames: List[str] = sorted(os.listdir(eval_dir))\n        filenames = [f for f in filenames if f.endswith(\".mp4\")]  \n\n        print(f\"📼 Generating submission for {len(filenames)} videos...\")\n\n        submission = []\n        for i, filename in enumerate(filenames):\n            print(f\"\\n▶️ [{i+1}/{len(filenames)}] Processing {filename}\")\n            video_path = os.path.join(eval_dir, filename)\n            prediction = self.predict(video_path)\n            submission.append((filename.split(\".\")[0], prediction))\n\n        submission_df = pd.DataFrame(submission, columns=[\"Filename\", \"Species\"])\n        submission_df.to_csv(output_path, index=False)\n        print(f\"\\n✅ Submission file saved to {output_path}\")\n\nclass CustomModel(BaseModel):\n    def _load_model(self) -> None:\n        self.model = YOLO(\"/kaggle/input/baseline/baseline.pt\")\n\n    def _predict(self, video_path: str) -> str:\n        frames = extract_16_frames(video_path)\n        preds = [self.model.predict(frame, verbose=False)[0] for frame in frames]  # Predict frame by frame\n        return aggregate_predictions(preds)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-22T23:06:20.038940Z","iopub.execute_input":"2025-06-22T23:06:20.039647Z","iopub.status.idle":"2025-06-22T23:06:20.053522Z","shell.execute_reply.started":"2025-06-22T23:06:20.039616Z","shell.execute_reply":"2025-06-22T23:06:20.052891Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = CustomModel()\nmodel.generate_submission(eval_dir=EVAL_DIR)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-22T23:06:23.624565Z","iopub.execute_input":"2025-06-22T23:06:23.624840Z","iopub.status.idle":"2025-06-22T23:51:13.596632Z","shell.execute_reply.started":"2025-06-22T23:06:23.624819Z","shell.execute_reply":"2025-06-22T23:51:13.595999Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_test = pd.read_csv(os.path.join(DATASET_ROOT, \"test.csv\")).sort_values(\"Filename\")\ndf_submission = pd.read_csv(\"submission.csv\").sort_values(\"Filename\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-22T23:56:27.651958Z","iopub.execute_input":"2025-06-22T23:56:27.652482Z","iopub.status.idle":"2025-06-22T23:56:27.678990Z","shell.execute_reply.started":"2025-06-22T23:56:27.652461Z","shell.execute_reply":"2025-06-22T23:56:27.678494Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"f1_score(df_test[\"Species\"].values, df_submission[\"Species\"].values, average=\"weighted\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-22T23:56:40.752059Z","iopub.execute_input":"2025-06-22T23:56:40.752675Z","iopub.status.idle":"2025-06-22T23:56:40.763917Z","shell.execute_reply.started":"2025-06-22T23:56:40.752654Z","shell.execute_reply":"2025-06-22T23:56:40.763359Z"}},"outputs":[],"execution_count":null}]}