Python调用Yolov8示例

import time
import fastdeploy as fd
import cv2


class YoloV8Prediction:
    def __init__(self, ModelPath: str, PreheatingImagesPath: str):
        option = fd.RuntimeOption()
        # 切换使用CPU/GPU
        # option.use_cpu()
        option.use_gpu()
        # 切换不同后端
        # option.use_paddle_infer_backend()
        # option.use_paddle_lite_backend()
        # option.use_paddle_backend()
        option.use_openvino_backend()
        # option.use_ort_backend()
        option.use_trt_backend()
        # option.use_poros_backend()
        self.yolov8 = fd.vision.detection.YOLOv8(ModelPath, runtime_option=option)
        self.yolov8.preprocessor.size = [640, 640]
        img = cv2.imread(PreheatingImagesPath)
        s = time.time()
        for i in range(100):
            self.yolov8.predict(img)
        print(f'模型预热成功,预测100张图片耗时{time.time() - s}秒')

    def Start_Prediction(self, Images: str):
        return self.yolov8.predict(Images)
from ultralytics import YOLO
import torch
import glob
import os
import xml.etree.ElementTree as ET
import random
import shutil
import time
from window.windowclass_en import WindowManager
import cv2
import Draw.D3Gui
from yolov8.YoloV8_Prediction import YoloV8Prediction


class YoloV8:
    def __init__(self, ModelPath: str, ParentWindowHandle, WindowClassName: str, WindowTitle: str,
                 PreheatingImagesPath: str, Mode: int = 1):
        """
        使用YOLOV8时常用的函数,mode为0时为训练模型,为1时为预测模式(训练和预测的环境不一样,训练支支持64位python。训练是cuda+torch+安装ultralytics,预测是TensorRT+fastdeploy)
        :param ModelPath: str,模型路径,训练模式为pt模型,预测模式为onnx模型
        :param ParentWindowHandle: int,父窗口句柄
        :param WindowClassName: str,窗口类名
        :param WindowTitle: str,窗口标题
        :param PreheatingImagesPath: str,用于预热预测模型的图片路径
        :param Mode: int,运行模式(训练/预测)
        """
        self.mode = Mode
        self.window = WindowManager()
        self.handle = self.window.Find_Windows(ParentWindowHandle, WindowClassName, WindowTitle)
        if Mode == 0:
            self.yolo = YOLO(model=ModelPath)
        elif Mode == 1:
            self.yolo = YoloV8Prediction(ModelPath, PreheatingImagesPath)
        pass

    @staticmethod
    # 转换一个xml文件为txt
    def single_xml_to_txt(xml_file, class_names):
        tree = ET.parse(xml_file)
        root = tree.getroot()
        # 保存txt文件路径
        text_dir, xml_name = os.path.split(xml_file)
        txt_name = f'{xml_name[:-4]}.txt'
        txt_file = os.path.join(text_dir, txt_name)
        with open(txt_file, 'w') as txt_file:
            for member in root.findall('object'):
                # 从xml获取图像的宽和高
                picture_width = int(root.find('size')[0].text)
                picture_height = int(root.find('size')[1].text)
                class_name = member[0].text

                # 类名对应的index
                class_num = class_names.index(class_name)
                box_x_min = int(member[4][0].text)  # 左上角横坐标
                box_y_min = int(member[4][1].text)  # 左上角纵坐标
                box_x_max = int(member[4][2].text)  # 右下角横坐标
                box_y_max = int(member[4][3].text)  # 右下角纵坐标
                # 转成相对位置和宽高(所有值处于0~1之间)
                x_center = (box_x_min + box_x_max) / (2 * picture_width)
                y_center = (box_y_min + box_y_max) / (2 * picture_height)
                width = (box_x_max - box_x_min) / picture_width
                height = (box_y_max - box_y_min) / picture_height
                print(class_num, x_center, y_center, width, height)
                txt_file.write(
                    str(class_num) + ' ' + str(x_center) + ' ' + str(y_center) + ' ' + str(width) + ' ' + str(
                        height) + '\n')

    @staticmethod
    def Batch_Rename(dir: str, ExtensionName: str):
        """
        批量重命名
        :param dir: str,文件目录
        :param ExtensionName: str,文件拓展名
        :return:
        """
        files = os.listdir(dir)
        i = 1
        for file in files:
            if ExtensionName in file:
                new_name = f"{i}.{ExtensionName}"
                i += 1
                old_path = os.path.join(dir, file)
                new_path = os.path.join(dir, new_name)
                os.rename(old_path, new_path)

    @staticmethod
    def Delete_obsolete_images(image_folder: str, label_folder: str):
        """
        匹配txt和jpg的数量,删除废图
        :param image_folder: str,图集路径
        :param label_folder: str,文本集路径
        :return:
        """
        # 获取label文件夹中的所有.txt文件
        label_files = os.listdir(label_folder)
        # 构建一个集合来存储存在于label中的文件名(不包括扩展名)
        label_file_names = {os.path.splitext(file)[0] for file in label_files}
        # 获取image文件夹中的所有.jpg文件
        image_files = os.listdir(image_folder)
        # 遍历每个.jpg文件
        for image_file in image_files:
            # 提取文件名(不包括扩展名)
            image_file_name = os.path.splitext(image_file)[0]
            # 检查文件名是否存在于label中
            if image_file_name not in label_file_names:
                # 如果文件名不在label中,输出提示信息并删除该.jpg文件
                print(f"删除多余的图像文件: {os.path.join(image_folder, image_file)}")
                os.remove(os.path.join(image_folder, image_file))

    @staticmethod
    def Data_Partitioning(image_folder, label_folder):
        """
        按照7:2:1划分train:val:test,不会删除源文件
        :param image_folder:  str,图集路径
        :param label_folder:  str,标注集路径
        :return:
        """
        # 创建train、val和test子文件夹
        os.makedirs(os.path.join(image_folder, 'train'), exist_ok=True)
        os.makedirs(os.path.join(image_folder, 'val'), exist_ok=True)
        os.makedirs(os.path.join(image_folder, 'test'), exist_ok=True)
        os.makedirs(os.path.join(label_folder, 'train'), exist_ok=True)
        os.makedirs(os.path.join(label_folder, 'val'), exist_ok=True)
        os.makedirs(os.path.join(label_folder, 'test'), exist_ok=True)
        # 获取image文件夹中的所有.jpg文件和label文件夹中的所有.txt文件
        image_files = 
        label_files = 
        # 随机打乱文件列表
        random.shuffle(image_files)
        random.shuffle(label_files)
        # 计算划分的数量
        total_files = len(image_files)
        train_ratio = 0.7
        val_ratio = 0.2
        test_ratio = 0.1
        train_count = int(train_ratio * total_files)
        val_count = int(val_ratio * total_files)
        # 将文件拷贝到对应的子文件夹中
        for i, image_file in enumerate(image_files):
            if i < train_count:
                dest_folder = 'train'
            elif i < train_count + val_count:
                dest_folder = 'val'
            else:
                dest_folder = 'test'
            # 拷贝图像文件
            shutil.copy(os.path.join(image_folder, image_file), os.path.join(image_folder, dest_folder, image_file))
            # 构建对应的label文件名
            label_file = image_file.replace('.jpg', '.txt')
            # 拷贝label文件
            shutil.copy(os.path.join(label_folder, label_file), os.path.join(label_folder, dest_folder, label_file))

    @staticmethod
    def Training_Model(ModelPath: str, DataPath: str, Epochs: int, Imgsz: int, Batch: int):
        """
        加载预先训练的模型用于训练,支持断点训练
        :param ModelPath: str,模型路径
        :param DataPath: str,数据集路径,eg:xxx/slide.yaml
        :param Epochs: int,训练轮数
        :param Imgsz: int,图像大小,eg:320,640
        :param Batch: int,显存大小,eg:2,4,6,8
        :return:
        """
        model = YOLO(ModelPath)  # 根据YAML构建并传递权重
        model.train(data=DataPath, epochs=Epochs, imgsz=Imgsz, batch=Batch, cache=True)

    @staticmethod
    def Build_Weights_And_Train(WeightFilePath: str, ModelPath: str, DataPath: str, Epochs: int, Imgsz: int,
                                Batch: int):
        """
        根据YAML构建并传递权重,然后开始训练
        :param WeightFilePath: str,权重路径
        :param ModelPath: str,模型路径
        :param DataPath: str,数据集路径,eg:xxx/slide.yaml
        :param Epochs: int,训练轮数
        :param Imgsz: int,图像大小,eg:320,640
        :param Batch: int,显存大小,eg:2,4,6,8
        :return:
        """
        model = YOLO(WeightFilePath).load(ModelPath)  # 根据YAML构建并传递权重
        model.train(data=DataPath, epochs=Epochs, imgsz=Imgsz, batch=Batch, cache=True)

    def Dir_Xml_To_Txt(self, path: str, class_names):
        """
        将xml标注转换为yolo标注
        :param path: str,标注集路径, 后面的斜杠要带
        :param class_names: 类名,eg:class_names = ['fei', 'jing']
        :return:
        """
        #  转换文件夹下的所有xml文件为txt
        for xml_file in glob.glob(path + '*.xml'):  # 查找符合规定的文件路径名
            print(xml_file)
            self.single_xml_to_txt(xml_file, class_names)

    def Using_CPU_Prediction(self, ImagePath: str):
        """
        使用CPU预测(仅用于训练时的预测测试)
        :param ImagePath: str,要预测的图片路径
        :return:
        """
        if self.mode == 0:
            self.yolo.predict(ImagePath, save=True)
        else:
            print("为保证性能预测模式不支持调用该函数")

    def Using_GPU_Prediction(self, ImagePath: str):
        """
        使用GPU预测(仅用于训练时的预测测试)
        :param ImagePath: str,要预测的图片路径
        :return:
        """
        if self.mode == 0:
            torch.cuda.is_available()
            self.yolo.predict(ImagePath, device="0")
        else:
            print("为保证性能预测模式不支持调用该函数")

    def Screenshot_Always(self, Path: str):
        """
        窗口截图,一直截(如果黑屏请更换模式Capture_Win32_Gpu(handle,mode))
        :param Path: str,图片保存路径,最后不要带/
        :return:
        """
        if not self.handle:
            print("获取窗口句柄失败")
            exit()
        i = 0
        try:
            while True:  # 无限循环
                image = self.window.Capture_Win32_Gpu(self.handle[0], 3)
                cv2.imwrite(f"{Path}/{i}.jpg", image)
                i += 1
                time.sleep(1)
        except KeyboardInterrupt:
            print("用户手动停止截图")

    def Screenshot_Breakpoint(self, start: int, end: int, Path: str):
        """
        窗口截图,指定数量(如果黑屏请更换模式Capture_Win32_Gpu(handle,mode))
        :param start: int,从几开始
        :param end: int,到几结束
        :param Path: str,图片保存路径,最后不要带/
        :return:
        """
        if not self.handle:
            print("获取窗口句柄失败")
            exit()
        else:
            for i in range(start, end + 1):
                image = self.window.Capture_Win32_Gpu(self.handle[0], 3)
                cv2.imwrite(f"{Path}/{i}.jpg", image)
                time.sleep(1)

    def Draw_Rectangle(self):
        """
        矩形绘制
        :return:
        """
        if self.mode == 0:
            if not self.handle:
                print("获取窗口句柄失败")
                exit()
            draw = Draw.D3Gui.ExecDraw(self.handle[0])  # 初始化模块
            while True:
                image = self.window.Capture_Win32_Gpu(self.handle[0], 3)
                result = self.yolo.Start_Prediction(image)
                if result:
                    for box in result.boxes:
                        # 获取四个顶点坐标
                        left = int(box[0])
                        top = int(box[1])
                        right = int(box[2])
                        bottom = int(box[3])
                        draw.startLoop()  # 开始绘制段
                        draw.drawRect(left, top, right, bottom, 5, (255, 254, 0))  # 绘制矩形框
                        draw.endLoop()  # 结束绘制段
        else:
            print("为保证性能预测模式不支持调用该函数")

    def Automatic_Dimension(self, ImagesPath: str, LabelsPath: str, ImageSuffix: str):
        """
        自动标注
        :return:
        """
        if self.mode == 1:
            image_files = 

            for file_name in image_files:
                # 分割文件名和后缀
                parts = file_name.split('.')
                # 检查是否有两个部分,并且后缀是指定后缀
                if len(parts) == 2 and parts[1] == ImageSuffix.replace(".", ""):
                    imagepath = f'{ImagesPath}/{file_name}'
                    img = cv2.imread(imagepath)
                    img_height, img_width, _ = img.shape
                    result = self.yolo.Start_Prediction(img)

                    if result:
                        with open(f'{LabelsPath}/{parts[0]}.txt', "a") as file:
                            for a in range(len(result.label_ids)):
                                c = result.label_ids[a]
                                x_min = result.boxes[a][0]
                                y_min = result.boxes[a][1]
                                x_max = result.boxes[a][2]
                                y_max = result.boxes[a][3]

                                # 计算归一化坐标
                                x_center = (x_min + x_max) / (2 * img_width)
                                y_center = (y_min + y_max) / (2 * img_height)
                                width = (x_max - x_min) / img_width
                                height = (y_max - y_min) / img_height

                                # 将归一化坐标写入文件
                                normalized_box_str = f'{c} {x_center} {y_center} {width} {height}\n'
                                file.write(normalized_box_str)
        else:
            print("为保证性能预测模式不支持调用该函数")


if __name__ == '__main__':
    yolo = YoloV8('models/csgo_0.onnx', None, 'Valve001', 'Counter-Strike', 'CSGO.jpg', 1)
    yolo.Automatic_Dimension(r'C:\Users\devcat\Desktop\CSGO\images\train', r'C:\Users\devcat\Desktop\瓦\datasets'
                                                                           r'\slide\YOLO\labels', '.jpg')