You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
IoT-For-Beginners/translations/zh/4-manufacturing/lessons/2-check-fruit-from-device/wio-terminal-classify-image.md

227 lines
11 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "32a1f23e7834fbe7715da8c4ebb450b9",
"translation_date": "2025-08-24T21:32:02+00:00",
"source_file": "4-manufacturing/lessons/2-check-fruit-from-device/wio-terminal-classify-image.md",
"language_code": "zh"
}
-->
# 分类图像 - Wio Terminal
在本节课程中,您将把摄像头捕获的图像发送到 Custom Vision 服务进行分类。
## 分类图像
Custom Vision 服务提供了一个 REST API您可以通过 Wio Terminal 调用该 API 来分类图像。这个 REST API 是通过 HTTPS 连接访问的——一种安全的 HTTP 连接。
在与 HTTPS 端点交互时,客户端代码需要从被访问的服务器请求公钥证书,并使用该证书加密发送的流量。您的网页浏览器会自动完成这些操作,但微控制器不会。您需要手动请求此证书,并使用它来创建与 REST API 的安全连接。这些证书不会更改,因此一旦获取证书,您可以将其硬编码到应用程序中。
这些证书包含公钥,不需要保密。您可以在源代码中使用它们,并在 GitHub 等公共平台上共享。
### 任务 - 设置 SSL 客户端
1. 如果尚未打开,请打开 `fruit-quality-detector` 应用项目。
1. 打开 `config.h` 头文件,并添加以下内容:
```cpp
const char *CERTIFICATE =
"-----BEGIN CERTIFICATE-----\r\n"
"MIIF8zCCBNugAwIBAgIQAueRcfuAIek/4tmDg0xQwDANBgkqhkiG9w0BAQwFADBh\r\n"
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\r\n"
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH\r\n"
"MjAeFw0yMDA3MjkxMjMwMDBaFw0yNDA2MjcyMzU5NTlaMFkxCzAJBgNVBAYTAlVT\r\n"
"MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKjAoBgNVBAMTIU1pY3Jv\r\n"
"c29mdCBBenVyZSBUTFMgSXNzdWluZyBDQSAwNjCCAiIwDQYJKoZIhvcNAQEBBQAD\r\n"
"ggIPADCCAgoCggIBALVGARl56bx3KBUSGuPc4H5uoNFkFH4e7pvTCxRi4j/+z+Xb\r\n"
"wjEz+5CipDOqjx9/jWjskL5dk7PaQkzItidsAAnDCW1leZBOIi68Lff1bjTeZgMY\r\n"
"iwdRd3Y39b/lcGpiuP2d23W95YHkMMT8IlWosYIX0f4kYb62rphyfnAjYb/4Od99\r\n"
"ThnhlAxGtfvSbXcBVIKCYfZgqRvV+5lReUnd1aNjRYVzPOoifgSx2fRyy1+pO1Uz\r\n"
"aMMNnIOE71bVYW0A1hr19w7kOb0KkJXoALTDDj1ukUEDqQuBfBxReL5mXiu1O7WG\r\n"
"0vltg0VZ/SZzctBsdBlx1BkmWYBW261KZgBivrql5ELTKKd8qgtHcLQA5fl6JB0Q\r\n"
"gs5XDaWehN86Gps5JW8ArjGtjcWAIP+X8CQaWfaCnuRm6Bk/03PQWhgdi84qwA0s\r\n"
"sRfFJwHUPTNSnE8EiGVk2frt0u8PG1pwSQsFuNJfcYIHEv1vOzP7uEOuDydsmCjh\r\n"
"lxuoK2n5/2aVR3BMTu+p4+gl8alXoBycyLmj3J/PUgqD8SL5fTCUegGsdia/Sa60\r\n"
"N2oV7vQ17wjMN+LXa2rjj/b4ZlZgXVojDmAjDwIRdDUujQu0RVsJqFLMzSIHpp2C\r\n"
"Zp7mIoLrySay2YYBu7SiNwL95X6He2kS8eefBBHjzwW/9FxGqry57i71c2cDAgMB\r\n"
"AAGjggGtMIIBqTAdBgNVHQ4EFgQU1cFnOsKjnfR3UltZEjgp5lVou6UwHwYDVR0j\r\n"
"BBgwFoAUTiJUIBiV5uNu5g/6+rkS7QYXjzkwDgYDVR0PAQH/BAQDAgGGMB0GA1Ud\r\n"
"JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMHYG\r\n"
"CCsGAQUFBwEBBGowaDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQu\r\n"
"Y29tMEAGCCsGAQUFBzAChjRodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGln\r\n"
"aUNlcnRHbG9iYWxSb290RzIuY3J0MHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6Ly9j\r\n"
"cmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RHMi5jcmwwN6A1oDOG\r\n"
"MWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RHMi5j\r\n"
"cmwwHQYDVR0gBBYwFDAIBgZngQwBAgEwCAYGZ4EMAQICMBAGCSsGAQQBgjcVAQQD\r\n"
"AgEAMA0GCSqGSIb3DQEBDAUAA4IBAQB2oWc93fB8esci/8esixj++N22meiGDjgF\r\n"
"+rA2LUK5IOQOgcUSTGKSqF9lYfAxPjrqPjDCUPHCURv+26ad5P/BYtXtbmtxJWu+\r\n"
"cS5BhMDPPeG3oPZwXRHBJFAkY4O4AF7RIAAUW6EzDflUoDHKv83zOiPfYGcpHc9s\r\n"
"kxAInCedk7QSgXvMARjjOqdakor21DTmNIUotxo8kHv5hwRlGhBJwps6fEVi1Bt0\r\n"
"trpM/3wYxlr473WSPUFZPgP1j519kLpWOJ8z09wxay+Br29irPcBYv0GMXlHqThy\r\n"
"8y4m/HyTQeI2IMvMrQnwqPpY+rLIXyviI2vLoI+4xKE4Rn38ZZ8m\r\n"
"-----END CERTIFICATE-----\r\n";
```
这是 *Microsoft Azure DigiCert Global Root G2 证书*——它是许多 Azure 服务全球使用的证书之一。
> 💁 要查看这是需要使用的证书,请在 macOS 或 Linux 上运行以下命令。如果您使用的是 Windows可以通过 [Windows Subsystem for Linux (WSL)](https://docs.microsoft.com/windows/wsl/?WT.mc_id=academic-17441-jabenn) 运行此命令:
>
> ```sh
> openssl s_client -showcerts -verify 5 -connect api.cognitive.microsoft.com:443
> ```
>
> 输出将列出 DigiCert Global Root G2 证书。
1. 打开 `main.cpp` 并添加以下 include 指令:
```cpp
#include <WiFiClientSecure.h>
```
1. 在 include 指令下方,声明一个 `WifiClientSecure` 实例:
```cpp
WiFiClientSecure client;
```
此类包含与 HTTPS 端点通信的代码。
1.`connectWiFi` 方法中,将 WiFiClientSecure 设置为使用 DigiCert Global Root G2 证书:
```cpp
client.setCACert(CERTIFICATE);
```
### 任务 - 分类图像
1.`platformio.ini` 文件的 `lib_deps` 列表中添加以下内容作为额外行:
```ini
bblanchon/ArduinoJson @ 6.17.3
```
这将导入 [ArduinoJson](https://arduinojson.org),一个 Arduino JSON 库,用于解码 REST API 的 JSON 响应。
1.`config.h` 中,为 Custom Vision 服务的预测 URL 和 Key 添加常量:
```cpp
const char *PREDICTION_URL = "<PREDICTION_URL>";
const char *PREDICTION_KEY = "<PREDICTION_KEY>";
```
`<PREDICTION_URL>` 替换为 Custom Vision 的预测 URL。将 `<PREDICTION_KEY>` 替换为预测密钥。
1.`main.cpp` 中,为 ArduinoJson 库添加 include 指令:
```cpp
#include <ArduinoJSON.h>
```
1.`main.cpp` 中,添加以下函数,放置在 `buttonPressed` 函数上方:
```cpp
void classifyImage(byte *buffer, uint32_t length)
{
HTTPClient httpClient;
httpClient.begin(client, PREDICTION_URL);
httpClient.addHeader("Content-Type", "application/octet-stream");
httpClient.addHeader("Prediction-Key", PREDICTION_KEY);
int httpResponseCode = httpClient.POST(buffer, length);
if (httpResponseCode == 200)
{
String result = httpClient.getString();
DynamicJsonDocument doc(1024);
deserializeJson(doc, result.c_str());
JsonObject obj = doc.as<JsonObject>();
JsonArray predictions = obj["predictions"].as<JsonArray>();
for(JsonVariant prediction : predictions)
{
String tag = prediction["tagName"].as<String>();
float probability = prediction["probability"].as<float>();
char buff[32];
sprintf(buff, "%s:\t%.2f%%", tag.c_str(), probability * 100.0);
Serial.println(buff);
}
}
httpClient.end();
}
```
此代码首先声明一个 `HTTPClient`——一个包含与 REST API 交互方法的类。然后使用之前设置的 Azure 公钥,通过 `WiFiClientSecure` 实例连接客户端到预测 URL。
一旦连接成功,它会发送请求头——关于即将对 REST API 发出的请求的信息。`Content-Type` 请求头表示 API 调用将发送原始二进制数据,`Prediction-Key` 请求头传递 Custom Vision 的预测密钥。
接下来,向 HTTP 客户端发出 POST 请求,上传一个字节数组。该数组将包含从摄像头捕获的 JPEG 图像,当此函数被调用时上传。
> 💁 POST 请求用于发送数据并获取响应。还有其他请求类型,例如 GET 请求,用于检索数据。您的网页浏览器使用 GET 请求加载网页。
POST 请求返回一个响应状态码。这些是定义明确的值,其中 200 表示 **OK**——POST 请求成功。
> 💁 您可以在 [维基百科的 HTTP 状态码列表页面](https://wikipedia.org/wiki/List_of_HTTP_status_codes) 中查看所有响应状态码。
如果返回 200则从 HTTP 客户端读取结果。这是 REST API 的预测结果,以 JSON 文档的形式返回的文本响应。JSON 格式如下:
```jSON
{
"id":"45d614d3-7d6f-47e9-8fa2-04f237366a16",
"project":"135607e5-efac-4855-8afb-c93af3380531",
"iteration":"04f1c1fa-11ec-4e59-bb23-4c7aca353665",
"created":"2021-06-10T17:58:58.959Z",
"predictions":[
{
"probability":0.5582016,
"tagId":"05a432ea-9718-4098-b14f-5f0688149d64",
"tagName":"ripe"
},
{
"probability":0.44179836,
"tagId":"bb091037-16e5-418e-a9ea-31c6a2920f17",
"tagName":"unripe"
}
]
}
```
这里重要的部分是 `predictions` 数组。它包含预测结果,每个标签都有一个条目,包含标签名称和概率。返回的概率是 0-1 的浮点数,其中 0 表示与标签匹配的可能性为 0%1 表示与标签匹配的可能性为 100%。
> 💁 图像分类器会返回所有使用过的标签的百分比。每个标签都会有一个图像与该标签匹配的概率。
该 JSON 被解码后,每个标签的概率会发送到串行监视器。
1.`buttonPressed` 函数中,将保存到 SD 卡的代码替换为对 `classifyImage` 的调用,或者在图像写入后添加调用,但 **在**删除缓冲区之前:
```cpp
classifyImage(buffer, length);
```
> 💁 如果您替换了保存到 SD 卡的代码,可以清理代码,删除 `setupSDCard` 和 `saveToSDCard` 函数。
1. 上传并运行代码。将摄像头对准一些水果并按下 C 按钮。您将在串行监视器中看到输出:
```output
Connecting to WiFi..
Connected!
Image captured
Image read to buffer with length 8200
ripe: 56.84%
unripe: 43.16%
```
您将能够看到拍摄的图像,以及这些值在 Custom Vision 的 **Predictions** 标签中。
![Custom Vision 中的香蕉预测结果:成熟 56.8%,未成熟 43.1%](../../../../../translated_images/custom-vision-banana-prediction.30cdff4e1d72db5d9a0be0193790a47c2b387da034e12dc1314dd57ca2131b59.zh.png)
> 💁 您可以在 [code-classify/wio-terminal](../../../../../4-manufacturing/lessons/2-check-fruit-from-device/code-classify/wio-terminal) 文件夹中找到此代码。
😀 您的水果质量分类程序成功了!
**免责声明**
本文档使用AI翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 进行翻译。尽管我们努力确保翻译的准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原文档的原始语言版本为权威来源。对于关键信息,建议使用专业人工翻译。我们对于因使用本翻译而引起的任何误解或误读不承担责任。