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

11 KiB

分类图像 - Wio Terminal

在本节课程中,您将把摄像头捕获的图像发送到 Custom Vision 服务进行分类。

分类图像

Custom Vision 服务提供了一个 REST API您可以通过 Wio Terminal 调用该 API 来分类图像。这个 REST API 是通过 HTTPS 连接访问的——一种安全的 HTTP 连接。

在与 HTTPS 端点交互时,客户端代码需要从被访问的服务器请求公钥证书,并使用该证书加密发送的流量。您的网页浏览器会自动完成这些操作,但微控制器不会。您需要手动请求此证书,并使用它来创建与 REST API 的安全连接。这些证书不会更改,因此一旦获取证书,您可以将其硬编码到应用程序中。

这些证书包含公钥,不需要保密。您可以在源代码中使用它们,并在 GitHub 等公共平台上共享。

任务 - 设置 SSL 客户端

  1. 如果尚未打开,请打开 fruit-quality-detector 应用项目。

  2. 打开 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 Global Root G2 证书——它是许多 Azure 服务全球使用的证书之一。

    💁 要查看这是需要使用的证书,请在 macOS 或 Linux 上运行以下命令。如果您使用的是 Windows可以通过 Windows Subsystem for Linux (WSL) 运行此命令:

    openssl s_client -showcerts -verify 5 -connect api.cognitive.microsoft.com:443
    

    输出将列出 DigiCert Global Root G2 证书。

  3. 打开 main.cpp 并添加以下 include 指令:

    #include <WiFiClientSecure.h>
    
  4. 在 include 指令下方,声明一个 WifiClientSecure 实例:

    WiFiClientSecure client;
    

    此类包含与 HTTPS 端点通信的代码。

  5. connectWiFi 方法中,将 WiFiClientSecure 设置为使用 DigiCert Global Root G2 证书:

    client.setCACert(CERTIFICATE);
    

任务 - 分类图像

  1. platformio.ini 文件的 lib_deps 列表中添加以下内容作为额外行:

    bblanchon/ArduinoJson @ 6.17.3
    

    这将导入 ArduinoJson,一个 Arduino JSON 库,用于解码 REST API 的 JSON 响应。

  2. config.h 中,为 Custom Vision 服务的预测 URL 和 Key 添加常量:

    const char *PREDICTION_URL = "<PREDICTION_URL>";
    const char *PREDICTION_KEY = "<PREDICTION_KEY>";
    

    <PREDICTION_URL> 替换为 Custom Vision 的预测 URL。将 <PREDICTION_KEY> 替换为预测密钥。

  3. main.cpp 中,为 ArduinoJson 库添加 include 指令:

    #include <ArduinoJSON.h>
    
  4. 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——一个包含与 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 状态码列表页面 中查看所有响应状态码。

    如果返回 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 数组。它包含预测结果,每个标签都有一个条目,包含标签名称和概率。返回的概率是 0-1 的浮点数,其中 0 表示与标签匹配的可能性为 0%1 表示与标签匹配的可能性为 100%。

    💁 图像分类器会返回所有使用过的标签的百分比。每个标签都会有一个图像与该标签匹配的概率。

    该 JSON 被解码后,每个标签的概率会发送到串行监视器。

  5. buttonPressed 函数中,将保存到 SD 卡的代码替换为对 classifyImage 的调用,或者在图像写入后添加调用,但 删除缓冲区之前:

    classifyImage(buffer, length);
    

    💁 如果您替换了保存到 SD 卡的代码,可以清理代码,删除 setupSDCardsaveToSDCard 函数。

  6. 上传并运行代码。将摄像头对准一些水果并按下 C 按钮。您将在串行监视器中看到输出:

    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%

💁 您可以在 code-classify/wio-terminal 文件夹中找到此代码。

😀 您的水果质量分类程序成功了!

免责声明
本文档使用AI翻译服务 Co-op Translator 进行翻译。尽管我们努力确保翻译的准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原文档的原始语言版本为权威来源。对于关键信息,建议使用专业人工翻译。我们对于因使用本翻译而引起的任何误解或误读不承担责任。