当前位置: 首页 > news >正文

半小时内实现Esp32-Cam模型训练和图像识别

Esp32-Cam图像识别

  • 一、网页显示视频流
    • 1、Linux式例程
    • 2、MicroPython式例程
      • 步骤1、下载Thonny
      • 步骤2、烧录Esp32-Cam固件
      • 步骤3、运行相应代码
    • 3、Arduino式例程
      • 步骤1、下载Arduino
      • 步骤2、安装Esp32-Cam库
      • 步骤3、选择例程
      • 步骤4、查看运行结果
  • 二、半小时内实现图像识别
    • 1、网页视频流
    • 2、通过视频流采集目标并训练
      • 步骤1、新建Spyder工程
      • 步骤2、训练数据获取
      • 步骤3、数据处理并建立模型
    • 3、生成代码移植到Esp32-Cam
      • (1)将HOG和RF算法转换为可以在 Esp32-cam 上运行的C++代码
      • (2)创建Arduino项目工程
      • (3)烧录到Esp32-Cam

这个项目可以让你在半个小时内实现模型训练和图像识别,非常简单。
开始前先放效果视频点击这里

一、网页显示视频流

现成资源有很多,只要稍微找下然后把程序烧录到Esp32-Cam都可以实现该功能。详细内容前往学习即可,此处不赘述。

1、Linux式例程

可以学习安信可官网的例程,权威。点击前往
教程很详细,有Linux基础的兄弟可以尝试一下,否则就别在这个上面折腾了(比如vim编辑器使用、shell脚本使用、linux配置等,都很费时间,而且寡人也没尝试成功)

2、MicroPython式例程

这种方式是让Esp32-Cam具备python环境,能够运行py文件。点击前往

步骤1、下载Thonny

下载地址:https://thonny.org/

步骤2、烧录Esp32-Cam固件

使用Thonny如果烧录固件后无法显示boot.py文件的话应该是底板有问题,可以去买指定的相应底板,但其实使用USB转ttl,杜邦线对应接5V、GND、TXD和RXD就可以了。

步骤3、运行相应代码

import socket
import network
import camera
import time


# 连接wifi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
    print('connecting to network...')
    wlan.connect('dongfeiqiu', 'wangmingdong1225')
    
    while not wlan.isconnected():
        pass
print('网络配置:', wlan.ifconfig())
 
 
# 摄像头初始化
try:
    camera.init(0, format=camera.JPEG)
except Exception as e:
    camera.deinit()
    camera.init(0, format=camera.JPEG)


# 其他设置:
# 上翻下翻
camera.flip(1)
#左/右
camera.mirror(1)

# 分辨率
camera.framesize(camera.FRAME_HVGA)
# 选项如下:
# FRAME_96X96 FRAME_QQVGA FRAME_QCIF FRAME_HQVGA FRAME_240X240
# FRAME_QVGA FRAME_CIF FRAME_HVGA FRAME_VGA FRAME_SVGA
# FRAME_XGA FRAME_HD FRAME_SXGA FRAME_UXGA FRAME_FHD
# FRAME_P_HD FRAME_P_3MP FRAME_QXGA FRAME_QHD FRAME_WQXGA
# FRAME_P_FHD FRAME_QSXGA
# 有关详细信息,请查看此链接:https://bit.ly/2YOzizz

# 特效
camera.speffect(camera.EFFECT_NONE)
#选项如下:
# 效果\无(默认)效果\负效果\ BW效果\红色效果\绿色效果\蓝色效果\复古效果
# EFFECT_NONE (default) EFFECT_NEG \EFFECT_BW\ EFFECT_RED\ EFFECT_GREEN\ EFFECT_BLUE\ EFFECT_RETRO

# 白平衡
# camera.whitebalance(camera.WB_HOME)
#选项如下:
# WB_NONE (default) WB_SUNNY WB_CLOUDY WB_OFFICE WB_HOME

# 饱和
camera.saturation(0)
#-2,2(默认为0). -2灰度
# -2,2 (default 0). -2 grayscale 

# 亮度
camera.brightness(0)
#-2,2(默认为0). 2亮度
# -2,2 (default 0). 2 brightness

# 对比度
camera.contrast(0)
#-2,2(默认为0).2高对比度
#-2,2 (default 0). 2 highcontrast

# 质量
camera.quality(10)
#10-63数字越小质量越高

# socket UDP 的创建
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)

try:
    while True:
        buf = camera.capture()  # 获取图像数据
        s.sendto(buf, ("192.168.31.53", 9090))  # 向服务器发送图像数据
        time.sleep(0.1)
except:
    pass
finally:
    camera.deinit()

3、Arduino式例程

这个也是我发现最简单的实现例程,而且资源也多,涉及的语言主要是C++。点击前往

步骤1、下载Arduino

下载地址:点击前往

步骤2、安装Esp32-Cam库

方法一:在IDE安装。
(1). 文件 → 首选项→附加开发板管理器网址,修改网址为

https://arduino.esp8266.com/stable/package_esp8266com_index.json
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json

(2). 工具 → 开发板 → 开发板管理器,搜索esp32,点击安装即可
方法二:github下载zip压缩包作为库
下载地址:点击前往
下载zip压缩包完成后,项目 →包含库 →添加.ZIP库

步骤3、选择例程

工具 →开发板 →esp32 →AI Thinker ESP32-CAM
在如下位置里边填充wifi和密码

const char* ssid = "Your wifi name";
const char* password = "wifi password";

完整代码截取如下

#include "esp_camera.h"
#include <WiFi.h>

//
// WARNING!!! Make sure that you have either selected ESP32 Wrover Module,
//            or another board which has PSRAM enabled
//

// Select camera model
//#define CAMERA_MODEL_WROVER_KIT
//#define CAMERA_MODEL_ESP_EYE
//#define CAMERA_MODEL_M5STACK_PSRAM
//#define CAMERA_MODEL_M5STACK_WIDE
#define CAMERA_MODEL_AI_THINKER

#include "camera_pins.h"

const char* ssid = "Your wifi name";
const char* password = "wifi password";

void startCameraServer();

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Serial.println();

  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  //init with high specs to pre-allocate larger buffers
  if(psramFound()){
    config.frame_size = FRAMESIZE_UXGA;
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }

#if defined(CAMERA_MODEL_ESP_EYE)
  pinMode(13, INPUT_PULLUP);
  pinMode(14, INPUT_PULLUP);
#endif

  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }

  sensor_t * s = esp_camera_sensor_get();
  //initial sensors are flipped vertically and colors are a bit saturated
  if (s->id.PID == OV3660_PID) {
    s->set_vflip(s, 1);//flip it back
    s->set_brightness(s, 1);//up the blightness just a bit
    s->set_saturation(s, -2);//lower the saturation
  }
  //drop down frame size for higher initial frame rate
  s->set_framesize(s, FRAMESIZE_QVGA);

#if defined(CAMERA_MODEL_M5STACK_WIDE)
  s->set_vflip(s, 1);
  s->set_hmirror(s, 1);
#endif

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");

  startCameraServer();

  Serial.print("Camera Ready! Use 'http://");
  Serial.print(WiFi.localIP());
  Serial.println("' to connect");
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(10000);
}

步骤4、查看运行结果

在这里插入图片描述

工具→串口监视器,然后按下esp32-cam的复位键
在这里插入图片描述
复制网址在网页打开,即可看摄像头实时内容了
在这里插入图片描述

二、半小时内实现图像识别

1、网页视频流

和前面的Arduino例程相似,但包含的库不是官方库,而是这个:点击这里
下载该zip库后在IDE操作包含该库,然后复制下面代码作为一个新工程ino文件。注意:要配置自己的板件,然后改成自己的wifi和密码

#include "eloquent.h"
#include "eloquent/networking/wifi.h"
#include "eloquent/vision/camera/esp32/webserver.h"
// 把 'm5wide' 替换成自己的模块,
// 支持的模块有 'aithinker', 'eye', 'm5stack', 'm5wide', 'wrover'
#include "eloquent/vision/camera/aithinker.h"//我用的是aithinker
void setup() {
    Serial.begin(115200);
    delay(2000);
    camera.jpeg();
    camera.qqvga();
    // 改成自己的wifi和密码
    while (!wifi.connectTo("Abc", "12345678"))
        Serial.println("Cannot connect to WiFi");
    while (!camera.begin())
        Serial.println("Cannot connect to camera");
    webServer.start();
    Serial.print("Camera web server started at http://");
    Serial.println(WiFi.localIP());
}
void loop() {
    // do nothing
}

编译烧到esp32-cam板子上后打开串口监视器,获取网址(我的是192.168.1.103),然后在网页打开即可,和常规Arduino的视频流例程差不多。
在这里插入图片描述
视频窗口设这么小是为了让视频更加流畅。

2、通过视频流采集目标并训练

训练环境是Python,我这边推荐Anaconda
简单介绍就是:数据可视化+JupyterNotebook+Spyder
下载用不了多长时间的,然后我们只需要用其中的IDE:Spyder
下载好之后安装everywhereml包,打开Anaconda Powershell Prompt输入以下指令,already表示包已经安装好了

pip install everywhereml>=0.2.19

在这里插入图片描述

步骤1、新建Spyder工程

project->new project
在这里插入图片描述
然后把学习训练模型的Python工程解压添加到工程里,点击获取Python工程
然后打开Spyder软件如图显示,左边工程文件栏目里就会显示Python工程,此外派上用场的还有交互界面和数据可视化显示界面
在这里插入图片描述

步骤2、训练数据获取

复制以下代码到交互界面并回车,从视频流中截取目标图片作为模型数据支撑

from logging import basicConfig, INFO
from everywhereml.data import ImageDataset
from everywhereml.data.collect import MjpegCollector
# 给将要存放数据的文件夹命名
base_folder = 'Images_Data'
# 视频流显示的那个网页地址
IP_ADDRESS_OF_ESP = 'http://192.168.1.103'
basicConfig(level=INFO)
try:
    image_dataset = ImageDataset.from_nested_folders(
        name='Dataset', 
        base_folder=base_folder
    )
except FileNotFoundError:
    mjpeg_collector = MjpegCollector(address=IP_ADDRESS_OF_ESP)
    image_dataset = mjpeg_collector.collect_many_classes(
        dataset_name='Dataset', 
        base_folder=base_folder,
        duration=30
    )
print(image_dataset)

然后就会弹出让你给创建的类命名,我先什么都不识别所以命名none然后回车,如下图所示
在这里插入图片描述
之后会显示提示拍了1272张图作为模型训练基础,并询问该类是否ok

INFO:root:Captured 1272 images
Is this class ok? (y|n)

接着输入y回车,如果是第一次的话会提示建立文件夹Images_Data存数据

INFO:root:creating D:\Esp_Cam\Spyder_Demo\Esp32_Cam\Images_Data folder
INFO:root:creating D:\Esp_Cam\Spyder_Demo\Esp32_Cam\Images_Data\none folder
Which class are you going to capture? (leave empty to exit) 

打开对应文件夹就会发现里边存了拍下来的图片数据
在这里插入图片描述
同样的,我训练了pen、napkin
在这里插入图片描述
在这里插入图片描述
如果不想添加了,不用输入直接回车

Which class are you going to capture? (leave empty to exit) 
Are you sure you want to exit? (y|n) 

然后输入y回车退出,这时候就会显示所训练的类

ImageDataset[Dataset](num_images=3704, num_labels=3, labels=['napkin', 'none', 'pen'])

步骤3、数据处理并建立模型

步骤2获取了纸巾、笔、空白的情况下各一千多张图片作为数据支撑
在这里插入图片描述
首先对图片进行灰化
在交互界面执行

image_dataset = image_dataset.gray().uint8()

可以在交互界面执行以下代码预览数据处理情况

image_dataset.preview(samples_per_class=10, rows_per_class=2, figsize=(20, 10), cmap='gray')

在这里插入图片描述
然后使用定向梯度直方图算法进行处理
定向梯度直方图( Histogram of Oriented Gradients,简称HOG),该算法是轻量级的很适合Esp32-cam使用。
在交互界面执行以下代码

from everywhereml.preprocessing.image.object_detection import HogPipeline
from everywhereml.preprocessing.image.transform import Resize
pipeline = HogPipeline(
    transforms=[
        Resize(width=40, height=30)#此处的分辨率会影响处理时间和模型建立的准确度,可自行调整
    ]
)
feature_dataset = pipeline.fit_transform(image_dataset)
feature_dataset.describe()

在这里插入图片描述
接着输出由特征向量组成的数据集

print(pipeline)

在这里插入图片描述
如果想看所提取的特征量信息情况,可以绘制配对图(pairplot)直观感受数据

feature_dataset.plot.features_pairplot(n=200, k=8)

在这里插入图片描述
可以直观的看到,这3个类(none、napkin、pen)的聚集性质良好,但在某种程度上彼此是有混合的情况。
使用降维算法进一步优化
使用的降维算法是统一流形逼近与投影(Uniform Manifold Approximation and Projection,简称UMAP)

feature_dataset.plot.umap()

在这里插入图片描述
分析点聚集性质可知,1(none)的模型最理想,0(napkin)和2(pen)的模型相对比较差。
总的来说,也算是能够用来表征我们的数据了。
最后训练分类器完成模型建立
使用的建模方法叫随机森林(Random Forest,简称RF)

from everywhereml.sklearn.ensemble import RandomForestClassifier
for i in range(10):
    clf = RandomForestClassifier(n_estimators=5, max_depth=10)
    train, test = feature_dataset.split(test_size=0.4, random_state=i)
    clf.fit(train)
    print('Score on test set: %.2f' % clf.score(test))
clf.fit(feature_dataset)

在这里插入图片描述
现在,我们已经训练并且建好模型了

3、生成代码移植到Esp32-Cam

(1)将HOG和RF算法转换为可以在 Esp32-cam 上运行的C++代码

HOG算法获取特征向量数据集

print(pipeline.to_arduino_file(
    filename='path-to-sketch/HogPipeline.h',
    instance_name='hog'
))

RF算法训练分类器

print(clf.to_arduino_file(
    filename='path-to-sketch/HogClassifier.h',
    instance_name='classifier', 
    class_map=feature_dataset.class_map
))

这时候就会生成两个.h文件在path-to-sketch/ 目录下

(2)创建Arduino项目工程

ino文件里替换成以下代码

#include "eloquent.h"
#include "eloquent/print.h"
#include "eloquent/tinyml/voting/quorum.h"
// 支撑的有 'aithinker', 'eye', 'm5stack', 'm5wide', 'wrover'
#include "eloquent/vision/camera/aithinker.h"//我用的是aithinker
#include "HogPipeline.h"//Spyder里生成的
#include "HogClassifier.h"//Spyder里生成的
Eloquent::TinyML::Voting::Quorum<7> quorum;
void setup() {
  Serial.begin(115200);
  delay(3000);
  Serial.println("Begin");
  camera.qqvga();
  camera.grayscale();
  while (!camera.begin())
    Serial.println("Cannot init camera");
}
void loop() {
  if (!camera.capture()) {
      Serial.println(camera.getErrorMessage());
      delay(1000);
      return;
  }
  hog.transform(camera.buffer);
  uint8_t prediction = classifier.predict(hog.features);
  int8_t stablePrediction = quorum.vote(prediction);
  if (quorum.isStable()) {
    eloquent::print::printf(
      Serial, 
      "Stable prediction: %s \t(DSP: %d ms, Classifier: %d us)\n", 
      classifier.getLabelOf(stablePrediction),
      hog.latencyInMillis(),
      classifier.latencyInMicros()
    );
  }
  camera.free();
}

找到前面生产的两个.h文件,然后包含进工程里(把两个.h文件复制到工程里边)
在这里插入图片描述

(3)烧录到Esp32-Cam

相关文章:

  • 关于一个大学生写一个题目写一天
  • 【C#进阶】C# 多线程
  • mlq移动最小二乘方法
  • 【Spring6】| Spring IoC注解式开发
  • 数据库+加密算法参考材料-2023.3.29
  • fastp软件介绍
  • 别再光靠工资过日子,外国程序员教你如何通过副业赚钱
  • 海心沙元宇宙音乐会虚拟主持人玩法再升级,虚拟动力技术全程助力
  • spark通过connector的方式读写starrocks
  • python实战应用讲解-【numpy专题篇】实用小技巧(六)(附python示例代码)
  • chatGPT陪你读源码
  • 37了解高可用技术方案,如冗余、容灾
  • 细思极恐,第三方跟踪器正在获取你的数据,如何防范?
  • 如何同时处理多个聊天
  • 一些idea操作
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • axios请求、和返回数据拦截,统一请求报错提示_012
  • Java 实战开发之spring、logback配置及chrome开发神器(六)
  • JavaScript 无符号位移运算符 三个大于号 的使用方法
  • Mysql5.6主从复制
  • python学习笔记 - ThreadLocal
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • vue:响应原理
  • 给初学者:JavaScript 中数组操作注意点
  • 基于webpack 的 vue 多页架构
  • 警报:线上事故之CountDownLatch的威力
  • 聊聊flink的BlobWriter
  • 使用Swoole加速Laravel(正式环境中)
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • ​Java并发新构件之Exchanger
  • ​Linux·i2c驱动架构​
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • #Linux(帮助手册)
  • #stm32驱动外设模块总结w5500模块
  • (4)事件处理——(7)简单事件(Simple events)
  • (42)STM32——LCD显示屏实验笔记
  • (Redis使用系列) Springboot 在redis中使用BloomFilter布隆过滤器机制 六
  • (八)Docker网络跨主机通讯vxlan和vlan
  • (超简单)使用vuepress搭建自己的博客并部署到github pages上
  • (二十三)Flask之高频面试点
  • (附源码)node.js知识分享网站 毕业设计 202038
  • (接口封装)
  • (原創) 如何解决make kernel时『clock skew detected』的warning? (OS) (Linux)
  • (轉貼) VS2005 快捷键 (初級) (.NET) (Visual Studio)
  • ***监测系统的构建(chkrootkit )
  • 、写入Shellcode到注册表上线
  • .bat文件调用java类的main方法
  • .L0CK3D来袭:如何保护您的数据免受致命攻击
  • .NET Core MongoDB数据仓储和工作单元模式封装
  • .NET Core 控制台程序读 appsettings.json 、注依赖、配日志、设 IOptions
  • .Net Core与存储过程(一)
  • .NET Standard、.NET Framework 、.NET Core三者的关系与区别?
  • .net开发引用程序集提示没有强名称的解决办法
  • .pop ----remove 删除
  • .vollhavhelp-V-XXXXXXXX勒索病毒的最新威胁:如何恢复您的数据?