用39块钱的全志V851se视觉开发板做了个小相机,还可以物品识别、自动追焦!

news2025/1/13 3:06:18

用39块钱的V851se视觉开发板做了个小相机。

在这里插入图片描述

可以进行物品识别、自动追焦!

在这里插入图片描述

在这里插入图片描述

这个超低成本的小相机是在V851se上移植使用全志在线开源版本的Tina Linux与OpenCV框架开启摄像头拍照捕获视频,并结合NPU实现Mobilenet v2目标分类识别以及运动追踪等功能…并最终实现功能完整的智能小相机。

ISP适配

可以适配V851se的Tina5.0升级到了21.05版本的OpenWrt,相较于商业量产版本的Tina Linux 支持更多新的软件包,不过可惜的是MPP包没有移植到Tina5.0,所以想使用摄像头就需要另辟蹊径。

虽然Tina5.0并没有移植MPP包,但也内置了一个libAWispApi包,支持在用户层对接 ISP,但是很可惜这个包也没有适配V85x系列,这里就需要自行适配。

其实适配很简单,SDK 已经提供了lib只是没提供编译支持,我们需要前往以下文件夹中新建一个v851se文件夹:

openwrt/package/allwinner/vision/libAWIspApi/machinfo

然后再新建文件build.mk写入如下配置:

ISP_DIR:=isp600

在这里插入图片描述

然后在menuconfig中勾选上这个包,并进行如下配置:

Allwinner  --->
  Vision  --->
    <*> camerademo........................................ camerademo test sensor  --->
      [*]   Enabel vin isp support

在这里插入图片描述

编译系统然后烧录系统,运行命令 camerademo ,可以看到是正常拍摄照片的

在这里插入图片描述

OpenCV适配

OpenCV在打包好的固件中已经默认适配好了,如果不想了解如何适配OpenCV可以直接前往点击链接获取资料并跳过这部分

OpenCV默认不支持开启RAW Sensor,不过现在需要配置为OpenCV开启RAW Sensor抓图,然后通过OpenCV送图到之前适配的libAWispApi库进行 ISP 处理。

在这里增加一个函数作为 RAW Sensor 抓图的处理。

#ifdef __USE_VIN_ISP__
bool CvCaptureCAM_V4L::RAWSensor()
{
    struct v4l2_control ctrl;
    struct v4l2_queryctrl qc_ctrl;

    memset(&ctrl, 0, sizeof(struct v4l2_control));
    memset(&qc_ctrl, 0, sizeof(struct v4l2_queryctrl));
    ctrl.id = V4L2_CID_SENSOR_TYPE;
    qc_ctrl.id = V4L2_CID_SENSOR_TYPE;

    if (-1 == ioctl (deviceHandle, VIDIOC_QUERYCTRL, &qc_ctrl)){
        fprintf(stderr, "V4L2: %s QUERY V4L2_CID_SENSOR_TYPE failed\n", deviceName.c_str());
        return false;
    }

    if (-1 == ioctl(deviceHandle, VIDIOC_G_CTRL, &ctrl)) {
        fprintf(stderr, "V4L2: %s G_CTRL V4L2_CID_SENSOR_TYPE failed\n", deviceName.c_str());
        return false;
    }

    return ctrl.value == V4L2_SENSOR_TYPE_RAW;
}
#endif

这段代码的功能是检查V4L2摄像头设备的传感器类型是否为RAW格式。它使用了V4L2的ioctl函数来查询和获取传感器类型信息。

然后在OpenCV的捕获流函数:

bool CvCaptureCAM_V4L::streaming(bool startStream)

添加 ISP 处理

#ifdef __USE_VIN_ISP__
  RawSensor = RAWSensor();

  if (startStream && RawSensor) {
    int VideoIndex = -1;

    sscanf(deviceName.c_str(), "/dev/video%d", &VideoIndex);

    IspPort = CreateAWIspApi();
    IspId = -1;
    IspId = IspPort->ispGetIspId(VideoIndex);
    if (IspId >= 0)
      IspPort->ispStart(IspId);
  } else if (RawSensor && IspId >= 0 && IspPort) {
    IspPort->ispStop(IspId);
    DestroyAWIspApi(IspPort);
    IspPort = NULL;
    IspId = -1;
  }
#endif

这段代码主要用于控制图像信号处理(ISP)的启动和停止。根据条件的不同,可以选择在开始视频流捕获时启动ISP流处理,或者在停止视频流捕获时停止ISP流处理,以便对视频数据进行处理和增强。

至于其他包括编译脚本的修改,全局变量定义等操作,可以参考原文链接中的补丁文件。

在执行完以上步骤后,可以快速测试摄像头输出demo:

OpenCV  --->
  <*> opencv....................................................... opencv libs
  [*]   Enabel sunxi vin isp support
  <*> opencv_camera.............................opencv_camera and display image

在这里插入图片描述

MobileNet V2

MobileNet V2是一种轻量级的卷积神经网络,它专为移动设备和嵌入式设备上的实时图像分类和目标检测任务设计。

MobileNet V2的关键特点包括使用深度可分离卷积来减少计算量和参数数量,引入带线性瓶颈的倒残差结构以增加非线性表示能力,以及提供宽度乘数参数以适应不同计算资源限制。这些特点使得MobileNet V2成为资源受限的移动设备上的理想选择。

首先对输入图像进行预处理,以适应MobileNet V2 SSD模型的输入要求。通过通道格式转换、图像大小调整和数据填充等操作,将输入图像转换为适合模型输入的格式。

void get_input_data(const cv::Mat& sample, uint8_t* input_data, int input_h, int input_w, const float* mean, const float* scale){
    cv::Mat img;
    if (sample.channels() == 1)
        cv::cvtColor(sample, img, cv::COLOR_GRAY2RGB);
    else
        cv::cvtColor(sample, img, cv::COLOR_BGR2RGB);
    cv::resize(img, img, cv::Size(input_h, input_w));
    uint8_t* img_data = img.data;
    /* nhwc to nchw */
    for (int h = 0; h < input_h; h++) {
        for (int w = 0; w < input_w; w++) {
            for (int c = 0; c < 3; c++) {
                int in_index = h * input_w * 3 + w * 3 + c;
                int out_index = c * input_h * input_w + h * input_w + w;
                input_data[out_index] = (uint8_t)(img_data[in_index]);  //uint8

关键步骤是要实现非极大值抑制算法(NMS),用于去除高度重叠的框,只保留得分最高的那个框。算法通过计算框之间的交集面积和设置的阈值来进行筛选,并将保留的框的索引存储在picked向量中。

// 非极大值抑制算法(NMS)
static void nms_sorted_bboxes(const std::vector<Bbox_t>& bboxs, std::vector<int>& picked, float nms_threshold) {
    picked.clear();
    const int n = bboxs.size();
    
    // 创建存储每个框面积的向量
    std::vector<float> areas(n);
    
    // 计算每个框的面积并存储
    for (int i = 0; i < n; i++){
        areas[i] = (bboxs[i].xmax - bboxs[i].xmin) * (bboxs[i].ymax - bboxs[i].ymin);

通过一系列操作,包括转换为向量、计算缩放比例、创建存储检测结果的向量等,将输出数据转换为检测结果,并按照置信度从高到低排序。然后应用非极大值抑制算法对检测结果进行筛选,最后将筛选后的目标框位置、大小和类别置信度等信息绘制在图像上。

// 按照分数对框进行排序
    std::sort(BBox.begin(), BBox.end(), comp);

    // 应用非极大值抑制算法,获取保留的框的索引
    std::vector<int> keep_index;
    nms_sorted_bboxes(BBox, keep_index, iou_threshold);

    // 创建存储框位置的向量
    std::vector<cv::Rect> bbox_per_frame;

    // 遍历保留的框,绘制框和标签
    for(int i = 0; i < keep_index.size(); i++) {
        int left = BBox[keep_index[i]].xmin;
        int top = BBox[keep_index[i]].ymin;
        int right = BBox[keep_index[i]].xmax;
        int bottom = BBox[keep_index[i]].ymax;
        cv::rectangle(bgr, cv::Point(left, top), cv::Point(right, bottom), cv::Scalar(0, 0, 255), 1);
        char text[256];
        sprintf(text, "%s %.1f%%", class_names[BBox[keep_index[i]].cls_idx], BBox[keep_index[i]].score * 100);
        cv::putText(bgr, text, cv::Point(left, top), cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(0, 255, 255), 1, 8, 0);
        bbox_per_frame.emplace_back(left, top, width, height);
    }

在这里插入图片描述

NPU开发流程

V851se芯片内置一颗NPU,其处理性能为最大0.5TOPS并有128KB内部高速缓存用于高速数据交换,NPU 开发完整的流程如下图所示:

在这里插入图片描述

模型训练

在模型训练阶段,用户根据需求和实际情况选择合适的框架(如Caffe、TensorFlow 等)使用数据集进行训练得到符合需求的模型,此模型可称为预训练模型。也可直接使用已经训练好的模型。V851s 的 NPU 支持包括分类、检测、跟踪、人脸、姿态估计、分割、深度、语音、像素处理等各个场景90 多个公开模型。

signal函数

在模型转化阶段,通过Acuity Toolkit把预训练模型和少量训练数据转换为NPU可用的模型NBG文件。一般步骤如下:

1、模型导入,生成网络结构文件、网络权重文件、输入描述文件和输出描述文件。
2、模型量化,生成量化描述文件和熵值文件,可改用不同的量化方式。
3、仿真推理,可逐一对比float和其他量化精度的仿真结果的相似度,评估量化后的精度是否满足要求。
4、模型导出,生成端侧代码和*.nb 文件,可编辑输出描述文件的配置,配置是否添加后处理节点等。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1471130.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CentOS删除除了最近5个JAR程序外的所有指定Java程序

帮我写一个shell脚本,ps -eo pid,lstart,cmd --sort=-start_time | grep "pgz-admin"查到的结果,返回的所有进程PID,第六个之上的,全部kill 当然,你可以创建一个简单的Shell脚本来完成这个任务。以下是一个例子: #!/bin/bash# 获取包含 "pgz-admin"…

SSM框架学习笔记07 | Spring MVC入门

文章目录 1. HTTP协议2. Spring MVC2.1. 三层架构2.2. MVC&#xff08;解决表现层的问题&#xff09;2.3. 核心组件 3. Thymeleaf3.1. 模板引擎3.2. Thymeleaf3.3. 常用语法 代码 1. HTTP协议 网址&#xff1a;https://www.ietf.org/ &#xff08;官网网址&#xff09; https:…

什么是系统工程(字幕)37

0 00:00:00,740 --> 00:00:03,200 但是呢&#xff0c;我们往后面看 1 00:00:04,100 --> 00:00:08,920 刚才我们讲到那个说&#xff0c;应该是组合关系 2 00:00:08,920 --> 00:00:09,522 对吧 3 00:00:09,522 --> 00:00:11,330 不是泛化关系 4 00:00:12,520 --&…

JavaWeb 自己给服务器安装SQL Server数据库遇到的坑

之前买的虚拟主机免费送了一个SQL Server数据库&#xff0c;由于服务器提供商今年下架我用的那款虚拟主机产品&#xff0c;所以数据库也被收回了。我买了阿里云云服务器&#xff0c;但是没有数据库&#xff0c;于是自己装了一个SQL Server数据库&#xff0c;总结一下遇到的坑。…

JAVA毕业设计129—基于Java+Springboot+thymeleaf的物业管理系统(源代码+数据库)

毕设所有选题&#xff1a; https://blog.csdn.net/2303_76227485/article/details/131104075 基于JavaSpringbootthymeleaf的物业管理系统(源代码数据库)129 一、系统介绍 本项目前后端分离&#xff0c;本系统分为管理员、小区管理员、用户三种角色 1、用户&#xff1a; 登…

栈和堆什么意思,Rust所有权机制又是什么

栈和堆什么意思 栈&#xff1a;存储基本数据类型和引用数据类型的指针引用(地址)&#xff0c;基本数据类型占据固定大小的内存空间。 堆&#xff1a;存储引用数据类型的值&#xff0c;引用数据类型包括对象&#xff0c;数组和函数&#xff0c;在堆中&#xff0c;引用数据类型…

WhatsApp Business API入门:功能、收费、申请方法全解析

WhatsApp Business API是为企业量身打造的一种高级服务&#xff0c;它提供了一组编程接口&#xff08;API&#xff09;&#xff0c;而不是一个可以直接操作的用户界面。与免费版的WhatsApp Business应用不同&#xff0c;WhatsApp Business API并不能在应用商店如App Store或Goo…

git之远程操作

一.分布式版本控制系统 分布式版本控制系统通常也有⼀台充当“中央服务器”的电脑&#xff0c;但这个服务器的作⽤仅仅是⽤来⽅便“交换”⼤家的修改&#xff0c;没有它⼤家也⼀样⼲活&#xff0c;只是交换修改不⽅便⽽已。有了这个“中央服务器”的电脑&#xff0c;这样就不怕…

01背包问题:组合问题

01背包问题&#xff1a;组合问题 题目 思路 将nums数组分成left和right两组&#xff0c;分别表示相加和相减的两部分&#xff0c;则&#xff1a; left - right targetleft right sum 进而得到left为确定数如下&#xff0c;且left必须为整数&#xff0c;小数表示组合不存在&…

osmnx笔记:从OpenStreetMap中提取点和边的shp文件(FMM文件准备内容)

1 导入库 import osmnx as ox import time from shapely.geometry import Polygon import os import numpy as np 2 提取Openstreetmap 的graph Gox.graph_from_place(Huangpu,Shanghai,China,network_typedrive,simplifyTrue) ox.plot_graph(G) 3 提取graph中的点和边 gdf…

2/22作业

1.按位置插入 void insert_pos(seq_p L,datetype value,int pos) { if(LNULL) { printf("入参为空\n"); return; } if(seq_full(L)) { printf("表已满\n"); return; } if(pos>L->len|…

软考45-上午题-【数据库】-数据操纵语言DML

一、INSERT插入语句 向SQL的基本表中插入数据有两种方式&#xff1a; ①直接插入元组值 ②插入一个查询的结果值 1-1、直接插入元组值 【注意】&#xff1a; 列名序列是可选的&#xff0c;若是所有列都要插入数值&#xff0c;则可以不写列名序列。 示例&#xff1a; 1-2、插…

100天精通Python(实用脚本篇)——第117天:基于selenium实现反反爬策略之代码输入账号信息登录网站

文章目录 专栏导读1. 前言2. 实现步骤3. 基础补充4. 代码实战4.1 创建连接4.2 添加请求头伪装浏览器4.3 隐藏浏览器指纹4.4 最大化窗口4.5 启动网页4.6 点击密码登录4.7 输入账号密码4.8 点击登录按钮4.9 完整代码4.10 GIF动图展示 五、总结 专栏导读 &#x1f525;&#x1f5…

three中界面交互gui.js库的使用

gui.js库(可视化改变三维场景) dat.gui.js说白了就是一个前端js库&#xff0c;对HTML、CSS和JavaScript进行了封装&#xff0c;学习开发的时候&#xff0c;借助dat.gui.js可以快速创建控制三维场景的UI交互界面&#xff0c;你打开课件中案例源码体验一下就能感受到。 学习dat…

AWK Programing Language | 史上最全 awk 一本通

博客原文 文章目录 awk 工作模式语法格式内置变量格式化输出 printf模式匹配awk 算数运算条件语句循环语句字符串函数awk 常用选项awk 与 shell 中数组shell 中数组awk 中数组 脚本练习 awk 工作模式 与 sed 相同, 都是逐行处理 语法格式 awk ‘BEGIN{}pattern{commands}END{…

matlab倒立摆小车LQR控制动画

1、内容简介 略 54-可以交流、咨询、答疑 2、内容说明 略 摆杆长度为 L&#xff0c;质量为 m 的单级倒立摆(摆杆的质心在杆的中心处)&#xff0c;小车的质量为 M。在水平方向施加控制力 u&#xff0c;相对参考系产生位移为 y。为了简化问题并且保其实质不变&#xff0c;忽…

[ffmpeg] x264 配置参数解析

背景 创建 x264 编码器后&#xff0c;其有一组默认的编码器配置参数&#xff0c;也可以根据需要修改参数&#xff0c;来满足编码要求。 具体参数 可修改的参数&#xff0c;比较多&#xff0c;这边只列举一些常用的。 获取可以配置的参数 方式1 查看 ffmpeg源码 libx264.c…

【JVM】聊聊JVM生产环境常见的OOM问题

对于JVM来说&#xff0c;因为划分有固定的区域来执行字节码文件&#xff0c;无外乎&#xff0c;出问题的&#xff0c;也就是按照对应分分区会有常见的OOM问题。 栈 对于栈来说&#xff0c;栈的主要作用就是用于方法的执行&#xff0c;方法调用入栈、方法调出出栈。但是如果我…

光伏预测 | Matlab基于CNN-SE-Attention-ITCN的多特征变量光伏预测

光伏预测 | Matlab基于CNN-SE-Attention-ITCN的多特征变量光伏预测 目录 光伏预测 | Matlab基于CNN-SE-Attention-ITCN的多特征变量光伏预测预测效果基本描述模型简介程序设计参考资料 预测效果 基本描述 Matlab基于CNN-SE-Attention-ITCN的多特征变量光伏预测 运行环境: Matla…

数学建模【插值与拟合】

一、插值与拟合简介 在数学建模过程中&#xff0c;通常要处理由试验、测量得到的大量数据或一些过于复杂而不便于计算的函数表达式&#xff0c;针对此情况&#xff0c;很自然的想法就是&#xff0c;构造一个简单的函数作为要考察数据或复杂函数的近似。插值和拟合就可以解决这…