XIAO ESP32S3之物体检测加入视频流

一、前言

        由于XIAO ESP32S3开发套件没有显示屏配件,因此加入http视频流功能,可通过浏览器请求ESP32S3上的视频流。

二、思路

1、XIAO ESP32S3启动后通过wifi连接到AP;

2、启动http服务器,注册get_mjpeg处理函数;

3、主任务将算法输出的图像压缩为jpg通过xMessageBuffer传递给get_mjpeg处理函数;

4、连接到同一个AP的终端启动浏览器输入XIAO ESP32S3的IP:8081获取视频流。

三、编写代码

1、加入文件

main文件夹下增加http_stream.cpp、http_stream.h文件

修改CMakeLists.txt文件,加入http_stream.cpp,内容如下:

idf_component_register(SRCS app_main.cppfomo_mobilenetv2_model_data.cpphttp_stream.cpp
)
2、主函数中加入连接AP和传递图像的功能

修改之后的app_main.cpp代码:

#include <inttypes.h>
#include <stdio.h>#include "img_converters.h"
#include "core/edgelab.h"
#include "fomo_mobilenetv2_model_data.h"#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/message_buffer.h"#include "http_stream.h"#define DEMO_WIFI_SSID   "huochaigun"
#define DEMO_WIFI_PASS   "12345678"#define STORAGE_SIZE_BYTES 128*1024static uint8_t *ucStorageBuffer;StaticMessageBuffer_t xMessageBufferStruct;#define kTensorArenaSize (1024 * 1024)uint16_t color[] = {0x0000,0x03E0,0x001F,0x7FE0,0xFFFF,
};extern "C" void app_main(void) {using namespace edgelab;Device*  device  = Device::get_device();device->init();printf("device_name:%s\n", device->get_device_name() );Network* net = device->get_network();el_printf(" Network Demo\n");uint32_t cnt_for_retry = 0;net->init();while (net->status() != NETWORK_IDLE) {el_sleep(100);if(cnt_for_retry++ > 50) {net->init();cnt_for_retry = 0;}}el_printf(" Network initialized!\n");cnt_for_retry = 0;net->join(DEMO_WIFI_SSID, DEMO_WIFI_PASS);while (net->status() != NETWORK_JOINED) {el_sleep(100);if(cnt_for_retry++ > 100) {net->join(DEMO_WIFI_SSID, DEMO_WIFI_PASS);cnt_for_retry = 0;}}el_printf(" WIFI joined!\n");  //    Display* display = device->get_display();Camera*  camera  = device->get_camera();//   display->init();camera->init(240, 240);ucStorageBuffer = (uint8_t *)malloc( STORAGE_SIZE_BYTES );if( ucStorageBuffer != NULL ){xMessageBuffer = xMessageBufferCreateStatic( STORAGE_SIZE_BYTES, ucStorageBuffer, &xMessageBufferStruct );if( xMessageBuffer == NULL ){// There was not enough heap memory space available to create the// message buffer.printf("Create xMessageBuffer fail\n");}}else{printf("malloc ucStorageBuffer fail\n");}start_http_stream();auto* engine       = new EngineTFLite();auto* tensor_arena = heap_caps_malloc(kTensorArenaSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);engine->init(tensor_arena, kTensorArenaSize);engine->load_model(g_fomo_mobilenetv2_model_data, g_fomo_mobilenetv2_model_data_len);auto* algorithm = new AlgorithmFOMO(engine);while (true) {el_img_t img;camera->start_stream();camera->get_frame(&img);algorithm->run(&img);uint32_t preprocess_time  = algorithm->get_preprocess_time();uint32_t run_time         = algorithm->get_run_time();uint32_t postprocess_time = algorithm->get_postprocess_time();uint8_t  i                = 0u;for (const auto& box : algorithm->get_results()) {el_printf("\tbox -> cx_cy_w_h: [%d, %d, %d, %d] t: [%d] s: [%d]\n",box.x,box.y,box.w,box.h,box.target,box.score);int16_t y = box.y - box.h / 2;int16_t x = box.x - box.w / 2;el_draw_rect(&img, x, y, box.w, box.h, color[++i % 5], 4);}el_printf("preprocess: %d, run: %d, postprocess: %d\n", preprocess_time, run_time, postprocess_time);
//        display->show(&img);uint8_t * jpg_buf;size_t jpg_buf_len;bool jpeg_converted = fmt2jpg( img.data, img.size, img.width, img.height, PIXFORMAT_RGB565, 30, &jpg_buf, &jpg_buf_len);if( jpeg_converted == true ){
//            printf("jpg_buf_len:%d\n", jpg_buf_len );if( xMessageBuffer != NULL ){xMessageBufferSend( xMessageBuffer, jpg_buf, jpg_buf_len , 0 );}free(jpg_buf);}camera->stop_stream();}delete algorithm;delete engine;
}

说明:RGB转为jpg的图像质量为30,这样转换后的图形数据小,视频流更流畅。

3、http服务

http_stream.cpp代码:

#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>#include "esp_log.h"
#include "esp_timer.h"#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"
#include "esp_http_server.h"
#include "freertos/message_buffer.h"#define PART_BOUNDARY "123456789000000000000987654321"static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";static const char *TAG = "http_stream";MessageBufferHandle_t xMessageBuffer;
/*** @brief  jpg_stream_httpd_handler* @param  None* @retval None*/
esp_err_t jpg_stream_httpd_handler(httpd_req_t *req)
{esp_err_t res = ESP_OK;size_t _jpg_buf_size;size_t _jpg_buf_len;uint8_t * _jpg_buf;char * part_buf[64];static int64_t last_frame = 0;if(!last_frame) {
//		last_frame = esp_timer_get_time();}_jpg_buf_size = 128*1024;_jpg_buf = (uint8_t *)malloc( _jpg_buf_size );if( _jpg_buf == NULL ){return res;}xMessageBufferReset( xMessageBuffer );res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);if(res != ESP_OK){return res;}while(true){size_t xReceivedBytes = xMessageBufferReceive( xMessageBuffer, _jpg_buf, _jpg_buf_size, pdMS_TO_TICKS(100) );_jpg_buf_len = xReceivedBytes;res = ESP_FAIL;if( _jpg_buf_len > 0 ){res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));}else{continue;}if(res == ESP_OK){size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);}if(res == ESP_OK){res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);}if(res != ESP_OK){break;}}free( _jpg_buf );last_frame = 0;return res;
}httpd_uri_t uri_get_mjpeg = {.uri = "/",.method = HTTP_GET,.handler = jpg_stream_httpd_handler,.user_ctx = NULL};/*** @brief  start http_stream.* @param  None.* @retval None.*/
httpd_handle_t start_http_stream( void )
{printf("start_http_stream\n");/* 生成默认的配置参数 */httpd_config_t config = HTTPD_DEFAULT_CONFIG();config.server_port = 8081;/* 置空 esp_http_server 的实例句柄 */httpd_handle_t server = NULL;/* 启动 httpd server */if (httpd_start(&server, &config) == ESP_OK){/* 注册 URI 处理程序 */// httpd_register_uri_handler(server, &uri_get);// httpd_register_uri_handler(server, &uri_get_1m);httpd_register_uri_handler(server, &uri_get_mjpeg);}/* 如果服务器启动失败,返回的句柄是 NULL */return server;
}

http_stream.h代码:

#ifndef __HTTP_STREAM_H__
#define __HTTP_STREAM_H__extern MessageBufferHandle_t xMessageBuffer;void start_http_stream( void );#endif
4、修改PSRAM的配置

由于增加的功能需要消耗很多RAM空间,所以需要将PSRAM充分利用起来。

执行idf.py menuconfig,修改SPI RAM config,将malloc()阀值改小一些,这里改为4096字节,意思是使用malloc()分配内存时,大于4096字节则从外部PSRAM获取,配置如下:

 Component config  --->

        ESP PSRAM  --->

                SPI RAM config  --->

                        (4096) Maximum malloc() size, in bytes, to always put in internal memory

四 、运行测试

1、编译、烧录、监视
idf.py build
idf.py flash
idf.py monitor

连接AP的日志:

I (813) wifi:mode : sta (dc:54:75:d7:a2:10)
I (813) wifi:enable tsfNetwork initialized!
I WAITING FOR IP...I (2033) wifi:new:<11,0>, old:<1,0>, ap:<255,255>, sta:<11,0>, prof:1
I (2403) wifi:state: init -> auth (b0)
I (2413) wifi:state: auth -> assoc (0)
I (2423) wifi:state: assoc -> run (10)
W (2423) wifi:[ADDBA]rx delba, code:39, delete tid:5
I (2443) wifi:<ba-add>idx:0 (ifx:0, 90:76:9f:23:a3:58), tid:5, ssn:6, winSize:64
I (2583) wifi:connected with CMCC-2106, aid = 1, channel 11, BW20, bssid = 90:76:9f:23:a3:58
I (2583) wifi:security: WPA2-PSK, phy: bgn, rssi: -63
I (2583) wifi:pm start, type: 1I (2583) wifi:dp: 1, bi: 102400, li: 3, scale listen interval from 307200 us to 307200 us
I (2583) wifi:set rx beacon pti, rx_bcn_pti: 0, bcn_timeout: 25000, mt_pti: 0, mt_time: 10000
I (2633) wifi:AP's beacon interval = 102400 us, DTIM period = 1
I (3583) esp_netif_handlers: edgelab ip: 192.168.10.104, mask: 255.255.255.0, gw: 192.168.10.1

 可见IP地址为192.168.10.104。

2、浏览器获取视频流

同一个局域网的电脑启动浏览器,地址栏输入:192.168.10.104:8081,然后回车,浏览器会显示视频,图像分辨率为240*240,因为转换为jpg的质量设置的较低,所以不怎么清晰,下图为浏览器显示的图像:

 

有方框表示识别到了物体。

五、总结

此模型主要是对尺寸较小且颜色较深的物体能检测。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PyTorch实战:基于Seq2seq模型处理机器翻译任务(模型预测)

文章目录 引言数据预处理加载字典对象en2id和zh2id文本分词 加载训练好的Seq2Seq模型模型预测完整代码结束语 引言 随着全球化的深入&#xff0c;翻译需求日益增长。传统的人工翻译方式虽然质量高&#xff0c;但效率低&#xff0c;成本高。机器翻译的出现&#xff0c;为解决这…

虚函数的讲解

文章目录 虚函数的声明与定义代码演示基类Person派生类Man派生类Woman 测试代码动态绑定静态绑定访问私有虚函数总结一下通过成员函数指针调用函数的方式 虚函数的声明与定义 虚函数存在于C的类、结构体等中&#xff0c;不能存在于全局函数中&#xff0c;只能作为成员函数存在…

IntelliJ IDEA [插件 MybatisX] mapper和xml间跳转

文章目录 1. 安装插件2. 如何使用3. 主要功能总结 MybatisX 是一款为 IntelliJ IDEA 提供支持的 MyBatis 开发插件 它通过提供丰富的功能集&#xff0c;大大简化了 MyBatis XML 文件的编写、映射关系的可视化查看以及 SQL 语句的调试等操作。本文将介绍如何安装、配置和使用 In…

知识库问答LangChain+LLM的二次开发:商用时的典型问题及其改进方案

前言 如之前的文章所述&#xff0c;我司下半年成立大模型项目团队之后&#xff0c;我虽兼管整个项目团队&#xff0c;但为让项目的推进效率更高&#xff0c;故分成了三大项目组 第一项目组由霍哥带头负责类似AIGC模特生成系统第二项目组由阿荀带头负责论文审稿GPT以及AI agen…

基于飞浆OCR的文本框box及坐标中心点检测JSON格式保存文本

OCR的文本框box及JSON数据保存 需求说明 一、借助飞浆框出OCR识别的文本框 二、以圆圈形式标出每个框的中心点位置 三、以JSON及文本格式保存OCR识别的文本 四、以文本格式保存必要的文本信息 解决方法 一、文本的坐标来自飞浆的COR识别 二、借助paddleocr的draw_ocr画出…

go语言,ent库与gorm库,插入一条null值的time数据

情景介绍 使用go语言&#xff0c;我需要保存xxxTime的字段至数据库中&#xff0c;这个字段可能为空&#xff0c;也可能是一段时间。我采取的是统一先赋值为空&#xff0c;若有需要&#xff0c;则再进行插入&#xff08;需要根据另一个字段判断是否插入&#xff09; 在我的数据…

最新国内使用GPT4教程,GPT语音对话使用,Midjourney绘画,ChatFile文档对话总结+DALL-E3文生图

一、前言 ChatGPT3.5、GPT4.0、GPT语音对话、Midjourney绘画&#xff0c;文档对话总结DALL-E3文生图&#xff0c;相信对大家应该不感到陌生吧&#xff1f;简单来说&#xff0c;GPT-4技术比之前的GPT-3.5相对来说更加智能&#xff0c;会根据用户的要求生成多种内容甚至也可以和…

HPCC:高精度拥塞控制

HPCC&#xff1a;高精度拥塞控制 文章目录 HPCC&#xff1a;高精度拥塞控制摘要1 引言1.1 背景1.2 现有CC的局限性1.3 HPCC的提出 2 研究动机2.1 大型RDMA部署2.2 RDMA目标2.3 当前RDMA CC中的权衡DCQCNTIMELY 2.4 下一代高速CC 3 技术方案3.1 INT3.2 HPCC设计3.3 HPPC的参数 4…

浅谈WPF之ToolTip工具提示

在日常应用中&#xff0c;当鼠标放置在某些控件上时&#xff0c;都会有相应的信息提示&#xff0c;从软件易用性上来说&#xff0c;这是一个非常友好的功能设计。那在WPF中&#xff0c;如何进行控件信息提示呢&#xff1f;这就是本文需要介绍的ToolTip【工具提示】内容&#xf…

数据结构入门到入土——List的介绍

目录 一&#xff0c;什么是List&#xff1f; 二&#xff0c;常见接口介绍 三&#xff0c;List的使用 一&#xff0c;什么是List&#xff1f; 在集合框架中&#xff0c;List是一个接口&#xff0c;继承自Collection。 Collection也是一个接口&#xff0c;该接口中规范了后序容…

MATLAB中./和/,.*和*,.^和^的区别

MATLAB中./和/&#xff0c;.*和*&#xff0c;.^ 和^ 的区别 MATLAB中./和/&#xff0c;.*和*&#xff0c;.^ 和^ 的区别./ 和 / 的区别.//实验实验结果 .* 和 * 的区别.**实验实验结果 .^ 和^ 的区别.^n^n实验运行结果 MATLAB中./和/&#xff0c;.和&#xff0c;.^ 和^ 的区别 …

关于SQL时间盲注(基于sleep函数)的手动测试、burpsuite爆破、sqlmap全自动化注入

SQL时间注入是一种常见的SQL注入攻击方式&#xff0c;攻击者通过在SQL语句中注入时间相关的代码&#xff0c;来获取敏感信息或者执行非法操作。其基本原理如下&#xff1a; 攻击者向Web应用程序中输入一段恶意代码&#xff0c;通过SQL语句查询数据库&#xff0c;并注入时间相关…

钉钉机器人接入定时器(钉钉API+XXL-JOB)

钉钉机器人接入定时器&#xff08;钉钉APIXXL-JOB&#xff09; 首先需要创建钉钉内部群 在群设置中找到机器人选项 选择“自定义”机器人 通过Webhook接入自定义服务 创建完成后会生成一个send URL和一个加签码 下面就是干货 代码部分了 DingDingUtil.sendMessageByText(webho…

什么是迁移学习(Transfer Learning)?定义,优势,方法

迄今为止&#xff0c;大多数人工智能&#xff08;AI&#xff09;项目都是通过监督学习技术构建的。监督学习是一种从无到有构建机器学习&#xff08;ML&#xff09;模型的方法&#xff0c;它对推动AI发展起到了关键作用。然而&#xff0c;由于需要大量的数据集和强大的计算能力…

账号租号平台PHP源码,支持单独租用或合租使用

源码简介 租号平台源码&#xff0c;采用常见的租号模式。 平台的主要功能如下&#xff1a; 支持单独租用或采用合租模式&#xff1b; 采用易支付通用接口进行支付&#xff1b; 添加邀请返利功能&#xff0c;以便站长更好地推广&#xff1b; 提供用户提现功能&#xff1b;…

PHP的Laravel加一个小页面出现问题(whereRaw的用法)

1.权限更新问题 因为是已经有样例了所以html和php页面很快写出来了 然后就是页面写完了路由不知道在哪写&#xff0c;后来想起来之前有要开权限来着&#xff0c;试了一下&#xff0c;还是不行&#xff0c;不过方向是对了 这是加的路由&#xff0c;不过需要在更新一下权限 这…

【产品经理】axure中继器的使用——表格增删改查分页实现

笔记为个人总结笔记&#xff0c;若有错误欢迎指出哟~ axure中继器的使用——表格增删改查分页实现 中继器介绍总体视图视频预览功能1.表头设计2.中继器3.添加功能实现4.删除功能实现5.修改功能实现6.查询功能实现7.批量删除 中继器介绍 在 Axure RP9 中&#xff0c;中继器&…

leetcode贪心算法题总结(一)

此系列分三章来记录leetcode的有关贪心算法题解&#xff0c;题目我都会给出具体实现代码&#xff0c;如果看不懂的可以后台私信我。 本章目录 1.柠檬水找零2.将数组和减半的最少操作次数3.最大数4.摆动序列5.最长递增子序列6.递增的三元子序列7.最长连续递增序列8.买卖股票的最…

C++:map和set的介绍及使用

目录 1. 关联式容器 2. 键值对 3. 树形结构的关联式容器 3.1 set 3.1.1 set的介绍 3.1.2 set的使用 3.2 map 3.2.1 map的介绍 3.2.2 map的使用 3.3 multiset 3.3.1 multiset的介绍 3.3.2 multiset的使用 3.4 multimap 3.4.1 multimap的介绍 3.4.2 multimap的使用…

Linux磁盘与文件系统管理

在linux系统中使用硬盘 建立分区 安装文件系统 挂载 磁盘的数据结构 磁盘&#xff1a;扇区固定大小&#xff0c;每个扇区4k。磁盘会进行磨损&#xff0c;损失生命周期。 扇区 磁道 柱面 磁盘接口类型 ide SATA SAS SCSI SCSI 设备类型 块设备&#xff1a;block …