实时训练Real-Time Training 教程(2023年全网第一)

在上一篇文章(https://blog.csdn.net/walchina2017/article/details/130334374)中,我介绍了什么是实时学习,并且开源了一个我做出来的作品。

接下来,我会手把手教你如何把它做出来。

首先,你得先下载Pycharm(https://www.jetbrains.com/pycharm/download/)和Python(有镜像),并知道如何在Pycharm中新建一个Project。

然后,新建一个Project,选择Virualvenv,然后点击创建:

等Pycharm创建完虚拟环境(Virtualvenv)后,会出现这个界面。

 接下来,点击画红圈的地方来打开Terminal。

接着,在Terminal中输入:pip install ultralytics -i https://pypi.douban.com/simple --trusted-host pypi.douban.com,然后点击回车键来安装所需要的Python库:

接着,在你的文件浏览器中找到你的项目的文件夹,并打开它。根据下图所显示的列表新建这些文件夹:

注:在dataset一下的所有文件夹都必须是和上图列表一样的,否则无法训练模型。

 接着,把在train · main · 何荣基 / Real Time Learning Yolov8 · GitCode中的yolov8n-face.pt 和 costum_data.yaml 下载到train文件夹内,文件列表应该是这样的:

接下来,进入右边的编辑器里,删掉所有的代码,我们要开始编写我们自己的代码了:

 1. 导入所需要的库:

import cv2
from threading import Thread
import numpy as np
from ultralytics import YOLO

  cv2: opencv-python的简称,可以帮助我们打开摄像头,并获取实时图像。

  threading(Python自带): 可以让多个代码同时运行

  numpy:可以进行复杂的矩阵乘法,还可以把tensor类型转换成numpy。

  ultralytics:Yolo的官方PythonAPI,可以在代码中实现训练,而不需要CLI。

  2. 做一个简单的用cv2打开摄像头的程序:

import cv2
from threading import Thread
import numpy as np
from ultralytics import YOLO

cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    
    cv2.imshow('returned', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()

cv2.destroyAllWindows()

  cap = cv2.VideoCapture(0):将cap设为摄像头

  ret, frame = cap.read():读取摄像头的画面

  cv2.imshow('returned', frame):显示摄像头看到的东西,并将打开的窗口的名字设为return.

  if cv2.waitKey(1) & 0xFF == ord('q'):检测q键是否按下, 如果是的话,就强制推出while循环。

  cap.release():关闭摄像头

  cv2.destroyAllWindows():把所有窗口都关闭。

我们来试一试效果:

接下来,我们就得实现检测到人脸,并且画出来位置:

1. 

import cv2
from threading import Thread
import numpy as np
from ultralytics import YOLO

model = YOLO('train/train_yolov8n-face.pt')

cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    results = model.predict(frame)
    annotated = results[0].plot()
    cv2.imshow('returned', annotated)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()

cv2.destroyAllWindows()

 model = YOLOY('train/train_yolov8n-face.pt'):把要运算的模型设为YOLO模型,位置在相对路径 train/yolov8n-face.pt(注意:应为yolov8n-face.pt是从gitee上下载下来的,名字会改成train_yolov8n-face.pt)

 results = model.predict(frame):检测人脸

 results[0].plot():把人脸的位置画出来。

 cv2.imshow('returned', annotated):把画出来的位置显示出来(注意,第二个参数已经换成了annotated,而不是frame)

让我们俩看一看效果:

然后,我们需要把‘问题’和‘回答’记录下来:

import cv2
from threading import Thread
import numpy as np
from ultralytics import YOLO

model = YOLO('train/train_yolov8n-face.pt')

cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    results = model.predict(frame)
    boxes = result[0].boxes
    if bool(boxes.numpy()):
        if face_count < 300:
            face_count += 1
        else:
            face_count = 0
        # 获取人脸位置信息


        # 保存人脸图像和位置信息到文件
        if face_count >= 0 and face_count < 100:
            # 记录到验证集
            filepath = f'train/dataset/images/val/{face_count}.png'
            labelspath = f'train/dataset/labels/val/{face_count}.txt'
        else:
            # 记录到训练集
            filepath = f'train/dataset/images/train/{face_count}.png'
            labelspath = f'train/dataset/labels/train/{face_count}.txt'

        # 保存人脸图像
        cv2.imwrite(filepath, frame)

        # 保存位置信息到文件
        with open(labelspath, 'w') as file:
            for bboxes in boxes:
                for xywh in bboxes.xywh:
                    xywh = xywh * np.array([1/frame.shape[1], 1/frame.shape[0], 1/frame.shape[1], 1/frame.shape[0]])
                    file.write(f'0 {xywh[0]} {xywh[1]} {xywh[2]} {xywh[3]}\n')
            file.close()


    annotated = results[0].plot()

    cv2.imshow('returned', annotated)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()

cv2.destroyAllWindows()

 夸嚓一下多了这么多行代码,是不是有点蒙?没事,我会慢慢帮你讲解。

 第一部分:

boxes = result[0].boxes
if bool(boxes.numpy()):
    if face_count < 300:
        face_count += 1
    else:
        face_count = 0

if bool(boxes.numpy()) 使用后来检测有咩有检测到人脸,这样就可以预防没有人脸的照片被录入训练文件夹。

这个是用来数有多少数据被收集了,如果超过300,就会归零。

第二部分:



    # 保存人脸图像和位置信息到文件
    if face_count >= 0 and face_count < 100:
        # 记录到验证集
        filepath = f'train/dataset/images/val/{face_count}.png'
        labelspath = f'train/dataset/labels/val/{face_count}.txt'
    else:
        # 记录到训练集
        filepath = f'train/dataset/images/train/{face_count}.png'
        labelspath = f'train/dataset/labels/train/{face_count}.txt'

首先,我们需要知道Yolov8在被训练的时候是需要train数据集和val数据集的,所以,我们决定把1/3的数据给val数据集,然后把剩下的2/3给train数据集。

第三部分:

    # 保存人脸图像
    cv2.imwrite(filepath, frame)

    # 保存位置信息到文件
    with open(labelspath, 'w') as file:
        for bboxes in boxes:
            for xywh in bboxes.xywh:
                xywh = xywh * np.array([1/frame.shape[1], 1/frame.shape[0], 1/frame.shape[1], 1/frame.shape[0]])
                file.write(f'0 {xywh[0]} {xywh[1]} {xywh[2]} {xywh[3]}\n')
    file.close()

我们会用cv2.imwrite把没有标记的图片记录下来(我们记录下来的的图片是frame,也就是摄像头实时读取的数据)。

接着,就到了把标记的参数记录下来。我们知道 yolo_label 的格式是这样的:

0 x_center/width_of_image y_center/img_heighy width/img_width height/img_height

在上一部分里有一个变量叫boxes,它被设为result[0].boxes。在result[0].boxes中有很多格式选项,但是只有result[0].boxes.xywh符合我们的要求。x在xywh中的意思就是bbox的x轴中心点,y在xywh中的意思就是bbox的y轴中心点,w在xywh中的意思就是bbox的宽度,而h在xywh中的意思就是bbox的高度。

我们只需要把数据乘以 [1/frame.shape[1](frame的宽度), 1/frame.shape[0] (frame的高度), 1/frame.shape[1](frame的宽度), 1/frame.shape[0] (frame的高度)]就可以了。

但是,bboxes.xywh的输出格式是tensor格式(tensor([x,xx,xxx,xxxx])),所以我们还要把它乘以一个numpy格式的列表才能同化tensor格式的列表。

最后就是把数据写到指定的文件夹中的一个.txt格式中。

最后,我们要实现训练模型。

先打开在train中的train_custom_data.yaml。

可以看到train和val数据集已经被设置好。

接下来就开始先训练模块了:

import cv2
from threading import Thread
import numpy as np
from ultralytics import YOLO

def train_it():
    global model, training
    def do():
        global model, training

        # 训练模型
        model.train(data='train/custom_data.yaml', epochs=epochs)

         # 将在训练设为 False(不在训练)
        training = False
    Thread(target=do).start()


model = YOLO('train/train_yolov8n-face.pt')


training = False

cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    results = model.predict(frame)
    boxes = result[0].boxes
    if not training:
        if bool(boxes.numpy):
            # 检测是否需要继续记录人脸
            if face_count < 300:
                face_count += 1
            else:
                # 开始训练新模型
                train_it()
            
                # 将在训练设为 True(在训练)
                training = True
            
                # 重置人脸录取次数
                face_count = 0

            # 获取人脸位置信息

            # 保存人脸图像和位置信息到文件
            if face_count >= 0 and face_count < 100:
                # 记录到验证集
                filepath = f'train/dataset/images/val/{face_count}.png'
                labelspath = f'train/dataset/labels/val/{face_count}.txt'
            else:
                # 记录到训练集
                filepath = f'train/dataset/images/train/{face_count}.png'
                labelspath = f'train/dataset/labels/train/{face_count}.txt'

            # 保存人脸图像
            cv2.imwrite(filepath, frame)

            # 保存位置信息到文件
            with open(labelspath, 'w') as file:
                for bboxes in boxes:
                    for xywh in bboxes.xywh:
                        xywh = xywh * np.array([1/frame.shape[1], 1/frame.shape[0], 1/frame.shape[1], 1/frame.shape[0]])
                        file.write(f'0 {xywh[0]} {xywh[1]} {xywh[2]} {xywh[3]}\n')
                file.close()

    annotated = results[0].plot()

    cv2.imshow('returned', annotated)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()

cv2.destroyAllWindows()

天哪!

还是老样子,我们拆分一下新增的程序:

第一部分:

def train_it():
    global model, training
    def do():
        global model, training

        # 训练模型
        model.train(data='train/train_custom_data.yaml', epochs=epochs)

         # 将在训练设为 False(不在训练)
        training = False
    Thread(target=do).start()

training = False

我们新加了一个函数,来训练新的模型,所以要global model。

我们新加training是应为我们不想要AI在学习的时候数据会改变,所以在学习的时候就会停止录取数据。

为了实现这个,我们在需要在负责记录的模块前面加一个if语句,这样的话,只有training是False(没有在训练)的时候才会记录数据。

第二部分:

if not training:
    if bool(boxes.numpy()):
        # 检测是否需要继续记录人脸
        if face_count < 300:
            face_count += 1
        else:
            # 开始训练新模型
            train_it()
            
            # 将在训练设为 True(在训练)
            training = True
            
            # 重置人脸录取次数
            face_count = 0

现在我们可以看到else下面加了一些代码,这些是用来启动训练程序的。

train_it():这个是启动训练模型的。我们把train_it函数设为thread,这可以让训练和检测同时进行。

当启动完成后,training就会被设为True(在训练),这样就可防止AI在训练的时候还在记录数据,导致数据变化。

每次训练的结果都能在runs/detect/trainx/weights中找到(x是第几个被训练的模型)。比如说,我想要第七次被训练的模型,就得进入这个文件路径:runs/detect/train9/weights/best.pt。

 最后,让我们测试一下(这里的epochs是2,有可能比较慢,所以我就快进一下):

看到视频里的进度条了吗?这个进度条就是训练完成了多少。虽然只是2个epochs,但是还是用了10分钟(没有加速的原始视频),如果电脑的配置高一点会更快。

但这不是重点,重点是训练和检测可以同步执行,并且新的模型和旧的模型可以无缝衔接!

OK!这个教程到这里就结束啦!最后,整理一下代码:

# 导入需要的库
import cv2
from threading import Thread
import numpy as np
from ultralytics import YOLO


# 初始化变量
def init_variables():


    # 导入人脸检测模型
    model = YOLO('train/train_yolov8n-face.pt')

    # 初始化人脸录取数变量
    face_count = 0

    # 初始化是否在训练中变量
    training = False

    # 初始化 epochs
    epochs = 5

    # 初始化摄像头
    cap = cv2.VideoCapture(0)

    return model, face_count, training, epochs, cap



# 记录人脸函数
def record_faces(frame, result):
    global face_count, training, model
    # 训练函数
    def train_it():
        global model, training
        def do():
            global model, training

            # 训练模型
            model.train(data='train/train_custom_data.yaml', epochs=epochs)

            # 将在训练设为 False(不在训练)
            training = False
        Thread(target=do).start()
    # 检测是否在训练
    if not training:
        boxes = result[0].boxes
        if bool(boxes.numpy()):
            #检测是否需要继续记录人脸
            
            if face_count < 300:
                face_count += 1
            else:
                # 开始训练新模型
                train_it()
                
                # 将在训练设为 True(在训练)
                training = True

                # 重置人脸录取次数
                face_count = 0

            # 获取人脸位置信息
            boxes = result[0].boxes

            # 保存人脸图像和位置信息到文件
            if face_count >= 0 and face_count < 100:
                # 记录到验证集
                filepath = f'train/dataset/images/val/{face_count}.png'
                labelspath = f'train/dataset/labels/val/{face_count}.txt'
            else:
                # 记录到训练集
                filepath = f'train/dataset/images/train/{face_count}.png'
                labelspath = f'train/dataset/labels/train/{face_count}.txt'

            # 保存人脸图像
            cv2.imwrite(filepath, frame)

            # 保存位置信息到文件
            with open(labelspath, 'w') as file:
                for bboxes in boxes:
                    for xywh in bboxes.xywh:
                        xywh = xywh * np.array(
                            [1 / frame.shape[1], 1 / frame.shape[0], 1 / frame.shape[1], 1 / frame.shape[0]])
                        file.write(f'0 {xywh[0]} {xywh[1]} {xywh[2]} {xywh[3]}\n')
                file.close()
model,face_count, training, epochs, cap = init_variables()

while True:
    # 读取实时图像
    ret, frame = cap.read()

    # 获取检测的结果
    results = model.predict(frame, show=False)

    # 记录人脸
    record_faces(frame, results)

    # 在图像上绘制检测结果
    annotated = results[0].plot()

    # 显示画出来的效果
    cv2.imshow('returned', annotated)

    # 检测是否要退出
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# 释放摄像头并关闭窗口
cap.release()
cv2.destroyAllWindows()

猜你喜欢

转载自blog.csdn.net/walchina2017/article/details/130460915
今日推荐