基于树莓派4B的YOLOv5-Lite目标检测的移植与部署(含训练教程)

前言:本文为手把手教学树莓派4B项目——YOLOv5-Lite目标检测,本次项目采用树莓派4B(Cortex-A72)作为核心 CPU 进行部署。该篇博客算是深度学习理论的初步实战,选择的网络模型为 YOLOv5 模型的变种 YOLOv5-Lite 模型。YOLOv5-Lite YOLOv5 相比虽然牺牲了部分网络模型精度,但是缺极大的提升了模型的推理速度,该模型属性将更适合实战部署使用。该项目的实践将帮助大家成功进入 “嵌入式AI” 领域,后续将在该项目上加入嵌入式的 “传统控制” 属性,读者朋友可以期待一下!(文末有代码开源!

硬件实物图:

效果图:

一、YOLOv5-Lite概述

1.1 YOLOv5概述

YOLOv5 网络模型算是 YOLO 系列迭代后特别经典的一代网络模型,作者为:Glenn Jocher。部分学者可能认为YOlOv5创新性不足,其是否称得上 YOLOv5 而议论纷纷。作者认为 YOLOv5 可以算是对 YOLO 系列之前的一次集大成者的总结突破,其属于非常优秀经典的网络模型框架,各种网络结构trick 是非常值得借鉴的!

代码地址:ultralytics/yolov5: YOLOv5 🚀 in PyTorch > ONNX > CoreML > TFLite (github.com)

Yolov5 官方代码中,给出的目标检测网络中一共有4个版本,分别是Yolov5sYolov5mYolov5lYolov5x四个模型。作者仅以 Yolov5s 的网络结构为对象进行讲解,其他版本的读者朋友可以参考其他博客!

Yolov5s 网络是 Yolov5 系列中深度最小,特征图的宽度最小的网络。后面的3种都是在此基础上不断加深,不断加宽。Yolov5 的网络结构图如下(源于江大白大佬的结构图):

上图即 Yolov5 的网络结构图,可以看出,还是分为输入端、Backbone、Neck、Prediction四个部分。

(1)输入端:Mosaic数据增强、自适应锚框计算、自适应图片缩放
(2)Backbone:Focus结构,CSP结构
(3)Neck:FPN+PAN结构
(4)Prediction:GIOU_Loss

上述四部分都是属于如今很常见的模块与Trick了,受限于博客篇幅,各部分的详解就不与读者朋友好好分析和交流了。建议对 YOLO 系列陌生的朋友可以去好好看看其他博主的博客亦或是去B站看视频教学!

下面丢上 Yolov5 作者的算法性能测试图:

到现在为止,Yolov5 已经更新迭代到 v7.0 版本了,科研学术圈以 Yolov5 为基础框架进行魔改的论文数不胜数。通过上述作者的概述读者朋友可能对 Yolov5 有了一个大致的了解,不难发现 Yolov5 是非常优秀的神经网络模型。

可考虑到实际情况,部署的本地机器通常并没有 PC 端那么计算能力强劲。这时候为了整体目标检测系统的稳定运行,往往需要牺牲掉网络模型的精度以换取足够快的检测速度。因此,轻量化部署的网络模型结构就此孕育而生!

1.2 YOLOv5-Lite详解

Yolov5-Lite 网络模型作为轻量化部署网络的代表作之一,深受算法部署工程师的偏爱(作者为中国ppogg大佬)!

Yolov5-Lite地址:GitHub - ppogg/YOLOv5-Lite: 🍅🍅🍅YOLOv5-Lite: lighter, faster and easier to deploy. Evolved from yolov5 and the size of model is only 930+kb (int8) and 1.7M (fp16). It can reach 10+ FPS on the Raspberry Pi 4B when the input size is 320×320~

Yolov5-Lite 算法的模型结构如图下。该算法去除了 Focus 结构层,减小了模型体量,使模型变得更为轻便;同时,去除了 4slice 操作,减少了对计算机芯片缓存的占用,降低了计算机的处理负担。与 Yolov5 算法相比,Yolov5-Lite 算法能避免反复使用 C3 Layer 模块。C3 Layer 模块会占用计算机很多运行空间,从而降低计算机的处理速度。这种方式能使 Yolov5-Lite 算法模型的精度控制在可靠范围内,从而使其更易部署。

在图像识别领域,主干网络结构(Backbone)和检测头(Head)中往往有一段中间层,即特征增强融合网络层(Neck),可更精准地提取融合特征。Yolov5-Lite 算法也采用 FPN+PAN 结构,但其对输出端(Head)进行了通道剪枝,改进了 YOLOv4 算法和 YOLOv5 算法中的 FPN+PAN 的结构,具体表现在以下2个方面:

(1)、YOLOv5 算法不同,Yolov5-Lite 算法自身各结构的通道数量相同,即模型特征增强网络通道网格数也是20×20×96,这样可优化对内存的访问和使用,提高模型的运行效率;

(2)、Yolov5-Lite 算法采用 PANet 结构,将 YOLOv5 算法的通道连接(cat)操作改进为叠加操作,这样可进一步优化对内存的使用,加快处理速度。如下图为 Yolov5-Lite 算法与 YOLOv4 和  YOLOv5 算法中的 PAN 结构对比:

作者总结:

Yolov5-Lite 网络模型源于 YOLOv5 模型,随处可以可见 YOLOv5 网络模型的影子。但是,出于移植部署的目的性,Yolov5-Lite 网络将工作重心放在如何进行快速推理,如何轻量化网络模型大小!

Yolov5-Lite 网络模型不仅通过直接改变网络模型的结构,偏向多使用计算量小的网络模型结构去提取和融合目标特征,同时侧重运用计算机的运行机制:通过降低计算机内存的存储和读取去变相提高网络推理速度!!!

作者这里仅对 Yolov5-Lite 做初步概述,详情读者朋友可以参考学术论文!

二、YOLOv5-Lite训练

2.1 数据集制作

★常规的神经网络模型训练是需要收集到大量语义丰富的数据集进行训练的。但是考虑实际工程下可能仅需要对已知场地且固定实物进行目标检测追踪等任务,这个时候我们可以采取偷懒的下方作者使用的方法!

1、作者使用树莓派4B的 Camera 直接在捕获需要识别目标物的图片信息(捕获期间转动待识别的目标物体);

树莓派4B的 Camera 定时捕获照片的python代码如下:

import cv2
from threading import Thread
import uuid
import os
import time
count = 0
def image_collect(cap):
    global count
    while True:
        success, img = cap.read()
        if success:
            file_name = str(uuid.uuid4())+'.jpg'
            cv2.imwrite(os.path.join('images',file_name),img)
            count = count+1
            print("save %d %s"%(count,file_name))
        time.sleep(0.4)

if __name__ == "__main__":
    
    os.makedirs("images",exist_ok=True)
    
    # 打开摄像头
    cap = cv2.VideoCapture(0)

    m_thread = Thread(target=image_collect, args=([cap]),daemon=True)
    
    while True:

        # 读取一帧图像

        success, img = cap.read()

        if not success:

            continue

        cv2.imshow("video",img)

        key =  cv2.waitKey(1) & 0xFF   

        # 按键 "q" 退出
        if key ==  ord('c'):
            m_thread.start()
            continue
        elif key ==  ord('q'):
            break

    cap.release() 

按动 “c” 开始采集待识别目标图像,按动 “q” 退出摄像头 Camera 的图片采集;

2、将捕获到的待识别目标物照片传输到PC端,利用 Labelme 软件进行标注(Labelme不会使用的建议相关博客);

作者的标注了 3 类目标:drug,prime,glue;读者朋友可以根据自己实际情况标注自己需要的数据集!由于我们标注的数据的标签 label 默认是 JSON 格式的不能被 YOLO 系列的神经网络模型直接进行利用训练。

3、使用 JSON 转 txt 的 YOLO 格式 label 的python代码进行转换(可以直接使用作者提供的代码):

dic_lab.py:

dic_labels= {'drug':0,
            'glue':1,
            'prime':2,
             'path_json':'labels',
             'ratio':0.9}

lablemetoyolo.py:

import os
import json
import random
import base64
import shutil
import argparse
from pathlib import Path
from glob import glob
from dic_lab import dic_labels

def generate_labels(dic_labs):
    path_input_json = dic_labels['path_json']
    ratio = dic_labs['ratio']
    for index, labelme_annotation_path in enumerate(glob(f'{path_input_json}/*.json')):

        # 读取文件名
        image_id = os.path.basename(labelme_annotation_path).rstrip('.json')
        
        # 计算是train 还是 valid
        train_or_valid = 'train' if random.random() < ratio else 'valid'

        # 读取labelme格式的json文件
        labelme_annotation_file = open(labelme_annotation_path, 'r')
        labelme_annotation = json.load(labelme_annotation_file)

        # yolo 格式的 lables
        yolo_annotation_path = os.path.join(train_or_valid, 'labels',image_id + '.txt')
        yolo_annotation_file = open(yolo_annotation_path, 'w')
        
        # yolo 格式的图像保存
        yolo_image = base64.decodebytes(labelme_annotation['imageData'].encode())
        yolo_image_path = os.path.join(train_or_valid, 'images', image_id + '.jpg')
        
        yolo_image_file = open(yolo_image_path, 'wb')
        yolo_image_file.write(yolo_image)
        yolo_image_file.close()
     

        # 获取位置信息
        for shape in labelme_annotation['shapes']:
            if shape['shape_type'] != 'rectangle':
                print(
                    f'Invalid type `{shape["shape_type"]}` in annotation `annotation_path`')
                continue
           

            points = shape['points']
            scale_width = 1.0 / labelme_annotation['imageWidth']
            scale_height = 1.0 / labelme_annotation['imageHeight']
            width = (points[1][0] - points[0][0]) * scale_width
            height = (points[1][1] - points[0][1]) * scale_height
            x = ((points[1][0] + points[0][0]) / 2) * scale_width
            y = ((points[1][1] + points[0][1]) / 2) * scale_height
            object_class = dic_labels[shape['label']]
            yolo_annotation_file.write(f'{object_class} {x} {y} {width} {height}\n')
        yolo_annotation_file.close()
        print("creat lab %d : %s"%(index,image_id))


if __name__ == "__main__":
    os.makedirs(os.path.join("train",'images'),exist_ok=True)
    os.makedirs(os.path.join("train",'labels'),exist_ok=True)
    os.makedirs(os.path.join("valid",'images'),exist_ok=True)
    os.makedirs(os.path.join("valid",'labels'),exist_ok=True)
    generate_labels(dic_labels)

我们需要根据自己的需要自定义字典 dic_lab,字典中的 ratio = 0.9 的作用是将数据集拆分成训练集和验证集 9:1。读者朋友可以根据自己的实际情况去修改字典的标签内容,成功执行 lablemetoyolo.py 代码后效果如下:

labels文件夹下的标签成功转换了 YOLO 系列可以使用的 label 标签,到此时就已经成功准备好我们需要的训练集了!

特别说明:该方法仅适用于上述作者所说的场景下,实际情况下,建议大家还是使用合格的数据集进行训练(即目标与背景语义丰富的数据集),使得训练出来的神经网络具有良好的泛化性与鲁棒性,否则训练出来的网络很容易过拟合!

2.2 YOLOv5-Lite训练

Yolov5-Lite 训练就是常规的神经网络模型训练,我们从 GitHub 上下载 Yolov5-Lite 的源代码,训练平台为:PyCharm 2020.1 x64,GPU:RTX3060 6G,CPU:AMD Ryzen 7 5800H  3.2GHZ

读者朋友可以使用  PyCharm 或者 VsCode 打开 Yolov5-Lite 的源码(作者使用PyCharm 2020.1 x64);

在 Yolov5-Lite 的目录下找到 train.py (训练文件)的 main 函数入口,进行如下配置:

我们设置如下几个核心配置:

--weights v5lite-s.pt

--cfg models/v5Lite-s.yaml

--data data/mydata.yaml

--img-size 320

--batch-size 16

--data data/mydata.yaml

device 0/cpu                        (可以不使用CUDA训练)

读者朋友一定要将数据集存放的地址位置搞正确!!!

mydata.yaml:

Yolov5-Lite 网络模型的训练可以不一定必须使用 CUDA 进行加速,但是 pytorch 架构等依赖库一定需要满足,模型训练依赖要求如下:

# base ----------------------------------------
matplotlib>=3.2.2
numpy>=1.18.5
opencv-python>=4.1.2
Pillow
PyYAML>=5.3.1
scipy>=1.4.1
torch>=1.8.0
torchvision>=0.9.0
tqdm>=4.41.0

# logging -------------------------------------
tensorboard>=2.4.1
# wandb

# plotting ------------------------------------
seaborn>=0.11.0
pandas

# export --------------------------------------
# coremltools>=4.1
# onnx>=1.9.1
# scikit-learn==0.19.2  # for coreml quantization

# extras --------------------------------------
thop  # FLOPS computation
pycocotools>=2.0  # COCO mAP

将训练环境与数据集都搞定之后,就可以点击运行按钮进行 Yolov5-Lite 的模型训练了!

训练成功之后,将会在当前目录下的 run 文件下的 trian 文件下找到 expx (x代表数字),expx 则存放了第 x 次训练时候的各种数据内容,包括:历史最优权重best_weight,当前权重last_weight,训练结果result等等;

三、树莓派4B部署YOLOv5-Lite

树莓派4B运行 Yolov5-Lite 网络模型进行目标检测需要依赖 OpenCV 等视觉Lib,读者朋友可以直接使用作者第一篇博客的配置。

博客地址:http://t.csdn.cn/jbHQm

3.1 ONNX概述

Open Neural Network Exchange(ONNX)是一个开放的生态系统,它使人工智能开发人员在推进项目时选择合适的工具,不用被框架或者生态系统所束缚。ONNX支持不同框架之间的互操作性,简化从研究到生产之间的道路。ONNX支持许多框架(TensorFlow, Pytorch, Keras, MxNet, MATLAB等等),这些框架中的模型都可以导出或者转换为标准ONNX格式。模型采用ONNX格式后,就可在各种平台和设备上运行。

开发者根据深度学习框架优劣选择某个框架,但是这些框架适应不同的开发阶段,由于必须进行转换,从而导致了研究和生产之间的重大延迟。ONNX格式一个通用的IR,能够使得开发人员在开发或者部署的任何阶段选择最适合他们项目的框架。ONNX通过提供计算图的通用表示,帮助开发人员为他们的任务选择合适的框架。

ONNX可视化:ONNX 模型可以通过 netron 进行可视化。

作者总结:

ONNX 顾名思义就是开放的神经网络模型转换,利用它可以轻松将模型更换框架,从而适配亦或是部署在各类设备上。

3.2 ONNX模型转换和移植

如今的开源 YOLO 系列神经网络模型的目录下作者都会预留 export.py 文件将该神经网络模型进行转换到 ONNX 模型,方便大家实际情况下部署使用!

将我们训练好的最优训练权重 weights 存放到 YOLOv5-Lite 主目录下,之后运行如下代码:

python export.py --weights best.pt

运行该指令后将会通过 best.pt 文件,生成 best.onnx ONNX 模型的权重文件;

同时为了成功运行 ONNX 格式的 YOLOv5-Lite 网络模型,需要在树莓派4B中安装 onnxruntim (可以直接使用作者提供的安装包),当然值得注意的是 onnxruntim 的安装需要依赖的 Numpy版本1.21 以上(这点需要大家注意,当然如果使用了作者的镜像完全没有问题!)。

pip install onnx     (tab补全安装包)

将转换成 ONNX 格式的权重文件导入到树莓派4B中,并于目标检测程序保持同一目录下:

到此 ONNX 模型的转换与移植工作就可以完成了!

四、YOLOv5-Lite目标检测

YOLOv5-Lite 的目标检测前向推理程序是很简单的,可以直接借鉴作者如下提供的代码:

import cv2
import numpy as np
import onnxruntime as ort
import time

def plot_one_box(x, img, color=None, label=None, line_thickness=None):
    """
    description: Plots one bounding box on image img,
                 this function comes from YoLov5 project.
    param: 
        x:      a box likes [x1,y1,x2,y2]
        img:    a opencv image object
        color:  color to draw rectangle, such as (0,255,0)
        label:  str
        line_thickness: int
    return:
        no return
    """
    tl = (
        line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1
    )  # line/font thickness
    color = color or [random.randint(0, 255) for _ in range(3)]
    c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3]))
    cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)
    if label:
        tf = max(tl - 1, 1)  # font thickness
        t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]
        c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3
        cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA)  # filled
        cv2.putText(
            img,
            label,
            (c1[0], c1[1] - 2),
            0,
            tl / 3,
            [225, 255, 255],
            thickness=tf,
            lineType=cv2.LINE_AA,
        )

def _make_grid( nx, ny):
        xv, yv = np.meshgrid(np.arange(ny), np.arange(nx))
        return np.stack((xv, yv), 2).reshape((-1, 2)).astype(np.float32)

def cal_outputs(outs,nl,na,model_w,model_h,anchor_grid,stride):
    
    row_ind = 0
    grid = [np.zeros(1)] * nl
    for i in range(nl):
        h, w = int(model_w/ stride[i]), int(model_h / stride[i])
        length = int(na * h * w)
        if grid[i].shape[2:4] != (h, w):
            grid[i] = _make_grid(w, h)

        outs[row_ind:row_ind + length, 0:2] = (outs[row_ind:row_ind + length, 0:2] * 2. - 0.5 + np.tile(
            grid[i], (na, 1))) * int(stride[i])
        outs[row_ind:row_ind + length, 2:4] = (outs[row_ind:row_ind + length, 2:4] * 2) ** 2 * np.repeat(
            anchor_grid[i], h * w, axis=0)
        row_ind += length
    return outs



def post_process_opencv(outputs,model_h,model_w,img_h,img_w,thred_nms,thred_cond):
    conf = outputs[:,4].tolist()
    c_x = outputs[:,0]/model_w*img_w
    c_y = outputs[:,1]/model_h*img_h
    w  = outputs[:,2]/model_w*img_w
    h  = outputs[:,3]/model_h*img_h
    p_cls = outputs[:,5:]
    if len(p_cls.shape)==1:
        p_cls = np.expand_dims(p_cls,1)
    cls_id = np.argmax(p_cls,axis=1)

    p_x1 = np.expand_dims(c_x-w/2,-1)
    p_y1 = np.expand_dims(c_y-h/2,-1)
    p_x2 = np.expand_dims(c_x+w/2,-1)
    p_y2 = np.expand_dims(c_y+h/2,-1)
    areas = np.concatenate((p_x1,p_y1,p_x2,p_y2),axis=-1)
    
    areas = areas.tolist()
    ids = cv2.dnn.NMSBoxes(areas,conf,thred_cond,thred_nms)
    if len(ids)>0:
        return  np.array(areas)[ids],np.array(conf)[ids],cls_id[ids]
    else:
        return [],[],[]
def infer_img(img0,net,model_h,model_w,nl,na,stride,anchor_grid,thred_nms=0.4,thred_cond=0.5):
    # 图像预处理
    img = cv2.resize(img0, [model_w,model_h], interpolation=cv2.INTER_AREA)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    img = img.astype(np.float32) / 255.0
    blob = np.expand_dims(np.transpose(img, (2, 0, 1)), axis=0)

    # 模型推理
    outs = net.run(None, {net.get_inputs()[0].name: blob})[0].squeeze(axis=0)

    # 输出坐标矫正
    outs = cal_outputs(outs,nl,na,model_w,model_h,anchor_grid,stride)

    # 检测框计算
    img_h,img_w,_ = np.shape(img0)
    boxes,confs,ids = post_process_opencv(outs,model_h,model_w,img_h,img_w,thred_nms,thred_cond)

    return  boxes,confs,ids




if __name__ == "__main__":

    # 模型加载
    model_pb_path = "best.onnx"
    so = ort.SessionOptions()
    net = ort.InferenceSession(model_pb_path, so)
    
    # 标签字典
    dic_labels= {0:'drug',
            1:'glue',
            2:'prime'}
    
    # 模型参数
    model_h = 320
    model_w = 320
    nl = 3
    na = 3
    stride=[8.,16.,32.]
    anchors = [[10, 13, 16, 30, 33, 23], [30, 61, 62, 45, 59, 119], [116, 90, 156, 198, 373, 326]]
    anchor_grid = np.asarray(anchors, dtype=np.float32).reshape(nl, -1, 2)
    
    video = 0
    cap = cv2.VideoCapture(video)
    flag_det = False
    while True:
        success, img0 = cap.read()
        if success:
            
            if flag_det:
                t1 = time.time()
                det_boxes,scores,ids = infer_img(img0,net,model_h,model_w,nl,na,stride,anchor_grid,thred_nms=0.4,thred_cond=0.5)
                t2 = time.time()
            
                
                for box,score,id in zip(det_boxes,scores,ids):
                    label = '%s:%.2f'%(dic_labels[id],score)
            
                    plot_one_box(box.astype(np.int16), img0, color=(255,0,0), label=label, line_thickness=None)
                    
                str_FPS = "FPS: %.2f"%(1./(t2-t1))
                
                cv2.putText(img0,str_FPS,(50,50),cv2.FONT_HERSHEY_COMPLEX,1,(0,255,0),3)
                
            
            cv2.imshow("video",img0)
        key=cv2.waitKey(1) & 0xFF    
        if key == ord('q'):
        
            break
        elif key & 0xFF == ord('s'):
            flag_det = not flag_det
            print(flag_det)
            
    cap.release() 

就该目标检测算法作者简单给大家讲解一下:

上方的 model_pb_path 和 dic_labels 是需要根据自己实际情况去改一下的,其余基本保持不变即可!

上述基本除了 anchor 锚框这个数据,其余都是不需要改动的,这里 anchor 直接使用了默认值。如果大家想在目标检测的时候可以更好地框选出自己地识别目标,可以用 K-means 聚类算法去自适应 anchor 的大小,从小聚类出符合自己数据类型的 anchor ,这样目标检测的时候可能效果更好!

一切准备就绪,直接运行咱们的 test_video.py 程序进行目标检测:

python3 test_video.py

按键 “s” 开启目标检测功能,按键 “q” 退出当前目标检测程序!

五、项目效果

5.1 实战视频

基于树莓派4B的YOLOv5-Lite目标检测

5.2 作者有话

作者本次仅使用了 ONNX 模型下直接跑 YOLOv5-Lite 的网络模型,目前该状态下的FPS仅维持在5左右,效果其实比直接跑 YOLOv5 网络模型已经好很多了(YOLOv5的FPS在0.3FPS左右)。但是距离可以与控制结合感觉还是差了点,所以,后续作者将对目标检测进行加速处理(使用NCNN,MNN等模型加速)。

当然,作者也会分享自己实验室提出的轻量化目标检测网络从网络模型出发加速推理。将计算机视觉与控制结合的嵌入式AI教学后续也会出博客分享给各位,希望给大家日常的工作或者是电赛提供些许帮助!

六、项目代码

代码地址:基于树莓派4B的YOLOv5-Lite目标检测的资源包资源-CSDN文库

如果积分不够的朋友,点波关注评论区留下邮箱,作者无偿提供源码和后续问题解答。求求啦关注一波吧 !!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/33705.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【AI底层逻辑】——篇章3(上):数据、信息与知识香农信息论信息熵

目录 引入 一、数据、信息、知识 二、“用信息丈量世界” 1、香农信息三定律 2、一条信息的价值 3、信息的熵 总结 引入 AI是一种处理信息的模型&#xff0c;我们把信息当作一种内容的载体&#xff0c;计算机发明以前很少有人思考它的本质是什么。随着通信技术的发展&a…

【ISO26262】汽车功能安全第3部分:概念阶段

GB/T34590《道路车辆 功能安全》分为以下部分: 需要文档的朋友,可以和我联系! tommi_wei@163.com GB/T34590的本部分规定了车辆在概念阶段的要求: ———相关项定义; ———安全生命周期启动; ———危害分析和风险评估;及 ———功能安全概念。 危害事件分类 对于每一个…

wsl子系统Ubuntu18.04,cuDNN安装

如果觉得本篇文章对您的学习起到帮助作用&#xff0c;请 点赞 关注 评论 &#xff0c;留下您的足迹&#x1f4aa;&#x1f4aa;&#x1f4aa; 本文主要wls子系统Ubuntu18.04安装cuDNN&#xff0c;安装cudnn坑巨多&#xff0c;因此记录以备日后查看&#xff0c;同时&#xff0…

GaussDB WDR报告分析

标题 问题描述问题现象告警业务影响原因分析处理方法步骤 1步骤 2步骤 3步骤 4步骤 6步骤 7步骤 8步骤9步骤 10步骤 11步骤 12 问题描述 CPU使用率高。 问题现象 出现CPU使用率超过阈值&#xff0c;CPU使用率快速上涨或短时间持续较高水平等现象。 告警 CPU使用率告警。 …

uniapp的表单校验方式整理

uniapp的表单校验方式整理 这里我使用的模板为&#xff1a; 第一种&#xff1a; uniapp本身自带表单校验的js文件&#xff0c;代码写的很简洁&#xff0c;也是比较全面的 只要按照规则校验即可&#xff0c;下面是对应的校验代码&#xff1a; /** 数据验证&#xff08;表…

PyQt中数据库的访问(一)

访问数据库的第一步是确保ODBC数据源配置成功&#xff0c;我接下来会写数据源配置的文章&#xff0c;请继续关注本栏&#xff01; &#xff08;一&#xff09;数据库连接 self.DBQSqlDatabase.addDatabase("QODBC") self.DB.setDatabaseName("Driver{sqlServer…

ModaHub AI模型开源社区——向量数据库Milvus存储操作教程

目录 存储操作 数据插入 数据落盘 定时触发 客户端触发 缓冲区达到上限触发 数据合并 建立索引 删除 删除集合 删除分区 删除实体 数据段整理 数据读取 常见问题 存储操作 阅读本文前&#xff0c;请先阅读 存储相关概念。 数据插入 客户端通过调用 insert 接…

【计算机视觉】DINO

paper&#xff1a;Emerging Properties in Self-Supervised Vision Transformers 源码&#xff1a;https://github.com/facebookresearch/dino 20230627周二目前只把第一部分看完了。 论文导读&#xff1a;DINO -自监督视觉Transformers - deephub的文章 - 知乎 综述类型&a…

线程不安全举例

1、举例说明集合类线程不安全 &#xff08;1&#xff09;查看源码可证明 看ArrayList源码 没有sync、lock&#xff0c;线程不安全 &#xff08;2&#xff09;创建多个线程写入读取数据 List<String> list new ArrayList<>(); for (int i 1; i <30 ; i) {n…

【Android】Android虚拟机

虚拟机 Android的虚拟机主要有两种&#xff1a;Dalvik 虚拟机和 ART&#xff08;Android Runtime&#xff09;虚拟机。 Dalvik 虚拟机 Dalvik 虚拟机是 Android 早期使用的虚拟机&#xff0c;它基于寄存器架构。从Android 2.2版本开始&#xff0c;支持JIT即时编译&#xff08…

基于多站点集中汇聚需求的远程调用直播视频汇聚平台解决方案

一、行业背景 随着视频汇聚需求的不断提升&#xff0c;智慧校园、智慧园区等项目中需要将各分支机构的视频统一汇聚到总部&#xff0c;进行统一管控&#xff0c;要满足在监控内部局域网、互联网、VPN网络等TCP/IP环境下&#xff0c;为用户提供低成本、高扩展、强兼容、高性能的…

【SpringBoot】基于SSM框架的题库系统的设计与实现

文章结构 课题&#xff1a;一、项目简介主要功能技术选型 二、 模块介绍学生端教师端(一)考试管理(二)试题管理(三)学生成绩管理 管理员三、 B站项目演示地址 四、本项目其余相关博客 课题&#xff1a; 题库系统的设计与实现一、项目简介 简介&#xff1a;主要分为三个端&…

DAY38——动态规划

步骤&#xff1a; 确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组 题目一. 斐波那契数列 1. 确定dp数组以及下标的含义 dp[i]的定义为&#xff1a;第i个数的斐波那契数值是dp[i] 2. 确定递推公式 状态…

【Zookeeper】win安装随笔

目录 下载地址下载目标解压后目录结构配置文件配置文件详情伪分布式安装LinuxZooKeeper audit is disabled启动解决报错&#xff1a;SLF4J: Class path contains multiple SLF4J bindings. _ 下载地址 https://zookeeper.apache.org/releases.html 下载目标 记住选择带bin的…

一步一步学OAK之四:实现如何在低延迟下使用高分辨率视频

目录 Setup 1: 创建文件Setup 2: 安装依赖Setup 3: 导入需要的包Setup 4: 创建pipelineSetup 5: 创建节点Setup 6: 设置节点的属性和参数。Setup 7: 建立链接关系Setup 8: 连接设备并启动管道Setup 9: 创建与DepthAI设备通信的输入队列和输出队列Setup 10: 主循环获取视频帧显示…

【C++】定制删除器和特殊类设计(饿汉和懒汉~)

文章目录 定制删除器一、设计一个只能在堆上(或栈上)创建的类二、单例模式 1.饿汉模式2.懒汉模式总结 定制删除器 我们在上一篇文章中讲到了智能指针&#xff0c;相信大家都会有一个问题&#xff0c;智能指针该如何辨别我们的资源是用new int开辟的还是new int[]开辟的呢&…

html5前端学习2

一篇思维题题解&#xff1a; 第五周任务 [Cloned] - Virtual Judge (vjudge.net) http://t.csdn.cn/SIHdM 快捷键&#xff1a; CtrlAltDown 向下选取 CtrlAltUp 向上选取&#xff08;会出现多个光标&#xff0c;可以同时输入&#xff09; CtrlEnter …

【Java】Java核心 78:Git 教程(1)Git 概述

文章目录 01.GIT概述目标内容小结 02.GIT相关概念目标内容小结 01.GIT概述 Git是一个分布式版本控制系统&#xff0c;常用于协同开发和版本管理的工具。它可以跟踪文件的修改、记录历史版本&#xff0c;并支持多人协同工作。通过Git&#xff0c;你可以轻松地创建和切换分支、合…

去除小程序home按钮

前言&#xff1a;当我们未登录时&#xff0c;会跳转到登录页&#xff0c;但是这时候登录页左上角会有一个跳转到首页的按钮&#xff0c;但是&#xff0c;我们不希望出现这个回到首页的按钮 去除前&#xff1a; 去除后&#xff1a; 代码&#xff1a; onShow() {wx.hideHomeBut…

从零搭建一台基于ROS的自动驾驶车-----2.运动控制

系列文章目录 北科天绘 16线3维激光雷达开发教程 基于Rplidar二维雷达使用Hector_SLAM算法在ROS中建图 Nvidia Jetson Nano学习笔记–串口通信 Nvidia Jetson Nano学习笔记–使用C语言实现GPIO 输入输出 Autolabor ROS机器人教程 从零搭建一台基于ROS的自动驾驶车-----1.整体介…