12 KiB
이미지 분류하기 - Wio Terminal
이 수업의 이 부분에서는 카메라로 촬영한 이미지를 Custom Vision 서비스로 보내 분류합니다.
이미지 분류하기
Custom Vision 서비스는 이미지를 분류하기 위해 Wio Terminal에서 호출할 수 있는 REST API를 제공합니다. 이 REST API는 HTTPS 연결을 통해 접근할 수 있으며, 이는 보안 HTTP 연결입니다.
HTTPS 엔드포인트와 상호작용할 때, 클라이언트 코드는 접근하려는 서버로부터 공개 키 인증서를 요청하고 이를 사용해 전송 데이터를 암호화해야 합니다. 웹 브라우저는 이를 자동으로 처리하지만, 마이크로컨트롤러는 그렇지 않습니다. 인증서를 수동으로 요청하고 이를 사용해 REST API와의 보안 연결을 설정해야 합니다. 이러한 인증서는 변경되지 않으므로, 한 번 인증서를 얻으면 애플리케이션에 하드코딩할 수 있습니다.
이 인증서는 공개 키를 포함하고 있으며, 보안이 필요하지 않습니다. 소스 코드에서 사용할 수 있으며 GitHub과 같은 공개 장소에서 공유할 수 있습니다.
작업 - SSL 클라이언트 설정하기
-
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 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 인증서를 나열합니다.
-
main.cpp
를 열고 다음 포함 지시문을 추가하세요:#include <WiFiClientSecure.h>
-
포함 지시문 아래에
WifiClientSecure
인스턴스를 선언하세요:WiFiClientSecure client;
이 클래스는 HTTPS를 통해 웹 엔드포인트와 통신하는 코드를 포함합니다.
-
connectWiFi
메서드에서WifiClientSecure
를 DigiCert Global Root G2 인증서를 사용하도록 설정하세요:client.setCACert(CERTIFICATE);
작업 - 이미지 분류하기
-
platformio.ini
파일의lib_deps
목록에 다음을 추가하세요:bblanchon/ArduinoJson @ 6.17.3
이는 ArduinoJson이라는 Arduino JSON 라이브러리를 가져오며, REST API의 JSON 응답을 디코딩하는 데 사용됩니다.
-
config.h
에서 Custom Vision 서비스의 예측 URL과 키에 대한 상수를 추가하세요:const char *PREDICTION_URL = "<PREDICTION_URL>"; const char *PREDICTION_KEY = "<PREDICTION_KEY>";
<PREDICTION_URL>
을 Custom Vision의 예측 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(); }
이 코드는 먼저 REST API와 상호작용하는 메서드를 포함한
HTTPClient
를 선언합니다. 그런 다음 Azure 공개 키를 설정한WiFiClientSecure
인스턴스를 사용해 클라이언트를 예측 URL에 연결합니다.연결 후, 헤더를 보냅니다. 이는 REST API에 대해 수행될 요청에 대한 정보를 전달합니다.
Content-Type
헤더는 API 호출이 원시 바이너리 데이터를 보낼 것임을 나타내고,Prediction-Key
헤더는 Custom Vision 예측 키를 전달합니다.이후 HTTP 클라이언트에 POST 요청을 보내며, 이는 카메라로 촬영한 JPEG 이미지를 포함한 바이트 배열을 업로드합니다.
💁 POST 요청은 데이터를 보내고 응답을 받는 데 사용됩니다. GET 요청과 같은 다른 요청 유형은 데이터를 검색하는 데 사용됩니다. GET 요청은 웹 브라우저가 웹 페이지를 로드할 때 사용됩니다.
POST 요청은 응답 상태 코드를 반환합니다. 이는 잘 정의된 값으로, 200은 OK를 의미하며 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
배열입니다. 이 배열은 각 태그에 대한 태그 이름과 확률을 포함한 예측을 담고 있습니다. 반환된 확률은 0-1 사이의 부동 소수점 숫자로, 0은 해당 태그와 일치할 확률이 0%임을, 1은 100%임을 나타냅니다.💁 이미지 분류기는 사용된 모든 태그에 대한 백분율을 반환합니다. 각 태그는 이미지가 해당 태그와 일치할 확률을 가집니다.
이 JSON은 디코딩되며, 각 태그의 확률이 시리얼 모니터로 전송됩니다.
-
buttonPressed
함수에서 SD 카드에 저장하는 코드를classifyImage
호출로 대체하거나, 이미지를 작성한 후 버퍼를 삭제하기 전에 추가하세요: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%
촬영된 이미지를 확인할 수 있으며, Custom Vision의 Predictions 탭에서 이러한 값을 볼 수 있습니다.
💁 이 코드는 code-classify/wio-terminal 폴더에서 찾을 수 있습니다.
😀 과일 품질 분류 프로그램이 성공적으로 완료되었습니다!
면책 조항:
이 문서는 AI 번역 서비스 Co-op Translator를 사용하여 번역되었습니다. 정확성을 위해 최선을 다하고 있지만, 자동 번역에는 오류나 부정확성이 포함될 수 있습니다. 원본 문서를 해당 언어로 작성된 상태에서 권위 있는 자료로 간주해야 합니다. 중요한 정보의 경우, 전문적인 인간 번역을 권장합니다. 이 번역 사용으로 인해 발생하는 오해나 잘못된 해석에 대해 당사는 책임을 지지 않습니다.