AI应用快速上手:基于YoloNAS的目标检测

     目标检测是通过算法找出图像中所有感兴趣的目标(物体),确定它们的类别和位置。本文介绍了基于 高通Snapdragon Neural Processing Engine SDK和YoloNAS模型实现一个图片检测功能的应用,通过手机的相机采集视频画面,并对画面的目标检测与分类。该应用的所有源代码都可以在https://github.com/quic/qidk/tree/master/Solutions/VisionSolution1-ObjectDetection-YoloNas

上获得。

       我们使用Snapdragon 8 Gen 2 的Android手机验证测试这些,实际上这些示例代码可以在任何支持Snapdragon Neural Processing Engine SDK的Android手机上都可以运行,真正的一次编写多次部署。

      这里使用到的模型为YoloNASYOLOYou Only Look Once)是一种对象检测算法,YOLO-NAS是一种新的目标检测模型,由Deci AI团队开发。“NAS”代表神经架构搜索,是一种用于自动化神经网络架构设计过程的技术。NAS 不依赖手动设计和人类直觉,而是采用优化算法来发现最适合给定任务的架构。NAS 的目标是找到一种在准确性、计算复杂性和模型大小之间实现最佳权衡的架构。

示例代码前置条件

  • 在运行Android示例应用程序之前,请使用提供的链接按照设置Qualcomm Neural Processing SDK 的说明进行操作。https://developer.qualcomm.com/sites/default/files/docs/snpe/setup.html
  • 下载Coco 2014 数据集,并将其路径提供给Generate_DLC.ipynb。在对应的位置子修改“dataset_path”的值。
  • 高通Snapdragon 安卓手机,推荐Snapdragon 8 Gen 2系列手机
  • 一台Linux机器

操作步骤:

1. 生成DLC

运行示例代码中的 jupyter notebook GenerateDLC.ipynb,生成YoloNAS量化的dlc。

YoloNAS模型是在COCOdataset 上为80类日常对象进行训练的。类的列表可以在dataset 中找到:https://cocodataset.org/#explore

2代码关键实现过程

此应用程序打开相机预览,收集所有帧并将其转换为位图。通过将Quant_YoloNas_s_320.dlc作为输入,通过神经网络生成器来构建网络。然后将位图提供给模型进行推理,该模型返回相应的对象预测和定位。

代码目录结构

demo : 包含演示GIF

app : 包含标准Android应用程序格式的源文件

app\src\main\assets:包含模型二进制文件DLC

app\src\main\java\com\qc\objectdetectionYoloNas:应用程序java源代码

app\src\main\cpp:本机源代码

sdk:包含openCV sdk

相机预览设置

相机预览帧的权限在以下文件中授予:

/app/src/main/AndroidManifest.xml

<uses-permission android:name="android.permission.CAMERA" />

为了使用camera2 API,请添加以下功能

<uses-feature android:name="android.hardware.camera2" />

加载模型

神经网络连接和加载模型的代码片段:

snpe = snpeBuilder.setOutputLayers({})

            .setPerformanceProfile(zdl::DlSystem::PerformanceProfile_t::BURST)

            .setExecutionPriorityHint(

                    zdl::DlSystem::ExecutionPriorityHint_t::HIGH)

            .setRuntimeProcessorOrder(runtimeList)

            .setUseUserSuppliedBuffers(useUserSuppliedBuffers)

            .setPlatformConfig(platformConfig)

            .setInitCacheMode(useCaching)

            .setCPUFallbackMode(true)

            .setUnconsumedTensorsAsOutputs(true) //To get output from two nodes

            .build();  //builds the network

图像处理

位图图像作为openCV Mat传递给本地,然后转换为大小为320x320x3的BGR Mat。基本图像处理取决于模型所需的输入形状类型,然后将处理后的图像写入应用程序缓冲区。

    cv::Mat img320;

    cv::resize(img,img320,cv::Size(320,320),cv::INTER_LINEAR);

    float inputScale = 0.00392156862745f;    //normalization value, this is 1/255

    float * accumulator = reinterpret_cast<float *> (&dest_buffer[0]);

    //opencv read in BGRA by default

    cvtColor(img320, img320, CV_BGRA2BGR);

    LOGI("num of channels: %d",img320.channels());

    int lim = img320.rows*img320.cols*3;

    for(int idx = 0; idx<lim; idx++)

        accumulator[idx]= img320.data[idx]*inputScale;

后期处理

这包括为每2100 boxes去获得具有最高置信度的类,并应用“Non-Max Suppression”来删除重叠的boxes。 // Non-Max Suppression-- 非最大抑制即抑制不是极大值的元素,搜索局部的极大值

for(int i =0;i<(2100);i++) 

   {

       int start = i*80;

       int end = (i+1)*80;

       auto it = max_element (BBout_class.begin()+start, BBout_class.begin()+end);

       int index = distance(BBout_class.begin()+start, it);

       std::string classname = classnamemapping[index];

       if(*it>=0.5 )

       {

           int x1 = BBout_boxcoords[i * 4 + 0];

           int y1 = BBout_boxcoords[i * 4 + 1];

           int x2 = BBout_boxcoords[i * 4 + 2];

           int y2 = BBout_boxcoords[i * 4 + 3];

           Boxlist.push_back(BoxCornerEncoding(x1, y1, x2, y2,*it,classname));

       }

   }

   std::vector<BoxCornerEncoding> reslist = NonMaxSuppression(Boxlist,0.20);

然后我们只缩放原始图像的坐标

        float top,bottom,left,right;

        left = reslist[k].y1 * ratio_1;   //y1

        right = reslist[k].y2 * ratio_1;  //y2

        bottom = reslist[k].x1 * ratio_2;  //x1

        top = reslist[k].x2 * ratio_2;   //x2

绘制边界框

RectangleBox rbox = boxlist.get(j);

            float y = rbox.left;

            float y1 = rbox.right;

            float x =  rbox.top;

            float x1 = rbox.bottom;

            String fps_textLabel = "FPS: "+String.valueOf(rbox.fps);

            canvas.drawText(fps_textLabel,10,70,mTextColor);

            String processingTimeTextLabel= rbox.processing_time+"ms";

            canvas.drawRect(x1, y, x, y1, mBorderColor);

            canvas.drawText(rbox.label,x1+10, y+40, mTextColor);

            canvas.drawText(processingTimeTextLabel,x1+10, y+90, mTextColor);

整体执行操作流程

  1. Clone QIDK repo。
  2. 从脚本所在的目录中运行以下脚本,以解析此项目的依赖项。
  • 把snpe-release.aar文件从$snpe_ROOT复制到Android project中的“snpe-reease”目录

 注意-如果您使用的是SNPE 2.11或更高版本,请更改resolveDependencies.sh中的以下行。

From: cp $SNPE_ROOT/android/snpe-release.aar snpe-release

 To : cp $SNPE_ROOT/lib/android/snpe-release.aar snpe-release

  • 下载opencv并粘贴到sdk目录,为android Java的使能opencv。

    bash resolveDependencies.sh

     3.运行jupyter notebook GenerateDLC.ipynb,为量化YOLO_NAMES DLC生成DLC(s)。另外,dataset_path更改Coco Dataset 路径。

  • 此脚本生成所需的dlc(s)并将其粘贴到适当的位置。

     4.进行Gradle Sync

     5.编译项目。

     6.生成APK文件:app-debug.APK

     7.使用Qualcomm Innovators开发工具包来安装应用程序(不要在模拟器上运行APK)

     8.如果未检测到未签名或已签名的DSP runtime,请检查logcat日志中的FastRPC错误。由于SELinux权限的原因,可能无法检测到DSP runtime。请尝试以下命令来设置SELinux权限。

adb disable-verity

adb reboot

adb root

adb remount

adb shell setenforce 0

     9.安装和测试应用程序: app-debug.apk

 adb install -r -t app-debug.apk

     10.启动应用程序

以下是“目标检测”Android应用程序的基本操作

           1.首次启动应用程序时,用户需要提供相机权限

           2.如果屏幕上可以看到人,相机将打开并看到目标

           3.用户可以为模型选择合适的run-time,并观察性能差异

应用程序演示

文章作者:高通工程师,戴忠忠  

更多高通开发者资源及技术问题请访问:高通开发者论坛

  • 19
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值