本教程介绍了使用 ESP32 摄像头网络服务器环境的 OpenCV.js 和 OpenCV 工具。例如,我们将构建一个简单的 ESP32 摄像头网络服务器,其中包括对移动物体的颜色检测和跟踪。
本教程绝不是对 OpenCV 可以提供给 ESP32 摄像头网络服务器的所有内容的详尽处理。此介绍将激发更多使用 ESP32 相机的 OpenCV 工作。
这个项目/教程是基于国外作者(Andrew R. Sass)的项目创建的。
ESP32 可以作为浏览器客户端的服务器,某些型号包括一个摄像头(例如,ESP32-CAM),允许客户端在浏览器中查看静态或视频图片。HTML、JavaScript 和其它浏览器语言可以利用 ESP32 及其相机的广泛功能。
对于那些对 ESP32 相机开发板经验很少或没有经验的人,可以从以下教程开始。
如docs.opencv.org 中所述,OpenCV(开源计算机视觉库:http : //opencv.org)是一个包含数百种计算机视觉算法的开源库。OpenCV.js 使用 Emscripten,一个 JavaScript 的编译器,为一个不断增长的 API 库编译 OpenCV 的函数。
OpenCV.js 在浏览器中运行,它允许仅具有适度 HTML 和 JavaScript 背景的人快速试用 OpenCV 功能。那些有 Esp32 相机应用背景的人已经有了这个背景。
我们将在本教程中构建的项目创建一个允许对移动对象进行颜色跟踪的 Web 服务器。在 Web 服务器界面上,您可以使用多种配置来正确选择要跟踪的颜色。然后,浏览器将移动物体实时 x 和 y 坐标发送到 ESP32 板。
这是 Web 服务器的预览。
在继续此项目之前,请确保正确安装了ESP32的环境组件。
我们将使用 Arduino IDE 对 ESP32 板进行编程。因此,您需要安装 Arduino IDE 以及 ESP32 组件:
该项目与任何具有 OV2640 摄像头的 ESP32 摄像头板兼容。有几种 ESP32 相机型号。
确保您知道您正在使用的相机板的引脚分配。对于最流行的开发板的引脚分配,请查看这篇文章:
该计划由两部分组成:
该程序分为两个文件: OCV_ColorTrack_P.ino 包含服务器程序和 index_OCV_ColorTrack.h 包含客户端程序的头文件(HTML、CSS 和 JavaScript with OpenCV.js)。
创建一个名为的新 Arduino 程序 OCV_ColorTrack_P 并复制以下代码。
/*********
The include file, index_OCV_ColorTrack.h, the Client, is an intoduction of OpenCV.js to the ESP32 Camera environment. The Client was
developed and written by Andrew R. Sass. Permission to reproduce the index_OCV_ColorTrack.h file is granted free of charge if this
entire copyright notice is included in all copies of the index_OCV_ColorTrack.h file.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
#include
#include
#include "esp_camera.h"
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "index_OCV_ColorTrack.h"
// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
String Feedback="";
String Command="",cmd="",P1="",P2="",P3="",P4="",P5="",P6="",P7="",P8="",P9="";
byte ReceiveState=0,cmdState=1,strState=1,questionstate=0,equalstate=0,semicolonstate=0;
//ANN:0
// AI-Thinker
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
WiFiServer server(80);
//ANN:2
void ExecuteCommand() {
if (cmd!="colorDetect") { //Omit printout
//Serial.println("cmd= "+cmd+" ,P1= "+P1+" ,P2= "+P2+" ,P3= "+P3+" ,P4= "+P4+" ,P5= "+P5+" ,P6= "+P6+" ,P7= "+P7+" ,P8= "+P8+" ,P9= "+P9);
//Serial.println("");
}
if (cmd=="resetwifi") {
WiFi.begin(P1.c_str(), P2.c_str());
Serial.print("Connecting to ");
Serial.println(P1);
long int StartTime=millis();
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
if ((StartTime+5000) < millis()) break;
}
Serial.println("");
Serial.println("STAIP: "+WiFi.localIP().toString());
Feedback="STAIP: "+WiFi.localIP().toString();
}
else if (cmd=="restart") {
ESP.restart();
}
else if (cmd=="cm"){
int XcmVal = P1.toInt();
int YcmVal = P2.toInt();
Serial.println("cmd= "+cmd+" ,VALXCM= "+XcmVal);
Serial.println("cmd= "+cmd+" ,VALYCM= "+YcmVal);
}
else if (cmd=="quality") {
sensor_t * s = esp_camera_sensor_get();
int val = P1.toInt();
s->set_quality(s, val);
}
else if (cmd=="contrast") {
sensor_t * s = esp_camera_sensor_get();
int val = P1.toInt();
s->set_contrast(s, val);
}
else if (cmd=="brightness") {
sensor_t * s = esp_camera_sensor_get();
int val = P1.toInt();
s->set_brightness(s, val);
}
else {
Feedback="Command is not defined.";
}
if (Feedback=="") {
Feedback=Command;
}
}
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
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; //0-63 lower number means higher quality
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12; //0-63 lower number means higher quality
config.fb_count = 1;
}
// camera init
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
delay(1000);
ESP.restart();
}
//drop down frame size for higher initial frame rate
sensor_t * s = esp_camera_sensor_get();
s->set_framesize(s, FRAMESIZE_CIF); //UXGA|SXGA|XGA|SVGA|VGA|CIF|QVGA|HQVGA|QQVGA 設定初始化影像解析度
WiFi.mode(WIFI_AP_STA);
WiFi.begin(ssid, password);
delay(1000);
long int StartTime=millis();
while (WiFi.status() != WL_CONNECTED) {
delay(500);
if ((StartTime+10000) < millis())
break;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.print("ESP IP Address: http://");
Serial.println(WiFi.localIP());
}
server.begin();
}
void loop() {
Feedback="";Command="";cmd="";P1="";P2="";P3="";P4="";P5="";P6="";P7="";P8="";P9="";
ReceiveState=0,cmdState=1,strState=1,questionstate=0,equalstate=0,semicolonstate=0;
WiFiClient client = server.available();
if (client) {
String currentLine = "";
while (client.connected()) {
if (client.available()) {
char c = client.read();
getCommand(c);
if (c == 'n') {
if (currentLine.length() == 0) {
if (cmd=="colorDetect") {
camera_fb_t * fb = NULL;
fb = esp_camera_fb_get();
if(!fb) {
Serial.println("Camera capture failed");
delay(1000);
ESP.restart();
}
//ANN:1
client.println("HTTP/1.1 200 OK");
client.println("Access-Control-Allow-Origin: *");
client.println("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");
client.println("Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS");
client.println("Content-Type: image/jpeg");
client.println("Content-Disposition: form-data; name="imageFile"; filename="picture.jpg"");
client.println("Content-Length: " + String(fb->len));
client.println("Connection: close");
client.println();
uint8_t *fbBuf = fb->buf;
size_t fbLen = fb->len;
for (size_t n=0;n
if (n+1024
client.write(fbBuf, 1024);
fbBuf += 1024;
}
else if (fbLen%1024>0) {
size_t remainder = fbLen%1024;
client.write(fbBuf, remainder);
}
}
esp_camera_fb_return(fb);
}
else {
//ANN:1
client.println("HTTP/1.1 200 OK");
client.println("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");
client.println("Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS");
client.println("Content-Type: text/html; charset=utf-8");
client.println("Access-Control-Allow-Origin: *");
client.println("Connection: close");
client.println();
String Data="";
if (cmd!="")
Data = Feedback;
else {
Data = String((const char *)INDEX_HTML);
}
int Index;
for (Index = 0; Index < Data.length(); Index = Index+1000) {
client.print(Data.substring(Index, Index+1000));
}
client.println();
}
Feedback="";
break;
} else {
currentLine = "";
}
}
else if (c != 'r') {
currentLine += c;
}
if ((currentLine.indexOf("/?")!=-1)&&(currentLine.indexOf(" HTTP")!=-1)) {
if (Command.indexOf("stop")!=-1) {
client.println();
client.println();
client.stop();
}
currentLine="";