Как передать изображение, снятое ESP32-Cam, через разъемы?

У меня есть ESP32-Cam, и я хотел бы сохранить изображение, сделанное с его помощью, на свой рабочий стол. Конечной целью было бы отобразить изображение в приложении для Android, но это другая история. Для этого я подумал, что мне нужно передать изображение с сокетами. Код ниже показывает мой пример кода. У меня есть очень простая программа на Python, которая подключается к WifiServer и печатает все, что пишет client.write ().

Однако я не уверен, как я должен передавать изображение таким образом. Это вообще возможно? Потому что мне нужно передать буфер изображений (в данном случае fb-> buf), верно?

Но как мне сделать из этого изображение, как только я получу буфер, например. в моем коде Python?

Было бы здорово, если бы кто-нибудь мог помочь мне запустить это, так как я никогда раньше не работал с этим (изображениями / сокетами).

Код ESP

#include "esp_camera.h"
#include "Arduino.h"
#include "WiFi.h"

// Pin definition for 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

// Variables
camera_fb_t * fb = NULL;
WiFiServer wifiServer(5005);
const char* ssid = "ssid";
const char* password = "password";

void setup() {
  Serial.begin(115200);

  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi.");
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }

  Serial.println("\nConnected!");
  Serial.println(WiFi.localIP());

  wifiServer.begin();

  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 (psramFound()) {
    config.frame_size = FRAMESIZE_UXGA;
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }

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

void loop() {
  WiFiClient client = wifiServer.available();
  if (client) {
    fb = esp_camera_fb_get();  
    if(!fb) {
      Serial.println("Camera capture failed");
      return;
    } else {
      Serial.println("Camera capture succesfull!");
    }

    while (client.connected()) {
        // How do I transfer the image? What do I have to do?
        client.write(fb->buf);
    }

    client.stop();
    Serial.println("Client disconnected");

  }
}

Код Python

import socket               

sock = socket.socket()

host = "192.168.178.103" # esp ip
port = 5005              # esp port   

sock.connect((host, port))

data = sock.recv(256)

print(data)

sock.close()

person Gereon99    schedule 06.09.2019    source источник
comment
Изображение - это не что иное, как последовательность байтов, хранящаяся в файле. Откройте файл изображения, прочтите эти байты и сохраните их в char* буфере и отправьте содержимое этого буфера с помощью сокета.   -  person kiner_shah    schedule 06.09.2019


Ответы (1)


Вот код Arduino, который работал у меня (начиная с функции цикла)

//=======================================================================
//                    Main Program Loop
//=======================================================================
void loop() {
  WiFiClient client;

  if (!client.connect(host, port)) {

      Serial.println("Connection to host failed");

      delay(1000);
      return;
  }

  Serial.println("Connected to server successful!");

  // capture camera frame
  camera_fb_t *fb = esp_camera_fb_get();
  if(!fb) {
     Serial.println("Camera capture failed");
      return;
  } else {
      Serial.println("Camera capture successful!");
  }
  const char *data = (const char *)fb->buf;
  // Image metadata.  Yes it should be cleaned up to use printf if the function is available
  Serial.print("Size of image:");
  Serial.println(fb->len);
  Serial.print("Shape->width:");
  Serial.print(fb->width);
  Serial.print("height:");
  Serial.println(fb->height);
  client.print("Shape->width:");
  client.print(fb->width);
  client.print("height:");
  client.println(fb->height);
  // Give the server a chance to receive the information before sending an acknowledgement.
  delay(1000);
  getResponse(client);
  Serial.print(data);
  client.write(data, fb->len);
  esp_camera_fb_return(fb);

  Serial.println("Disconnecting...");
  client.stop();

  delay(2000);
}

void getResponse(WiFiClient client) {
  byte buffer[8] = { NULL };
  while (client.available() > 0 || buffer[0] == NULL) {
    int len = client.available();
    Serial.println("Len" + len);
    if (len > 8) len = 8;
    client.read(buffer, len);
    if (printReceivedData) {
       Serial.write(buffer, len); // show in the serial monitor (slows some boards)
       Serial.println("");
    }
  }
}

Код python для получения изображения

import io
import socket
import time

import numpy as np
from PIL import Image

import connection_and_network_constants


serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# address '0.0.0.0' or '' work to allow connections from other machines.  'localhost' disallows external connections.
# see https://www.raspberrypi.org/forums/viewtopic.php?t=62108
serv.bind(('0.0.0.0', connection_and_network_constants.SOCKET_PORT))
serv.listen(5)
print("Ready to accept 5 connections")


def create_image_from_bytes(image_bytes) -> Image.Image:
    stream = io.BytesIO(image_bytes)
    return Image.open(stream)


while True:
    conn, addr = serv.accept()
    array_from_client = bytearray()
    shape = None
    chunks_received = 0
    start = time.time()
    shape_string = ''
    while True:
        # print('waiting for data')
        # Try 4096 if unsure what buffer size to use. Large transfer chunk sizes (which require large buffers) can cause corrupted results
        data = conn.recv(connection_and_network_constants.BUFFER_SIZE)
        if not data or data == b'tx_complete':
            break
        elif shape is None:
            shape_string += data.decode("utf-8")
            # Find the end of the line.  An index other than -1 will be returned if the end has been found because 
            # it has been received
            if shape_string.find('\r\n') != -1:
                width_index = shape_string.find('width:')
                height_index = shape_string.find('height:')
                width = int(shape_string[width_index + len('width:'): height_index])
                height = int(shape_string[height_index + len('height:'): ])
                shape = (width, height)
            print("shape is {}".format(shape))
        else:
            chunks_received += 1
            # print(chunks_received)
            array_from_client.extend(data)
            # print(array_from_client)
        conn.sendall(b'ack')
        # print("sent acknowledgement")
    #     TODO: need to check if sending acknowledgement of the number of chunks and the total length of the array is a good idea
    print("chunks_received {}. Number of bytes {}".format(chunks_received, len(array_from_client)))
    img: Image.Image = create_image_from_bytes(array_from_client)
    img.show()
    array_start_time = time.time()
    image_array = np.asarray(img)
    print('array conversion took {} s'.format(time.time() - array_start_time))
    conn.close()
    print('client disconnected')
person bitrock    schedule 07.03.2020
comment
Здравствуйте! Можно ли отправить и сохранить поток JPEG https://github.com/espressif/esp32-camera#jpeg-http-stream на сервере python прямо из ESP32 Cam - person Ishaan Puniani; 29.03.2020
comment
Судя по всему, код, на который вы ссылаетесь, перебирает код, который делает снимок и отправляет его (и имеет гораздо более надежную обработку ошибок, чем код, который я опубликовал). Вы сможете легко адаптировать концепцию из кода, который я опубликовал, для сохранения потока jpg. Попробуйте непрерывно перебирать код от // кадра захвата камеры до esp_camera_fb_return (fb); включительно. Обратите внимание, что сохранение изображения происходит намного медленнее, чем его создание и отправка из того, что я наблюдал. Если это полезно, пожалуйста, проголосуйте за мой ответ. - person bitrock; 30.03.2020
comment
Здравствуйте, это один из вариантов, но проблема здесь в том, что это не непрерывный поток, потому что с каждой отправкой jpeg на сервер мне нужно устанавливать задержку в несколько миллисекунд. Также такие клиенты, как vlc, не будут получать эти изображения в виде потока. Я разместил stackoverflow.com/questions/60908201/ как вопрос с изображениями для подробного объяснения - person Ishaan Puniani; 31.03.2020