{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":114094,"databundleVersionId":13610510,"sourceType":"competition"}],"dockerImageVersionId":31041,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# README\n\n- Mục đích: Học bài toán Object Detection trong Computer Vision\n- Cách làm:\n    - Tham gia 1 competition đơn giản về bài toán này - Grocery Items: Multi-class Object Detection được chọn vì:\n        - Bộ dữ liệu nhỏ + Chỉ có 3 classes -> Nhẹ, dễ train\n        - Bài toán tường minh, dễ hiểu, phù hợp nhu cầu nhận diện các vật dụng thường gặp\n    - Copy&Edit 1 bài có sẵn để học nhanh hơn\n- Nhận xét:\n    - Competition mới, Notebook gốc cũng mới nên không cần chỉnh sửa gì thêm đã chạy được\n    - Bài toán Oject Detection có 1 giải pháp chuẩn hóa và phổ biến là YOLO nên code khá đơn giản, bản thân tác giả bài gốc cũng không cần setting gì nhiều nên mình cũng không chú thích thêm\n    - Độ chính xác của kết quả còn khá hạn chế (Recall=45%, Precision=86% -> Dự đoán khá đúng nhưng bỏ sót nhiều), tuy nhiên để làm 1 task học tập cơ bản thì vẫn oke","metadata":{}},{"cell_type":"code","source":"!pip install ultralytics > /dev/null","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-09-07T09:06:31.690002Z","iopub.execute_input":"2025-09-07T09:06:31.690236Z","iopub.status.idle":"2025-09-07T09:07:42.402244Z","shell.execute_reply.started":"2025-09-07T09:06:31.690215Z","shell.execute_reply":"2025-09-07T09:07:42.400879Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport shutil\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nfrom pathlib import Path\nfrom ultralytics import YOLO","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T09:07:42.405255Z","iopub.execute_input":"2025-09-07T09:07:42.405581Z","iopub.status.idle":"2025-09-07T09:07:46.541363Z","shell.execute_reply.started":"2025-09-07T09:07:42.405546Z","shell.execute_reply":"2025-09-07T09:07:46.540609Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_images = Path('/kaggle/input/grocery-items-multi-class-object-detection/train/train/images')\ntrain_masks = Path('/kaggle/input/grocery-items-multi-class-object-detection/train/train/labels')\n\ntest_images = Path(\"/kaggle/input/grocery-items-multi-class-object-detection/testImages/testImages/images\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T09:07:46.544582Z","iopub.execute_input":"2025-09-07T09:07:46.545216Z","iopub.status.idle":"2025-09-07T09:07:46.548539Z","shell.execute_reply.started":"2025-09-07T09:07:46.545197Z","shell.execute_reply":"2025-09-07T09:07:46.547813Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"output_dir = Path(\"/kaggle/working/predictions/labels\")\noutput_dir.mkdir(parents=True, exist_ok=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T09:07:46.549596Z","iopub.execute_input":"2025-09-07T09:07:46.549929Z","iopub.status.idle":"2025-09-07T09:07:48.235988Z","shell.execute_reply.started":"2025-09-07T09:07:46.549897Z","shell.execute_reply":"2025-09-07T09:07:48.235210Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 🤩 First, let's look at the images!","metadata":{}},{"cell_type":"code","source":"def visualize_random_masks(images_dir, masks_dir, counts=5):\n    \"\"\"\n    Vizualize random images with masks.\n    \n    images_dir: path to directory with images\n    masks_dir: path to directory with masks\n    counts: Numbers of images to show\n    \"\"\"\n\n    image_paths = list(images_dir.glob(\"*\"))\n    samples = np.random.choice(image_paths, counts, replace=False)\n\n    plt.figure(figsize=(counts * 5, 8))\n\n    for i, img_path in enumerate(samples, 1):\n        mask_path = masks_dir / img_path.with_suffix(\".txt\").name\n\n        image = cv2.imread(str(img_path))\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n        h, w, _ = image.shape\n\n        with open(mask_path, \"r\") as f:\n            lines = f.readlines()\n    \n        for line in lines:\n            parts = line.strip().split()\n\n            try:\n                class_id, x_center, y_center, box_w, box_h = map(float, parts)\n            except:\n                class_id, confidence, x_center, y_center, box_w, box_h = map(float, parts)\n\n            x_center *= w\n            y_center *= h\n            box_w *= w\n            box_h *= h\n    \n            x1 = int(x_center - box_w / 2)\n            y1 = int(y_center - box_h / 2)\n            x2 = int(x_center + box_w / 2)\n            y2 = int(y_center + box_h / 2)\n\n            cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 5)\n\n        plt.subplot(1, counts, i)\n        plt.imshow(image)\n        plt.axis(\"off\")\n        plt.tight_layout()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T09:07:48.236812Z","iopub.execute_input":"2025-09-07T09:07:48.237073Z","iopub.status.idle":"2025-09-07T09:07:48.246904Z","shell.execute_reply.started":"2025-09-07T09:07:48.237031Z","shell.execute_reply":"2025-09-07T09:07:48.246376Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"visualize_random_masks(train_images, train_masks)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T09:07:48.248841Z","iopub.execute_input":"2025-09-07T09:07:48.249083Z","iopub.status.idle":"2025-09-07T09:07:51.143969Z","shell.execute_reply.started":"2025-09-07T09:07:48.249065Z","shell.execute_reply":"2025-09-07T09:07:51.143230Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data_yaml = \"\"\"\ntrain: /kaggle/input/grocery-items-multi-class-object-detection/train/train/images\nval: /kaggle/input/grocery-items-multi-class-object-detection/val/val/images\ntest: /kaggle/input/grocery-items-multi-class-object-detection/testImages/testImages/images\n\nnc: 3\nnames: ['cheerios', 'soup', 'candle']\n\"\"\"\n\nwith open('data.yaml', 'w') as file:\n    file.write(data_yaml)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T09:07:51.145130Z","iopub.execute_input":"2025-09-07T09:07:51.145675Z","iopub.status.idle":"2025-09-07T09:07:51.157297Z","shell.execute_reply.started":"2025-09-07T09:07:51.145654Z","shell.execute_reply":"2025-09-07T09:07:51.156573Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Detection models YOLOv11\n\n    yolo11n.pt 🚀✨ — Nano\n    \n    yolo11s.pt 🐦⚡ — Small\n    \n    yolo11m.pt 🦅🔥 — Medium\n    \n    yolo11l.pt 🐉💪 — Large\n    \n    yolo11x.pt 🦾🏋️‍♂️ — Extra Large\n\nDo you need more information❓\n[YOLOv11](https://docs.ultralytics.com/ru/tasks/detect/#models)","metadata":{}},{"cell_type":"code","source":"model = YOLO(\"yolo11n.pt\")\n\nresults = model.train(\n    data=\"data.yaml\",\n    epochs=80,\n    batch=4,\n    imgsz=960,\n    device=[0,1],\n    optimizer=\"Adam\",\n    seed=42,\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T09:17:40.526826Z","iopub.execute_input":"2025-09-07T09:17:40.527557Z","iopub.status.idle":"2025-09-07T09:41:34.124229Z","shell.execute_reply.started":"2025-09-07T09:17:40.527530Z","shell.execute_reply":"2025-09-07T09:41:34.123532Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 🎇 We predict the test dataset and save it in txt","metadata":{}},{"cell_type":"code","source":"for img_path in test_images.glob(\"*\"):\n    results = model.predict(img_path, conf=0.1, device=0, verbose=False) # 0 - GPU or \"cpu\"\n    output_txt = output_dir / f\"{img_path.stem}.txt\"\n\n    with open(output_txt, \"w\") as f:\n        found = False\n        for result in results:\n            img_height, img_width = result.orig_shape\n            boxes = result.boxes.data\n\n            if boxes is None or len(boxes) == 0:\n                continue\n\n            filtered_boxes = boxes[boxes[:, 4] >= 0.05]\n            if len(filtered_boxes) == 0:\n                continue\n\n            found = True\n            for box in filtered_boxes:\n                x1, y1, x2, y2, confidence, cls_id = box.tolist()\n\n                x_center = ((x1 + x2) / 2) / img_width\n                y_center = ((y1 + y2) / 2) / img_height\n                width = (x2 - x1) / img_width\n                height = (y2 - y1) / img_height\n\n                f.write(f\"0 {confidence:.6f} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\\n\")\n\n        if not found:\n            f.write(\"\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T09:42:53.556521Z","iopub.execute_input":"2025-09-07T09:42:53.557182Z","iopub.status.idle":"2025-09-07T09:43:38.670461Z","shell.execute_reply.started":"2025-09-07T09:42:53.557157Z","shell.execute_reply":"2025-09-07T09:43:38.669769Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 😎 Let's see the results","metadata":{}},{"cell_type":"code","source":"visualize_random_masks(test_images, output_dir)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T09:43:38.671660Z","iopub.execute_input":"2025-09-07T09:43:38.671938Z","iopub.status.idle":"2025-09-07T09:43:57.085790Z","shell.execute_reply.started":"2025-09-07T09:43:38.671921Z","shell.execute_reply":"2025-09-07T09:43:57.085003Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# ✉️ Saving the results","metadata":{}},{"cell_type":"code","source":"rows = []\ntest_imgs = {p.stem for p in test_images.glob(\"*\") if p.suffix.lower() in {\".jpg\", \".jpeg\", \".png\"}}\npredicted = set()\n\nfor file in output_dir.glob(\"*.txt\"):\n    name = file.stem\n    predicted.add(name)\n\n    try:\n        lines = [l.strip() for l in open(file) if len(l.strip().split()) == 6]\n    except:\n        lines = []\n\n    rows.append({\"image_id\": name, \"prediction_string\": \" \".join(lines) if lines else \"no boxes\"})\n\nfor name in test_imgs - predicted:\n    rows.append({\"image_id\": name, \"prediction_string\": \"no boxes\"})","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T09:43:57.086585Z","iopub.execute_input":"2025-09-07T09:43:57.086803Z","iopub.status.idle":"2025-09-07T09:43:57.101914Z","shell.execute_reply.started":"2025-09-07T09:43:57.086787Z","shell.execute_reply":"2025-09-07T09:43:57.101227Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"work_dir = '/kaggle/working'\n\nfor filename in os.listdir(work_dir):\n    file_path = os.path.join(work_dir, filename)\n\n    try:\n        if os.path.isfile(file_path) or os.path.islink(file_path):\n            os.unlink(file_path)\n        elif os.path.isdir(file_path):\n            shutil.rmtree(file_path)\n    except Exception as e:\n        print(f'Error file: {file_path}. Cause: {e}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T09:43:57.103352Z","iopub.execute_input":"2025-09-07T09:43:57.103573Z","iopub.status.idle":"2025-09-07T09:43:57.116909Z","shell.execute_reply.started":"2025-09-07T09:43:57.103557Z","shell.execute_reply":"2025-09-07T09:43:57.116233Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"rows = pd.DataFrame(rows)\nrows.to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T09:43:57.117714Z","iopub.execute_input":"2025-09-07T09:43:57.117964Z","iopub.status.idle":"2025-09-07T09:43:57.129505Z","shell.execute_reply.started":"2025-09-07T09:43:57.117948Z","shell.execute_reply":"2025-09-07T09:43:57.128820Z"}},"outputs":[],"execution_count":null}]}