然而data_preprocess.py并不适合初学者进行阅读,因为其兼容了太多东西,很简单的一些功能硬是写复杂了,那么就围绕这个模型,给各位缕缕校准数据到底要准备啥

首先要搞清楚,我们要准备的校准数据是什么样的: 校准数据要将图像数据按照目标尺寸目标颜色(rgb or bgr等)目标排布(CHW or HWC) 进行存储
那么下面,带着这些问题进行处理,先构建一个基本处理流程:

  1. 加载一个文件夹下的所有图像地址信息。图像目录为/open_explorer/ddk/samples/ai_toolchain/horizon_model_convert_sample/01_common/calibration_data/coco
  2. 对每个图像按照校准格式进行输出。从prototxt我们知道图像的尺寸为416x416,从./03_build.sh调用的yaml文件可知图像输入格式为rgb,数据排布为CHW
  3. 将转换后的图像利用numpy.tofile函数存到目标文件夹下/open_explorer/ddk/samples/ai_toolchain/horizon_model_convert_sample/04_detection/02_yolov3_darknet53/mapper/calibration_data。你在哪转换的,就要在哪个目录存校准数据文件夹calibration_data

下面,开始写我们自己的Python代码,每个步骤都写了注释,各位可以直接理解。

# prepare_calibration_data.py
import os
import cv2
import numpy as np

src_root = '/open_explorer/ddk/samples/ai_toolchain/horizon_model_convert_sample/01_common/calibration_data/coco'
cal_img_num = 100  # 想要的图像个数
dst_root = '/open_explorer/ddk/samples/ai_toolchain/horizon_model_convert_sample/04_detection/02_yolov3_darknet53/mapper/calibration_data'


## 1. 从原始图像文件夹中获取100个图像作为校准数据
num_count = 0
img_names = []
for src_name in sorted(os.listdir(src_root)):
    if num_count > cal_img_num:
        break
    img_names.append(src_name)
    num_count += 1

# 检查目标文件夹是否存在,如果不存在就创建
if not os.path.exists(dst_root):
    os.system('mkdir {0}'.format(dst_root))

## 2 为每个图像转换
# 参考了OE中/open_explorer/ddk/samples/ai_toolchain/horizon_model_convert_sample/01_common/python/data/下的相关代码
# 转换代码写的很棒,很智能,考虑它并不是官方python包,所以我打算换一种写法

## 2.1 定义图像缩放函数,返回为np.float32
# 图像缩放为目标尺寸(W, H)
# 值得注意的是,缩放时候,长宽等比例缩放,空白的区域填充颜色为pad_value, 默认127
def imequalresize(img, target_size, pad_value=127.):
    target_w, target_h = target_size
    image_h, image_w = img.shape[:2]
    img_channel = 3 if len(img.shape) > 2 else 1

    # 确定缩放尺度,确定最终目标尺寸
    scale = min(target_w * 1.0 / image_w, target_h * 1.0 / image_h)
    new_h, new_w = int(scale * image_h), int(scale * image_w)

    resize_image = cv2.resize(img, (new_w, new_h))

    # 准备待返回图像
    pad_image = np.full(shape=[target_h, target_w, img_channel], fill_value=pad_value)

    # 将图像resize_image放置在pad_image的中间
    dw, dh = (target_w - new_w) // 2, (target_h - new_h) // 2
    pad_image[dh:new_h + dh, dw:new_w + dw, :] = resize_image

    return pad_image

## 2.2 开始转换
for each_imgname in img_names:
    img_path = os.path.join(src_root, each_imgname)

    img = cv2.imread(img_path)  # BRG, HWC
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # RGB, HWC
    img = imequalresize(img, (416, 416))
    img = np.transpose(img, (2, 0, 1))  # RGB, CHW

    # 将图像保存到目标文件夹下
    dst_path = os.path.join(dst_root, each_imgname + '.rgbchw')
    print("write:%s" % dst_path)
    # 图像加载默认就是uint8,但是不加这个astype的话转换模型就会出错
    # 转换模型时候,加载进来的数据竟然是float64,不清楚内部是怎么加载的。
    img.astype(np.uint8).tofile(dst_path) 

print('finish')
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">

执行这些代码之后,在目录下,就生成了我们的校准数据
在这里插入图片描述

(ii) 原理解读:转换配置

模型转换的核心在于配置目标的yaml文件,官方也提供了一个yolov3_darknet53_config.yaml可供用户直接试用,每个参数都给了注释,我能感受到开发者的诚意。然而模型转换的配置文件参数太多,如果想改参数都不知道如何下手。

本节目的是引导各位快速上手,因此一些参数我暂时不解释意义,用默认即可。该模板可将待配置的30多个参数压缩到9个参数,方便各位快速的配置简单模型。本yaml模板适用于的模型具有如下属性:

先复制这个模板到代码根目录,命名为"yolov3_simple.yaml",然后根据后面的思维导图进行配置具体参数。

model_parameters:
  # [待配置参数],见思维导图"模型参数组"部分
  prototxt: '***.prototxt'
  caffe_model: '****.caffemodel'
  onnx_model: '****.onnx'
  output_model_file_prefix: 'mobilenetv1'
  
  # 默认参数,暂不需要理解
  march: 'bernoulli2'

input_parameters:
  # [待配置参数],见思维导图"输入信息参数组/原始模型参数"部分
  input_type_train: 'bgr'
  input_layout_train: 'NCHW'

  # [待配置参数],见思维导图"输入信息参数组/转换后模型参数"部分
  input_type_rt: 'yuv444'

  # [待配置参数],见思维导图"输入信息参数组/输入数据预处理"部分
  norm_type: 'data_mean_and_scale'
  mean_value: '103.94 116.78 123.68'
  scale_value: '0.017'
  
  # 默认参数,暂不需要理解
  input_layout_rt: 'NHWC'
  

# 校准参数组,全部默认
calibration_parameters:
  cal_data_dir: './calibration_data'
  calibration_type: 'max'
  max_percentile: 0.9999

# 编译参数组,全部默认
compiler_parameters:
  compile_mode: 'latency'
  optimize_level: 'O3'
  debug: False # 别看官网写的可选,实际上不写这个出bug
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

思维导图如下所示,带着这个图,请各位耐心地跟我一步步配置,仅需要配置9个即可
在这里插入图片描述

最终,我们的yaml文件内容如下所示:

model_parameters:
  prototxt: '../../../01_common/model_zoo/mapper/detection/yolov3_darknet53/yolov3_transposed.prototxt'
  caffe_model: '../../../01_common/model_zoo/mapper/detection/yolov3_darknet53/yolov3.caffemodel'
  output_model_file_prefix: 'yolov3_selfyaml'
  march: 'bernoulli2'
input_parameters:
  input_type_train: 'rgb'
  input_layout_train: 'NCHW'
  input_type_rt: 'nv12'
  norm_type: 'data_scale'
  scale_value: 0.003921568627451
  input_layout_rt: 'NHWC'
calibration_parameters:
  cal_data_dir: './calibration_data'
  calibration_type: 'max'
  max_percentile: 0.9999
compiler_parameters:
  compile_mode: 'latency'
  optimize_level: 'O3'
  debug: False
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

之后,用我们亲手准备的校准数据和配置的轻量yaml进行模型转换,在控制台输入指令hb_mapper makertbin --config yolov3_simple.yaml --model-type caffe

2.1.4 模型推理

在官方给的demo中,04_inference.sh可以直接调用执行好的模型进行推理,但是为了我觉得这种方案对于未来要如何部署自己的模型是无意义的。因此我阅读了官方推理的demo之后,自己写个完整的推理过程。

模型推理流程主要可以分为以下三个步骤:

使用的测试图像路径为/open_explorer/ddk/samples/ai_toolchain/horizon_model_convert_sample/01_common/test_data/det_images/kite.jpg

在上一节中,模型转换后有三个关键文件:

下面我给出推理一张图像的相关代码,其中我把图像格式转换,以及yolo后处理的细节封装在一个包里,相关的代码已经放在百度云(提取码:0a09 )里供大家参考。
在这里插入图片描述
下面是inference_model.py的代码细节,在每个关键过程中都给出了相关的注释。
下图是yolov3网络的输出,从代码可以获知模型会输出三层,每层的维度为(1, 13, 13, 255) (1, 26, 26, 255) (1, 52, 52, 255),对着下图,可以很容易对应的是网络的哪一层。

import numpy as np
import cv2
import os
from horizon_tc_ui import HB_ONNXRuntime
from bputools.format_convert import imequalresize, bgr2nv12_opencv, nv122yuv444
from bputools.yolo_postproc import modelout2predbbox, recover_boxes, nms, draw_bboxs

modelpath_prefix = '/open_explorer/ddk/samples/ai_toolchain/horizon_model_convert_sample'

# img_path 图像完整路径
img_path = os.path.join(modelpath_prefix, '01_common/test_data/det_images/kite.jpg')
# model_path 量化模型完整路径
model_root = os.path.join(modelpath_prefix, '04_detection/02_yolov3_darknet53/mapper/model_output')
model_path = os.path.join(model_root, 'yolov3_selfyaml_quantized_model.onnx')

# 1. 加载模型,获取所需输出HW
sess = HB_ONNXRuntime(model_file=model_path)
sess.set_dim_param(0, 0, '?')
model_h, model_w = sess.get_hw()

# 2 加载图像,根据前面模型,转换后的模型是以NV12作为输入的
# 但在OE验证的时候,需要将图像再由NV12转为YUV444
imgOri = cv2.imread(img_path)
img = imequalresize(imgOri, (model_w, model_h))
nv12 = bgr2nv12_opencv(img)
yuv444 = nv122yuv444(nv12, [model_w, model_h])

# 3 模型推理
input_name = sess.input_names[0]
output_name = sess.output_names
output = sess.run(output_name, {input_name: np.array([yuv444])}, input_offset=128)
print(output_name)
print(output[0].shape, output[1].shape, output[2].shape)
# ['layer82-conv-transposed', 'layer94-conv-transposed', 'layer106-conv-transposed']
# (1, 13, 13, 255) (1, 26, 26, 255) (1, 52, 52, 255)

# 4 检测结果后处理
# 由output恢复416*416模式下的目标框
pred_bbox = modelout2predbbox(output)
# 将目标框恢复到原始分辨率
bboxes = recover_boxes(pred_bbox, (imgOri.shape[0], imgOri.shape[1]),
                       input_shape=(model_h, model_w), score_threshold=0.3)
# 对检测出的框进行非极大值抑制,抑制后得到的框就是最终检测框
nms_bboxes = nms(bboxes, 0.45)
print("detected item num: {0}".format(len(nms_bboxes)))

# 绘制检测框
draw_bboxs(imgOri, nms_bboxes)
cv2.imwrite('detected.png', imgOri)
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

运行之后,我们可以得到检测结果。

2.1.5 上板运行

我们将下图所示的一些文件拖到旭日X3派开发板中,注意inference_model_bpu.py跟docker中是有微小的改动的。

相关代码,放在百度云的test_yolov3文件夹下。

注意,在执行前要安装一些包sudo pip3 install EasyDict pycocotools,切记要加sudo,这样安装的路径不是用户目录,在运行BPU模型时候,也是必须要加sudo的

inference_model_bpu.py的源码如下所示,与在docker中不同,nv12不需要再转为yuv444了,模型的运行也有一些差别。而后处理几乎没有变化。

import numpy as np
import cv2
import os
from hobot_dnn import pyeasy_dnn as dnn
from bputools.format_convert import imequalresize, bgr2nv12_opencv, nv122yuv444
from bputools.yolo_postproc import modelout2predbbox, recover_boxes, nms, draw_bboxs

def get_hw(pro):
    if pro.layout == "NCHW":
        return pro.shape[2], pro.shape[3]
    else:
        return pro.shape[1], pro.shape[2]

modelpath_prefix = ''

# img_path 图像完整路径
img_path = 'COCO_val2014_000000181265.jpg'
# model_path 量化模型完整路径
model_path = 'yolov3_selfyaml.bin'

# 1. 加载模型,获取所需输出HW
models = dnn.load(model_path)
model_h, model_w = get_hw(models[0].inputs[0].properties)

# 2 加载图像,根据前面模型,转换后的模型是以NV12作为输入的
# 但在OE验证的时候,需要将图像再由NV12转为YUV444
imgOri = cv2.imread(img_path)
img = imequalresize(imgOri, (model_w, model_h))
nv12 = bgr2nv12_opencv(img)

# 3 模型推理
t1 = cv2.getTickCount()
outputs = models[0].forward(nv12)
t2 = cv2.getTickCount()
outputs = (outputs[0].buffer, outputs[1].buffer, outputs[2].buffer)
print(outputs[0].shape, outputs[1].shape, outputs[2].shape)
# (1, 13, 13, 255) (1, 26, 26, 255) (1, 52, 52, 255)
print('time consumption {0} ms'.format((t2-t1)*1000/cv2.getTickFrequency()))

# 4 检测结果后处理
# 由output恢复416*416模式下的目标框
pred_bbox = modelout2predbbox(outputs)
# 将目标框恢复到原始分辨率
bboxes = recover_boxes(pred_bbox, (imgOri.shape[0], imgOri.shape[1]),
                       input_shape=(model_h, model_w), score_threshold=0.3)
# 对检测出的框进行非极大值抑制,抑制后得到的框就是最终检测框
nms_bboxes = nms(bboxes, 0.45)
print("detected item num: {0}".format(len(nms_bboxes)))

# 绘制检测框
draw_bboxs(imgOri, nms_bboxes)
cv2.imwrite('detected.png', imgOri)
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

BPU上的检测结果如下图所示,推理耗时178ms,几乎赶上jetson TX2的性能了,开心。

2.1.6 小结

这里,对整个BPU部署流程我进行了一个梳理,如果按照官方教程,很简单,但我这里,想彻底完整地教会各位如何部署一个模型。

至少,校准数据、模型预处理、后处理、以及上板运行的代码都是我参考官方代码,一点点写出来的。我个人认为,这些代码能够让各位快速地理解这个过程的意义。

下面,以手部关键点检测网络,作为个测试,开始真正部署非官方的模型!!!!

2.2 手部关键点检测网络

手部关键点检测是做手势识别的一个关键过程,该代码基于Caffe,而且无自定义层,因此作为个引子,带领各位先初步使用BPU。

相关资料已经放在开头分享给各位的百度云里了(我把源码都放进去的目的是方便各位训练)

在转换BPU时,我们只需要对应的Prototxt和caffemodel文件,下面按照前面介绍的流程开始部署我们的模型。

2.2.1 模型准备

还记得在安装准备中,我们挂载了一个目录-v "D:\05 - 项目\01 - 旭日x3派\BPUCodes":/data/horizon_x3/codes吗,我们下载好代码后,按照如下方式放置相关文件,这时候我们就可以发现docker中也有这些文件啦。
在这里插入图片描述

2.2.2 验证模型

验证前,先将docker根目录切换到模型根目录下cd /data/horizon_x3/codes/HandKeypointDetection/hand/

前面已经介绍了,模型验证需要利用hb_mapper checker后面跟一堆参数来对模型进行配置。下面带各位来配置这些参数:

综上所述,在docker中需要输入如下指令来完成模型验证过程:

hb_mapper checker \
--model-type caffe \
--march bernoulli2 \
--proto pose_deploy.prototxt \
--model pose_iter_102000.caffemodel \
--input-shape image 1x3x368x368
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

输出结果如下所示,可以看到整个流程的转换状态以及每个节点是在BPU还是CPU上运行的。整个控制台的运行结果默认存在根目录的hb_mapper_checker.log中。
在这里插入图片描述

2.2.3 转换模型

与前面流程不同,这里我打算先配置yaml文件,再准备校准数据。

(i) 配置yaml文件

根据前文的思维导图,我们要进行配置。

最终,我们的yaml文件handpoint.yaml内容为:

model_parameters:
  prototxt: 'pose_deploy.prototxt'
  caffe_model: 'pose_iter_102000.caffemodel'
  output_model_file_prefix: 'handkpdet'
  march: 'bernoulli2'
input_parameters:
  input_type_train: 'bgr'
  input_layout_train: 'NCHW'
  input_type_rt: 'bgr'
  norm_type: 'data_scale'
  scale_value: '0.0039'
  input_layout_rt: 'NHWC'
calibration_parameters:
  cal_data_dir: './calibration_data'
  calibration_type: 'max'
  max_percentile: 0.9999
compiler_parameters:
  compile_mode: 'latency'
  optimize_level: 'O3'
  debug: False
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">
(ii) 准备校准数据

考虑到这个模型的输入只有一个,因此,准备校准数据部分的代码可以参考上一节的内容,需要修改的只有两个地方,原始数据地址,和颜色转换部分(取消了BGR转RGB的过程),数据集用的是FreiHAND_pub_v2_eval.zip,校准后的数据也放在了百度云里。

在这里插入图片描述

docker中,校准数据形式如下图所示,共计100张。
在这里插入图片描述

(iii) 开始转换

数据准备就绪,输入命令hb_mapper makertbin --config handpoint.yaml --model-type caffe开始转换我们的模型!
等待一段时间之后,模型转换成功,从结果可以看出来,模型的损失并不是很高!!感觉有戏,(☆▽☆)。
在这里插入图片描述

2.2.4 模型推理

由于该模型与前面的模型相似,都是以一张图像作为输入的,因此自己要补充的工作主要有两点:

在docker中推理的完整代码如下所示。

import numpy as np
import cv2
import os
from horizon_tc_ui import HB_ONNXRuntime
import copy

# img_path 图像完整路径
img_path = '/data/horizon_x3/codes/HandKeypointDetection/hand/FreiHAND_pub_v2_eval/evaluation/rgb/00000253.jpg'
# model_path 量化模型完整路径
model_path = '/data/horizon_x3/codes/HandKeypointDetection/hand/model_output/handkpdet_quantized_model.onnx'

# 1. 加载模型,获取所需输出HW
sess = HB_ONNXRuntime(model_file=model_path)
sess.set_dim_param(0, 0, '?')
model_h, model_w = sess.get_hw()

# 2 加载图像,根据前面yaml,量化后的模型以BGR NHWC形式输入
imgOri = cv2.imread(img_path)
img = cv2.resize(imgOri, (model_w, model_h))

# 3 模型推理
input_name = sess.input_names[0]
output_name = sess.output_names
output = sess.run(output_name, {input_name: np.array([img])}, input_offset=128)
print(output_name)
print(output[0].shape)
# ['net_output']
# (1, 22, 46, 46)

# 4 检测结果后处理
# 绘制关键点
nPoints = 22
threshold = 0.1
POSE_PAIRS = [[0, 1], [1, 2], [2, 3], [3, 4], [0, 5], [5, 6], [6, 7], [7, 8], [0, 9], [9, 10], [10, 11], [11, 12],
              [0, 13], [13, 14], [14, 15], [15, 16], [0, 17], [17, 18], [18, 19], [19, 20]]

imgh, imgw = imgOri.shape[:2]
points = []
imgkp = copy.deepcopy(imgOri)
for i in range(nPoints):
    probMap = output[0][0, i, :, :]
    probMap = cv2.resize(probMap, (imgw, imgh))
    minVal, prob, minLoc, point = cv2.minMaxLoc(probMap)

    if prob > threshold:
        cv2.circle(imgkp, (int(point[0]), int(point[1])), 8, (0, 255, 255), thickness=-1, lineType=cv2.FILLED)
        cv2.putText(imgkp, "{}".format(i), (int(point[0]), int(point[1])), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2,
                    lineType=cv2.LINE_AA)
        points.append((int(point[0]), int(point[1])))
    else:
        points.append(None)

# 绘制骨架
imgskeleton = copy.deepcopy(imgOri)
for pair in POSE_PAIRS:
    partA = pair[0]
    partB = pair[1]
    if points[partA] and points[partB]:
        cv2.line(imgskeleton, points[partA], points[partB], (0, 255, 255), 2)
        cv2.circle(imgskeleton, points[partA], 8, (0, 0, 255), thickness=-1, lineType=cv2.FILLED)
        cv2.circle(imgskeleton, points[partB], 8, (0, 0, 255), thickness=-1, lineType=cv2.FILLED)
# 保存关键点和骨架图
cv2.imwrite('handkeypoint.png', imgkp)
cv2.imwrite('imgskeleton.png', imgskeleton)
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

最终结果图如下图所示,感觉还蛮不错哦
在这里插入图片描述

2.2.5 上板运行

在开发板运行的程序与上述推理代码差异不大,注意好模型的输入数据格式即可,这里要注意,输出的outputs与docker中有差异,要做output = (outputs[0].buffer,)转换,这样可以直接兼容后面的后处理部分,进而生成结果图。

整个板端运行代码放在百度云中的test_handpose文件夹

import numpy as np
import cv2
import os
from hobot_dnn import pyeasy_dnn as dnn
import copy

def get_hw(pro):
    if pro.layout == "NCHW":
        return pro.shape[2], pro.shape[3]
    else:
        return pro.shape[1], pro.shape[2]

# img_path 图像完整路径
img_path = '20220806023323.jpg'
# model_path 量化模型完整路径
model_path = 'handkpdet.bin'

# 1. 加载模型,获取所需输出HW
models = dnn.load(model_path)
model_h, model_w = get_hw(models[0].inputs[0].properties)

# 2 加载图像,根据前面yaml,量化后的模型以BGR NHWC形式输入
imgOri = cv2.imread(img_path)
img = cv2.resize(imgOri, (model_w, model_h))

# 3 模型推理
t1 = cv2.getTickCount()
outputs = models[0].forward(img)
t2 = cv2.getTickCount()
output = (outputs[0].buffer,)
print(outputs[0].buffer.shape)
# (1, 22, 46, 46)
print('time consumption {0} ms'.format((t2-t1)*1000/cv2.getTickFrequency()))


# 4 检测结果后处理
# 绘制关键点
nPoints = 22
threshold = 0.1
POSE_PAIRS = [[0, 1], [1, 2], [2, 3], [3, 4], [0, 5], [5, 6], [6, 7], [7, 8], [0, 9], [9, 10], [10, 11], [11, 12],
              [0, 13], [13, 14], [14, 15], [15, 16], [0, 17], [17, 18], [18, 19], [19, 20]]

imgh, imgw = imgOri.shape[:2]
points = []
imgkp = copy.deepcopy(imgOri)
for i in range(nPoints):
    probMap = output[0][0, i, :, :]
    probMap = cv2.resize(probMap, (imgw, imgh))
    minVal, prob, minLoc, point = cv2.minMaxLoc(probMap)

    if prob > threshold:
        cv2.circle(imgkp, (int(point[0]), int(point[1])), 8, (0, 255, 255), thickness=-1, lineType=cv2.FILLED)
        cv2.putText(imgkp, "{}".format(i), (int(point[0]), int(point[1])), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2,
                    lineType=cv2.LINE_AA)
        points.append((int(point[0]), int(point[1])))
    else:
        points.append(None)

# 绘制骨架
imgskeleton = copy.deepcopy(imgOri)
for pair in POSE_PAIRS:
    partA = pair[0]
    partB = pair[1]
    if points[partA] and points[partB]:
        cv2.line(imgskeleton, points[partA], points[partB], (0, 255, 255), 2)
        cv2.circle(imgskeleton, points[partA], 8, (0, 0, 255), thickness=-1, lineType=cv2.FILLED)
        cv2.circle(imgskeleton, points[partB], 8, (0, 0, 255), thickness=-1, lineType=cv2.FILLED)
# 保存关键点和骨架图
cv2.imwrite('handkeypoint.png', imgkp)
cv2.imwrite('imgskeleton.png', imgskeleton)
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

我自己拍了两张图进行测试,第一排是晚上拍的,手指头有点串味哈哈,整体检测耗时在480ms左右,网络深度没有yolo高,也许是横向的特征比较多。
在这里插入图片描述

2.2.6 小结

BPU的学习,最难的就是第一步,成功捋清楚yolo的部署方式后,这个手部关键点检测按照这个教程就不到1个小时就搞定了。

三 总结

花费了将近两个月,总共耗时60多个小时,我终于把BPU的基础部分搞的差不多明白了,昨晚是一鼓作气搞到快凌晨3点,早上起来后赶紧把代码流程整理下,兴奋地都没睡好。

这是我耗时最多,周期最长的一个博客了,所以关于其中的优缺点,我要多多说一说。先列优点:

关于缺点,也是很明显,最致命的就是官方文档无法有效引导新手上手BPU,但我希望这个博客能够缓解这个问题,因为只要成功走了一次,之后的工作就会轻松很多。

BPU要降低学习成本,我个人认为非常重要。(我读书时候都不爱看书听课,还能专心下来看这么多文档?→_→)

data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/Zhaoxi_Li/article/details/125516265","extend1":"pc","ab":"new"}">>
注:本文转载自blog.csdn.net的小玺玺的文章"https://blog.csdn.net/Zhaoxi_Li/article/details/125516265"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接

评论记录:

未查询到任何数据!