{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":107469,"databundleVersionId":13058354,"sourceType":"competition"},{"sourceId":257222961,"sourceType":"kernelVersion"},{"sourceId":258125989,"sourceType":"kernelVersion"}],"dockerImageVersionId":31089,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"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\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,"execution":{"iopub.status.busy":"2025-08-18T23:01:56.380294Z","iopub.execute_input":"2025-08-18T23:01:56.380612Z","iopub.status.idle":"2025-08-18T23:01:56.385888Z","shell.execute_reply.started":"2025-08-18T23:01:56.380571Z","shell.execute_reply":"2025-08-18T23:01:56.384991Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sub = pd.read_csv('/kaggle/input/fork-of-final-e2c8ce/submission.csv')\nsub","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-18T23:01:56.387800Z","iopub.execute_input":"2025-08-18T23:01:56.388096Z","iopub.status.idle":"2025-08-18T23:01:56.415569Z","shell.execute_reply.started":"2025-08-18T23:01:56.388073Z","shell.execute_reply":"2025-08-18T23:01:56.414755Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nfrom typing import Union, Dict\n\ndef keep_topk_per_class(df: pd.DataFrame, k: Union[int, Dict[int, int]]) -> pd.DataFrame:\n    \"\"\"\n    Keep only the top-k highest-confidence boxes *per class* for each image.\n    - df must have columns: ['image_id', 'prediction_string']\n    - prediction_string format: 'cls conf xc yc w h ...' (normalized coords)\n    - k can be an int (same K for all classes) or a dict {class_id: K_for_that_class}\n    - Rows with 'no boxes' remain unchanged.\n    \"\"\"\n    def _per_image_topk(pred_str: str) -> str:\n        if not isinstance(pred_str, str) or pred_str.strip().lower() == \"no boxes\":\n            return \"no boxes\"\n\n        toks = pred_str.strip().split()\n        n = len(toks) // 6\n        if n == 0:\n            return \"no boxes\"\n\n        # group by class -> list of (conf, cls, xc, yc, w, h)\n        groups = {}\n        for i in range(n):\n            seg = toks[i*6:(i+1)*6]\n            if len(seg) != 6:\n                continue\n            try:\n                cls  = int(float(seg[0]))\n                conf = float(seg[1])\n                xc   = float(seg[2]); yc = float(seg[3])\n                w    = float(seg[4]); h  = float(seg[5])\n            except Exception:\n                continue\n            groups.setdefault(cls, []).append((conf, cls, xc, yc, w, h))\n\n        kept = []\n        for cls, items in groups.items():\n            items.sort(key=lambda x: x[0], reverse=True)  # by confidence\n            k_cls = k.get(cls, len(items)) if isinstance(k, dict) else int(k)\n            kept.extend(items[:max(0, k_cls)])\n\n        if not kept:\n            return \"no boxes\"\n\n        # (optional) sort final by confidence desc for readability\n        kept.sort(key=lambda x: x[0], reverse=True)\n        return \" \".join(f\"{cls} {conf:.6f} {xc:.6f} {yc:.6f} {w:.6f} {h:.6f}\"\n                        for conf, cls, xc, yc, w, h in kept)\n\n    out = df.copy()\n    out[\"prediction_string\"] = out[\"prediction_string\"].apply(_per_image_topk)\n    return out\n\n# ---- usage ----\nsub = pd.read_csv('/kaggle/input/final-inference/submission_2_1024.csv')\n\n# Same K for every class:\nsub_topk = keep_topk_per_class(sub, k=2)\n\n\nsub_topk.to_csv('/kaggle/working/submission.csv', index=False)\nprint(\"saved -> /kaggle/working/submission.csv\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-18T23:04:02.080381Z","iopub.execute_input":"2025-08-18T23:04:02.081116Z","iopub.status.idle":"2025-08-18T23:04:02.104417Z","shell.execute_reply.started":"2025-08-18T23:04:02.081082Z","shell.execute_reply":"2025-08-18T23:04:02.103510Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import pandas as pd\n# import matplotlib.pyplot as plt\n# from pathlib import Path\n# from PIL import Image\n# from matplotlib.patches import Rectangle\n\n# def plot_submission_predictions(\n#     submission_csv: str,\n#     test_images_path: str,\n#     K: int = 5,\n#     min_conf: float = 0.0\n# ):\n#     \"\"\"\n#     Plot the first K images from submission.csv with their predicted boxes,\n#     but only show boxes with confidence >= min_conf.\n    \n#     submission_csv:   path to your final submission.csv\n#     test_images_path: folder containing the test images\n#     K:                number of images to visualize\n#     min_conf:         minimum confidence threshold for drawing a box\n#     \"\"\"\n#     df = pd.read_csv(submission_csv)\n#     img_folder = Path(test_images_path)\n    \n#     for _, row in df.head(K).iterrows():\n#         image_id = row[\"image_id\"]\n#         pred_str = row[\"prediction_string\"]\n        \n#         # locate the image file\n#         matches = list(img_folder.glob(f\"{image_id}.*\"))\n#         if not matches:\n#             print(f\"⚠️  Could not find file for {image_id}\")\n#             continue\n#         img = Image.open(matches[0])\n        \n#         fig, ax = plt.subplots(figsize=(8, 6))\n#         ax.imshow(img)\n#         ax.axis(\"off\")\n        \n#         if pred_str.lower() != \"no boxes\":\n#             toks = pred_str.split()\n#             for i in range(0, len(toks), 6):\n#                 lbl   = int(toks[i])\n#                 score = float(toks[i+1])\n#                 if score < min_conf:\n#                     continue\n#                 x_c   = float(toks[i+2])\n#                 y_c   = float(toks[i+3])\n#                 w     = float(toks[i+4])\n#                 h     = float(toks[i+5])\n                \n#                 # convert normalized center w,h to absolute top-left corner + size\n#                 x1    = (x_c - w/2) * img.width\n#                 y1    = (y_c - h/2) * img.height\n#                 abs_w = w * img.width\n#                 abs_h = h * img.height\n                \n#                 rect = Rectangle((x1, y1), abs_w, abs_h,\n#                                  fill=False, edgecolor=\"red\", lw=2)\n#                 ax.add_patch(rect)\n#                 ax.text(\n#                     x1, y1 - 3,\n#                     f\"{lbl}:{score:.2f}\",\n#                     color=\"yellow\", fontsize=10,\n#                     backgroundcolor=\"black\", alpha=0.7\n#                 )\n        \n#         plt.show()\n# plot_submission_predictions(\n#     submission_csv=\"/kaggle/working/submission.csv\",\n#     test_images_path= \"/kaggle/input/multi-class-object-detection-challenge/testImages/images\",\n#     K=280,\n#     min_conf=0.005   # only show boxes with confidence ≥ min_conf\n# )\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-18T23:04:02.454668Z","iopub.execute_input":"2025-08-18T23:04:02.455486Z","iopub.status.idle":"2025-08-18T23:04:17.491878Z","shell.execute_reply.started":"2025-08-18T23:04:02.455457Z","shell.execute_reply":"2025-08-18T23:04:17.490661Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}