当前位置:   article > 正文

香橙派 AIpro开发体验:使用YOLOV8对USB摄像头画面进行目标检测_orange pi yolov8s

orange pi yolov8s

前言

YOLOv8 作为最新的目标检测算法,以其高精度、高速度和易用性,成为许多开发者首选。而香橙派 AIpro 作为一款高性能嵌入式开发板,采用昇腾AI技术路线,集成图形处理器,拥有8GB/16GB LPDDR4X,8/20 TOPS AI算力,为 AI 应用提供了坚实的硬件基础。本篇文章将分享使用香橙派 AIpro 和 YOLOv8 结合 USB 摄像头进行物体检测的实战经验,并探讨其在实际应用中的价值。

一、香橙派AIpro硬件准备

香橙派 AIpro 开发板、USB 摄像头、电源适配器,网线,micro SD卡预烧录ubuntu系统。

img

二、连接香橙派AIpro

1. 通过网线连接路由器和香橙派AIpro

为了确保香橙派AIpro与网络的稳定连接,我们采用网线将其直接接入路由器。随后,在电脑上运行ip扫描器对内网进行全面扫描,成功识别到设备“orangepiaipro”,其IP地址为192.168.1.7。

img

2. 通过wifi连接香橙派AIpro

我们在登录香橙派AIpro之后,可以参照以下方法进行wifi的连接。
扫描wifi

sudo nmcli dev wifi
  • 1

连接wifi

sudo nmcli dev wifi connect wifi名称 password wifi密码
  • 1

3. 使用vscode 通过ssh连接香橙派AIpro

Tip:使用vscode可以像本地开发一样 ,在香橙派AIpro上进行远程开发。
安装vscode 插件
1.Remote - SSH
2.Remote - SSH: Editing
3.Remote Explorer
img
创建一个ssh连接,用户名默认为HwHiAiUser,登录密码默认为Mind@123

ssh HwHiAiUser@192.168.1.7
  • 1

img
我们连接上之后打开桌面文件夹,在桌面进行开发
img
选择桌面路径
img
同时我们打开终端
img

三、USB摄像头测试

1. 配置ipynb远程开发环境

1.1 创建一个video.ipynb 文件

img
创建好之后保存在桌面文件夹内,vscode会同步这个目录的文件,方便我们进行开发。
img
img

1.2 在远程主机中安装jupyter插件和python 插件

我们对这两个主要的插件进行安装,其他插件会自动安装上。
img
然后我们打开video.ipynb 文件 选择我们需要的python版本。
python3.10.12 是系统自动的python版本。
base(python 3.9.2) 是anaconda的基础python版本。
我们应该使用conda 环境,最好是新建conda环境,来避免可能出现的环境依赖问题。

img

2. 使用opencv读取USB摄像头进行拍照

我们可能会遇到无法读取摄像头的错误,是因为没有权限访问摄像头。
img
直接对摄像头的权限进行降级,让当前用户可以访问

sudo  chmod 666 /dev/video0
  • 1

我们在video.ipynb中进行代码编写,可以直接显示摄像头画面

import cv2
from IPython.display import display, Image

camera = cv2.VideoCapture(0)
camera.set(cv2.CAP_PROP_FOURCC,cv2.VideoWriter_fourcc('M','J','P','G'))
if not camera.isOpened():
    raise IOError("Impossible d'ouvrir la webcam")
ret, frame = camera.read()
if not ret:
    raise IOError("Impossible de capturer une image")
display(Image(data=cv2.imencode('.jpg', frame)[1]))
camera.release()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

运行效果如下:
img

3. 使用opencv读取USB摄像头进行实时拍摄显示

我们在video.ipynb中进行如下python代码编写,可以直接显示摄像头画面

import cv2
import numpy as np
from IPython.display import display, clear_output,Image

# Initialize the camera
camera = cv2.VideoCapture(0)  # Use 0 for the default camera
# Set the codec to MJPG if it is supported
if camera.isOpened():
    camera.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))
else:
    raise IOError("Cannot open the webcam")
try:
    while True:
        # Capture frame-by-frame
        ret, frame = camera.read()
        if not ret:
            raise IOError("Cannot capture frame")
        # Display the image
        clear_output(wait=True)
        # Afficher l'image capturée
        display(Image(data=cv2.imencode('.jpg', frame)[1]))
finally:
    # When everything done, release the capture
    camera.release()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

本次使用的usb摄像头帧率比较低,所以有拖影,但从实时性的体验上来说,还是非常不错的。
在这里插入图片描述

四、使用yolov8进行目标检测

1. 使用torch cpu推理yolov8

本次测试使用的版本为yolov8.2 ,首先将yolov8中的ultralytics文件夹拖到香橙派AIpro的桌面上。

img
然后在video.ipynb中进行代码编写,调用yolov8库进行推理

import cv2
import numpy as np
from IPython.display import display, clear_output,Image
from ultralytics import YOLO
from time import time
# Load a model
model = YOLO('yolov8n.pt')  # pretrained YOLOv8n model
# Initialize the camera
camera = cv2.VideoCapture(0)  # Use 0 for the default camera
# Set the codec to MJPG if it is supported
if camera.isOpened():
    # camera.set(cv2.CAP_PROP_FRAME_WIDTH, 1280.0)
    # camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 720.0)
    camera.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))
else:
    raise IOError("Cannot open the webcam")
try:
    while True:
        # Capture frame-by-frame
        ret, frame = camera.read()
        if not ret:
            raise IOError("Cannot capture frame")
        s = time()
        results = model(frame,conf=0.25,iou=0.5,verbose=False)
        print(time()-s)
        for r in results:
            im = r.plot()
        # Display the image
        clear_output(wait=True)
        # Afficher l'image capturée
        display(Image(data=cv2.imencode('.jpg', im)[1]))
finally:
    # When everything done, release the capture
    camera.release()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

直接调用原始库推理速度约为0.5s 一次

img

香橙派AIpro直接调用yolov8库使用torch cpu进行推理,推理时占用2核cpu,整体占用50%,如果多线程实现应该在0.2s左右,就是4-5帧。推理时内存占用也不高,表现还是不错的。

img

2. 使用opencv推理onnx模型

2.1 导出yolov8的onnx模型

img

2.2 onnx推理

编写python代码,使用opencv dnn推理onnx模型并读取usb摄像头进行检测

import cv2
import numpy as np
from IPython.display import display, clear_output,Image
from time import time
import cv2.dnn
from ultralytics.utils import ASSETS, yaml_load
from ultralytics.utils.checks import check_yaml
CLASSES = yaml_load(check_yaml('coco128.yaml'))['names']
colors = np.random.uniform(0, 255, size=(len(CLASSES), 3))
model: cv2.dnn.Net = cv2.dnn.readNetFromONNX("yolov8n.onnx")

def draw_bounding_box(img, class_id, confidence, x, y, x_plus_w, y_plus_h):
    label = f'{CLASSES[class_id]} ({confidence:.2f})'
    color = colors[class_id]
    cv2.rectangle(img, (x, y), (x_plus_w, y_plus_h), color, 2)
    cv2.putText(img, label, (x - 10, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)

def main(original_image):
    [height, width, _] = original_image.shape
    length = max((height, width))
    image = np.zeros((length, length, 3), np.uint8)
    image[0:height, 0:width] = original_image
    scale = length / 640
    blob = cv2.dnn.blobFromImage(image, scalefactor=1 / 255, size=(640, 640), swapRB=True)
    model.setInput(blob)
    outputs = model.forward()
    outputs = np.array([cv2.transpose(outputs[0])])
    rows = outputs.shape[1]
    boxes = []
    scores = []
    class_ids = []
    for i in range(rows):
        classes_scores = outputs[0][i][4:]
        (minScore, maxScore, minClassLoc, (x, maxClassIndex)) = cv2.minMaxLoc(classes_scores)
        if maxScore >= 0.25:
            box = [
                outputs[0][i][0] - (0.5 * outputs[0][i][2]), outputs[0][i][1] - (0.5 * outputs[0][i][3]),
                outputs[0][i][2], outputs[0][i][3]]
            boxes.append(box)
            scores.append(maxScore)
            class_ids.append(maxClassIndex)
    result_boxes = cv2.dnn.NMSBoxes(boxes, scores, 0.25, 0.45, 0.5)

    detections = []
    for i in range(len(result_boxes)):
        index = result_boxes[i]
        box = boxes[index]
        detection = {
            'class_id': class_ids[index],
            'class_name': CLASSES[class_ids[index]],
            'confidence': scores[index],
            'box': box,
            'scale': scale}
        detections.append(detection)
        draw_bounding_box(original_image, class_ids[index], scores[index], round(box[0] * scale), round(box[1] * scale),
                          round((box[0] + box[2]) * scale), round((box[1] + box[3]) * scale))

# Initialize the camera
camera = cv2.VideoCapture(0)  # Use 0 for the default camera
# Set the codec to MJPG if it is supported
if camera.isOpened():
    # camera.set(cv2.CAP_PROP_FRAME_WIDTH, 1280.0)
    # camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 720.0)
    camera.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))
else:
    raise IOError("Cannot open the webcam")
try:
    while True:
        # Capture frame-by-frame
        ret, frame = camera.read()
        if not ret:
            raise IOError("Cannot capture frame")
        s = time()
        main(frame)
        print(time()-s)
        # Display the image
        clear_output(wait=True)
        # Afficher l'image capturée
        display(Image(data=cv2.imencode('.jpg', frame)[1]))

finally:
    # When everything done, release the capture
    camera.release()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83

onnx推理使用单核cpu,推理一次的速度约为0.7s

img

3. 使用npu 推理yolov8

3.1 onnx转换为OM模型

将ONNX模型转换为OM模型,用CANN提供的ATC工具将其转换为昇腾AI处理器能识别的OM模型。

atc --framework=5 --model=yolov8n.onnx --input_format=NCHW --output=yolov8n --soc_version=Ascend310B4
  • 1

atc命令中各参数的含义如下:
–framework:原始框架类型,5表示ONNX。
–model:ONNX模型文件存储路径。
–input_format:输入的格式定义
–output:离线om模型的路径以及文件名。
–soc_version:昇腾AI处理器的型号。
在服务器种执行npu-smi info命令进行查询,在查询到的“Name”前增加Ascend信息,例如“Name”对应取值为310B4,实际配置的–soc_version值为Ascend310B4。

img

3.2 添加交换空间

若出现以下错误则是内存不足,可以添加交换空间

BrokenPipeError: [Errno 32] Broken pipe
/usr/local/miniconda3/lib/python3.9/multiprocessing/resource_tracker.py:216: UserWarning: resource_tracker: There appear to be 97 leaked semaphore objects to clean up at shutdown
warnings.warn('resource_tracker: There appear to be %d ’

  1. 使用 root 用户执行:
su -
  • 1
  1. 创建一个用于交换空间的文件,创建8GB的交换文件:
mkswap /swapfile
  • 1
  1. 设置交换文件
mkswap /swapfile
  • 1
  1. 启用交换空间
swapon /swapfile
  • 1
  1. 编辑/etc/fstab文件,使交换空间开机自动挂载:
echo '/swapfile none swap defaults 0 0' >> /etc/fstab
  • 1

6.验证交换空间是否生效

free -m
  • 1

通过top监控可以看到 转换过程占用内存大概 12G左右,不添加虚拟缓存内存是不够用的。
img

3.3 npu推理

编写python代码使用npu推理yolov8 对usb摄像头进行检测

import os

# Verify the path
print(os.environ['LD_LIBRARY_PATH'])
import cv2
import numpy as np
from IPython.display import display, clear_output,Image
from time import time
from ais_bench.infer.interface import InferSession
from ultralytics.utils import ASSETS, yaml_load
from ultralytics.utils.checks import check_yaml

CLASSES = yaml_load(check_yaml('coco128.yaml'))['names']

colors = np.random.uniform(0, 255, size=(len(CLASSES), 3))

model = InferSession(device_id=0, model_path="yolov8n.om")

def draw_bounding_box(img, class_id, confidence, x, y, x_plus_w, y_plus_h):
    label = f'{CLASSES[class_id]} ({confidence:.2f})'
    color = colors[class_id]
    cv2.rectangle(img, (x, y), (x_plus_w, y_plus_h), color, 2)
    cv2.putText(img, label, (x - 10, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)


def main(original_image):
    [height, width, _] = original_image.shape
    length = max((height, width))
    image = np.zeros((length, length, 3), np.uint8)
    image[0:height, 0:width] = original_image
    scale = length / 640

    blob = cv2.dnn.blobFromImage(image, scalefactor=1 / 255, size=(640, 640), swapRB=True)
    
    begin_time = time()
    outputs = model.infer(feeds=blob, mode="static")
    end_time = time()
    print("om infer time:", end_time - begin_time)

    outputs = np.array([cv2.transpose(outputs[0][0])])
    rows = outputs.shape[1]

    boxes = []
    scores = []
    class_ids = []

    for i in range(rows):
        classes_scores = outputs[0][i][4:]
        (minScore, maxScore, minClassLoc, (x, maxClassIndex)) = cv2.minMaxLoc(classes_scores)
        if maxScore >= 0.25:
            box = [
                outputs[0][i][0] - (0.5 * outputs[0][i][2]), outputs[0][i][1] - (0.5 * outputs[0][i][3]),
                outputs[0][i][2], outputs[0][i][3]]
            boxes.append(box)
            scores.append(maxScore)
            class_ids.append(maxClassIndex)

    result_boxes = cv2.dnn.NMSBoxes(boxes, scores, 0.25, 0.45, 0.5)

    detections = []
    for i in range(len(result_boxes)):
        index = result_boxes[i]
        box = boxes[index]
        detection = {
            'class_id': class_ids[index],
            'class_name': CLASSES[class_ids[index]],
            'confidence': scores[index],
            'box': box,
            'scale': scale}
        detections.append(detection)
        draw_bounding_box(original_image, class_ids[index], scores[index], round(box[0] * scale), round(box[1] * scale),
                          round((box[0] + box[2]) * scale), round((box[1] + box[3]) * scale))

# Initialize the camera
camera = cv2.VideoCapture(0)  # Use 0 for the default camera

# Set the codec to MJPG if it is supported
if camera.isOpened():
    # camera.set(cv2.CAP_PROP_FRAME_WIDTH, 1280.0)
    # camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 720.0)
    camera.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))
else:
    raise IOError("Cannot open the webcam")

    # Define the codec and create VideoWriter object
# Get the width and height of the frames
frame_width = int(camera.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(camera.get(cv2.CAP_PROP_FRAME_HEIGHT))
print(f"Frame width: {frame_width}, Frame height: {frame_height}")

# Define the codec and create VideoWriter object
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('output.avi', fourcc, 30.0, (frame_width, frame_height))  # 20.0 is the frame rate

try:
    _start_time = time()
    while time() - _start_time < 5:
        # Capture frame-by-frame
        ret, frame = camera.read()
        if not ret:
            raise IOError("Cannot capture frame")
        main(frame)
        out.write(frame)

        # Display the image
        # clear_output(wait=True)
        
        # # Afficher l'image capturée
        # display(Image(data=cv2.imencode('.jpg', frame)[1]))

finally:
    # When everything done, release the capture
    camera.release()
    out.release()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114

与前面torch和onnx 的推理相比,基于昇腾CANN的推理效果,在速度上有了质的飞跃。

img
yolov8使用npu推理一帧的速度达到了惊人的0.017s,相比cpu提升了20-30倍。

img

五、总结

昇腾CANN框架的优势:

  1. 推理速度显著提升: 在使用YOLOv8模型进行推理时,我发现昇腾CANN的单帧处理速度能达到0.017秒,相比CPU提升了20-30倍,这对于实时性要求高的应用场景(如自动驾驶、安防监控)至关重要。
  2. 兼容性与扩展性强: 昇腾CANN支持多种模型和算法,并随着昇腾硬件的升级不断提升性能,为开发者提供了更广阔的选择空间。
    香橙派AIpro开发板的优势:
  3. 硬件性能出色: 能够流畅运行复杂的AI算法和模型,满足我的开发需求。
  4. 易用性高: 简单的设置和配置就能将AI应用部署到开发板上进行测试和验证,极大提升了开发效率。
  5. 扩展性强: 丰富的接口方便连接其他硬件设备,为开发更复杂的AI应用提供了更多可能性。

总的来说,我对其高效的推理速度、便捷的开发体验以及强大的扩展性印象深刻。
香橙派AIpro开发板为开发者提供了一个优秀的平台,可以方便地体验和利用昇腾CANN强大的AI推理能力。我相信,随着昇腾CANN和香橙派AIpro开发板的不断发展,它们将进一步推动AI技术的应用和普及。

六、参考

CANN开发指南
香橙派官网
昇腾论坛

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/658970
推荐阅读
相关标签
  

闽ICP备14008679号