| 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部署 c++跨平台版本的服务端(cmake),支持windows和linux系统
 技术架构
 开发技术(1)C++版本 C++11 里面使用了一些C++11以上的新语法,对于老程序员来说,语言只是一种工具,花时间熟悉一下新语法即可,问题不大。Boost C++跨平台库,C++11中的大部分新特性都是来自Boost准标准库,但是还有很多新特性暂时并未引入到c++中,它可以跨平台编码,开发人员并不需要写一些预编译语法来适应不同平台,只需要一套代码即可实现所有平台,这就是它的优势,也是为什么C++新语法基本都出自它的原因。mysql 系统涉及到的一些模型数据、设备、场景、报警、配置等数据的存储既可以使用mysql存储,也可以使用sqlite存储,根据自己需要,可以通过配置文件配置存储方式,唯一的区别是mysql需要安装数据库而已。sqlite 同上,AI盒子数据存储支持使用sqlite存储,所以需要用到sqlite库。oatpp 一个开源的支持多语言的成熟http库,跨平台,支持DTO数据封装、SSL等,一个非常不错的HTTP协议库。spdlog 一个开源的系统日志库,支持控制台打印、文件日志打印、支持自动清理、按日存储等功能。opencv 一个很关键的一个库了,python使用pt模型,c++使用onnx模型,这里就是基于跨平台opencv来加载onnx模型实现推理的。vue2.0 AI盒子提供了一个web管理端,通过其实现模型管理、设备管理、场景管理、报警管理、录像管理、系统配置、预警监控等功能。 注:此开源版本提供基于vue2.0开发的web管理端,提供与AI盒子内部通信的交互操作控制台,如果需要定制自己的模型、设备、场景、配置等,可能需要修改web后台管理端源码,需要此部分的源码,需联系作者付费采购,感谢理解。
yolov8 c++版本使用的yolov8的模型,通过yolov8模型或yolov5模型转换的onnx模型实现模型推理和目标识别。 
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 这里从下至上进行一一说明 功能介绍设备层 AI盒子接入的设备为网络摄像机,当然也支持本地摄像头的采集和分析(一般用于做demon),AI盒子作为边缘计算盒,一般部署在与网络摄像机相同的局域网内,通过标准的RTSP协议采样抽帧的方式分析摄像机的视频流,然后对采样的图像帧使用yolov8模型库进行一一分析,这里的摄像头采用标准的rtsp协议,可以是任意厂家的摄像头,包括常见的海康、大华、安士讯等。接入层 接入层其实就只我们本文介绍的主角,也就是AI智能盒子,它作为边缘计算盒子,与摄像机同属一个局域网(当然排除服务云端化后提供云识别接口之外),它通过局域网与摄像机相连,通过USB或Lane接口连接,通过USB连接IO控制器(如果AI盒子需要控制IO开关如信号灯、语音喊话、硬件信号输入到其他系统等),AI盒子通过分析网络摄像机视频流,当分析到检测预警时,记录报警信息(时间、设备、类型、报警图片、报警前后n秒录像片段),实时存储到AI盒子本地,并异步将报警信息推送到第三方平台。服务层 sass云服务时一个多租户平台,主要功能就是提供基于AI盒子的高级应用,包括预警推送(手机、短信、公众号等)、预警处理、视频播放、预警统计、设备管理、云模型管理、策略管理、第三方数据对接、数据推送等功能应用。应用层 基于sass云的移动端应用和管理,用户可以通过手机端接收系统预警信息、处理预警、处理各自的任务、实时查看预警和视频等功能。 
 智能AI盒子提供了如下功能(部分c++版本功能可能没有,python版本有,后面会指出来,感兴趣的各位网友,也希望可以私下联系共同开发新增)web管理端 web管理端指AI盒子的启动后台的交互网页终端,它基于AI盒子提供的HTTP接口实现AI盒子的数据管理、配置等功能。
 AI盒子端资源国际化 为适应不同用户的需求,AI盒子web管理端提供了中文简体、中文繁体、英文3种语言的切换。模型管理 管理端提供模型管理功能,包括模型上传、模型添加、模型修改、模型删除、模型更新等功能,具体见演示效果。设备管理 管理端提供了设备管理功能,包括设备添加、更新、移除、查询、设备区域范围配置、图片预览等功能。场景管理 管理端提供指定设备的多场景管理,包括一个设备添加多个场景、查询设备场景、更新设备场景、移除设备场景功能。系统配置 可以通过web端动态配置AI盒子的常用配置,包括盒子编码(对接第三方唯一id)、盒子保存数据天数、是否展示识别标签、第三方平台推送地址等。重启盒子 如果有必要,可以通过web端重启AI盒子(linux下部署时候需要root权限部署才能正常重启,启动后自动启动AI盒子服务程序) 
 示例下载windowsHTTP、websocket协议 AI盒子支持通过Http协议管理AI盒子配置、模型、设备、场景等(如AI的WEB端),也可以通过websocket协议下发和操作AI盒子,实现与AI和的多协议交互。模型热加载 模型可以在系统启动后随时进行更新,盒子自动检测变化重新加载模型信息。设备热加载 被检测的设备更新后,AI盒子自动侦测信息变化,自动重新加载分析设备(如分析时间段、分析帧率等)场景热加载 一个分析视频设备,可以叠加多种分析场景算法,当某一算法发生变化后,AI盒子自动更新加载对应场景。配置热加载 系统配置会实时更新到内存中,实时更改对应系统配置(如保存时长、是否显示label、是否自动重启等)抽帧采样分析 AI盒子可以根据用户配置动态分析设备抽帧采样帧率,如25帧分析一张(1秒一张)多算法分析 一个分析设备,可以叠加多种分析场景算法,也就是分析的某张图可以叠加n种分析算法(串行叠加分析)分析时段控制 可以根据用户需要配置不同时间段进行分析,如晚上18:00-23:00时间段内进行分析,不在改时间段则停止分析。分析区域配置 python版本支持划定的多边形内的目标进行分析,也支持多个线段进行越界侦测分析。模型推理分析 支持yolov5、yolov8的模型进行推理分析,其中python版本使用yolov5的pt模型,yolov8使用的是onnx模型报警存储 当AI盒子检测到对应预警类型的目标后,将指定时间、设备、预警类型、预警图片、预警前后5秒(预录前5秒)的录像临时保存下来,然后异步推送到第三方平台。报警录像 当AI盒子检测到预警类型的目标后,AI盒子根据用户配置进行是否录像,录制事件发生前5秒和后5秒的录像(mp4格式或flv)GPIO录像 AI盒子支持抓取GPIO信号,进行联动,开启或停止录像。GPIO抓拍 AI盒子支持抓取GPIO信号,进行联动,开启关联摄像机的图片抓拍。报警推送 AI盒子可以配置报警推送http地址(https),然后将报警信息(设备、报警类型、时间、图片、录像)推送至第三方平台。报警广播 AI盒子自带的web端具有报警预览功能,当进入AI盒子报警预览页面后,发现报警会自动推送到web端,并使用tts语音播报方式将对应的报警内容以语音的形式播放出来。报警清理 AI盒子sd卡存储能力有限,为了保证AI盒子能够持续工作,AI盒子会自动覆盖最老的报警信息(主要包括数据和录像清理)视频播放 AI盒子与IPC摄像机一般处于同一个局域网,所以AI盒子具有能直接访问摄像机的条件,而云端要看摄像机的视频必须通过盒子(或其他第三方视频云平台)转发,AI盒子正好集成了改功能,它通过websocket下发指令启动视频的开始、停止播放(rtmp推送到服务端),启动远程抓拍功能。远程抓拍 如上说,AI盒子既具备视频转发能力,也具备远程控制抓拍功能。远程录像 AI盒子及支持关联摄像机(或非关联但是与其摄像机在同一张局域网内)的实时视频转发,也具有开启远程录像、停止远程录像的手动录像功能,录像完成后AI盒子会立即将录像信息推送到第三方云平台中(mp4格式-可以在web端播放)。自动重启 如果有必要,可以配置AI盒子自动重启(每晚12点),但是一般情况下不会这么做,除非网络有问题。自动校时 AI盒子会定时与第三方云平台进行心跳,为了保证AI盒子时间与云平台保持一致,在心跳的时候云平台如果返回服务器时间则AI盒子会自动将服务器时间设置为本地时间,以便与服务器进行时间对齐。分析接口 AI盒子具有自己抓取摄像头图片进行分析的能力,此外,AI盒子也提供的对外的http接口,用户可以通过接口将推送推送给AI盒子,然后进行识别,最后将结果以接口结果形式返回的第三方平台(不推荐使用) 
 如果windows是win7或者xp系统,可能运行demon报缺少ucrtbase.dll,该文件可以在源码路径中找到,找到后放exe所在目录即可AiBoxrdpartylibwin32 
 
 (1)默认80分类检测模型demon windows7或win10以上,编译打包的下载链接如下链接:https://pan.baidu.com/s/1tCrcjAK-1lERMut013kPXw?pwd=abcd 提取码:abcd 
 
 使用:下载后直接解压到当前文件夹,然后运行AiBox.exe即可 注意,如果电脑没有摄像头,请插入usb摄像头后尝试!
 (2)安全帽反光衣检测模型demon链接:https://pan.baidu.com/s/1UkfkLnTFGgn_w87xQ6u0bA?pwd=62h3 提取码:62h3 
 ubuntu
 (1)默认80分类检测模型demon x64_86架构的,系统为ubuntu20.04版本编译的demon连接如下链接:https://pan.baidu.com/s/1dGd4SYjWtIKEmcEbYlYrGA?pwd=abcd 提取码:abcd 
 
 使用tar -zxvf ai_box_ubuntu20.04.tar.gzcd ai_box/./AiBox 
 
 orangePI注意: 如果是WMWare Workshop启动的虚拟机,请将主机的摄像头共享给Ubuntu虚拟机,操作步骤如下: windows打开服务:ctrl+r输入services.msc,然后找到VM的USB服务,名字为VMware USB Arbitration Service,设置为开机启动,并手动启动该服务。重启WMWare Workshop及Ubuntu虚拟机(如果usb重启的情况下必须重启)启动Ubuntu20.04机器在VM上选择该机器,鼠标右键菜单,选择对应设备,然后连接即可 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...  使用ubuntu再带的茄子工具cheese打开测试摄像头是否可用 
 (1)默认80分类检测模型demon 国产华为升腾芯片(主要跑NPU,还没空适配NPU,目前也是跑CPU)demon下载链接如下链接:https://pan.baidu.com/s/1j3FzFwx1uNKNnwVGRg95hQ?pwd=abcd 提取码:abcd 
 
 上传到orangePI中,解压运行即可tar -zxvf ai_box_ubuntu20.04.tar.gzcd ai_box/./AiBox 
 jetson Nano
 jetson nano是跑python、GPU版本的,不是跑C++的,追求实时性、识别速度和高性能的请私下联系我采购商业python版本,功能比cpp版本更强,所以这里就没有CPU版本的适配demon,见谅!VOC2YOLO VOC格式数据是一种标准格式数据,而在yolo中有自己的格式数据,它是txt格式的,每一行表示一个对象的基本信息,所以我们需要将VOC格式的数据转换为YOLO格式数据之后,才能拿到yolo中进行训练,当然,我们可以一开始的时候就标注yolo格式数据,这里我不太推荐,原因如下 所以,我们在数据标注的时候,首先选择VOC格式,标注完成后自己协议个python脚本实现VOC格式数据到YOLO格式数据转换即可,这里我已经写好了python脚本,各种问题已经考虑好了,你拿过去用就好了VOC是通用格式数据,记录信息非常详细,yolo格式不是通用格式数据,信息损失大VOC格式数据兼容性和移植性较好,可以较好的与其他平台对接,如paddle飞桨、阿里天池等开源工具基本都支持VOC格式标注 这里我们使用的是labeling标注工具标注,它支持voc和yolo格式数据。 
 百度爬虫复制代码"""
注意:
1、首先更改VOC格式根目录路径,然后将生成的images和labels目录
拷贝到训练数据集目录下(名字自定义)即可
2、修改classes类别名称列表
3、配置是否进行文件重命名:batch_rename_voc_samples
"""
import xml.etree.ElementTree as ET
import os
import uuid
import shutil
import random
from PIL import Image
# voc中的类别名对应yolo的类别下标
# 改成自己的类别名称,注意类别顺序
classes = ["person", "hat", "reflective_clothes", "other_clothes"]
# 转成yolov5-txt格式图片大小
# yolov5长宽必须是32像素整倍
# yolov5默认大小为640x640
# 为了提高精度建议放大1280x1280
# 摄像机分辨率1280x720
glob_resize_img = False
global_img_size = (1280, 1280)
# 是否对原来的样本文件进行重命名
# 如果文件名为非uuid的文件,建议重命名
# 以免与其他数据文件名相同覆盖(不同分类训练样本粘贴的时候)
batch_rename_voc_samples = False
# VOC图片格式目录根路径
# VOC满足其约定的目录结构
# ROOT(dir)
# --Annotations(存放标注的标签文件(目标真值标注数据,xml格式))
# --ImageSets(类别标签其中包含4个文件夹Action|Layout|Main|Segmentation,识别任务必须有Main)
# --JPEGImages(存放需要打标签的图片文件(图像jpg格式))
voc_root_path = 'D:/datasets/per_hat'
# 添加后斜杠
if not voc_root_path.endswith('\\') and not voc_root_path.endswith('/'):
    voc_root_path += '/'
# voc格式标签文件路径
xml_file_path = voc_root_path + 'Annotations/'
# voc格式标注图片目录
# 最好不要用"images"作文件夹名字,
# 后续会对图片进行划分,命名的新文件夹叫"images"
images_file_path = voc_root_path + 'JPEGImages/'
# 数据集划分比例建议:训练集80%,验证集10%,测试集10%(标准的8:1:1)
train_percent = 0.9
val_percent = 0.1
test_percent = 0.0
# 批量重命名图片(uuid名称)
def rename_images(xml_dir, images_dir):
    # 获取xml文件列表
    xml_file_list = os.listdir(xml_dir)
    # 处理所有xml文件
    for index, fn in enumerate(xml_file_list):
        print(f'正在重命名第{index}个文件:{fn}')
        # 新文件的文件名
        new_name = str(uuid.uuid1()).replace('-', '')
        # 老文件的文件名
        old_name = fn[:-4]
        # xml原文件路径
        xml_oldname = xml_dir + fn
        # xml新文件路径
        xml_newname = xml_dir + new_name + '.xml'
        # 重命名新文件名
        os.rename(xml_oldname, xml_newname)
        # 对应jpeg图片
        jpg_oldname = images_dir + old_name + '.jpg'
        # 对应的jpg图片存在
        if os.path.exists(jpg_oldname):
            # jpg新文件路径
            jpg_newname = images_dir + new_name + '.jpg'
            # 删除老的图片
            os.rename(jpg_oldname, jpg_newname)
# voc盒子型=>中点+宽高
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
# 转换标签文件
def convert_annotations(source_file, dest_file):
    print(f'正在转换文件:{source_file}')
    in_file = open(source_file, encoding='UTF-8')
    out_file = open(dest_file, 'w', encoding='UTF-8')
    tree = ET.parse(in_file)
    root = tree.getroot()
    size = root.find('size')
    w = 0
    h = 0
    if size is not None:
        w = int(size.find('width').text)
        h = int(size.find('height').text)
    # 图片尺寸为0处理
    if 0 == w or 0 == h:
        # xxx.jpg
        name = os.path.basename(source_file)
        name = name[:-4]
        # 获取jpg文件
        image_file = images_file_path + name + '.jpg'
        if not os.path.exists(image_file):
            image_file = images_file_path + name + '.png'
        # 只有xml没有图片
        if not os.path.exists(image_file):
            in_file.close()
            out_file.close()
            os.remove(source_file)
            os.remove(dest_file)
            return
        # 获取图片宽高
        img = Image.open(image_file)
        w, h = img.size
        img.close()
    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
        if cls not in classes:
            print(f'class {cls} not in config list')
            continue
        # 类别名转为yolov5的类别索引下标
        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')
# *********************************************** #
#  parent folder
#  --data
#  ----images
#       ----train
#       ----val
#       ----test
#  ----labels
#       ----train
#       ----val
#       ----test
# *********************************************** #
# 创建yolov5格式目录
def create_dir():
    if not os.path.exists(voc_root_path + 'images/'):
        os.makedirs(voc_root_path + 'images/')
    if not os.path.exists(voc_root_path + 'labels/'):
        os.makedirs(voc_root_path + 'labels/')
    if not os.path.exists(voc_root_path + 'images/train/'):
        os.makedirs(voc_root_path + 'images/train')
    if not os.path.exists(voc_root_path + 'images/val/'):
        os.makedirs(voc_root_path + 'images/val/')
    if not os.path.exists(voc_root_path + 'images/test/'):
        os.makedirs(voc_root_path + 'images/test/')
    if not os.path.exists(voc_root_path + 'labels/train/'):
        os.makedirs(voc_root_path + 'labels/train/')
    if not os.path.exists(voc_root_path + 'labels/val/'):
        os.makedirs(voc_root_path + 'labels/val/')
    if not os.path.exists(voc_root_path + 'labels/test/'):
        os.makedirs(voc_root_path + 'labels/test/')
    return
# png转jpg
def png_to_jpg_if_png_exists(jpg_image):
    # 文件不存在
    if not os.path.exists(jpg_image):
        # xxx.jpg
        name = os.path.basename(jpg_image)
        name = name[:-4]
        # png文件
        png_image = images_file_path + name + '.png'
        # png文件也不存在
        if not os.path.exists(png_image):
            print(f'file {jpg_image} not exists')
            return
        # 转行png文件为jpg
        img = Image.open(png_image)
        # 转换为JPG格式图片
        img = img.convert("RGB")
        # 保存为新的jpg格式图片
        img.save(jpg_image)
        # 关闭图片
        img.close()
        # 删除原来的png格式图片
        os.remove(png_image)
# 重置图片大小
def resize_source_image(source_path, dest_path):
    img = None
    try:
        # 打开图片
        img = Image.open(source_path)
        # 放大图片
        image = img.resize(global_img_size)
        # 将新图像保存至桌面
        image.save(dest_path)
    except IOError as e:
        print("缩放图像失败:" + e)
    finally:
        # 关闭图像
        if img is not None:
            img.close()
    pass
# 转行所有数据集
def convert_all_data():
    # 创建yolov5格式目录
    create_dir()
    # 原数据集XML文件列表及总数
    total_xml = os.listdir(xml_file_path)
    num_xml = len(total_xml)
    # 计算训练集、验证集、测试集数量
    num_train = int(num_xml * train_percent)
    num_val = int(num_xml * val_percent)
    num_test = num_xml - num_train - num_val
    print(f"训练集数目:{num_train}, 验证集数目:{num_val},测试集数目:{num_test}")
    # 随机训练集
    train = random.sample(total_xml, num_train)
    # 验证测试集[排除训练集数据]
    val_test = [i for i in total_xml if not i in train]
    # 从训练测试集取出验证集
    val = random.sample(val_test, num_val)
    # 测试集[排除训练集及验证集数据]
    test = [i for i in total_xml if not i in train and not i in val]
    # 开始转换训练集标签
    for index, fn in enumerate(train):
        print(f'正在转换第{index}个训练数据集文件:{fn}')
        # 原来文件名
        old_name = fn[:-4]
        new_name = old_name
        # 重命名文件
        if batch_rename_voc_samples:
            new_name = str(uuid.uuid1()).replace('-', '')
        # 转换xml文件
        xml_old_file = xml_file_path + old_name + '.xml'
        xml_new_file = voc_root_path + 'labels/train/' + new_name + '.txt'
        convert_annotations(xml_old_file, xml_new_file)
        # 拷贝jpg文件
        src_image = images_file_path + old_name + '.jpg'
        dst_image = voc_root_path + 'images/train/' + new_name + '.jpg'
        png_to_jpg_if_png_exists(src_image)
        if os.path.exists(src_image):
            if glob_resize_img:
                resize_source_image(src_image, dst_image)
            else:
                shutil.copyfile(src_image, dst_image)
    # 开始转换验证集标签
    for index, fn in enumerate(val):
        print(f'正在转换第{index}个验证数据集文件:{fn}')
        # 原来文件名
        old_name = fn[:-4]
        new_name = old_name
        # 重命名文件
        if batch_rename_voc_samples:
            new_name = str(uuid.uuid1()).replace('-', '')
        # 转换xml文件
        xml_old_file = xml_file_path + old_name + '.xml'
        xml_new_file = voc_root_path + 'labels/val/' + new_name + '.txt'
        convert_annotations(xml_old_file, xml_new_file)
        # 拷贝jpg文件
        src_image = images_file_path + old_name + '.jpg'
        dst_image = voc_root_path + 'images/val/' + new_name + '.jpg'
        png_to_jpg_if_png_exists(src_image)
        if os.path.exists(src_image):
            if glob_resize_img:
                resize_source_image(src_image, dst_image)
            else:
                shutil.copyfile(src_image, dst_image)
    # 开始转换测试集标签
    for index, fn in enumerate(test):
        print(f'正在转换第{index}个测试数据集文件:{fn}')
        # 原来文件名
        old_name = fn[:-4]
        new_name = old_name
        # 重命名文件
        if batch_rename_voc_samples:
            new_name = str(uuid.uuid1()).replace('-', '')
        # 转换xml文件
        xml_old_file = xml_file_path + old_name + '.xml'
        xml_new_file = voc_root_path + 'labels/test/' + new_name + '.txt'
        convert_annotations(xml_old_file, xml_new_file)
        # 拷贝jpg文件
        src_image = images_file_path + old_name + '.jpg'
        dst_image = voc_root_path + 'images/test/' + new_name + '.jpg'
        png_to_jpg_if_png_exists(src_image)
        if os.path.exists(src_image):
            if glob_resize_img:
                resize_source_image(src_image, dst_image)
            else:
                shutil.copyfile(src_image, dst_image)
    pass
# 开始执行转行
convert_all_data()
模型的训练关键在于数据集的准备,如何弄到我们需要的数据集?其实有很多方面,我这里大概找了几个方法,大部分可以通过网上获取,也可以自己写爬虫从百度爬取(后面我也找了很多数据集获取地址,参见《数据集采集》一页。 百度爬虫我也准备好了,只需执行python脚本然后输入你要搜索的关键词、要下载的图片数量即可下载到对应关键词的图片。千图爬虫 和百度图库图片爬虫一样,我也写了一个关于千图网的一个爬虫,脚本如下 复制代码"""
千图网图库图片抓取
汉字转拼音:pip install pohan -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
"""
import uuid
import requests
import re
from urllib import parse
import os
import pohan
from pohan.pinyin.pinyin import Style
# 判断词语是否为中文
def is_chinese(strings):
    for _char in strings:
        if '\u4e00' <= _char <= '\u9fa5':
            return True
class ImageSpider(object):
    def __init__(self, save_dir):
        # 分页从1开始
        self.url = 'https://www.58pic.com/tupian/{}-852-0-{}.html'
        self.headers = {'User-Agent': 'Mozilla/4.0'}
        self.word_parse = ''
        self.save_dir = save_dir
        self.image_count = 1000
        self.i = 1
    # 获取图片递归函数
    def getimage(self, url, word):
        # 使用 requests模块得到响应对象
        res = requests.get(url, headers=self.headers)
        # 更改编码格式
        res.encoding = "utf-8"
        # 得到html网页
        html = res.text
        # print(html)
        # 正则解析
        pattern = re.compile('data-original="(.*?)"', re.S)
        img_link_list = pattern.findall(html)
        # 存储图片的url链接
        print(img_link_list)
        # 创建目录,用于保存图片
        directory = str(self.save_dir + '/{}').format(word)
        # 如果目录不存在则创建新目录
        if not os.path.exists(directory):
            os.makedirs(directory, mode=0o777, exist_ok=True)
        previous_image = ''
        for img_link in img_link_list:
            # 添加请求前缀https
            img_link = "https:" + img_link
            # 忽略图片是图标图等且与上一张图不相同
            if img_link.find("icon.qiantucdn.com/static") == -1 and previous_image != img_link:
                # 本地文件路径
                filename = '{}/{}_{}.jpg'.format(directory, word, str(uuid.uuid1()))
                self.save_image(img_link, filename)
            # 上一张图地址
            previous_image = img_link
            self.i += 1
            # 每页只能下载60张图片,这里可以直接跳出,或者按需要的数量更改
            if self.i == self.image_count:
                print(f'下载完成{self.image_count}张图,退出下载')
                return
            # 也可以改成翻页下载的形式
            if self.i % 77 == 0:
                pn = (int)(self.i / 77) + 1
                urlnew = self.url.format(self.word_parse, pn)
                print(f'请求下一页{urlnew}')
                self.getimage(urlnew, word)
    # 保存图片
    def save_image(self, img_link, filename):
        try:
            # https:\\/\\/ns-strategy.cdn.bcebos.com\\/ns-strategy\\/upload\\/fc_big_pic\\/part-00384-1388.jpg
            img_link = img_link.replace("\\/", '/')
            html = requests.get(url=img_link, headers=self.headers).content
            with open(filename, 'wb') as f:
                f.write(html)
            print(f'下载{img_link}到{filename}成功')
        except Exception as e:
            print(f'下载图片{img_link}异常:{e}')
    # 执行函数
    def run(self):
        word = input("您想要搜索下载的图片关键词?")
        # 千图网汉字自动转拼音搜索
        pinyin_list = pohan.pinyin.han2pinyin(word, style=Style.NORMAL)
        key_word = ''
        for item in pinyin_list:
            key_word += item[0]
        self.word_parse = parse.quote(key_word)
        count = input("抓取图片最大数量?")
        self.image_count = int(count)
        url = self.url.format(self.word_parse, '1')
        print(url)
        self.getimage(url, word)
if __name__ == '__main__':
    spider = ImageSpider('D:/capture')
    spider.run()
千图网图片可能精准一些,当然可能是数据量没有百度图库的大,但是也是一个不错选择,执行过程和百度一样,这里不在赘述。 测试模型注意: 千图网之前的爬虫可以爬,现在发现这个脚本不能用了,应该是静态图片访问千图网做了token访问,还需要登录,人没百度大,心机耍的一溜一溜的,真要命,各位大神请贡献你们自己的其他图库的爬虫,在此为大家感谢你!
 在源码里我提供了如下几个onnx测试模型,各位可以修改自己的代码,然后使用对应模型进行测试,模型路径见源码 演示效果AI盒子管理端(web)AiBox\3rdparty\model
 
 
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 AI盒子服务端(C++)
 
 (1)ubuntu系统启动 
基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   (2)windows系统启动 
AI预警云平台(Sass)基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 基于yolov8框架,使用C++实现的跨平台目标识别系统,支持windows、linux(Ubuntu或centos)跨平台编译及部 ...   
 开发环境机器准备
 ubuntu、centos、windows、VMware Workstation Pro
 本次开源部分为c++版跨平台服务端,开发技术使用cmake、boost、sqlite、opencv、oatpp(http)、oatpp-websocket、spdlog(项目中已有),所以必须先安装对应的软件和必要的库,软件和库请移步此处下载:链接:https://pan.baidu.com/s/1bPi_l4fKvhZOSducrd4eZw?pwd=ghwm 提取码:ghwm 
 
 除了必要的软件之外,我们需要准备一台windows开发机和一台linux开发机器(ubuntu或者centos都可以)环境准备 vs2019、boost、oatpp、oatpp-websocket、opencv、spdlog
 本次实际开发环境为vs2019,各位可以使用vs2019或以上都可以,支持跨平台开发调试工具,各个环境和软件安装详细细节如下所示。 提取码下载: 
 
 
 |