手把手带你实现 ”在浏览器上进行目标检测“

3,036 阅读17分钟

注意该文章仅提供思路哈,最好不要使用文章内提供的数据集,另外距离当时写这篇文章的时间也比较久远了,现在最新版的yolo不确定是否有别的break change,请大家以yolo的最新版文档和api为准。

读完这篇文章,你将可以学习到如何在浏览器里进行”目标检测“!”目标检测“看似很难实现、无从下手,但其实也没有你想象的那么遥不可及啦,相反,现有的一些库与工具的出现,让它的实现变得越来越简单。下面我将从“收集图片”开始,通过配图和文字(一些我自己的理解),一步步带你训练出轻量、高准确度的模型文件,最终成功在浏览器里识别出我们自定义的目标。

这篇文章几乎不涉及机器学习原理,什么神经网络、卷积等,你在这里是看不到的,讲道理自己对这些原理 & 算法是不太了解的,是不敢发表自己的见解的。

本文主要以实战为主,自己的一些使用理解为辅,训练时采用最新的预置模型来训练,更贴合轻量级使用场景。当你走完整个流程之后,再去学习机器学习相关的原理知识,可以更好的帮你理解。

为何写这篇文章

大概在上周,自己为了完成一个效果,去学习并成功实践了在浏览器端进行目标检测,通过各方查阅资料、踩坑,终于趟出了一条明路,过程中也苦于没有一篇完整的博客来向没有相关经验的小白来介绍如何在浏览器中进行目标检测,那为何自己不写一篇呢?所以就有了写这篇文章的动力

自己之前想完成的效果是如何在一个大图中,找到二维码的位置,然后截取到对应的二维码图像,进行解析,最终将解析结果回显到对应的位置上。

最终的效果图如下:

finally.png

在此也推荐一下最终完成的浏览器插件!!!

下面是介绍它的一篇文章,有兴趣的话可以读读看

机器学习 + 浏览器插件 = 东半球最好用的二维(条形)码识别器

先明确一下你即将要做的事情

我们接下来要做的是在浏览器中进行目标检测,注意了,关键词是“浏览器”“目标检测”

根据以上关键词,首先便引出了本文的重头戏之一:Tensorflow.js

浏览器进行目标检测,是借助tensorflow库来实现的,它有很多语言版本(python/java/...),因为我们本次使用浏览器作为载体,所以我们采用js版本,以下是它的js版本的介绍。

image.png

使用Tensorflow.js,你可以做以下事情:

  • 浏览器环境中训练你的模型(区别于其他比如python等)并可视化训练过程。
  • 部署训练好的模型,进行后续操作(识别预测)。
  • ...

在这里,我们就是使用第二种方式:使用其他语言训练好的模型,在浏览器端加载模型并进行识别

Q: 为什么使用其他语言进行训练呢?我总结大致有两个原因吧:
1、其他平台比如python,训练模型的库(算法)更丰富;
2、训练的速度更快一些(相比于在浏览器中)。

使用一个图来总结一下接下来的流程的话,大致如下:

image.png

OK,看到这里,如果你觉得真的有兴趣的话,那就可以继续看下去了!

后面我们以二维码识别为例,逐步讲解,相信你也可以跟着流程做出你的第一个浏览器端目标检测程序

步骤一:收集图片

image.png

这一步,我分享一下我是怎么收集图片 & 整理图片的 (一些小经验):

1、收集图片

收集图片阶段,我是怎么简单怎么来的。

打开百度,搜索二维码,点击搜图片,就可以搜到无数个二维码图片了:

image.png

因为这一屏的二维码实在是有点多,我的解决的主要场景是一个页面有1-10个二维码就很足够了,那么这么多图其实也没必要,所以就进入每个图的详情页,依次截屏,放到图片文件夹中,就像下图。

image.png

最终我整理了2-300张图片,这个过程很枯燥,大家也要多去多用心去准备这些图片,要相信付出总有收获的,图片收集的质量越高/场景越多样,后面训练、识别的效果越好

我大概准备了250张图片:

image.png

image.png

2、统一命名

在进行下一步标注之前,希望你可以先给你的图片文件们统一命个名,不然看起来太乱了,就像下面这样,一眼看不懂文件名是什么意思:

image.png

你可以通过MacOS自带的批量操作功能完成这件事,它在这里:

image.png

重新命名后大致是这样,清爽多了:

image.png

OK,当你完成该步骤的时候,你就可以进行下一步了,去给你的图片进行标注!

步骤二:图片标注

image.png

所谓的图片标注,就是需要你来手动处理每一张图片,使用工具标出来你认为正确的信息,比如该案例是二维码,就是需要你在每一张图片中标出来哪里是二维码。

标注的意义就是告诉计算机,这个图片中,我圈的这里就是二维码,你在训练的时候要将你预测的结果和我标注的结果进行比对,不对的话就要朝着这个方向调整。

1、安装工具

图片标注我采用的工具是:labelImg,下面是它的github地址:

GitHub - tzutalin/labelImg: 🖍️ LabelImg is a graphical image annotation tool and label object bounding boxes in images

按文档中的README安装好之后,就可以通过命令启动它了!

$ python3 labelImg.py

启动后的界面如下:

image.png

首先选择标注结果格式,这里选择PascalVOC(文件是.xml格式),个人认为.xml格式的结果文件便于清晰的看到标注结果,也方便后面使用脚本划分结果集时使用。

下图是同一份标注的.xml.txt的结果对比:

image.png

明显看到左侧的信息可以一眼读出来,右边会更抽象,不便于理解,但是便于计算机读取。

2、开始标注

导入图片后你就可以使用快捷键w愉快的进行标注了,标注后,会提示让你输入类别名称,这里我们就使用code作为二维码分类:

image.png

全部标注后,你将得到以下密密麻麻的文件。

image.png

至此,图片标注结束,下面我们将进行训练集的划分

步骤三:划分训练集、验证集

image.png

1、将xml格式的结果集转化成txt格式

接下来,我们需要一段脚本来划分训练集验证集,该脚本主要做了两件事:(1).xml文件转.txt文件 (2)按百分比将图片 + 标注结果划分为训练集、验证集

训练集:拿给训练算法的用于训练的集合
验证集:拿给训练算法用于验证训练结果的集合

在这里你可能会有疑问,为什么要将.xml转成.txt呢?如果一开始就需要.txt的话,那么上面我们特意将结果选择为.xml格式是为了什么?

大概有下面几种考虑吧:

  • 后续使用的训练算法yolo是使用.txt格式的标注结果文件的。

  • 使用.xml格式可以看一下标注过程到底做了什么、结果文件中包含什么,因为.txt中的信息几乎不可看。

  • 网上分享的很多训练集大部分都是.xml格式的,后续你在使用其他分享的标注集的时候,完全可以使用以下操作来进行划分。

清楚原因后,下面我们开始划分吧!

首先,需要整理一下你的目录为以下结构(建议这么做!),这样你可以不需要大改后续提供的转化脚本,否则你需要改一下下面提供的python脚本代码(将脚本中的路径换成你的路径,要对齐)。

image.png

说明:图片文件(250张)放在JPEGImages中,XML文件(250个)放在Annotations

执行划分逻辑的python脚本如下(保存到你的本地,需与VOCdevkit文件夹平级):

import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
import random
from shutil import copyfile
 
classes = ["code"] // !!! 需要关注这里,这里是你所有标注的class1个或多个,我们本次只有一个目标就是二维码,所以这里只写code(code就是之前在标注的时候给的名字)
 
TRAIN_RATIO = 70  // !!! 需要关注这里,这里是划分训练集的百分比(相对于验证集来说),当这里是70的时候,代表训练集是250 * 70%,验证集是250 * 30%(划分你的标注文件)
 
def clear_hidden_files(path):
    dir_list = os.listdir(path)
    for i in dir_list:
        abspath = os.path.join(os.path.abspath(path), i)
        if os.path.isfile(abspath):
            if i.startswith("._"):
                os.remove(abspath)
        else:
            clear_hidden_files(abspath)
 
def convert(size, box):
    dw = 1./size[0]
    dh = 1./size[1]
    x = (box[0] + box[1])/2.0
    y = (box[2] + box[3])/2.0
    w = box[1] - box[0]
    h = box[3] - box[2]
    x = x*dw
    w = w*dw
    y = y*dh
    h = h*dh
    return (x,y,w,h)
 
// 将 xml 转 txt
def convert_annotation(image_id):
    in_file = open('VOCdevkit/VOC2007/Annotations/%s.xml' %image_id)
    out_file = open('VOCdevkit/VOC2007/YOLOLabels/%s.txt' %image_id, 'w')
    tree=ET.parse(in_file)
    root = tree.getroot()
    size = root.find('size')
    w = int(size.find('width').text)
    h = int(size.find('height').text)
 
    for obj in root.iter('object'):
        difficult = obj.find('difficult').text
        cls = obj.find('name').text
        if cls not in classes or int(difficult) == 1:
            continue
        cls_id = classes.index(cls)
        xmlbox = obj.find('bndbox')
        b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))
        bb = convert((w,h), b)
        out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
    in_file.close()
    out_file.close()
 
wd = os.getcwd()
wd = os.getcwd()
data_base_dir = os.path.join(wd, "VOCdevkit/")
if not os.path.isdir(data_base_dir):
    os.mkdir(data_base_dir)
work_sapce_dir = os.path.join(data_base_dir, "VOC2007/")
if not os.path.isdir(work_sapce_dir):
    os.mkdir(work_sapce_dir)
annotation_dir = os.path.join(work_sapce_dir, "Annotations/")
if not os.path.isdir(annotation_dir):
        os.mkdir(annotation_dir)
clear_hidden_files(annotation_dir)
image_dir = os.path.join(work_sapce_dir, "JPEGImages/")
if not os.path.isdir(image_dir):
        os.mkdir(image_dir)
clear_hidden_files(image_dir)
yolo_labels_dir = os.path.join(work_sapce_dir, "YOLOLabels/")
if not os.path.isdir(yolo_labels_dir):
        os.mkdir(yolo_labels_dir)
clear_hidden_files(yolo_labels_dir)
yolov5_images_dir = os.path.join(data_base_dir, "images/")
if not os.path.isdir(yolov5_images_dir):
        os.mkdir(yolov5_images_dir)
clear_hidden_files(yolov5_images_dir)
yolov5_labels_dir = os.path.join(data_base_dir, "labels/")
if not os.path.isdir(yolov5_labels_dir):
        os.mkdir(yolov5_labels_dir)
clear_hidden_files(yolov5_labels_dir)
yolov5_images_train_dir = os.path.join(yolov5_images_dir, "train/")
if not os.path.isdir(yolov5_images_train_dir):
        os.mkdir(yolov5_images_train_dir)
clear_hidden_files(yolov5_images_train_dir)
yolov5_images_test_dir = os.path.join(yolov5_images_dir, "val/")
if not os.path.isdir(yolov5_images_test_dir):
        os.mkdir(yolov5_images_test_dir)
clear_hidden_files(yolov5_images_test_dir)
yolov5_labels_train_dir = os.path.join(yolov5_labels_dir, "train/")
if not os.path.isdir(yolov5_labels_train_dir):
        os.mkdir(yolov5_labels_train_dir)
clear_hidden_files(yolov5_labels_train_dir)
yolov5_labels_test_dir = os.path.join(yolov5_labels_dir, "val/")
if not os.path.isdir(yolov5_labels_test_dir):
        os.mkdir(yolov5_labels_test_dir)
clear_hidden_files(yolov5_labels_test_dir)
 
train_file = open(os.path.join(wd, "yolov5_train.txt"), 'w')
test_file = open(os.path.join(wd, "yolov5_val.txt"), 'w')
train_file.close()
test_file.close()
train_file = open(os.path.join(wd, "yolov5_train.txt"), 'a')
test_file = open(os.path.join(wd, "yolov5_val.txt"), 'a')
list_imgs = os.listdir(image_dir) # list image files
prob = random.randint(1, 100)
print("Probability: %d" % prob)
for i in range(0,len(list_imgs)):
    path = os.path.join(image_dir,list_imgs[i])
    if os.path.isfile(path):
        image_path = image_dir + list_imgs[i]
        voc_path = list_imgs[i]
        (nameWithoutExtention, extention) = os.path.splitext(os.path.basename(image_path))
        (voc_nameWithoutExtention, voc_extention) = os.path.splitext(os.path.basename(voc_path))
        annotation_name = nameWithoutExtention + '.xml'
        annotation_path = os.path.join(annotation_dir, annotation_name)
        label_name = nameWithoutExtention + '.txt'
        label_path = os.path.join(yolo_labels_dir, label_name)
    prob = random.randint(1, 100)
    print("Probability: %d" % prob)
    if(prob < TRAIN_RATIO): # train dataset
        if os.path.exists(annotation_path):
            train_file.write(image_path + '\n')
            convert_annotation(nameWithoutExtention) # convert label
            copyfile(image_path, yolov5_images_train_dir + voc_path)
            copyfile(label_path, yolov5_labels_train_dir + label_name)
    else: # test dataset
        if os.path.exists(annotation_path):
            test_file.write(image_path + '\n')
            convert_annotation(nameWithoutExtention) # convert label
            copyfile(image_path, yolov5_images_test_dir + voc_path)
            copyfile(label_path, yolov5_labels_test_dir + label_name)
train_file.close()
test_file.close()

将上面代码保存到你本地,将上面脚本注释中你需要关注的地方改好,并按照结构放置好你的文件之后(如果不按照上面给到的文件夹结构放置你的文件,那么你需要修改脚本中相关涉及到的路径),执行以下命令:

$ python3 transform-xml2txt.py

有问题解决问题,没问题你将得到如下目录:

image.png

VOCdevkit目录下生成imageslabels文件夹,文件夹下分别生成了train文件夹和val文件夹,里面分别保存着训练集的照片和txt格式的标签,还有验证集的照片和txt格式的标签。images文件夹和labels文件夹就是训练yolo模型所需的训练集和验证集。在VOCdevkit/VOC2007目录下还生成了一个YOLOLabels文件夹,里面存放着所有的txt格式的标签文件。

至此,你的训练集、验证集就划分好了,下面准备打包一下你的训练集吧!

2、小声明

在这里需要感谢”炮哥“,以上划分训练集、验证集的部分图片、脚本是学习自该同学

文章:手把手教你搭建自己的YOLOv5目标检测平台

视频:www.bilibili.com/video/BV1f4…

文章和视频都很不错,以示感谢!如果你对本节有疑问,可以仔细阅读该文章。

步骤四:打包训练集

这里的打包不是真正意义上的打包,而是使用一个骚操作来把你的训练集文件快速导入另一个平台(Colaboratory)来进行训练。

不卖关子啦,这里使用的骚操作就是将刚才的文件夹初始化为git仓库,并上传到giteegithub等远程库中,最后在Colaboratory上使用git拉下来即可,这样一来可以保存你的训练集,二来可以快速更新你的训练集,因为Colaboratory的文件访问是谷歌云盘,云盘上传下载巨巨巨慢,更别提200+图片 & 文件了,不使用git的话,最少得1-2小时才能上传好。

如何初始化git并上传到远程仓库,这里就不做展开描述了,如果不会使用git的话,实属不该哈。

如下图,我已经初始化git & 上传远程仓库完毕:

image.png

步骤五:选择训练算法

目标检测算法有很多种(我了解到的):R-CNNFast-RCNNFaster_RCNNYOLO...

其实到这一步之前,我们已经交代过了,我们将会采用yolo来作为我们的训练算法,为什么采用yolo呢?有两点吧:配置简单 + 速度够快

官网:docs.ultralytics.com/

image.png

官网比较低调的介绍自己:由于其速度和准确性,YOLO 算法是最著名的目标检测算法之一

注:其实没有深度对比过以上几种算法的优劣,我只使用yolo来训练过模型,并且效果是非常符合预期的(不需要太多的配置与超参数修改、部署后识别速度都能在瞬间完成),大家有其他更好的推荐,可以评论下方,我可以去尝试下更好的替代方案。

步骤六:选择训练平台

image.png

在这里,你将有两种方式来训练你的模型:使用你自己的电脑 or 使用云平台。

我没有使用自己电脑来训练,因为使用的是笔记本,没有外接GPU,所以干脆直接放弃在自己电脑上训练,采取云平台来训练。

这里采用的是谷歌(牛批)的 Colaboratory,其提供完善的python环境 + GPU,敲重点!免费的!你好像可以一直免费使用(科学使用,不浪费,不使用的时候需要立马断开链接),我是因为使用姿势有问题,被锁了24小时,就买了它的pro模式,不过平台整体使用感受还是相当不错的。

下面是平台截图:

image.png

步骤七:配置训练平台环境

1、配置Colaboratory运行时模式

你需要配置你的运行时为GPU模式:

image.png

image.png

2、拉取相关文件到平台

(1)首先你需要先配置好你的git参数,登录上你的git

!git config --global user.email "your email" 
!git config --global user.name "your name"

(2)挂载你的谷歌云盘到 Colaboratory

import os from google.colab 
import drive 
drive.mount('/content/drive') 
path = "/content/drive/My Drive" 
os.chdir(path) 
os.listdir(path)

(3)创建 & 进入对应目录,我本次使用的目录是新创建的/learn-for-book/

(4)拉取你的图片、标注文件到 Colaboratory 所挂载的云盘上

!git clone https://gitee.com/xicunyang/learn-image.git

(5)拉取yolov5源码Colaboratory 所挂载的云盘上(和图片文件平级即可)

!git clone https://github.com/ultralytics/yolov5.git

(6)安装依赖

!pip install -r requirements.txt

(7)最后检查一下所需的资源是否拉下来了

image.png

OK,如果你能得到目前的效果,接下来,就可以开始调整超参数了!

步骤八:调整超参数

image.png

在调整超参数前,我们先做一些前置工作:验证环境是否正确、使用yolo识别一张图片。

1、验证环境正确

!python -V

image.png

import torch
print(torch.cuda.is_available())
print(torch.backends.cudnn.is_available())

image.png

2、使用yolo官方示例权重,检测人像

我们再来验证一下你下载的yolo源码是不是可以用,使用yolo自带的detect.py脚本 + 预置权重来检测一张人像图,看看是不是可以正确检测出来。

1、上传一张带人像的图片,到yolov5目录下

person.jpeg

image.png

2、在yolov5目录下,运行检测脚本,--source的值是我们上传的图片路径

!python detect.py --source pic/person.jpeg

image.png

可以看到,检测结果保存在了runs/detect/exp目录下

image.png

我们吧图片下载到本地后,可以看到,相关信息已经被识别出来,并成功标注到了对应位置

person (1).jpeg

此时,你可能完成了你人生中第一张机器识别的图片,接着,我们再接再厉,开始准备训练我们自定义的模型啦!

3、了解什么是预置权重文件

这里先展开讲一下什么是预置权重文件

一般为了缩短训练时间,并且可以达到预期的识别精度,我们需要选择预置权重文件来进行网络的训练。

image.png

可以看到,yolo提供了一些适合不同场景的预置权重文件,从左往右会越来越复杂,相应训练的时长会更长,最终生成的权重文件也会越来越大,因为我们是一个轻量级的使用场景,当然是越快越好,且二维码的特征也比较清晰,识别难度不会很大,所以在这里我们选择 YOLOv5n !

是不是没有看到这个文件,那是因为在202112月份yolo才刚刚推出了tag v6,其中就包含这个v5n(6),其比v5s还要轻便,这次更新,一共带来了两个让人振奋的功能:

(1)提供了更小巧的weights文件:YOLOV5n,使得训练出来的权重体积比YOLOV5s缩小75%,更适合移动端使用。

(2)提供export功能,可以直接将yolo训练后的权重导出成tensorflow.js所识别的权重文件!

image.png

4、选择预置权重文件

经过上面的简单了解,这里我们最终选择yolov5n6.pt,可以看到以下官方的对比,发现v5n6AP(平均准确度)上是要优于v5n的。

image.png

权重下载地址 这里下载对应的文件,并上传到对应的目录下 yolov5/weights/

image.png

5、调整超参数

在这一步上,我们主要是调整yolo暴露给我们的一些参数,以达到我们训练自定义模型的目的。

(1)修改 data/VOC.yaml 文件

image.png

  • train: 训练集路径
  • val: 验证集路径
  • nc: 类型数量
  • names: 类型名称

(2)修改 models/yolov5n.yaml

image.png

  • nc: 类型数量

(3)修改 train.py

image.png

  • --weights: 你上传的yolov5n6.pt路径
  • --cfg: 刚才修改的yolov5n.yaml路径
  • --data: 刚才修改的VOC.yaml路径

其余的一些参数介绍,你可以根据你自己的情况来动态配置:

 opt模型主要参数解析:
    --weights:初始化的权重文件的路径地址
    --cfg:模型yaml文件的路径地址
    --data:数据yaml文件的路径地址
    --hyp:超参数文件路径地址
    --epochs:训练轮次
    --batch-size:喂入批次文件的多少
    --img-size:输入图片尺寸
    --rect:是否采用矩形训练,默认False
    --resume:接着打断训练上次的结果接着训练
    --nosave:不保存模型,默认False
    --notest:不进行test,默认False
    --noautoanchor:不自动调整anchor,默认False
    --evolve:是否进行超参数进化,默认False
    --bucket:谷歌云盘bucket,一般不会用到
    --cache-images:是否提前缓存图片到内存,以加快训练速度,默认False
    --image-weights:使用加权图像选择进行训练
    --device:训练的设备,cpu;0(表示一个gpu设备cuda:0);0,1,2,3(多个gpu设备)
    --multi-scale:是否进行多尺度训练,默认False
    --single-cls:数据集是否只有一个类别,默认False
    --adam:是否使用adam优化器
    --sync-bn:是否使用跨卡同步BN,在DDP模式使用
    --local_rank:DDP参数,请勿修改
    --workers:最大工作核心数
    --project:训练模型的保存位置
    --name:模型保存的目录名称
    --exist-ok:模型目录是否存在,不存在就创建

————————————————
版权声明:本文为CSDN博主「炮哥带你学」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/didiaopao/article/details/119954291

步骤九:开始训练(炼丹)

image.png

以上配置完成后,我们就可以通过下面的命令,来开始我们的训练啦!

我们执行:

!python train.py

不出意外,你将看到以下输出:

image.png

恭喜你,你的yolo已经配置完毕,而且正在全力训练中!

你可以关注这个R值,这个是正确率,1是百分之百的正确率。可以看到,随着训练次数的增多,R的值也在不断趋近于1

image.png

你只需要等待训练完成,就可以得到以下输出:

image.png

步骤十:获取训练权重文件

我们去到对应文件夹下,就可以看到训练完成的权重文件了,我们此时采用yolo来测试下刚刚训练成功的权重文件是否有效果。

我们找一个测试图片:

code.jpg

上传到pic文件夹下:

image.png

修改detect.py文件,修改weights文件地址,让其指向我们刚训练好的权重文件上:

image.png

执行:

!python detect.py --source pic/code.jpg

image.png

看到已经成功识别!!!

怀着机动的心情,我们down下来runs/detect/exp3文件夹下的这张图片:

image.png

可以看到,二维码已经被精准的识别出来了!

code.jpg

此时,如果你计划使用yolo来检测图形的话,就可以到此结束了。

不过因为我们是准备在浏览器中使用,所以,还得继续看下去。

步骤十一:导出web格式权重文件

image.png

在这一步,我们需要将yolo可以识别的.pt文件,转为tensorflow.js可以识别的文件(json + bin),转化脚本我们可以使用上面提到的tag v6提供的export.py来实现,我们执行:

!python3 export.py --weights /content/drive/MyDrive/learn-for-book/yolov5/runs/train/exp3/weights/best.pt --include tfjs

运行结束,你将得到:

image.png

还是在同一文件夹下,多出了一个best_web_model文件夹,文件夹下的所有文件就是我们后续在浏览器中可以使用的权重文件啦!

你可以将其down到本地,接着我们就搞一个demo项目来在本地测试一个权重文件是不是好用。

步骤十二:安装 & 调整

image.png

这里我就不一步一步去描述如何搭建一个测试demo了,github上刚好有一个测试的demo项目,我们正好可以使用它!

github地址:github.com/zldrobit/tf…

我们先观察一下这个项目使用的依赖:

image.png

bingo!这个就是我们前面说到的tensorflowjs版本!

不难看出这是一个react项目,down到本地之后,先装一下依赖、再使用yarn start即可启动,启动后的页面如下:

image.png

可以看到,会有一个报错。

别慌,这里报错的原因应该就是模型文件找不到的报错,我们需要修改一些代码

(1)本地起一个静态服务,把刚才保存的导出的模型文件丢进去,并开启跨域,我这里使用的是http-server,代码如下:

http-server ./ --cors

(2)修改demo文件如下:

image.png

(3)重新运行,可以看到,报错消失了

image.png

步骤十三:检测

image.png

选择文件后,稍等2-3s,可以看到,二维码成功被识别出来了!!!

image.png

至此,在浏览器中通过目标检测算法进行二维码识别的流程就到此结束啦。

步骤十四:说点什么吧

在你看完或者跟着做完以上流程之后,你会发现,机器学习好像也没那么难哈哈哈,不过呢,我们是踩在巨人(yolov5)的肩膀上,如果没有这位巨人,我们就得自己去手撕识别算法、卷积模型,那可不是几千字的小文章可以带你入门的(还是要对其他学科保持敬畏)。

由于tensorflow.js的推出,我们可以用熟悉的javascript来训练模型、预测结果,这很爽。

浏览器里加载模型去识别特征(不依赖服务器),对一些快速识别高频度识别的场景来说,还是很有帮助的(不用频繁调用API接口)。

最后呢,希望你在看完这篇文章后,可以对这个领域有一些简单的认识,而不是听到这个就心生畏惧,假设后面某一天你遇到一些必须要用机器学习才可以解决的场景的时候,我希望你可以低调的说,I can do this (all day)