将 ESP32 CAM 拍摄的图像发布到 Clarifai 不起作用

Posting a image taken by ESP32 CAM to Clarifai not working

我有以下 ESP32CAM 草图,应该拍照并 post 澄清一下:

#include "Arduino.h"
#include "esp_camera.h"
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <base64.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

// Select camera model
//#define CAMERA_MODEL_WROVER_KIT // Has PSRAM
//#define CAMERA_MODEL_ESP_EYE // Has PSRAM
//#define CAMERA_MODEL_M5STACK_PSRAM // Has PSRAM
//#define CAMERA_MODEL_M5STACK_WIDE  // Has PSRAM
#define CAMERA_MODEL_AI_THINKER // Has PSRAM
//#define CAMERA_MODEL_TTGO_T_JOURNAL // No PSRAM

//CAMERA_MODEL_AI_THINKER
#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

const char* ssid = "mySSID";
const char* password = "myPass";

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Serial.println();

  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;
  }

#if defined(CAMERA_MODEL_ESP_EYE)
  pinMode(13, INPUT_PULLUP);
  pinMode(14, INPUT_PULLUP);
#endif

  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }


  #if defined(CAMERA_MODEL_M5STACK_WIDE)
    s->set_vflip(s, 1);
    s->set_hmirror(s, 1);
  #endif

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  
  classifyImage();

  Serial.println("\nSleep....");
  esp_deep_sleep_start();
 
}

void loop(){
  
}

void classifyImage() {
  String response;
  
  // Capture picture
  camera_fb_t * fb = NULL;
  fb = esp_camera_fb_get();
   
  if(!fb) {
    Serial.println("Camera capture failed");
    return;
  } else {
    Serial.println("Camera capture OK");
  }

  size_t size = fb->len;
  String buffer = base64::encode((uint8_t *) fb->buf, fb->len);
  
  String imgPayload = "{\"inputs\": [{ \"data\": {\"image\": {\"base64\": \"" + buffer + "\"}}}]}";

  buffer = "";
  // Uncomment this if you want to show the payload
  Serial.println(imgPayload);

  esp_camera_fb_return(fb);
  
  // Generic model
  String model_id = "General";

  HTTPClient http;
  http.begin("https://api.clarifai.com/v2/models/" + model_id + "/outputs");
  http.addHeader("Content-Type", "application/json");     
  http.addHeader("Authorization", "c7f894790533332388e23d4d21278321"); 
  int httpResponseCode = http.POST(imgPayload);

  if(httpResponseCode>0){
    Serial.print(httpResponseCode);
    Serial.print(" Returned String: ");
    Serial.println(http.getString());
  } else {      
    Serial.print("POST Error: ");
    Serial.print(httpResponseCode);
  }                      
 
  // Parse the json response: Arduino assistant
  const int jsonSize = JSON_ARRAY_SIZE(1) + JSON_ARRAY_SIZE(20) + 3*JSON_OBJECT_SIZE(1) + 6*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + 20*JSON_OBJECT_SIZE(4) + 2*JSON_OBJECT_SIZE(6);
  
  StaticJsonDocument<jsonSize> doc;
  // Deserialize the JSON document
  DeserializationError error = deserializeJson(doc, response); 
  // Test if parsing succeeds.
  if (error) {
    Serial.print(F("deserializeJson() failed: "));
    Serial.println(error.f_str());
    return;
  }  

  Serial.println(jsonSize);
  Serial.println(response);

  for (int i=0; i < 10; i++) {
//    const name = doc["outputs"][0]["data"]["concepts"][i]["name"];
//    const float p = doc["outputs"][0]["data"]["concepts"][i]["value"];

    const char* name = doc["outputs"][0]["data"]["concepts"][i]["name"];
    const char* p = doc["outputs"][0]["data"]["concepts"][i]["value"];    
    
    Serial.println("=====================");
    Serial.print("Name:");
    Serial.println(name[i]);
    Serial.print("Prob:");
    Serial.println(p);
    Serial.println();
  }
}

Clarifai 的图像 post我在 return 中得到的是:

-400 返回字符串:{"status":{"code":11102,"description":"Invalid request","details":"Empty or malformed authorization header. 请提供 API密钥或会话令牌。","req_id":"39d7b4f1b7ad489fb3a9a878000f6e88"},"输出":[]} -deserializeJson() 失败:EmptyInput

我需要确认 HTTP POST 请求的格式是否正确。

这个问题不是您的 POST 请求的格式问题,而是您的授权 header 不正确(如错误“授权为空或格式不正确 header”所示) .

如 Clarafai 文档所示,授权 header 应该是:

Authorization: Key YOUR_API_KEY

您的代码正在发送

Authorization: YOUR_API_KEY

将设置授权 header 的行更改为在 API 密钥之前添加“密钥”。

鉴于 ESP32 是一个繁琐的环境,HTTP 请求可能会出现很多错误,调试这些问题的一个好方法是使用 curl 实用程序在更多 full-featured环境。在这种情况下,在 Mac 或 Linux 机器上,您可以 运行

curl -X POST -F filename -H 'Authorization: YOUR_API_KEY' -H 'Content-type: application/json' https://api.clarifai.com/v2/models/MODEL_ID/outputs

您要测试的照片存储在 filename 中。您可以确定 POST 请求是正确的,并找出其他可能出错的地方。

另外,您似乎已将 API 密钥发布到互联网上。如果是这种情况,我建议使您发布的代码中的那个无效并生成一个新的。