图像分类 - Wio Terminal 在这节课的这一部分,你将把相机捕获的图像发送到自定义视觉服务进行分类。 图像分类 自定义视觉服务有一个REST API,可以从Wio Terminal调用以对图像进行分类。这个REST API通过HTTPS连接访问。 当与HTTPS端点交互时,客户端代码需要从被访问的服务器请求公钥证书,并使用该证书加密发送的数据。你的网络浏览器会自动执行此操作,但微控制器不会。你需要手动请求这个证书,并使用它来创建一个安全连接到REST API。这些证书不会改变,因此一旦你拥有了证书,就可以将其硬编码到你的应用程序中。 这些证书包含公钥,不需要保密。你可以在源代码中使用它们,并在GitHub等公共平台上分享它们。
在这节课的这一部分,你将把相机捕获的图像发送到自定义视觉服务进行分类。
自定义视觉服务有一个REST API,可以从Wio Terminal调用以对图像进行分类。这个REST API通过HTTPS连接访问。
当与HTTPS端点交互时,客户端代码需要从被访问的服务器请求公钥证书,并使用该证书加密发送的数据。你的网络浏览器会自动执行此操作,但微控制器不会。你需要手动请求这个证书,并使用它来创建一个安全连接到REST API。这些证书不会改变,因此一旦你拥有了证书,就可以将其硬编码到你的应用程序中。
这些证书包含公钥,不需要保密。你可以在源代码中使用它们,并在GitHub等公共平台上分享它们。
如果尚未打开,请打开fruit-quality-detector应用程序项目。
打开config.h头文件,并添加以下内容:
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全球根G2证书——它是许多Azure服务在全球范围内使用的证书之一。
要确认这是要使用的证书,请在macOS或Linux上运行以下命令。如果你使用的是Windows,可以使用Windows Subsystem for Linux (WSL)运行此命令:
openssl s_client -showcerts -verify 5 -connect api.cognitive.microsoft.com:443输出将列出DigiCert全球根G2证书。
打开main.cpp并添加以下包含指令:
#include <WiFiClientSecure.h>
在包含指令下方,声明一个WifiClientSecure实例:
WiFiClientSecure client;
此类包含通过HTTPS与Web端点通信的代码。
在connectWiFi方法中,设置WiFiClientSecure使用DigiCert全球根G2证书:
client.setCACert(CERTIFICATE);
在lib_deps list in the platformio.ini文件中作为附加行添加以下内容:
bblanchon/ArduinoJson @ 6.17.3
这导入了ArdunioJson,一个Arduino JSON库,并将用于解码来自REST API的JSON响应。
在config.h中,为自定义视觉服务中的预测URL和密钥添加常量:
const char *PREDICTION_URL = "<PREDICTION_URL>"; const char *PREDICTION_KEY = "<PREDICTION_KEY>";
将<PREDICTION_URL>和<PREDICTION_KEY>替换为你的实际值。在main.cpp中,添加ArduinoJson库的包含指令:
#include <ArduinoJSON.h>
向main.cpp添加以下函数,以添加到buttonPressed函数中。
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实例,并使用WiFiClientSecure实例进行HTTPS通信。它还设置了Content-Type和Prediction-Key头部,传递自定义视觉预测密钥。
接下来,向HTTP客户端发送POST请求,上传字节数组。这将在调用此函数时包含从相机捕获的JPEG图像。
POST请求用于发送数据并获取响应。还有其他类型的请求,如GET请求,用于检索数据。你的网络浏览器使用GET请求来加载网页。
POST请求返回响应状态码。这些值是明确定义的,其中200表示成功 - POST请求成功。
你可以在Wikipedia上的HTTP状态码列表页面查看所有响应状态码。
如果返回200,则从HTTP客户端读取结果。这是来自REST API的文本响应,其中包含预测结果的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数组,其中包含每个标签的预测概率。
在buttonPressed函数中或在图像写入之后但在缓冲区被删除之前,添加以下代码:
classifyImage(buffer, length);
如果你替换了保存到SD卡的代码,可以清理你的代码,删除
setupSDCard、saveToSDCard函数。
上传并运行你的代码。将相机对准一些水果并按下C按钮。你将在串行监视器中看到输出:
Connecting to WiFi.. Connected! Image captured Image read to buffer with length 8200 ripe: 56.84% unripe: 43.16%
你将能够看到拍摄的图像以及这些值在自定义视觉的预测选项卡中。

你可以在code-classify/wio-terminal文件夹中找到此代码。
你的水果质量分类程序成功了!
声明:
本文件灏天文库团队进行了翻译。尽管我们力求准确,但请注意,翻译可能包含错误或不准确之处。原文档以其原始语言为准。我们不对因使用此翻译而产生的任何误解或误译负责。