{"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":129135,"databundleVersionId":15624924},{"sourceType":"datasetVersion","sourceId":14977991,"datasetId":9587413,"databundleVersionId":15850926},{"sourceType":"kernelVersion","sourceId":300426655}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install -q ultralytics\n\nimport os\nimport shutil\nimport pandas as pd\nfrom sklearn.model_selection import train_test_split\nimport yaml\nfrom ultralytics import RTDETR\nfrom PIL import Image","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-03-15T17:11:22.313809Z","iopub.execute_input":"2026-03-15T17:11:22.314202Z","iopub.status.idle":"2026-03-15T17:11:39.429589Z","shell.execute_reply.started":"2026-03-15T17:11:22.314144Z","shell.execute_reply":"2026-03-15T17:11:39.428498Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"INPUT_DIR = '/kaggle/input/competitions/faic-kaggle-competitions/public'\nORIG_IMG_DIR = os.path.join(INPUT_DIR, 'train/images')\nORIG_LBL_DIR = os.path.join(INPUT_DIR, 'train/labels')\n\nWORK_DIR = '/kaggle/working/vinfast_data'\nos.makedirs(WORK_DIR, exist_ok=True)\n\nall_images = [f for f in os.listdir(ORIG_IMG_DIR) if f.endswith(('.jpg', '.png', '.jpeg'))]\n\ntrain_imgs, val_imgs = train_test_split(all_images, test_size=0.1, random_state=42)\n\ndef process_split(split_name, img_list):\n    img_dest = os.path.join(WORK_DIR, 'images', split_name)\n    lbl_dest = os.path.join(WORK_DIR, 'labels', split_name)\n    os.makedirs(img_dest, exist_ok=True)\n    os.makedirs(lbl_dest, exist_ok=True)\n    \n    empty_label_count = 0\n    \n    for img_name in img_list:\n        shutil.copy(os.path.join(ORIG_IMG_DIR, img_name), os.path.join(img_dest, img_name))\n\n        lbl_name = os.path.splitext(img_name)[0] + '.txt'\n        orig_lbl_path = os.path.join(ORIG_LBL_DIR, lbl_name)\n        dest_lbl_path = os.path.join(lbl_dest, lbl_name)\n        \n        if os.path.exists(orig_lbl_path):\n            shutil.copy(orig_lbl_path, dest_lbl_path)\n        else:\n            open(dest_lbl_path, 'a').close() \n            empty_label_count += 1\n            \n    print(f\"[{split_name.upper()}] Đã xử lý {len(img_list)} ảnh. Số ảnh không có xe (Empty labels): {empty_label_count}\")\n\nprocess_split('train', train_imgs)\nprocess_split('val', val_imgs)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-26T14:04:26.642432Z","iopub.execute_input":"2026-02-26T14:04:26.643258Z","iopub.status.idle":"2026-02-26T14:05:14.571705Z","shell.execute_reply.started":"2026-02-26T14:04:26.643214Z","shell.execute_reply":"2026-02-26T14:05:14.570765Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data_config = {\n    'path': WORK_DIR,\n    'train': 'images/train',\n    'val': 'images/val',\n    'names': {0: 'vin_car'}\n}\n\nyaml_path = '/kaggle/working/vinfast.yaml'\nwith open(yaml_path, 'w') as f:\n    yaml.dump(data_config, f, default_flow_style=False)\n\nprint(f\"Đã tạo file cấu hình: {yaml_path}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-26T14:05:45.418499Z","iopub.execute_input":"2026-02-26T14:05:45.419563Z","iopub.status.idle":"2026-02-26T14:05:45.426948Z","shell.execute_reply.started":"2026-02-26T14:05:45.419484Z","shell.execute_reply":"2026-02-26T14:05:45.425763Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = RTDETR('rtdetr-l.pt')\n\nresults = model.train(\n    data=yaml_path,\n    epochs=80,                  \n    imgsz=800,                  \n    batch=4,                    \n    device=0,                   \n    optimizer='AdamW',\n    lr0=0.0001,\n    mosaic=1.0,\n    mixup=0.15,                 \n    hsv_h=0.015, hsv_s=0.7, hsv_v=0.4,\n    fliplr=0.5,                 \n    close_mosaic=15,          \n    save_period=10,             \n    patience=20,                \n    project='/kaggle/working/vinfast_run',\n    name='best_model'\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-26T14:08:47.303336Z","iopub.execute_input":"2026-02-26T14:08:47.303584Z","iopub.status.idle":"2026-02-26T14:08:47.318386Z","shell.execute_reply.started":"2026-02-26T14:08:47.303555Z","shell.execute_reply":"2026-02-26T14:08:47.317178Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"best_model_path = '/kaggle/working/vinfast_run/best_model/weights/best.pt'\nmodel_test = RTDETR(best_model_path)\n\ntest_images_dir = os.path.join(INPUT_DIR, 'test/images')\ntest_imgs = os.listdir(test_images_dir)\n\nconfidence_thresholds = [0.20, 0.25, 0.30] \n\nfor conf_thresh in confidence_thresholds:\n    results_list = []\n    print(f\"\\nĐang dự đoán với ngưỡng Confidence: {conf_thresh}...\")\n    \n    for img_name in test_imgs:\n        img_path = os.path.join(test_images_dir, img_name)\n        \n        preds = model_test.predict(img_path, imgsz=800, conf=conf_thresh, augment=True, verbose=False)\n        \n        pred_strings = []\n        for box in preds[0].boxes:\n            conf = float(box.conf)\n            x_min, y_min, x_max, y_max = box.xyxy[0].tolist()\n            \n            pred_strings.append(f\"{conf:.4f} {x_min:.1f} {y_min:.1f} {x_max:.1f} {y_max:.1f}\")\n            \n        final_string = \" \".join(pred_strings)\n        results_list.append({\"image_id\": img_name, \"prediction_string\": final_string})\n        \n    # Lưu file CSV\n    df = pd.DataFrame(results_list)\n    csv_name = f'submission_conf_{conf_thresh}.csv'\n    df.to_csv(csv_name, index=False)\n    print(f\"Đã lưu: {csv_name}\")\n\nprint(\"\\nHOÀN THÀNH!!\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nfor root, dirs, files in os.walk('/kaggle/input'):\n    for file in files:\n        if file == 'best.pt':\n            print(f\"🎯 Đã tìm thấy file tại: {os.path.join(root, file)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T06:14:17.850497Z","iopub.execute_input":"2026-02-27T06:14:17.851323Z","iopub.status.idle":"2026-02-27T06:14:37.934885Z","shell.execute_reply.started":"2026-02-27T06:14:17.851289Z","shell.execute_reply":"2026-02-27T06:14:37.934052Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\nfrom ultralytics import RTDETR\n\nbest_model_path = '/kaggle/input/notebooks/nvfus12/rt-detr/vinfast_run/best_model/weights/best.pt'\nmodel_test = RTDETR(best_model_path)\n\ntest_images_dir = '/kaggle/input/competitions/faic-kaggle-competitions/public/test/images'\ntest_imgs = os.listdir(test_images_dir)\n\nconfidence_thresholds = [0.1, 0.05] \n\nfor conf_thresh in confidence_thresholds:\n    results_list = []\n    print(f\"🚀 Đang xử lý: conf={conf_thresh}, imgsz=1024, augment=True...\")\n    \n    for img_name in test_imgs:\n        img_path = os.path.join(test_images_dir, img_name)\n        \n        preds = model_test.predict(img_path, imgsz=1024, conf=conf_thresh, augment=True, verbose=False)\n        \n        pred_strings = []\n        for box in preds[0].boxes:\n            conf = float(box.conf)\n            x_min, y_min, x_max, y_max = [int(round(float(x))) for x in box.xyxy[0].tolist()]\n            pred_strings.append(f\"{conf:.4f} {x_min} {y_min} {x_max} {y_max}\")\n            \n        final_string = \" \".join(pred_strings)\n        results_list.append({\"image_id\": img_name, \"prediction_string\": final_string if final_string.strip() != \"\" else \" \"})\n        \n    df = pd.DataFrame(results_list)\n    df.to_csv(f'submission_img1024_conf_{conf_thresh}.csv', index=False)\n    print(f\"✅ Đã lưu: submission_img1024_conf_{conf_thresh}.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T06:15:03.786383Z","iopub.execute_input":"2026-02-27T06:15:03.787032Z","iopub.status.idle":"2026-02-27T06:17:36.064311Z","shell.execute_reply.started":"2026-02-27T06:15:03.786995Z","shell.execute_reply":"2026-02-27T06:17:36.063446Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install ensemble-boxes -q","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T08:10:25.867535Z","iopub.execute_input":"2026-02-27T08:10:25.867910Z","iopub.status.idle":"2026-02-27T08:10:29.360972Z","shell.execute_reply.started":"2026-02-27T08:10:25.867882Z","shell.execute_reply":"2026-02-27T08:10:29.360195Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom ensemble_boxes import *\nimport os\nfrom PIL import Image\n\ndef run_wbf_ensemble(file_paths, weights, output_name, iou_thr=0.55, skip_box_thr=0.15):\n    dfs = [pd.read_csv(f) for f in file_paths]\n    image_ids = dfs[0]['image_id'].unique()\n    \n    test_img_dir = '/kaggle/input/competitions/faic-kaggle-competitions/public/test/images'\n    final_data = []\n\n    print(f\"🚀 Đang gộp {len(file_paths)} file bằng WBF...\")\n\n    for img_id in image_ids:\n        boxes_list = []\n        scores_list = []\n        labels_list = []\n        \n        with Image.open(os.path.join(test_img_dir, img_id)) as img:\n            width, height = img.size\n\n        for df in dfs:\n            row = df[df['image_id'] == img_id]\n            if row.empty or pd.isna(row.iloc[0]['prediction_string']):\n                continue\n                \n            p_str = str(row.iloc[0]['prediction_string']).strip()\n            if not p_str: continue\n            \n            parts = p_str.split()\n            bxs, scs, lbs = [], [], []\n            for i in range(0, len(parts), 5):\n                s = float(parts[i])\n                x1 = float(parts[i+1]) / width\n                y1 = float(parts[i+2]) / height\n                x2 = float(parts[i+3]) / width\n                y2 = float(parts[i+4]) / height\n                \n                bxs.append([max(0, min(1, x1)), max(0, min(1, y1)), \n                            max(0, min(1, x2)), max(0, min(1, y2))])\n                scs.append(s)\n                lbs.append(0) \n            \n            if bxs:\n                boxes_list.append(bxs)\n                scores_list.append(scs)\n                labels_list.append(lbs)\n\n        if not boxes_list:\n            final_data.append({'image_id': img_id, 'prediction_string': ' '})\n            continue\n\n        boxes, scores, labels = weighted_boxes_fusion(\n            boxes_list, scores_list, labels_list, \n            weights=weights, iou_thr=iou_thr, skip_box_thr=skip_box_thr\n        )\n\n        res_parts = []\n        for b, s in zip(boxes, scores):\n            x1, y1, x2, y2 = b\n            res_parts.append(f\"{s:.4f} {int(round(x1*width))} {int(round(y1*height))} {int(round(x2*width))} {int(round(y2*height))}\")\n        \n        final_data.append({'image_id': img_id, 'prediction_string': \" \".join(res_parts)})\n\n    pd.DataFrame(final_data).to_csv(output_name, index=False)\n    print(f\"✅ Đã tạo xong: {output_name}\")\n\ninput_csvs = [\n    '/kaggle/input/datasets/nvfus12/final-fix/final_fixed_submission_conf_0.2.csv',    \n    '/kaggle/input/datasets/nvfus12/final-fix/final_fixed_submission_conf_0.25.csv',   \n    '/kaggle/input/datasets/nvfus12/final-fix/final_fixed_submission_conf_0.3.csv'     \n]\nweights = [3, 2, 1] \n\nrun_wbf_ensemble(input_csvs, weights, 'submission_WBF_Gold.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T06:37:23.831392Z","iopub.execute_input":"2026-02-27T06:37:23.832411Z","iopub.status.idle":"2026-02-27T06:37:28.935739Z","shell.execute_reply.started":"2026-02-27T06:37:23.832319Z","shell.execute_reply":"2026-02-27T06:37:28.934888Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\nimport numpy as np\nimport glob\nfrom ultralytics import RTDETR\nfrom ensemble_boxes import weighted_boxes_fusion\nfrom PIL import Image\n\ntry:\n    best_model_path = glob.glob('/kaggle/input/**/best.pt', recursive=True)[0]\n    print(f\"🎯 Đang sử dụng model tại: {best_model_path}\")\nexcept:\n    best_model_path = '/kaggle/working/vinfast_run/best_model/weights/best.pt'\n\nmodel = RTDETR(best_model_path)\ntest_dir = '/kaggle/input/competitions/faic-kaggle-competitions/public/test/images'\ntest_imgs = sorted(os.listdir(test_dir))\nconf_list = [0.18, 0.20, 0.22]\nweights = [1, 5, 1]\nimgsz_fixed = 800  \n\nall_preds = {img_id: {c: {'boxes': [], 'scores': []} for c in conf_list} for img_id in test_imgs}\n\n# 3. Bước Inference (Dự đoán)\nfor conf in conf_list:\n    print(f\"🚀 Đang chạy Inference với conf={conf}...\")\n    for img_name in test_imgs:\n        img_path = os.path.join(test_dir, img_name)\n        \n        results = model.predict(img_path, imgsz=imgsz_fixed, conf=conf, augment=True, verbose=False)\n        \n        boxes = []\n        scores = []\n        for box in results[0].boxes:\n            # WBF yêu cầu tọa độ chuẩn hóa [0, 1]\n            x1, y1, x2, y2 = box.xyxyn[0].tolist()\n            boxes.append([x1, y1, x2, y2])\n            scores.append(float(box.conf))\n            \n        all_preds[img_name][conf]['boxes'] = boxes\n        all_preds[img_name][conf]['scores'] = scores\n\nfinal_submissions = []\nprint(\"....................\")\n\nfor img_name in test_imgs:\n    boxes_list = [all_preds[img_name][c]['boxes'] for c in conf_list]\n    scores_list = [all_preds[img_name][c]['scores'] for c in conf_list]\n    labels_list = [[0] * len(sc) for sc in scores_list] # Chỉ có 1 class: vin_car\n\n    boxes, scores, labels = weighted_boxes_fusion(\n        boxes_list, scores_list, labels_list, \n        weights=weights, iou_thr=0.6, skip_box_thr=0.18\n    )\n\n    with Image.open(os.path.join(test_dir, img_name)) as img:\n        w, h = img.size\n        \n    pred_strings = []\n    for b, s in zip(boxes, scores):\n        x1, y1, x2, y2 = b\n        ix1 = max(0, min(w, int(round(x1 * w))))\n        iy1 = max(0, min(h, int(round(y1 * h))))\n        ix2 = max(0, min(w, int(round(x2 * w))))\n        iy2 = max(0, min(h, int(round(y2 * h))))\n        \n        if ix2 > ix1 and iy2 > iy1:\n            pred_strings.append(f\"{s:.4f} {ix1} {iy1} {ix2} {iy2}\")\n    \n    final_data = \" \".join(pred_strings)\n    final_submissions.append({\n        \"image_id\": img_name,\n        \"prediction_string\": final_data if final_data.strip() != \"\" else \" \"\n    })\n\n# 5. Xuất file nộp bài\npd.DataFrame(final_submissions).to_csv('submission_Threshold_WBF_Final1.csv', index=False)\nprint(\"Done\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-27T08:45:12.258464Z","iopub.execute_input":"2026-02-27T08:45:12.259133Z","iopub.status.idle":"2026-02-27T08:47:44.561391Z","shell.execute_reply.started":"2026-02-27T08:45:12.259101Z","shell.execute_reply":"2026-02-27T08:47:44.560549Z"}},"outputs":[],"execution_count":null}]}