{"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":"gpu","dataSources":[{"sourceId":19989,"databundleVersionId":1160143,"sourceType":"competition"},{"sourceId":11869597,"sourceType":"datasetVersion","datasetId":7459097},{"sourceId":211097053,"sourceType":"kernelVersion"}],"dockerImageVersionId":31041,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"PSUDO_LABELING_ENABLED = False\nTTA = False\nWEIGHT_FILE = \"/kaggle/input/mymodel/best.pt\"","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-05-19T12:10:26.976296Z","iopub.execute_input":"2025-05-19T12:10:26.976555Z","iopub.status.idle":"2025-05-19T12:10:26.980383Z","shell.execute_reply.started":"2025-05-19T12:10:26.976535Z","shell.execute_reply":"2025-05-19T12:10:26.979726Z"},"_kg_hide-output":true,"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip uninstall -y ray","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T12:10:28.737107Z","iopub.execute_input":"2025-05-19T12:10:28.737802Z","iopub.status.idle":"2025-05-19T12:10:33.599773Z","shell.execute_reply.started":"2025-05-19T12:10:28.737780Z","shell.execute_reply":"2025-05-19T12:10:33.598830Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!tar xfvz /kaggle/input/ultralytics-for-offline-install/archive.tar.gz\n!pip install --no-index --find-links=./packages ultralytics\n!rm -rf ./packages","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T12:10:33.601211Z","iopub.execute_input":"2025-05-19T12:10:33.601495Z","iopub.status.idle":"2025-05-19T12:12:12.192380Z","shell.execute_reply.started":"2025-05-19T12:10:33.601469Z","shell.execute_reply":"2025-05-19T12:12:12.191280Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Inference for Global Wheat Detection – fixed bbox + tuneable threshold\nimport os, glob, torch, numpy as np, pandas as pd\nfrom ultralytics import YOLO\nfrom tqdm.notebook import tqdm\n\n# 01 paths\nTEST_DIR    = \"/kaggle/input/global-wheat-detection/test\"\n\nWEIGHT_FILE = \"/kaggle/input/mymodel/best.pt\"\n\ndevice   = 0 if torch.cuda.is_available() else \"cpu\"\nmodel    = YOLO(WEIGHT_FILE)\n\nmodel.fuse()\nmodel.to(device).eval()\n\n# 02 params －－ 建議先把 CONF 拉高到 0.30 附近\nIMGSZ   = 1024\nCONF_TH = 0.2\nIOU_TH  = 0.5\nBATCH   = 8\n\n# 03 inference\ntest_imgs = sorted(glob.glob(f\"{TEST_DIR}/*.jpg\"))\nrows = []\n\nfor i in tqdm(range(0, len(test_imgs), BATCH)):\n    paths = test_imgs[i:i+BATCH]\n    preds = model.predict(\n        source=paths,\n        imgsz=IMGSZ,\n        conf=CONF_TH,\n        iou=IOU_TH,\n        half=False,\n        device=device,\n        verbose=False,\n        augment=TTA,\n    )\n    for res in preds:\n        img_id = os.path.basename(res.path).split('.')[0]\n        xyxy   = res.boxes.xyxy.cpu().numpy()      # (x1, y1, x2, y2)\n        scores = res.boxes.conf.cpu().numpy()\n\n        # 轉成 (x, y, w, h) 且全部 clip 到影像邊界內\n        if len(xyxy):\n            h_img, w_img = res.orig_shape\n            xyxy[:, [0, 2]] = np.clip(xyxy[:, [0, 2]], 0, w_img - 1)\n            xyxy[:, [1, 3]] = np.clip(xyxy[:, [1, 3]], 0, h_img - 1)\n            w_h   = xyxy[:, 2:] - xyxy[:, :2]       # w = x2-x1, h = y2-y1\n            xywh  = np.hstack([xyxy[:, :2], w_h])   # (x, y, w, h)\n\n            pred_str = \" \".join(\n                f\"{s:.4f} {x:.2f} {y:.2f} {w:.2f} {h:.2f}\"\n                for (x, y, w, h), s in zip(xywh, scores)\n                if w > 0 and h > 0                       # 忽略異常框\n            )\n        else:\n            pred_str = \"\"\n\n        rows.append({\"image_id\": img_id, \"PredictionString\": pred_str})\n\n# 04 save\nsub = pd.DataFrame(rows).drop_duplicates(\"image_id\", keep=\"first\")\nsub.to_csv(\"/kaggle/working/submission.csv\", index=False)\nsub.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T12:12:12.194063Z","iopub.execute_input":"2025-05-19T12:12:12.194348Z","iopub.status.idle":"2025-05-19T12:12:25.820790Z","shell.execute_reply.started":"2025-05-19T12:12:12.194319Z","shell.execute_reply":"2025-05-19T12:12:25.820180Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 可視化 Global Wheat Detection submission.csv\n# ----------------------------------------------------\n# 需求：pandas, cv2, matplotlib\nVISUALIZE = True\n\nif VISUALIZE:\n    import os, cv2\n    import pandas as pd\n    import numpy as np\n    import matplotlib.pyplot as plt\n    \n    # === 路徑設定（依自己環境調整） =======================\n    SUB_CSV  = \"/kaggle/working/submission.csv\"                   # 你的 submission\n    TEST_DIR = \"/kaggle/input/global-wheat-detection/test\"        # 測試影像資料夾\n    SAVE_DIR = \"/kaggle/working/vis_pred\"                         # 輸出示意圖\n    os.makedirs(SAVE_DIR, exist_ok=True)\n    \n    # === 讀取 submission ==================================\n    sub_df = pd.read_csv(SUB_CSV)\n    print(f\"Loaded {len(sub_df)} predictions\")\n    \n    # ------------------------------------------------------\n    # 將 PredictionString 解析並畫回影像\n    def visualize_one(row, save_png=False, score_th=0.0):\n        \"\"\"\n        row: 一列，含 image_id 與 PredictionString\n        score_th: 若想只看高分框，可設 0.3, 0.5 ...\n        \"\"\"\n        img_path = os.path.join(TEST_DIR, f\"{row.image_id}.jpg\")\n        img = cv2.imread(img_path)\n        if img is None:\n            print(f\"!!! 找不到影像 {img_path}\")\n            return\n    \n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    \n        if isinstance(row.PredictionString, str) and row.PredictionString.strip():\n            vals = list(map(float, row.PredictionString.split()))\n            # 每 5 個數：score x y w h\n            for s, x, y, w, h in np.array(vals).reshape(-1, 5):\n                if s < score_th:           # 篩掉低分框\n                    continue\n                x1, y1 = int(x), int(y)\n                x2, y2 = int(x + w), int(y + h)\n                # 畫框\n                cv2.rectangle(img, (x1, y1), (x2, y2), (255, 0, 0), 2)\n                # 標註分數\n                cv2.putText(img, f\"{s:.2f}\", (x1, max(y1-4, 0)),\n                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)\n    \n        plt.figure(figsize=(8, 8))\n        plt.axis('off')\n        plt.imshow(img)\n        if save_png:\n            out_path = os.path.join(SAVE_DIR, f\"{row.image_id}.png\")\n            plt.savefig(out_path, bbox_inches='tight', pad_inches=0)\n            print(f\"saved -> {out_path}\")\n        plt.show()\n    \n    \n    # === Demo: 隨機挑 5 張圖來看 ===========================\n    sample_rows = sub_df.sample(5, random_state=0)\n    for _, r in sample_rows.iterrows():\n        visualize_one(r, save_png=False, score_th=0.25)  # 只畫 score ≥0.25 的框\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T12:14:52.459565Z","iopub.execute_input":"2025-05-19T12:14:52.459884Z","iopub.status.idle":"2025-05-19T12:14:54.404091Z","shell.execute_reply.started":"2025-05-19T12:14:52.459863Z","shell.execute_reply":"2025-05-19T12:14:54.403240Z"}},"outputs":[],"execution_count":null}]}