RK3568 您所在的位置:网站首页 图像识别例子图片大全 RK3568

RK3568

2024-07-10 02:55| 来源: 网络整理| 查看: 265

RK3568具有1TOPS算力的NPU,可以在板子上进行AI计算。飞凌OK3568板子资料中自带了一些AI识别的例子,但只是对一张图片进行识别,且需要单独使用图片查看工具查看识别结果。

为了更直观的体验RK3568的AI算力,将AI识别例程与摄像头功能结合起来,对摄像头的每一帧图像进行物品识别,这里使用的是RK提供的SSD模型。

演示视频: RK3568-AI物品检测

1 SSD模型介绍

SSD,全称为Single Shot MultiBox Detector,是Wei Liu在ECCV 2016上提出的一种目标检测算法,属于一阶段One Stage方法,SSD 模型利用不同尺度的特征图进行目标的检测,其模型结构图如下:

SSD具有如下主要特点:

从YOLO中继承了将detection转化为regression的思路,同时一次即可完成网络训练基于Faster RCNN中的anchor,提出了相似的prior box加入基于特征金字塔(Pyramidal Feature Hierarchy)的检测方式,相当于半个FPN思路

SSD网络结构图如下:

其算法步骤为:

将图像输入预训练好的分类网络(基于VGG16-Atrous)得到不同大小的特征映射分别提取Conv4_3、Conv7、Conv8_2、Conv9_2、Conv10_2、Conv11_2层的特征映射feature map,在每个特征映射的每个点构造6个不同大小尺度的bounding box,进行检测和分类来生成一些列bounding box采用NMS处理不同特征映射的bounding box,删掉部分重叠或者不正确的bounding box,得到最终的检测框

OK3568-C开发板中自带了已训练好的AI模型,位于/userdata/model目录下的ssd_inception_v2.rknn,我们直接用就可以了。

2 USB摄像头实现物品识别代码

先来看下整个代码的项目结构,然后再来分别介绍各个功能模块。

imageutil.h:图像类型转换相关函数myvideosourceface.cpp/h:用于USB摄像头图像显示qtcamera.cpp/h:qt界面rknn_ssd_process.cpp/h:用于SSD模型进行AI物品识别的接口函数rknn_ssd.cpp/h:SSD模型相关函数

3 按帧获取USB摄像头图像

Qt读取并显示USB摄像头,需要3个基本元素:

QCamera:它是用于读取摄像头视频信号的接口函数QCameraInfo:它提供相机设备的常规信息,可以用来查询系统上当前可用的相机设备QCameraViewfinder:它提供了一个相机取景器的小部,该类继承于QVideoWidget类,用于显示多媒体类提供的视频 3.1 USB相机获取图像

查找USB相机

//可用相机列表 const QList availableCameras = QCameraInfo::availableCameras(); for (const QCameraInfo &cameraInfo : availableCameras) { qDebug() setText(cameraInfo.description()); camera->setFont(font); camera->setCheckable(true); if (cameraInfo == QCameraInfo::defaultCamera()) { camera->setDefault(true); } else { camera->setDefault(false); } //启动相机 connect(camera, SIGNAL(clicked(bool)), this, SLOT(on_cameraClick())); vLayout->addWidget(camera); m_cameraInfo = cameraInfo; break; } }

启动相机与显示

//创建摄像头对象 m_camera = new QCamera(m_cameraInfo); //创建取景器 m_viewfinder = new QCameraViewfinder(); //配置摄像头的模式--捕获静止图像 QCamera::CaptureModes captureMode = QCamera::CaptureStillImage; if (m_camera->isCaptureModeSupported(captureMode)) { m_camera->unload(); m_camera->setCaptureMode(captureMode); //设置取景器显示 m_camera->setViewfinder(m_viewfinder); //启动摄像头 m_camera->start(); } 3.2 改为自己的Viewfinder

上面的USB摄像头显示程序,使用的是Qt的QCameraViewfinder用来显示摄像头图像,为了能获取到每一帧的图像,可以自己实现一个Viewfinder,然后在m_camera->setViewfinder时设置为自己的,并添加槽函数rcvFrame,当获取到一帧图像时,会触发此函数。

void qtCamera::on_cameraClick() { //创建摄像头对象 m_camera = new QCamera(m_cameraInfo); m_camera->unload(); //配置摄像头的模式--捕获静止图像 m_camera->setCaptureMode(QCamera::CaptureStillImage); //设置默认摄像头参数 QCameraViewfinderSettings set; set.setResolution(640, 480); //设置显示分辨率 set.setMaximumFrameRate(25); //设置帧率 //自己用QPainter将每一帧视频画出来 myvideosurface *surface = new myvideosurface(this); //设置取景器显示 m_camera->setViewfinder(surface); connect(surface, SIGNAL(frameAvailable(QVideoFrame)), this, SLOT(rcvFrame(QVideoFrame)), Qt::DirectConnection); connect(this,SIGNAL(sendOneQImage(QImage)), this, SLOT(recvOneQImage(QImage))); //启动摄像头 m_camera->start(); }

接收到一帧图像后,其原始图像格式是QVideoFrame类型的,需要先转为QImage类型,然后就可以进行显示或进行图像处理了,这里触发一个sendOneQImage信号来通知进行图像处理:

void qtCamera::rcvFrame(QVideoFrame m_currentFrame) { m_currentFrame.map(QAbstractVideoBuffer::ReadOnly); QImage videoImg = QImage(m_currentFrame.bits(), m_currentFrame.width(), m_currentFrame.height(), QVideoFrame::imageFormatFromPixelFormat(m_currentFrame.pixelFormat())).copy(); m_currentFrame.unmap(); QWidget::update(); emit sendOneQImage(videoImg); //发送信号 } 4 图像类型的转换与显示 4.1 QImage转Mat

Qt是QCamera创建的USB摄像头,获取到的图片格式是QImage类型,而使用OpenCV进行图像处理,需要转换为cv::Mat类型,转换的方式如下:

cv::Mat QImageToMat(QImage image) { image = image.convertToFormat(QImage::Format_RGB888); cv::Mat tmp(image.height(), image.width(), CV_8UC3, (uchar *)image.bits(), image.bytesPerLine()); cv::Mat result; // deep copy just in case (my lack of knowledge with open cv) cvtColor(tmp, result, CV_BGR2RGB); return result; } 4.2 Mat转QImage

OpenCV进行图像处理完成后,比如进行AI物品识别完成,并将识别的信息标记到图像上后,需要再转成QImage的类型用于在Qt中显示出来,转换的方式如下:

QImage MatToQImage(cv::Mat mat) { cv::cvtColor(mat, mat, CV_BGR2RGB); QImage qim((const unsigned char *)mat.data, mat.cols, mat.rows, mat.step, QImage::Format_RGB888); return qim; } 4.3 QImage转QPixmap

QImage在Qt中还不能直接显示出来,还需要再转为QPixmap类型,转换的方式如下:

QImage qImage; QPixmap tempPixmap = QPixmap::fromImage(qImage); 4.4 图像的显示

这里创建一个QLabel用于显示图像,调用setPixmap方法即可将图像显示出来,最后的adjustSize用来自动调整大小。

//创建一个label用于显示图像 m_lableShowImg = new QLabel(); m_lableShowImg->setPixmap(tempPixmap); m_lableShowImg->adjustSize(); 5 RKNN例程移植

飞凌OK3568-C开发板资料中,自带了ssd模型的测试程序,代码位置如下,ssd的测试代码是这3个文件:

测试代码,需要在执行时,输入模型的目录位置和测试图片的位置,AI物品识别之后会产生一个输出图片,需要再使用图片查看器查看结果。

为了方便功能的调用,这里将fltest_opencv_rknn_ssd_main.cc改写为rknn_ssd_process.cpp,并将具体功能进行拆分,封装为C++的形式。

5.1 按功能封装为C++形式

自己封装的RknnSsdModel类定义:

class RknnSsdModel { public: RknnSsdModel(){}; ~RknnSsdModel(){}; int RknnInit(const char *model_path); int RknnDeInit(); unsigned char *LoadModel(const char *filename, int *model_size); int DoRknnSsd(cv::Mat &src, cv::Mat &res); private: unsigned char *m_pModel = nullptr; rknn_context m_rknnCtx; rknn_input_output_num m_rknnIoNum; }; 5.1.1 RKNN初始化

主要功能是根据传入的rknn模型进行相关的初始化

int RknnSsdModel::RknnInit(const char *model_path) { int ret = 0; int model_len = 0; // Load RKNN Model printf("Loading model ...\n"); m_pModel = LoadModel(model_path, &model_len); printf("rknn_init ...\n"); ret = rknn_init(&m_rknnCtx, m_pModel, model_len, 0, NULL); if (ret < 0) { printf("rknn_init fail! ret=%d\n", ret); return -1; } //省略... } 5.1.2 RKNN运行

传入一张Mat格式的图片(一帧视频图像),经过AI识别,并将识别的信息标注到图片上后,将识别结果也以Mat格式传出:

int RknnSsdModel::DoRknnSsd(cv::Mat &src, cv::Mat &res) { const int img_width = 300; const int img_height = 300; const int img_channels = 3; int ret = 0; cv::Mat img = src.clone(); if (src.cols != img_width || src.rows != img_height) { printf("resize %d %d to %d %d\n", src.cols, src.rows, img_width, img_height); cv::resize(src, img, cv::Size(img_width, img_height), (0, 0), (0, 0), cv::INTER_LINEAR); } // Set Input Data rknn_input inputs[1]; memset(inputs, 0, sizeof(inputs)); inputs[0].index = 0; inputs[0].type = RKNN_TENSOR_UINT8; inputs[0].size = img.cols * img.rows * img.channels(); inputs[0].fmt = RKNN_TENSOR_NHWC; inputs[0].buf = img.data; ret = rknn_inputs_set(m_rknnCtx, m_rknnIoNum.n_input, inputs); if (ret < 0) { printf("rknn_input_set fail! ret=%d\n", ret); return -1; } // Run printf("rknn_run\n"); ret = rknn_run(m_rknnCtx, nullptr); if (ret < 0) { printf("rknn_run fail! ret=%d\n", ret); return -1; } // Get Output rknn_output outputs[2]; memset(outputs, 0, sizeof(outputs)); outputs[0].want_float = 1; outputs[1].want_float = 1; ret = rknn_outputs_get(m_rknnCtx, m_rknnIoNum.n_output, outputs, NULL); if (ret < 0) { printf("rknn_outputs_get fail! ret=%d\n", ret); return -1; } // Post Process detect_result_group_t detect_result_group; postProcessSSD((float *)(outputs[0].buf), (float *)(outputs[1].buf), src.cols, src.rows, &detect_result_group); // Release rknn_outputs rknn_outputs_release(m_rknnCtx, 2, outputs); // Draw Objects for (int i = 0; i < detect_result_group.count; i++) { detect_result_t *det_result = &(detect_result_group.results[i]); printf("%s @ (%d %d %d %d) %f\n", det_result->name, det_result->box.left, det_result->box.top, det_result->box.right, det_result->box.bottom, det_result->prop); int x1 = det_result->box.left; int y1 = det_result->box.top; int x2 = det_result->box.right; int y2 = det_result->box.bottom; rectangle(src, Point(x1, y1), Point(x2, y2), Scalar(255, 0, 0, 255), 3); putText(src, det_result->name, Point(x1, y1 - 12), 1, 4, Scalar(0, 255, 0, 255), 4); } res = src; return 0; } 5.2 AI识别调用

OK3568-C开发板中自带了已训练好的AI模型,位于/userdata/model目录下的ssd_inception_v2.rknn,在程序初始化时需要用到。

AI识别的代码逻辑为:先在qtCamera初始化时调用RKNN的初始化,然后打开USB摄像头,USB获取到每帧图像后, 调用DoRknnSsd进行AI物品识别,最后将识别的结果通过setPixmap方法展示出来

//先在qtCamera初始化时调用RKNN的初始化 std::string ssd_model = "/userdata/model/ssd_inception_v2.rknn"; m_rknnModel.RknnInit(ssd_model.c_str()); //USB获取到每帧图像后, 调用DoRknnSsd进行AI物品识别 void qtCamera::recvOneQImage(QImage qImage) { cv::Mat srcImg = ImageUtil::QImageToMat(qImage); cv::Mat dstImg; m_rknnModel.DoRknnSsd(srcImg, dstImg); QImage qDstImage = ImageUtil::MatToQImage(dstImg); QPixmap tempPixmap = QPixmap::fromImage(qDstImage); m_lableShowImg->setPixmap(tempPixmap); m_lableShowImg->adjustSize(); } 5.3 编译

需要注意下Qt工程的配置文件,要把opencv的一些库链接进去

qcamera.pri INCLUDEPATH += $$PWD/src HEADERS += \ $$PWD/src/qtcamera.h \ $$PWD/src/myvideosurface.h \ $$PWD/src/rknn_ssd.h \ $$PWD/src/rknn_ssd_process.h \ $$PWD/src/imageutil.h SOURCES += \ $$PWD/src/qtcamera.cpp \ $$PWD/src/myvideosurface.cpp \ $$PWD/src/rknn_ssd.cpp \ $$PWD/src/rknn_ssd_process.cpp qcamera.pro TARGET = USBCameraSSD TEMPLATE = app QT += widgets multimedia multimediawidgets SOURCES += main.cpp include($$PWD/qcamera.pri) LIBS+=-lopencv_core -lopencv_objdetect -lopencv_highgui -lopencv_videoio -lopencv_imgproc -lopencv_imgcodecs -lrknn_api -lOpenCL -lpthread #temp file DESTDIR = $$PWD/app_bin MOC_DIR = $$PWD/build/qcamera OBJECTS_DIR = $$PWD/build/qcamera

最后的编译脚本还和之前的一样:

#! /bin/bash mkdir -p build cd build export PATH=/home/xxpcb/myTest/OK3568/sourcecode/OK3568-linux-source/buildroot/output/OK3568/host/bin:$PATH qmake .. && make 6 总结

本篇介绍了在飞凌OK3568-C开发板中,外接USB摄像头,利用Qt和RKNN进行AI物品识别,通过已训练好的SSD模型,进行摄像头画面的实时AI物品检查的代码实现原理。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有