{"metadata":{"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":19989,"databundleVersionId":1160143,"sourceType":"competition"},{"sourceId":9532914,"sourceType":"datasetVersion","datasetId":5805822},{"sourceId":9551946,"sourceType":"datasetVersion","datasetId":5820005},{"sourceId":9440389,"sourceType":"datasetVersion","datasetId":5736720},{"sourceId":126079,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":106130,"modelId":130421},{"sourceId":126187,"sourceType":"modelInstanceVersion","modelInstanceId":106222,"modelId":130514},{"sourceId":126804,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":106752,"modelId":131080},{"sourceId":126936,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":106867,"modelId":131200}],"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true},"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.14"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import sys\nsys.path.append('/kaggle/input/ultralytics/ultralytics-main')\nsys.path.append('/kaggle/input/ensemble-boxes/Weighted-Boxes-Fusion-master')","metadata":{"execution":{"iopub.status.busy":"2024-10-09T07:47:18.588280Z","iopub.execute_input":"2024-10-09T07:47:18.589088Z","iopub.status.idle":"2024-10-09T07:47:18.599430Z","shell.execute_reply.started":"2024-10-09T07:47:18.589044Z","shell.execute_reply":"2024-10-09T07:47:18.598289Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn\nimport ultralytics\nfrom ultralytics import YOLO\nimport cv2\nimport torch\nimport glob\nimport os\nfrom tqdm.auto import tqdm\nfrom shutil import copyfile\nfrom ensemble_boxes import *\nimport yaml\nimport torchvision\nfrom torchvision.io import read_image\nfrom torchvision.utils import draw_bounding_boxes\nimport os\nos.environ['WANDB_MODE'] = 'dryrun'\nimport warnings\nwarnings.filterwarnings('ignore')","metadata":{"execution":{"iopub.status.busy":"2024-10-09T07:47:18.601419Z","iopub.execute_input":"2024-10-09T07:47:18.601719Z","iopub.status.idle":"2024-10-09T07:47:29.676743Z","shell.execute_reply.started":"2024-10-09T07:47:18.601688Z","shell.execute_reply":"2024-10-09T07:47:29.675969Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"Pseudo = True\nTEST_PATH = '/kaggle/input/global-wheat-detection/test'\nLABEL_PATH = '/kaggle/working/predict/labels'\nIMAGE_PATH = '/kaggle/working/predict/images'\nPREDICT_PATH = '/kaggle/working/predict/'\ntest_model = YOLO('/kaggle/input/yolov8s/pytorch/default/1/best.pt')\nos.makedirs(LABEL_PATH, exist_ok=True)\nos.makedirs(IMAGE_PATH, exist_ok=True)","metadata":{"execution":{"iopub.status.busy":"2024-10-09T07:47:29.678040Z","iopub.execute_input":"2024-10-09T07:47:29.678636Z","iopub.status.idle":"2024-10-09T07:47:30.422209Z","shell.execute_reply.started":"2024-10-09T07:47:29.678581Z","shell.execute_reply":"2024-10-09T07:47:30.421200Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test = os.listdir(TEST_PATH)\ntest_images = []\nfor p in test:\n    test_images.append(os.path.join(TEST_PATH, p))","metadata":{"execution":{"iopub.status.busy":"2024-10-09T07:47:30.423298Z","iopub.execute_input":"2024-10-09T07:47:30.423592Z","iopub.status.idle":"2024-10-09T07:47:30.434666Z","shell.execute_reply.started":"2024-10-09T07:47:30.423563Z","shell.execute_reply":"2024-10-09T07:47:30.433924Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if Pseudo:\n    # 将测试集图片拷贝到label_path\n    for p in tqdm(test):\n        copyfile(os.path.join(TEST_PATH, p), os.path.join(IMAGE_PATH,p))","metadata":{"execution":{"iopub.status.busy":"2024-10-09T07:47:30.438013Z","iopub.execute_input":"2024-10-09T07:47:30.438306Z","iopub.status.idle":"2024-10-09T07:47:30.541796Z","shell.execute_reply.started":"2024-10-09T07:47:30.438277Z","shell.execute_reply":"2024-10-09T07:47:30.540951Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# TTA + WBF \n\ndef run_wbf(boxes_list, scores_list, image_size=1024, weight=None, skip_thr=0.5, iou_thr=0.5):\n    \"\"\"\n    Coordinates for boxes expected to be normalized e.g in range [0; 1]. Order: x1, y1, x2, y2.\n    \"\"\"\n    \n    # Merge boxes for single model predictions\n    # boxes, scores, labels = weighted_boxes_fusion([boxes_list], [scores_list], [labels_list], weights=None, method=method, iou_thr=iou_thr, thresh=thresh)\n    labels = [np.zeros(score.shape[0]) for score in scores_list]\n    boxes = [box/(image_size) for box in boxes_list]\n    boxes, scores, labels = weighted_boxes_fusion(\n        boxes, scores_list, labels, weights=weight, iou_thr=iou_thr, skip_box_thr=skip_thr)\n    boxes = boxes*(image_size)\n    return boxes, scores, labels\n\ndef rotate_img(img,index):\n    \"\"\"\n    对测试图片进行简单的旋转\n    \"\"\"\n    img1 = img.copy()\n    if index == 0:\n        img1=cv2.rotate(img1, cv2.ROTATE_90_CLOCKWISE)\n        return img1\n    elif index == 1:\n        img2 = cv2.rotate(img1, cv2.ROTATE_90_CLOCKWISE)\n        img3 = cv2.rotate(img2, cv2.ROTATE_90_CLOCKWISE)\n        return img3\n    elif index==2:\n        img2 = cv2.rotate(img1, cv2.ROTATE_90_CLOCKWISE)\n        img3 = cv2.rotate(img2, cv2.ROTATE_90_CLOCKWISE)\n        img4 = cv2.rotate(img3, cv2.ROTATE_90_CLOCKWISE)\n        return img4\n    else:\n        return img1\n    \n\ndef rotBoxes90(boxes, im_w, im_h):\n    #　绝对值的边界框　x1 y1 x2 y2\n    \n    ret_boxes = []\n    \n    for box in boxes:\n        x1, y1, x2, y2 = box\n        x1, y1, x2, y2 = x1-im_w//2, im_h//2 - y1, x2-im_w//2, im_h//2 - y2\n        x1, y1, x2, y2 = y1, -x1, y2, -x2\n        x1, y1, x2, y2 = int(x1+im_w//2), int(im_h//2 -\n                                              y1), int(x2+im_w//2), int(im_h//2 - y2)\n        x1a, y1a, x2a, y2a = min(x1, x2), min(y1, y2), max(x1, x2), max(y1, y2)\n        ret_boxes.append([x1a, y1a, x2a, y2a])\n    return np.array(ret_boxes)\n\n\ndef draw_boxes_with_scores(img, boxes, scores, color=(0, 255, 0), thickness=2, font_scale=0.5):\n    \"\"\"\n    在图像上绘制给定的框，并显示框的分数，框的形式为 xyxy。\n\n    参数:\n        img: 输入的图像，可以是BGR格式（OpenCV读入的图像格式）。\n        boxes: 框的列表，格式为 [x1, y1, x2, y2]，表示框的左上角和右下角坐标。\n        scores: 框对应的分数列表，格式为 [score1, score2, ...]。\n        color: 绘制框的颜色，默认为绿色 (0, 255, 0)。\n        thickness: 框的线条厚度，默认为2。\n        font_scale: 文本字体大小，默认为0.5。\n    \n    返回:\n        带有框和分数的图像。\n    \"\"\"\n    # 确保 boxes 和 scores 是 numpy 数组\n    boxes = np.array(boxes)\n    scores = np.array(scores)\n\n    # 遍历每个框，并在图像上绘制框和分数\n    for box, score in zip(boxes, scores):\n        x1, y1, x2, y2 = map(int, box)  # 将坐标转换为整数\n        img = cv2.rectangle(img, (x1, y1), (x2, y2), color, thickness)\n\n        # 绘制分数文本，放在框的左上角\n        score_text = f\"{score:.2f}\"  # 保留两位小数的分数\n        img = cv2.putText(img, score_text, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX,\n                          font_scale, color, thickness)\n\n    return img\n\n# 显示绘制框和分数的图像\n\n\ndef show_image_with_boxes_and_scores(img, boxes, scores):\n    img_with_boxes = draw_boxes_with_scores(img.copy(), boxes, scores)\n\n    # OpenCV 读取图像为 BGR, 需要转换为 RGB 才能正确显示\n    img_rgb = cv2.cvtColor(img_with_boxes, cv2.COLOR_BGR2RGB)\n\n    # 使用 matplotlib 显示图像\n    plt.figure(figsize=(10, 10))\n    plt.imshow(img_rgb)\n    plt.axis(\"off\")  # 关闭坐标轴显示\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2024-10-09T07:47:30.542996Z","iopub.execute_input":"2024-10-09T07:47:30.543283Z","iopub.status.idle":"2024-10-09T07:47:30.561910Z","shell.execute_reply.started":"2024-10-09T07:47:30.543253Z","shell.execute_reply":"2024-10-09T07:47:30.560971Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for p in test_images:\n    prefix = p.split('/')[-1][:-4]\n    print(prefix)\n    boxes = []\n    scores = []\n\n    img = cv2.imread(p)\n    img_height, img_width = img.shape[:2]  # 获取图像的高度和宽度\n\n    for i in range(4):\n        img1 = img.copy()\n        rot_img = rotate_img(img1, i)\n        res = test_model(rot_img, imgsz=1024,device='cuda:0', augment=True, conf=0.1, iou=0.5)\n        # 边界框\n        box = res[0].boxes.cpu().numpy().xyxy\n        # 得分\n        conf = res[0].boxes.cpu().numpy().conf\n        for _ in range(3 - i):\n            box = rotBoxes90(box, *img.shape[:2])\n\n        boxes.append(box)\n        scores.append(conf)\n\n    res = test_model(img, imgsz=1024,device='cuda:0', augment=True, conf=0.1, iou=0.5)\n    # 边界框\n    box = res[0].boxes.cpu().numpy().xyxy\n    # 得分\n    conf = res[0].boxes.cpu().numpy().conf\n    boxes.append(box)\n    scores.append(conf)\n\n    weight = np.ones(len(boxes))\n    boxes, scores, labels = run_wbf(boxes, scores, weight=weight)\n\n    # 显示图像和框\n    show_image_with_boxes_and_scores(img, boxes, scores)\n\n    # 将 boxes 的 [x1, y1, x2, y2] 转换为 [x_center, y_center, w, h]\n    boxes[:, 2] = boxes[:, 2] - boxes[:, 0]  # w = x2 - x1\n    boxes[:, 3] = boxes[:, 3] - boxes[:, 1]  # h = y2 - y1\n\n    # 写入到 .txt 文件中\n    output_file_path = os.path.join(LABEL_PATH, prefix + '.txt')\n    with open(output_file_path, 'w') as f:\n        for box, score in zip(boxes, scores):\n            # 假设类标签为 0，类标签可以根据需要修改\n            class_id = 0  # 你可以替换为实际的类别信息\n\n            # 中心点坐标计算\n            # x中心坐标归一化 (x1 + w/2) / img_width\n            x_center = (box[0] + box[2] / 2) / img_width\n            # y中心坐标归一化 (y1 + h/2) / img_height\n            y_center = (box[1] + box[3] / 2) / img_height\n\n            # 宽高归一化\n            w = box[2] / img_width  # 宽度归一化 w / img_width\n            h = box[3] / img_height  # 高度归一化 h / img_height\n            if score < 0.5:\n                score = 0.7\n\n            # 写入格式：class x_center y_center w h score\n            f.write(\n                f\"{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\\n\")","metadata":{"execution":{"iopub.status.busy":"2024-10-09T07:47:30.563539Z","iopub.execute_input":"2024-10-09T07:47:30.563882Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Pseudo\n\n# 写入YAML文件\n\ntext = {\n    'train':PREDICT_PATH,\n    'val':PREDICT_PATH,\n    'nc':1,\n    'names': ['wheats']\n}\n\n# 文件名\nfilename = 'Pseudo.yaml'\n\n# 写文件\nwith open(filename,'w') as f:\n    yaml.dump(text, f, allow_unicode=True)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if Pseudo:\n    !wandb disabled\n    result = test_model.train(data=filename, epochs=20, batch=4, device='cuda', imgsz=1024, patience=5,amp=False)\n    # save_dir\n    weight = os.path.join(result.save_dir, 'weights/best.pt')\n    test_model = YOLO(weight)","metadata":{"execution":{"iopub.status.idle":"2024-10-09T07:52:03.791259Z","shell.execute_reply.started":"2024-10-09T07:47:41.391361Z","shell.execute_reply":"2024-10-09T07:52:03.790302Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for p in test_images:\n    prefix = p.split('/')[-1][:-4]\n    print(prefix)\n    boxes = []\n    scores = []\n\n    img = cv2.imread(p)\n    img_height, img_width = img.shape[:2]  # 获取图像的高度和宽度\n\n    for i in range(4):\n        img1 = img.copy()\n        rot_img = rotate_img(img1, i)\n        res = test_model(rot_img, imgsz=1024, augment=True, conf=0.1, iou=0.5)\n        # 边界框\n        box = res[0].boxes.cpu().numpy().xyxy\n        # 得分\n        conf = res[0].boxes.cpu().numpy().conf\n        for _ in range(3 - i):\n            box = rotBoxes90(box, *img.shape[:2])\n\n        boxes.append(box)\n        scores.append(conf)\n\n    res = test_model(img, imgsz=1024, augment=True, conf=0.1, iou=0.5)\n    # 边界框\n    box = res[0].boxes.cpu().numpy().xyxy\n    # 得分\n    conf = res[0].boxes.cpu().numpy().conf\n    boxes.append(box)\n    scores.append(conf)\n\n    weight = np.ones(len(boxes))\n    boxes, scores, labels = run_wbf(boxes, scores, weight=weight)\n\n    # 显示图像和框\n    show_image_with_boxes_and_scores(img, boxes, scores)\n\n    # 将 boxes 的 [x1, y1, x2, y2] 转换为 [x_center, y_center, w, h]\n    boxes[:, 2] = boxes[:, 2] - boxes[:, 0]  # w = x2 - x1\n    boxes[:, 3] = boxes[:, 3] - boxes[:, 1]  # h = y2 - y1\n\n    # 写入到 .txt 文件中\n    output_file_path = os.path.join(LABEL_PATH, prefix + '.txt')\n    with open(output_file_path, 'w') as f:\n        for box, score in zip(boxes, scores):\n            # 假设类标签为 0，类标签可以根据需要修改\n            class_id = 0  # 你可以替换为实际的类别信息\n\n            # 中心点坐标计算\n            # x中心坐标归一化 (x1 + w/2) / img_width\n            x_center = (box[0] + box[2] / 2) / img_width\n            # y中心坐标归一化 (y1 + h/2) / img_height\n            y_center = (box[1] + box[3] / 2) / img_height\n\n            # 宽高归一化\n            w = box[2] / img_width  # 宽度归一化 w / img_width\n            h = box[3] / img_height  # 高度归一化 h / img_height\n            if score < 0.5 : \n                score = 0.8\n\n            # 写入格式：class x_center y_center w h score\n            f.write(\n                f\"{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f} {score:.6f}\\n\")","metadata":{"execution":{"iopub.status.busy":"2024-10-09T07:52:03.793123Z","iopub.execute_input":"2024-10-09T07:52:03.793475Z","iopub.status.idle":"2024-10-09T07:52:13.302955Z","shell.execute_reply.started":"2024-10-09T07:52:03.793441Z","shell.execute_reply":"2024-10-09T07:52:13.301973Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"prediction_files = os.listdir(LABEL_PATH)","metadata":{"execution":{"iopub.status.busy":"2024-10-09T07:52:13.304248Z","iopub.execute_input":"2024-10-09T07:52:13.304553Z","iopub.status.idle":"2024-10-09T07:52:13.309014Z","shell.execute_reply.started":"2024-10-09T07:52:13.304520Z","shell.execute_reply":"2024-10-09T07:52:13.308086Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(prediction_files)","metadata":{"execution":{"iopub.status.busy":"2024-10-09T07:52:13.310366Z","iopub.execute_input":"2024-10-09T07:52:13.310668Z","iopub.status.idle":"2024-10-09T07:52:13.319744Z","shell.execute_reply.started":"2024-10-09T07:52:13.310637Z","shell.execute_reply":"2024-10-09T07:52:13.318826Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def display_preds_with_box(num):\n    pred = []\n    bbox = []\n    image_id = []\n    k = 1\n    plt.figure(figsize=(30,30))\n    for i in prediction_files[:num]:\n        with open(f'{LABEL_PATH}/{i}') as f:\n            img = read_image(f'{TEST_PATH}/{i[:-4]}.jpg')\n            bbox = []\n            for j in f:\n                pred = i[:-4] + \" \" + j[:-1]\n                pred = pred.split(\" \")\n                \n                pred.pop(0)\n                pred.pop(0)\n                pred.pop(-1)\n                pred = [eval(i) for i in pred]\n                pred = [i*1024 for i in pred]\n                [x, y, w, h] = pred\n                [xmin, ymin, xmax, ymax] = [x-w/2, y-h/2, x+w/2, y+h/2]\n                pred = [xmin, ymin, xmax, ymax]\n                bbox.append(pred)\n            bbox = torch.tensor(bbox, dtype=torch.int)\n            img = draw_bounding_boxes(img, bbox, width=10,colors=(255,0,0))\n            img = torchvision.transforms.ToPILImage()(img)\n            plt.subplot(num//3+num%3,3,k)\n            k+=1\n            plt.imshow(img)\n            plt.axis('off')","metadata":{"execution":{"iopub.status.busy":"2024-10-09T07:52:13.320896Z","iopub.execute_input":"2024-10-09T07:52:13.321204Z","iopub.status.idle":"2024-10-09T07:52:13.333539Z","shell.execute_reply.started":"2024-10-09T07:52:13.321172Z","shell.execute_reply":"2024-10-09T07:52:13.332505Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display_preds_with_box(10)","metadata":{"execution":{"iopub.status.busy":"2024-10-09T07:52:13.339428Z","iopub.execute_input":"2024-10-09T07:52:13.339834Z","iopub.status.idle":"2024-10-09T07:52:17.409586Z","shell.execute_reply.started":"2024-10-09T07:52:13.339787Z","shell.execute_reply":"2024-10-09T07:52:17.408110Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"results = []\nwidth = 1024\nheight = 1024\nfor filename in glob.glob(os.path.join(LABEL_PATH, '*.txt')):\n    prefix = filename.split(\"/\")[-1].split(\".\")[0]\n    predict_str = \"\"\n    with open(filename, 'r') as fp:\n        for cnt, line in enumerate(fp):\n            entries = np.array(line.strip('\\n').split()).astype(float)\n            bbox = np.round([(entries[1]-entries[3]/2.0)*width,(entries[2]-entries[4]/2.0)*height,entries[3]*width,entries[4]*height],3).tolist()\n            conf = entries[-1]\n            predict_str = predict_str + str(conf) + \" \" + \" \".join(map(str,bbox)) + \" \" \n        results.append({'image_id':prefix, 'PredictionString':predict_str.rstrip()}) \n\nresultFrame = pd.DataFrame(results)\nprint(resultFrame)","metadata":{"execution":{"iopub.status.busy":"2024-10-09T07:52:17.411831Z","iopub.execute_input":"2024-10-09T07:52:17.412199Z","iopub.status.idle":"2024-10-09T07:52:17.436179Z","shell.execute_reply.started":"2024-10-09T07:52:17.412163Z","shell.execute_reply":"2024-10-09T07:52:17.435331Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 　没检测到的设为空字符串\nimport re\ntemp = []\n\nfor filename in glob.glob('test/*.jpg'):\n    \n    prefix = filename.split(\"/\")[-1].split(\".\")[0]\n    escaped_prefix = re.escape(prefix)\n    if resultFrame['image_id'].str.contains(escaped_prefix).any():\n        pass\n    else:\n        temp.append({'image_id': escaped_prefix, 'PredictionString': \"\"})\n\ndf = pd.DataFrame(temp)\nsubmission = pd.concat([resultFrame, df])","metadata":{"execution":{"iopub.status.busy":"2024-10-09T07:52:17.437399Z","iopub.execute_input":"2024-10-09T07:52:17.437710Z","iopub.status.idle":"2024-10-09T07:52:17.445253Z","shell.execute_reply.started":"2024-10-09T07:52:17.437678Z","shell.execute_reply":"2024-10-09T07:52:17.444347Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission.head()","metadata":{"execution":{"iopub.status.busy":"2024-10-09T07:52:17.446644Z","iopub.execute_input":"2024-10-09T07:52:17.447175Z","iopub.status.idle":"2024-10-09T07:52:17.461946Z","shell.execute_reply.started":"2024-10-09T07:52:17.447134Z","shell.execute_reply":"2024-10-09T07:52:17.460981Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2024-10-09T07:52:17.463145Z","iopub.execute_input":"2024-10-09T07:52:17.463418Z","iopub.status.idle":"2024-10-09T07:52:17.472173Z","shell.execute_reply.started":"2024-10-09T07:52:17.463388Z","shell.execute_reply":"2024-10-09T07:52:17.471089Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!rm -rf predict/","metadata":{"execution":{"iopub.status.busy":"2024-10-09T07:52:17.473281Z","iopub.execute_input":"2024-10-09T07:52:17.473615Z","iopub.status.idle":"2024-10-09T07:52:18.534802Z","shell.execute_reply.started":"2024-10-09T07:52:17.473576Z","shell.execute_reply":"2024-10-09T07:52:18.533459Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}