在本 ESP32-CAM 教程中,我们将使用机器学习技术使用 ESP32 CAM 构建图像分类项目。ESP32-CAM 将用于捕获图像,然后使用训练有素的机器学习模型对其进行识别。对于这个项目,我们将使用 Clarifai 的图像识别人工智能模型。
目录
连接 ESP32-CAM 与 FTDI 编程器
该项目需要以下组件:
- ESP32-CAM开发板
- FTDI 编程器/ USB 串行转 TTL 转换器
- 连接线
- 外部5V电源(可选)
与 ESP32 开发板不同,ESP32-CAM 不附带 USB 端口。因此,要将程序上传到 ESP32-CAM,我们需要使用 FTDI 编程器(USB 到 TTL 串行转换器)。
也可以用其它USB转TTL的编程器。
下表显示了 ESP32-CAM 和 FTDI 编程器之间的连接:
ESP32-CAM | FTDI编程器 |
---|---|
5V | VCC |
UOR (GPIO3) | TX |
UOT(GPIO1) | RX |
GND | GND |
将 ESP32-CAM 的 5V 引脚与 FTDI 编程器的 VCC 引脚连接即可上电。两个设备的两个接地将连接在一起。FTDI 编程器的 TX 引脚将与 ESP32-CAM 的 UOR(GPIO3)连接。同样,RX 引脚将与 ESP32-CAM 模块的 UOT(GPIO1)连接。
此外,您还需要将 GPIO0 与 GND 连接,以使 ESP32-CAM 模块进入下载模式。 将程序上传到模块后拔掉此连接。
在某些 ESP32-CAM 板上,您会收到欠压检测器错误,这是由于 FTDI 电缆提供的电压不足造成的。在这种情况下,您应该将外部 5V 电源连接到 ESP32,如下所示:
与 Clarifai 联系
“Clarifai Inc.是一家人工智能公司,专注于计算机视觉,并使用机器学习和深度神经网络来识别和分析图像和视频。”
我们将使用 Clarifai(一个免费使用的云机器学习平台)来识别从 ESP32-CAM 拍摄的图像。我们只需点击此链接 ( https://www.clarifai.com/ ) 创建一个免费帐户。
选择“立即免费开始并设置您的帐户”。
成功设置您的免费帐户后,获取我们在对 ESP32-CAM 进行编程时使用的 API 密钥,以成功连接 Clarifai 平台。
设置 Arduino IDE
在我们继续之前,您应该确保您的系统上安装了最新版本的 Arduino IDE。此外,您还应该在 Arduino IDE 中安装 ESP32 环境。如果您的 IDE 没有安装该ESP32环境,您可以访问以下链接:
安装 ArduinoJSON 库
您必须安装 Benoit Blanchon 的 ArduinoJSON 库,因为我们将处理 JSON 脚本。单击 Sketch > Include Library > Manage Libraries打开 Arduino Library Manager 。在搜索选项卡中输入“ArduinoJSON”,然后按 Enter 键。安装下面突出显示的库版本 6.17.2。
ESP32-CAM图像分类Arduino程序
打开 Arduino IDE 并转到 “文件”>“新建” 以打开一个新文件。将下面给出的代码复制到该文件中。要使此代码与您的 ESP32-CAM 板配合使用,您必须替换 Clarifai 的 Wi-Fi 网络凭据和 API 密钥。
代码如何运作?
现在,让我们了解代码的每个部分是如何工作的。
包括库
首先,我们将包含该项目所需的相关库。
WiFi.h库用于将我们的ESP32模块与本地WIFI网络连接。ArduinoJSON.h 将用于 JSON 脚本。初始化 ESP32-CAM 需要 Arduino.h 和 esp_camera。此外,我们还需要base64.h将图像编码为Base64格式和HTTPClient.h以成功连接机器学习平台
#include "Arduino.h"
#include "esp_camera.h"
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <base64.h>
#include <WiFi.h>
接下来,我们将创建两个全局变量,一个用于 SSID,另一个用于密码。这些将保存我们的网络凭据,用于连接到我们的无线路由器。将它们都替换为您的网络凭据以确保连接成功。
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
以下定义针对 OV2640 摄像头模块引脚。我们正在使用 CAMERA_MODEL_AI_THINKER。
#define CAMERA_MODEL_AI_THINKER // Has PSRAM
#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
setup()
在 setup() 函数中,我们将以 115200 的波特率打开串行连接。
Serial.begin(115200)
以下代码部分将把我们的 ESP32-CAM 板连接到我们上面已经指定的网络凭据的本地网络。我们将使用 WiFi.begin() 函数。参数将是我们之前在代码中定义的 SSID 和密码。连接成功后,“WiFi Connected!” 显示在串行监视器上。
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
以下代码设置 OV2640 相机模块以及照片捕获所需的设置。
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;
// if PSRAM IC present, init with UXGA resolution and higher JPEG quality
// for larger pre-allocated frame buffer.
if(psramFound()){
config.frame_size = FRAMESIZE_QVGA;
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_QVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
初始化 ESP32-CAM:
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
接下来,我们将调用负责图像分类的classify()函数
classify();
此外,之后我们将使 ESP32-CAM 保持在深度睡眠模式,并在 RESET 后唤醒。
Serial.println("n Going to Sleep…");
esp_deep_sleep_start();
图像分类
Classify() 函数将首先捕获图像,将其编码为 Base64,然后对其应用图像识别。
void classify() {
camera_fb_t * fb = NULL;
fb = esp_camera_fb_get();
if(!fb) {
Serial.println("Camera capture failed");
return;
}
size_t size = fb->len;
String buffer = base64::encode((uint8_t *) fb->buf, fb->len);
String payload = "{"inputs": [{ "data": {"image": {"base64": "" + buffer + ""}]}";
buffer = "";
Serial.println(payload);
esp_camera_fb_return(fb);
String model_id = "aaa03c23b3724a16a56b629203edc62c"; //General Model
//String model_id = "bd367be194cf45149e75f01d59f77ba7"; //Food Model
HTTPClient http;
http.begin("https://api.clarifai.com/v2/models/" + model_id + "/outputs");
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", "Key 16f848599c3c4c5e8c8b5c15f4c4a457");
int response_code = http.POST(payload);
String response;
if(response_code >0){
Serial.print(response_code );
Serial.print("Returned String: ");
response = http.getString();
Serial.println(response);
}
else {
Serial.print("POST Error: ");
Serial.print(response_code);
return;
}
const int jsonSize = 2*JSON_ARRAY_SIZE(0) + JSON_ARRAY_SIZE(1) + JSON_ARRAY_SIZE(20) + 4*JSON_OBJECT_SIZE(0) + 7*JSON_OBJECT_SIZE(1) + 5*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + 21*JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(6) + JSON_OBJECT_SIZE(7) + JSON_OBJECT_SIZE(18)+ 3251;
DynamicJsonDocument doc(jsonSize);
deserializeJson(doc, response );
for (int i=0; i < 10; i++) {
const String name = doc["outputs"][0]["data"]["concepts"][i]["name"];
const float prob = doc["outputs"][0]["data"]["concepts"][i]["value"];
Serial.println("________________________");
Serial.print("Name:");
Serial.println(name);
Serial.print("Probability:");
Serial.println(prob);
Serial.println();
}
}
我们首先使用 esp_camera_fb_get() 方法使用 ESP32-CAM 捕获图像。以下几行使我们能够做到这一点。
camera_fb_t * fb = NULL;
fb = esp_camera_fb_get();
if(!fb) {
Serial.println("Camera capture failed");
return;
}
然后我们将图像编码为base64格式:
size_t size = fb->len;
String buffer = base64::encode((uint8_t *) fb->buf, fb->len);
String payload = "{"inputs": [{ "data": {"image": {"base64": "" + buffer + ""}]}";
buffer = "";
Serial.println(payload);
之后,我们将连接到Clarifai平台,使用其预训练的模型进行图像识别。
在这里,我们指定了从 Clarifai for Image Recognition AI ( https://www.clarifai.com/models/image-recognition-ai )获取的通用模型 ID 。这将使我们能够对 11,000 个不同的概念进行分类,包括对象、主题等等。
String model_id = "aaa03c23b3724a16a56b629203edc62c"; //General Model
现在,通过提供您的 API 密钥和型号 ID 来启动 Clarifai 平台之间的连接。
HTTPClient http;
http.begin("https://api.clarifai.com/v2/models/" + model_id + "/outputs");
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", "Key 16f84859*******e8c8b5c15f4c4a457");
此外,将现在存储在有效负载中的base64编码图像传输到云机器学习平台。
int response_code = http.POST(payload);
String response;
if(response_code >0){
Serial.print(response_code );
Serial.print("Returned String: ");
response = http.getString();
Serial.println(response);
}
else {
Serial.print("POST Error: ");
Serial.print(response_code);
return;
}
接下来我们将使用 JSON 库来管理从图像中提取的概念。这些将包括已以一定概率值为图像识别的不同标签。我们将在串行监视器中显示这些以便对图像进行分类。
const int jsonSize = 2*JSON_ARRAY_SIZE(0) + JSON_ARRAY_SIZE(1) + JSON_ARRAY_SIZE(20) + 4*JSON_OBJECT_SIZE(0) + 7*JSON_OBJECT_SIZE(1) + 5*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + 21*JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(6) + JSON_OBJECT_SIZE(7) + JSON_OBJECT_SIZE(18)+ 3251;
DynamicJsonDocument doc(jsonSize);
deserializeJson(doc, response );
for (int i=0; i < 10; i++) {
const String name = doc["outputs"][0]["data"]["concepts"][i]["name"];
const float prob = doc["outputs"][0]["data"]["concepts"][i]["value"];
Serial.println("________________________");
Serial.print("Name:");
Serial.println(name);
Serial.print("Probability:");
Serial.println(prob);
Serial.println();
}
ESP32 CAM 图像分类演示
现在,我们准备编译代码并将其上传到 ESP32-CAM。确保 FTDI 编程器与模块正确连接,并且 GPIO0 也接地。
单击上传按钮将代码上传到 ESP32-CAM 板。
如果您在错误窗口中看到 Connecting….._____….._____….. ,请按下 ESP32-CAM 上的 RESET 按钮,如下所示:
将代码成功上传到开发板后,移除 GPIO0 和 GND 上的连接线。
现在打开串行监视器。稍后,Wi-Fi 将连接,图像将被捕获、编码,然后发送到 Clarifai 平台。
在这里,我们捕获了水中鱼的图像,因此预训练模型识别出以下概念中鱼和无人的概率最高。
这里我们在相框中拍摄了一张花朵的照片:
这里我们拍了一张香蕉的照片:
这里我们拍了一张球的照片:
同样,您可以使用 Clarifai 提供的其他预训练模型来构建出色的计算机视觉项目。
不错