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

news2024/11/16 12:01:44

一、前言

        由于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.cpp
    fomo_mobilenetv2_model_data.cpp
    http_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*1024

static 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 tsf
 Network 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: 1

I (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.coloradmin.cn/o/1339253.html

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

相关文章

判断电话号码是否重复-excel

有时候重复的数据不需要或者很烦人&#xff0c;就需要采取措施&#xff0c;希望以下的方法能帮到你。 1.判断是否重复 方法一&#xff1a; 1&#xff09;针对第一个单元格输入等号&#xff0c;以及公式countif(查找记录数的范围&#xff0c;需要查找的单元格&#xff09; 2…

Linux:不同计算机使用NFS共享资源

一&#xff0c;安装NFS文件系统 NFS即网络文件系统(network file system)&#xff0c;它允许网络中的计算机之间通过网络共享资源。目前&#xff0c;NFS只用于在Linux和UNIX主机间共享文件系统。 #使用mount命令可以将远程主机的文件系统 安装到 本地&#xff1a; #将远程主机…

2.3_3 进程互斥的硬件实现办法

2.3_3 进程互斥的硬件实现办法 1.中断屏蔽方法 利用“开/关中断指令”实现&#xff08;与原语的实现思想相同,即在某进程开始访问临界区到结束访问为止都不允许被中断,也就不能发生进程切换,因此也不可能发生两个同时访问临界区的情况&#xff09; 优点&#xff1a;简单、高效…

浏览器Post请求出现413 Request Entity Too Large (Nginx)

环境 操作系统 window server 2016 前端项目 Vue2 Nginx-1.25.3 一、错误信息 前端是vue项目&#xff0c;打包后部署在Nginx上&#xff0c;前端post请求出现Request Entity Too Large错误信息。 ​这种问题一般是请求实体太大&#xff08;包含参数&#xff0c;文件等&#xf…

是德科技E9304A功率传感器

是德科技E9304A二极管功率传感器测量频率范围为9 kHz至6 GHz的平均功率&#xff0c;功率范围为-60至20 dBm。该传感器非常适合甚低频(VLF)功率测量。E系列E9304A功率传感器有两个独立的测量路径&#xff0c;设计用于EPM系列功率计。功率计自动选择合适的功率电平路径。为了避免…

HTML制作暴雨特效

🎀效果展示 🎀代码展示 <body> <!-- partial:index.partial.html --> <canvas id="canvas-club">

c语言结构体(初阶)

1. 结构体的声明 1.1 结构体的基础知识 结构是一些值的集合&#xff0c;这些值被称为成员变量。结构的每个成员可以是不同类型的变量。 1.2 结构的声明 struct tag {member - list; }variable-list; 例&#xff1a;描述一个人的信息&#xff1a;名字电话性别身高 //声明的…

如何利用ChatGPT处理文本、论文写作、AI绘图、文献查阅、PPT编辑、编程等

无论是进行代码生成、错误调试还是解决编程难题&#xff0c;ChatGPT都能为您提供实用且高质量的建议和指导&#xff0c;提高编程效率和准确性。此外&#xff0c;ChatGPT是一位出色的合作伙伴&#xff0c;可以为您提供论文写作的支持。它可以为您提供论文结构指导、段落重组建议…

LabVIEW快速入门

参考&#xff1a; 知乎labview快捷键 https://www.zhihu.com/question/287709225github 上 LabVIEW教程 https://lv.qizhen.xyz/LabVIEW 参考书籍 链接&#xff1a;https://pan.baidu.com/s/1irc0iftM0Gg3GCN2cBHCKw?pwdy1mz 提取码&#xff1a;y1mz --来自百度网盘超级会员V…

.NET 使用Camunda快速入门

一.工作流介绍 1. 什么是工作流 工作流&#xff08;Workflow&#xff09;&#xff0c;是对工作流程及其各操作步骤之间业务规则的抽象、概括描述。 工作流将一套大的业务逻辑分解成业务逻辑段&#xff0c; 并统一控制这些业务逻辑段的执行条件&#xff0c;执行顺序以及相互通…

【双系统】Windows10+Ubuntu22.04安装全过程以及安装后的配置

文章目录 0 背景0.1 电脑配置0.2 电脑磁盘的初始情况 1 准备空闲的磁盘1.1 文件整理1.2 压缩卷和扩展卷的操作 2 准备系统启动盘3 设置电脑的模式&#xff08;未验证&#xff09;并进入Ubuntu安装4 安装Ubuntu5 Ubuntu的配置6 其他 0 背景 之前一直在虚拟机上使用Ubuntu系统&am…

CentOS 5/6/7 基于开源项目制作openssh 9.6p1 rpm包—— 筑梦之路

背景介绍 开源项目地址&#xff1a;https://github.com/boypt/openssh-rpms.git 该项目主要支持了centos 5 、6、7版本&#xff0c;针对使用了比较老的操作系统进行openssh安全加固&#xff0c;还是不错的项目&#xff0c;使用简单、一件制作&#xff0c;欢迎大家去支持作者。…

YOLOv5改进 | 主干篇 | 利用SENetV2改进网络结构 (全网首发改进)

一、本文介绍 本文给大家带来的改进机制是SENetV2&#xff0c;其是2023.11月的最新机制(所以大家想要发论文的可以在上面下点功夫)&#xff0c;其是一种通过调整卷积网络中的通道关系来提升性能的网络结构。SENet并不是一个独立的网络模型&#xff0c;而是一个可以和现有的任何…

电影“AI化”已成定局,华为、小米转战入局又将带来什么?

从华为、Pika、小米等联合打造电影工业化实验室、到Pika爆火&#xff0c;再到国内首部AI全流程制作《愚公移山》开机……业内频繁的新动态似乎都在预示着2023年国内电影开始加速进入新的制片阶段&#xff0c;国内AI电影热潮即将来袭。 此时以华为为首的底层技术科技企业加入赛…

openmediavault(OMV) (19)云相册(3)mt-photos

简介 MT Photos是一款为Nas用户量身打造的照片管理系统。通过AI技术,自动将您的照片整理、分类,包括但不限于时间、地点、人物、照片类型。可以在任何支持Docker的系统中运行它。详情可查看mtmt.tech官网,mt-photos是付费订阅使用的,也可以一次性付费永久使用,具体使用mt…

修复Apache httpd中的SSL/TLS 协议信息泄露漏洞(CVE-2016-2183)

漏洞详情如下&#xff1a; 详细描述TLS是安全传输层协议&#xff0c;用于在两个通信应用程序之间提供保密性和数据完整性。 TLS, SSH, IPSec协商及其他产品中使用的IDEA、DES及Triple DES密码或者3DES及Triple 3DES存在大约四十亿块的生日界&#xff0c;这可使远程攻击者通过S…

记录 | ubuntu源码编译python3.7.3(指定版本)

一、安装依赖包 sudo apt-get install -y make build-essential libssl-dev zlib1g-dev sudo apt-get install -y libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm sudo apt-get install -y libncurses5-dev libncursesw5-dev xz-utils tk-dev 二、从Python网…

node.js express框架开发入门教程

文章目录 前言一、Express 生成器&#xff08;express-generator&#xff09;二、快速安装1.express框架express-generator生成器安装2.使用pug视图引擎创建项目,projectName 为项目名称自定义 三、安装热更新插件 nodemon四、目录结构1. public文件夹2.routes路由其他请求方式…

本地部署生成式AI,选显卡or笔记本电脑?!新款酷睿Ultra举票

来源 | 算力豹 200亿个大模型参数无压力&#xff0c;新一代酷睿Ultra凭什么&#xff1f; 12月14日报道&#xff0c;在大模型军备竞赛如火如荼的今天&#xff0c;真正让AI铺开惠民&#xff0c;那么移动端、PC将成为首选&#xff0c;AI PC或成标配。英特尔今日奉上AI硬件大招&am…

对于c++的总结与思考

笔者觉得好用的学习方法&#xff1a;模板法 1.采用原因&#xff1a;由于刚从c语言面向过程的学习中解脱出来&#xff0c;立即把思路从面向过程转到面向对象肯定不现实&#xff0c;加之全新的复杂语法与操作&#xff0c;着实给新手学习这门语言带来了不小的困难。所以&#xff…