{"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":1219292,"sourceType":"datasetVersion","datasetId":648709},{"sourceId":1251699,"sourceType":"datasetVersion","datasetId":719180},{"sourceId":1285895,"sourceType":"datasetVersion","datasetId":742407},{"sourceId":7002739,"sourceType":"datasetVersion","datasetId":4025711},{"sourceId":11939672,"sourceType":"datasetVersion","datasetId":7506336},{"sourceId":11948807,"sourceType":"datasetVersion","datasetId":7511979},{"sourceId":11948875,"sourceType":"datasetVersion","datasetId":7511937},{"sourceId":410672,"sourceType":"modelInstanceVersion","modelInstanceId":335344,"modelId":356366},{"sourceId":411729,"sourceType":"modelInstanceVersion","modelInstanceId":336158,"modelId":357164},{"sourceId":412185,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":336502,"modelId":357495},{"sourceId":412550,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":336781,"modelId":357772},{"sourceId":412757,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":336944,"modelId":357925},{"sourceId":412925,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":337083,"modelId":358062}],"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport os\nfrom tqdm import tqdm\nimport shutil as sh","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2025-05-26T00:51:46.884062Z","iopub.execute_input":"2025-05-26T00:51:46.884339Z","iopub.status.idle":"2025-05-26T00:51:48.029966Z","shell.execute_reply.started":"2025-05-26T00:51:46.884316Z","shell.execute_reply":"2025-05-26T00:51:48.029368Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"用滑动窗口的方式，先使用分类识别出需要检测的目标，再使用回归将窗口调整为和被检测的目标一样的大小\nhttps://yiyan.baidu.com/share/F9YWF5EXtB ","metadata":{}},{"cell_type":"code","source":"!python3 --version","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T00:51:48.031069Z","iopub.execute_input":"2025-05-26T00:51:48.031360Z","iopub.status.idle":"2025-05-26T00:51:48.152677Z","shell.execute_reply.started":"2025-05-26T00:51:48.031343Z","shell.execute_reply":"2025-05-26T00:51:48.151949Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install --no-index --find-links=/kaggle/input/d/binghualiu/package/whl ultralytics ensemble_boxes","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T00:51:48.153775Z","iopub.execute_input":"2025-05-26T00:51:48.154090Z","iopub.status.idle":"2025-05-26T00:54:14.697195Z","shell.execute_reply.started":"2025-05-26T00:51:48.154054Z","shell.execute_reply":"2025-05-26T00:54:14.696219Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from ultralytics import YOLO","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T00:54:14.699092Z","iopub.execute_input":"2025-05-26T00:54:14.699350Z","iopub.status.idle":"2025-05-26T00:54:18.859238Z","shell.execute_reply.started":"2025-05-26T00:54:14.699326Z","shell.execute_reply":"2025-05-26T00:54:18.858653Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 获取 yolov5 代码库","metadata":{}},{"cell_type":"code","source":"weight='/kaggle/input/exp1x_400_best_last/pytorch/default/1/exp1x_400_best_last.pt'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T00:54:18.859879Z","iopub.execute_input":"2025-05-26T00:54:18.860221Z","iopub.status.idle":"2025-05-26T00:54:18.863723Z","shell.execute_reply.started":"2025-05-26T00:54:18.860202Z","shell.execute_reply":"2025-05-26T00:54:18.863190Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#!git clone https://github.com/ultralytics/yolov5\n#!mv yolov5/* ./\n\n!cp -r ../input/yolov5train/* .","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_kg_hide-input":true,"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T00:54:18.864497Z","iopub.execute_input":"2025-05-26T00:54:18.864736Z","iopub.status.idle":"2025-05-26T00:54:19.311624Z","shell.execute_reply.started":"2025-05-26T00:54:18.864720Z","shell.execute_reply":"2025-05-26T00:54:19.310783Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install --no-deps '../input/weightedboxesfusion/' > /dev/null","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T00:54:19.312691Z","iopub.execute_input":"2025-05-26T00:54:19.312970Z","iopub.status.idle":"2025-05-26T00:54:22.819960Z","shell.execute_reply.started":"2025-05-26T00:54:19.312945Z","shell.execute_reply":"2025-05-26T00:54:22.819021Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.read_csv('../input/global-wheat-detection/train.csv')\nbboxs = np.stack(df['bbox'].apply(lambda x: np.fromstring(x[1:-1], sep=',')))\nfor i, column in enumerate(['x', 'y', 'w', 'h']):\n    df[column] = bboxs[:,i]\n    #enumerate(['x', 'y', 'w', 'h'])用于遍历列名列表，并将bbox数组中的对应列值分配给新的DataFrame列\ndf.drop(columns=['bbox'], inplace=True)\ndf['x_center'] = df['x'] + df['w']/2\ndf['y_center'] = df['y'] + df['h']/2\ndf['classes'] = 0\n#由于只检测一种物体（小麦），所以所有标签都设置为0\ndf = df[['image_id','x', 'y', 'w', 'h','x_center','y_center','classes']]\n#重新排序并选择DataFrame的某些列，得到一个新的DataFrame，只包含指定的列","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T00:54:22.821180Z","iopub.execute_input":"2025-05-26T00:54:22.821526Z","iopub.status.idle":"2025-05-26T00:54:23.525800Z","shell.execute_reply.started":"2025-05-26T00:54:22.821487Z","shell.execute_reply":"2025-05-26T00:54:23.525032Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T00:54:23.526625Z","iopub.execute_input":"2025-05-26T00:54:23.526855Z","iopub.status.idle":"2025-05-26T00:54:23.551538Z","shell.execute_reply.started":"2025-05-26T00:54:23.526832Z","shell.execute_reply":"2025-05-26T00:54:23.550959Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"index = list(set(df.image_id))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T00:54:23.553621Z","iopub.execute_input":"2025-05-26T00:54:23.554083Z","iopub.status.idle":"2025-05-26T00:54:23.567348Z","shell.execute_reply.started":"2025-05-26T00:54:23.554065Z","shell.execute_reply":"2025-05-26T00:54:23.566783Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Convert train data to yolov5 format\nBased on [this notebook](https://www.kaggle.com/orkatz2/yolov5-train)","metadata":{}},{"cell_type":"code","source":"def convertTrainLabel():\n    source = 'train'\n    if True:\n        for fold in [0]:\n            val_index = index[len(index)*fold//5:len(index)*(fold+1)//5]\n            #len(index)*fold//5 计算出验证集的起始索引，len(index)*(fold+1)//5 计算出验证集的结束索引\n            #除以5是为了将数据集分成5份，进行5折交叉验证。每次交叉验证会将数据集分成5份，其中4份用作训练集，1份用作验证集。\n            for name,mini in tqdm(df.groupby('image_id')):\n            #使用df.groupby('image_id')对数据集进行分组，name表示分组的名称，mini表示分组的数据。\n            #遍历每个分组，如果name在val_index中，将path2save设为'val2017/'，否则设为'train2017/'。\n                if name in val_index:\n                    path2save = 'val2017/'\n                else:\n                    path2save = 'train2017/'\n                if not os.path.exists('convertor/fold{}/labels/'.format(fold)+path2save):\n                    os.makedirs('convertor/fold{}/labels/'.format(fold)+path2save)\n                 #检查是否存在文件夹'convertor/fold{}/labels/'.format(fold)+path2save，如果不存在，则创建该文件夹。\n            #根据索引，数据集被分为训练集和验证集。对于每个image_id，根据其是否在val_index中来确定它应该被放在哪个集合中\n                with open('convertor/fold{}/labels/'.format(fold)+path2save+name+\".txt\", 'w+') as f:\n                    row = mini[['classes','x_center','y_center','w','h']].astype(float).values\n                    row = row/1024\n                    row = row.astype(str)\n                    for j in range(len(row)):\n                        text = ' '.join(row[j])\n                        f.write(text)\n                        f.write(\"\\n\")\n                #对于每个image_id，其相关的标签数据（类别、中心坐标、宽度和高度）被提取出来，除以1024进行归一化，然后保存为文本文件。每个小麦的标签占一行，格式是class x_center y_center width height。\n                if not os.path.exists('convertor/fold{}/images/{}'.format(fold,path2save)):\n                    os.makedirs('convertor/fold{}/images/{}'.format(fold,path2save))\n                    # #检查是否存在文件夹'convertor/fold{}/labels/'.format(fold)+path2save，如果不存在，则创建该文件夹。\n                sh.copy(\"../input/global-wheat-detection/{}/{}.jpg\".format(source,name),'convertor/fold{}/images/{}/{}.jpg'.format(fold,path2save,name))\n                #最后，图像文件从原始位置复制到相应的训练和验证文件夹中。","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T00:54:23.567984Z","iopub.execute_input":"2025-05-26T00:54:23.568193Z","iopub.status.idle":"2025-05-26T00:54:23.586258Z","shell.execute_reply.started":"2025-05-26T00:54:23.568178Z","shell.execute_reply":"2025-05-26T00:54:23.585519Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def convert_yolo_to_submission(predictions, image_id, original_size):\n    \"\"\"Convert YOLO predictions to submission format\n    \n    Format: image_id,confidence x y width height [confidence x y width height ...]\n    Example: ce4833752,0.5 0 0 100 100\n    Multiple boxes: 1da9078c1,0.3 0 0 50 50 0.5 10 10 30 30\n    Empty prediction: 6ca7b2650,\n    \"\"\"\n    if len(predictions) == 0:\n        return f\"{image_id},\"\n    \n    original_width, original_height = original_size\n    boxes = []\n    \n    for pred in predictions:\n        # 获取置信度\n        conf = pred.conf.item()\n        \n        # 获取归一化的边界框坐标（xywh格式）\n        x_center, y_center, width, height = pred.xywhn[0][:4].tolist()\n        \n        # 转换为原始图像像素坐标（左上角格式）\n        x = max(0, int((x_center - width/2) * original_width))\n        y = max(0, int((y_center - height/2) * original_height))\n        w = int(width * original_width)\n        h = int(height * original_height)\n        \n        # 格式化为整数坐标\n        boxes.append(f\"{conf:.1f} {x} {y} {w} {h}\")\n    \n    return f\"{image_id},{' '.join(boxes)}\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T00:54:23.587031Z","iopub.execute_input":"2025-05-26T00:54:23.587208Z","iopub.status.idle":"2025-05-26T00:54:23.604566Z","shell.execute_reply.started":"2025-05-26T00:54:23.587194Z","shell.execute_reply":"2025-05-26T00:54:23.604031Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from ensemble_boxes import weighted_boxes_fusion\n#用于合并重叠的边界框\ndef run_wbf(boxes, scores, image_size=1023, iou_thr=0.5, skip_box_thr=0.7, weights=None):\n    \n    labels = [np.zeros(score.shape[0]) for score in scores]\n    #初始化一个与 scores 列表长度相同的 labels 列表，其中每个元素都是一个全零的NumPy数组，其长度与对应 score 数组的长度相同。\n    boxes = [box/(image_size) for box in boxes]\n    #将 boxes 列表中的每个边界框坐标除以 image_size，以将其标准化到0到1的范围内\n    boxes, scores, labels = weighted_boxes_fusion(boxes, scores, labels, weights=None, iou_thr=iou_thr, skip_box_thr=skip_box_thr)\n    #调用 weighted_boxes_fusion 函数，传入标准化后的边界框、分数、标签和可选的权重。这个函数将执行加权边界框融合，并返回融合后的边界框、分数和标签。\n    boxes = boxes*(image_size)\n    #将融合后的边界框坐标乘以 image_size，以将其转换回原始像素空间。\n    return boxes, scores, labels\n    #返回融合后的边界框、分数和标签\n#分数通常指的是检测框的置信度分数，分数越高，表示模型越确信检测框内的内容符合某一类别的物体\n    \n#处理图像旋转时的边界框变换时非常有用，尤其是在使用测试时增强（TTA）技术时，需要确保边界框与旋转后的图像保持一致。\ndef TTAImage(image, index):\n    image1 = image.copy()\n    #创建了一个image的副本，命名为image1，这样在后续处理中不会修改原始的image\n    if index==0: \n        rotated_image = cv2.rotate(image1, cv2.ROTATE_90_CLOCKWISE)\n        return rotated_image\n    #如果index为0，它将image1顺时针旋转90度，并返回旋转后的图像。\n    elif index==1:\n        rotated_image2 = cv2.rotate(image1, cv2.ROTATE_90_CLOCKWISE)\n        rotated_image2 = cv2.rotate(rotated_image2, cv2.ROTATE_90_CLOCKWISE)\n        return rotated_image2\n    #如果index为1，它首先将image1顺时针旋转90度，然后再将结果顺时针旋转90度（总共旋转180度），并返回旋转后的图像。\n    elif index==2:\n        rotated_image3 = cv2.rotate(image1, cv2.ROTATE_90_CLOCKWISE)\n        rotated_image3 = cv2.rotate(rotated_image3, cv2.ROTATE_90_CLOCKWISE)\n        rotated_image3 = cv2.rotate(rotated_image3, cv2.ROTATE_90_CLOCKWISE)\n        return rotated_image3\n    #如果index为2，它首先将image1顺时针旋转90度，然后将结果再顺时针旋转90度，接着将再次旋转后的结果顺时针旋转90度（总共旋转270度），并返回旋转后的图像。\n    elif index == 3:\n        return image1\n    #如果index为3，它直接返回原始的image1，不进行任何旋转。\n    \n    \n    \n#确保了在旋转图像后，边界框也相应地进行了旋转，并且坐标仍然位于正确的位置。\ndef rotBoxes90(boxes, im_w, im_h):\n    ret_boxes =[]\n    #创建了一个空列表ret_boxes，用于存储旋转后的边界框坐标\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        #将边界框的坐标从图像坐标系转换为以图像中心为原点的坐标系。这样做是为了方便进行后续的旋转操作。这里通过减去图像宽度的一半和图像高度的一半来实现坐标变换。\n        x1, y1, x2, y2 = y1, -x1, y2, -x2\n        #对边界框的坐标进行90度的顺时针旋转\n        x1, y1, x2, y2 = int(x1+im_w//2), int(im_h//2 - y1), int(x2+im_w//2), int(im_h//2 - y2)\n        #将旋转后的坐标从以图像中心为原点的坐标系转换回图像坐标系。\n        x1a, y1a, x2a, y2a = min(x1, x2), min(y1, y2), max(x1, x2), max(y1, y2)\n        #为了确保边界框的坐标是有序的（即左上角坐标在左上角，右下角坐标在右下角），这里使用min和max函数重新计算(x1, y1)和(x2, y2)。\n        ret_boxes.append([x1a, y1a, x2a, y2a])\n        #将调整后的边界框坐标添加到ret_boxes列表中。\n    return np.array(ret_boxes)\n    #将ret_boxes列表转换为NumPy数组，并返回\n\n    \n#使用给定的深度学习模型对单个图像进行物体检测，并返回检测到的物体的边界框坐标和对应的置信度。\ndef detect1Image(im0, imgsz, model, device, conf_thres, iou_thres):\n    #im0：原始图像。imgsz：目标图像大小，模型输入所需的大小。model：预训练的深度学习模型。device：模型运行在哪个设备上，例如 'cuda' 或 'cpu'。conf_thres：置信度阈值，用于过滤低置信度的检测结果。iou_thres：IoU阈值，用于非极大值抑制（NMS）。\n    img = letterbox(im0, new_shape=imgsz)[0]\n    img = img[:, :, ::-1].transpose(2, 0, 1)  # BGR to RGB, to 3x416x416\n    img = np.ascontiguousarray(img)\n    #使用`letterbox`函数调整图像大小到`imgsz`，同时保持图像的宽高比。将图像从BGR格式转换为RGB格式，并改变其维度顺序以匹配模型的输入格式。确保图像数据是连续的，这对于后续转换为PyTorch张量很重要。\n\n    img = torch.from_numpy(img).to(device)\n    img =  img.float()  # uint8 to fp16/32\n    img /= 255.0  \n    #将图像从NumPy数组转换为PyTorch张量。  将图像数据从uint8格式转换为float格式。将图像的像素值归一化到0-1范围。\n    if img.ndimension() == 3:\n        img = img.unsqueeze(0)\n    #如果图像是一个三维张量（HxWxC），则增加一个额外的维度使其成为一个四维张量（1xHxWxC），这是大多数深度学习模型所期望的输入格式。\n    # Inference\n    # pred = model(img, augment=False)[0]\n    results = model.predict(\n        source=img,\n        conf=0.3,\n        imgsz=1024,  # 使用相同的输入尺寸\n        verbose=False,\n        # save=True,\n        # name=f'{image_id}_pred',\n        nms=True,\n        agnostic_nms=True,\n    )\n\n    pred = results[0].boxes\n    # print(results[0].boxes.xywhn)\n    #使用预处理后的图像作为输入，对模型进行推理，得到检测结果。\n    # Apply NMS\n    # pred = non_max_suppression(pred, conf_thres, iou_thres)\n    #使用NMS处理检测结果，去除重叠度较高的检测框，只保留置信度较高的框\n    boxes = []\n    scores = []\n    # for i, det in enumerate(pred):  # detections per image\n    #     # save_path = 'draw/' + image_id + '.jpg'\n    #     if det is not None and len(det):\n    #         det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()\n    #         for *xyxy, conf, cls in det:\n    #             boxes.append([int(xyxy[0]), int(xyxy[1]), int(xyxy[2]), int(xyxy[3])])\n    #             scores.append(conf.cpu())\n    # #遍历每个检测结果。  如果检测结果不为空，则将其坐标从模型输入大小缩放到原始图像大小。 提取每个检测框的坐标和置信度，并分别添加到`boxes`和`scores`列表中\n\n    for i in pred:\n        x_center, y_center, width, height = i.xywh[0][:4].tolist()\n        x = max(0, (x_center - width/2) )\n        y = max(0, (y_center - height/2))\n        w = width \n        h = height \n        x1, y1, x2, y2 = x, y, x+w, y+h\n        boxes.append([int(x1), int(y1), int(x2), int(y2)])\n        scores.append(i.conf.item())\n    \n\n    return np.array(boxes), np.array(scores) \n   #将边界框和置信度转换为NumPy数组并返回。","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T00:54:23.605357Z","iopub.execute_input":"2025-05-26T00:54:23.605574Z","iopub.status.idle":"2025-05-26T00:54:24.662697Z","shell.execute_reply.started":"2025-05-26T00:54:23.605551Z","shell.execute_reply":"2025-05-26T00:54:24.662115Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Make pseudo labels for Yolov5","metadata":{}},{"cell_type":"code","source":"from utils.datasets import *\nfrom utils.utils import *\n\ndef makePseudolabel():\n    source = '../input/global-wheat-detection/test/'\n    # weights = \n    imgsz = 1024\n    conf_thres = 0.5\n    iou_thres = 0.6\n    is_TTA = True\n    \n    imagenames =  os.listdir(source)\n    \n    device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\n\n    # Load model\n    # model = torch.load(weights, map_location=device)['model'].float()  # load to FP32\n    model = YOLO(weight)\n    # model.to(device).eval()\n    \n    dataset = LoadImages(source, img_size=imgsz)\n\n    path2save = 'train2017/'\n    if not os.path.exists('convertor/fold0/labels/'+path2save):\n        os.makedirs('convertor/fold0/labels/'+path2save)\n    if not os.path.exists('convertor/fold0/images/{}'.format(path2save)):\n        os.makedirs('convertor/fold0/images/{}'.format(path2save))\n            \n    for name in imagenames:\n        image_id = name.split('.')[0]\n        im01 = cv2.imread('%s/%s.jpg'%(source,image_id))  # BGR\n        if im01.shape[0]!=1024 or im01.shape[1]!=1024:\n            continue\n        assert im01 is not None, 'Image Not Found '\n        # Padded resize\n        im_w, im_h = im01.shape[:2]\n        if is_TTA:\n            enboxes = []\n            enscores = []\n            for i in range(4):\n                im0 = TTAImage(im01, i)\n                boxes, scores = detect1Image(im0, imgsz, model, device, conf_thres, iou_thres)\n                for _ in range(3-i):\n                    boxes = rotBoxes90(boxes, im_w, im_h)\n                    \n                enboxes.append(boxes)\n                enscores.append(scores) \n\n            boxes, scores, labels = run_wbf(enboxes, enscores, image_size = im_w, iou_thr=0.6, skip_box_thr=0.43)\n            boxes = boxes.astype(np.int32).clip(min=0, max=im_w)\n        else:\n            boxes, scores = detect1Image(im01, imgsz, model, device, conf_thres, iou_thres)\n\n        boxes[:, 2] = boxes[:, 2] - boxes[:, 0]\n        boxes[:, 3] = boxes[:, 3] - boxes[:, 1]\n        \n        boxes = boxes[scores >= 0.05].astype(np.int32)\n        scores = scores[scores >=float(0.05)]\n        \n        lineo = ''\n        for box in boxes:\n            x1, y1, w, h = box\n            xc, yc, w, h = (x1+w/2)/1024, (y1+h/2)/1024, w/1024, h/1024\n            lineo += '0 %f %f %f %f\\n'%(xc, yc, w, h)\n            \n        fileo = open('convertor/fold0/labels/'+path2save+image_id+\".txt\", 'w+')\n        fileo.write(lineo)\n        fileo.close()\n        sh.copy(\"../input/global-wheat-detection/test/{}.jpg\".format(image_id),'convertor/fold0/images/{}/{}.jpg'.format(path2save,image_id))\n            \n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T00:54:24.663473Z","iopub.execute_input":"2025-05-26T00:54:24.663659Z","iopub.status.idle":"2025-05-26T00:54:26.607677Z","shell.execute_reply.started":"2025-05-26T00:54:24.663645Z","shell.execute_reply":"2025-05-26T00:54:26.606924Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"convertTrainLabel()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T00:54:26.608512Z","iopub.execute_input":"2025-05-26T00:54:26.609009Z","iopub.status.idle":"2025-05-26T00:55:05.911043Z","shell.execute_reply.started":"2025-05-26T00:54:26.608989Z","shell.execute_reply":"2025-05-26T00:55:05.910308Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"makePseudolabel()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T00:55:05.911909Z","iopub.execute_input":"2025-05-26T00:55:05.912157Z","iopub.status.idle":"2025-05-26T00:55:17.564515Z","shell.execute_reply.started":"2025-05-26T00:55:05.912124Z","shell.execute_reply":"2025-05-26T00:55:17.563933Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pwd","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T00:55:17.565315Z","iopub.execute_input":"2025-05-26T00:55:17.565531Z","iopub.status.idle":"2025-05-26T00:55:17.728076Z","shell.execute_reply.started":"2025-05-26T00:55:17.565515Z","shell.execute_reply":"2025-05-26T00:55:17.727339Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Retrain yolov5 with pseudo data","metadata":{}},{"cell_type":"code","source":"print(weight)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T00:55:17.729266Z","iopub.execute_input":"2025-05-26T00:55:17.729945Z","iopub.status.idle":"2025-05-26T00:55:17.734477Z","shell.execute_reply.started":"2025-05-26T00:55:17.729910Z","shell.execute_reply":"2025-05-26T00:55:17.733924Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if len(os.listdir('../input/global-wheat-detection/test/'))<11:\n    pass\n    #!python train.py --img 1024 --batch 4 --epochs 1 --data ../input/configyolo5/wheat0.yaml --cfg ../input/yolov5/v5/v5/models/yolov5x.yaml  --weights ../input/yolov5/bestv4.pt   \nelse:\n    # !python /kaggle/input/pl-train/pl_train.py --batch_size 4 --epochs 10 --data_yaml /kaggle/input/mywheat0yml/wheat0.yaml --ckpt '/kaggle/input/exp1x_bigdata_best/pytorch/default/1/exp1x_bigdata_best.pt'\n    hyper_params = {\n        'optimizer': 'AdamW',\n        'cos_lr': True,\n        # 優化器參數\n        'lr0': 0.001,\n        # 'lrf': 0.12,\n        # 'momentum': 0.843,\n        # 'weight_decay': 0.00036,\n        \n        # # 熱身階段配置\n        # 'warmup_epochs': 2.0,\n        # 'warmup_momentum': 0.5,\n        # 'warmup_bias_lr': 0.05,\n        \n        # 損失函數權重\n        'box': 0.0296,\n        'cls': 0.243,\n    \n        # 數據增強參數\n        'hsv_h': 0.0138,\n        'hsv_s': 0.664,\n        'hsv_v': 0.464,\n        'degrees': 0.373,\n        'translate': 0.245,\n        'scale': 0.898,\n        'shear': 0.602,\n        'perspective': 0.0,\n        'flipud': 0.5,\n        'fliplr': 0.5,\n        \n        # 進階增強策略\n        'mosaic': 1.0,\n        'mixup': 0.3\n    }\n\n    # print(f\"使用的模型: {weith}\")\n    model = YOLO(weight)\n    # 訓練配置\n    results = model.train(\n        data='/kaggle/input/mywheat0yml/wheat0.yaml',\n        epochs=20,\n        imgsz=1024,\n        batch=2,\n        device='0',  # 使用 GPU\n        project='yolo_train_results',\n        name='123',\n        **hyper_params,\n    )\n    \n    \n!rm -rf convertor","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T00:55:17.735373Z","iopub.execute_input":"2025-05-26T00:55:17.735715Z","iopub.status.idle":"2025-05-26T01:30:05.417497Z","shell.execute_reply.started":"2025-05-26T00:55:17.735690Z","shell.execute_reply":"2025-05-26T01:30:05.416367Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Final prediction","metadata":{}},{"cell_type":"code","source":"def detect():\n    source = '../input/global-wheat-detection/test/'\n    # weights = '/kaggle/input/12n_05250307/pytorch/default/1/12n_05250307.pt'\n    # if not os.path.exists(weights):\n    #     weights = '/kaggle/input/12n_05250307/pytorch/default/1/12n_05250307.pt'\n    imgsz = 1024\n    conf_thres = 0.5\n    iou_thres = 0.6\n    is_TTA = True\n    \n    imagenames =  os.listdir(source)\n    \n    device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\n\n    # Load model\n    # model = torch.load(weights, map_location=device)['model'].float()  # load to FP32\n    model = YOLO(weight)\n    # model.to(device).eval()\n    \n    dataset = LoadImages(source, img_size=imgsz)\n\n    results = []\n    fig, ax = plt.subplots(5, 2, figsize=(30, 70))\n    count = 0\n    # img = torch.zeros((1, 3, imgsz, imgsz), device=device)  # init img\n    #for path, img, im0s, _ in dataset:\n    for name in imagenames:\n        image_id = name.split('.')[0]\n        im01 = cv2.imread('%s/%s.jpg'%(source,image_id))  # BGR\n        assert im01 is not None, 'Image Not Found '\n        # Padded resize\n        im_w, im_h = im01.shape[:2]\n        if is_TTA:\n            enboxes = []\n            enscores = []\n            for i in range(4):\n                im0 = TTAImage(im01, i)\n                boxes, scores = detect1Image(im0, imgsz, model, device, conf_thres, iou_thres)\n                for _ in range(3-i):\n                    boxes = rotBoxes90(boxes, im_w, im_h)\n                    \n                if 1: #i<3:\n                    enboxes.append(boxes)\n                    enscores.append(scores) \n            boxes, scores = detect1Image(im01, imgsz, model, device, conf_thres, iou_thres)\n            enboxes.append(boxes)\n            enscores.append(scores)\n\n            boxes, scores, labels = run_wbf(enboxes, enscores, image_size = im_w, iou_thr=0.6, skip_box_thr=0.5)\n            boxes = boxes.astype(np.int32).clip(min=0, max=im_w)\n        else:\n            boxes, scores = detect1Image(im01, imgsz, model, device, conf_thres, iou_thres)\n\n        boxes[:, 2] = boxes[:, 2] - boxes[:, 0]\n        boxes[:, 3] = boxes[:, 3] - boxes[:, 1]\n        \n        boxes = boxes[scores >= 0.05].astype(np.int32)\n        scores = scores[scores >=float(0.05)]\n        if count<10:\n            #sample = image.permute(1,2,0).cpu().numpy()\n            for box, score in zip(boxes,scores):\n                cv2.rectangle(im0,\n                              (box[0], box[1]),\n                              (box[2]+box[0], box[3]+box[1]),\n                              (220, 0, 0), 2)\n                cv2.putText(im0, '%.2f'%(score), (box[0], box[1]), cv2.FONT_HERSHEY_SIMPLEX ,  \n                   0.5, (255,255,255), 2, cv2.LINE_AA)\n            ax[count%5][count//5].imshow(im0)\n            count+=1\n            \n        result = {\n            'image_id': image_id,\n            'PredictionString': format_prediction_string(boxes, scores)\n        }\n\n        results.append(result)\n    return results","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T01:30:05.419051Z","iopub.execute_input":"2025-05-26T01:30:05.419384Z","iopub.status.idle":"2025-05-26T01:30:05.432160Z","shell.execute_reply.started":"2025-05-26T01:30:05.419351Z","shell.execute_reply":"2025-05-26T01:30:05.431494Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def format_prediction_string(boxes, scores):\n    pred_strings = []\n    for j in zip(scores, boxes):\n        pred_strings.append(\"{0:.4f} {1} {2} {3} {4}\".format(j[0], j[1][0], j[1][1], j[1][2], j[1][3]))\n\n    return \" \".join(pred_strings)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T01:30:05.432989Z","iopub.execute_input":"2025-05-26T01:30:05.433229Z","iopub.status.idle":"2025-05-26T01:30:05.452898Z","shell.execute_reply.started":"2025-05-26T01:30:05.433205Z","shell.execute_reply":"2025-05-26T01:30:05.452265Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"results = detect()\ntest_df = pd.DataFrame(results, columns=['image_id', 'PredictionString'])\ntest_df.to_csv('submission.csv', index=False)\ntest_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T01:30:05.453758Z","iopub.execute_input":"2025-05-26T01:30:05.454299Z","iopub.status.idle":"2025-05-26T01:30:23.299268Z","shell.execute_reply.started":"2025-05-26T01:30:05.454282Z","shell.execute_reply":"2025-05-26T01:30:23.297958Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}