Hello,
I am currently using a ESP32-CAM to recognize digits on a water meter.
I would like to crop the picture tooked by the cam in order to isolate the digits. In order to check the picture cropped I send it on a webServer (temporary solution for testing) but when I crop an image, it doesn't display on the websever
I printed in the console the picture values and they exist (picture has pixels with different values). I just need help to check the cropped image. Someone has an idea why I have this problem ?
Currently, here is my code :
#include "esp_camera.h"
#include <WiFi.h>
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "esp_http_server.h"
#include <HTTPClient.h>
#include "gsc_model_fixed.h"
// WiFi Credentials
const char* ssid = "Khu S";
const char* password = "khu@s2022";
// HTTP server handles
httpd_handle_t stream_httpd = NULL;
httpd_handle_t camera_httpd = NULL;
// LED control parameters
#define LED_CHANNEL 0
#define LED_RESOLUTION 8
#define LED_GPIO_NUM 4
const int defaultFlashIntensity = 20;
// Region of Interest (ROI) for cropping
#define ROI_X_MIN 0
#define ROI_Y_MIN 0
#define ROI_X_MAX 800
#define ROI_Y_MAX 600
// Interval for capturing images
const unsigned long captureInterval = 100000;
unsigned long lastCaptureTime = 0;
// Camera model configuration
#define CAMERA_MODEL_AI_THINKER
#if defined(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
#else
#error "Camera model not selected"
#endif
// Function Prototypes
void setupFlashPWM();
void setFlashIntensity(int intensity);
camera_fb_t* crop_image(camera_fb_t* src_fb, int x_min, int y_min, int x_max, int y_max);
static esp_err_t capture_handler(httpd_req_t *req);
void startCameraServer();
void setup();
void loop();
// Flash LED setup
void setupFlashPWM() {
ledcAttachPin(LED_GPIO_NUM, LED_CHANNEL);
ledcSetup(LED_CHANNEL, 5000, LED_RESOLUTION);
}
uint8_t* resizeImageBilinear(uint8_t* inputImage, int inputWidth, int inputHeight, int outputWidth, int outputHeight) {
uint8_t* outputImage = (uint8_t*)malloc(outputWidth * outputHeight); // Allouer de la mémoire pour l'image de sortie
float scaleX = (float)inputWidth / (float)outputWidth;
float scaleY = (float)inputHeight / (float)outputHeight;
for (int y = 0; y < outputHeight; y++) {
for (int x = 0; x < outputWidth; x++) {
float sourceX = x * scaleX;
float sourceY = y * scaleY;
int sourceX0 = (int)sourceX;
int sourceY0 = (int)sourceY;
int sourceX1 = min(sourceX0 + 1, inputWidth - 1);
int sourceY1 = min(sourceY0 + 1, inputHeight - 1);
float xFraction = sourceX - sourceX0;
float yFraction = sourceY - sourceY0;
uint8_t topLeft = inputImage[sourceY0 * inputWidth + sourceX0];
uint8_t topRight = inputImage[sourceY0 * inputWidth + sourceX1];
uint8_t bottomLeft = inputImage[sourceY1 * inputWidth + sourceX0];
uint8_t bottomRight = inputImage[sourceY1 * inputWidth + sourceX1];
// Interpoler les valeurs des pixels
float topInterpolated = topLeft * (1 - xFraction) + topRight * xFraction;
float bottomInterpolated = bottomLeft * (1 - xFraction) + bottomRight * xFraction;
float interpolatedValue = topInterpolated * (1 - yFraction) + bottomInterpolated * yFraction;
outputImage[y * outputWidth + x] = (uint8_t)interpolatedValue;
}
}
return outputImage;
}
// Set the intensity of the flash LED
void setFlashIntensity(int intensity) {
ledcWrite(LED_CHANNEL, intensity);
}
// Crop the captured image
camera_fb_t* crop_image(camera_fb_t* src_fb, int x_min, int y_min, int x_max, int y_max) {
int src_width = src_fb->width;
int src_height = src_fb->height;
if (x_min < 0) x_min = 0;
if (y_min < 0) y_min = 0;
if (x_max > src_width) x_max = src_width;
if (y_max > src_height) y_max = src_height;
int crop_width = x_max - x_min;
int crop_height = y_max - y_min;
camera_fb_t* cropped_fb = esp_camera_fb_get();
if (!cropped_fb) {
Serial.println("Failed to allocate memory for cropped image");
return NULL;
}
cropped_fb->format = PIXFORMAT_GRAYSCALE;
cropped_fb->width = crop_width;
cropped_fb->height = crop_height;
cropped_fb->len = crop_width * crop_height;
uint8_t* src_data = src_fb->buf;
uint8_t* cropped_data = cropped_fb->buf;
for (int y = y_min; y < y_max; y++) {
for (int x = x_min; x < x_max; x++) {
int src_index = y * src_width + x;
int cropped_index = (y - y_min) * crop_width + (x - x_min);
cropped_data[cropped_index] = src_data[src_index];
}
}
return cropped_fb;
}
// Capture handler for HTTP requests
// Capture handler for HTTP requests
// Capture handler for HTTP requests
static esp_err_t capture_handler(httpd_req_t *req) {
setFlashIntensity(defaultFlashIntensity);
delay(50);
camera_fb_t * fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
httpd_resp_send_500(req);
setFlashIntensity(0);
return ESP_FAIL;
}
camera_fb_t* cropped_fb = crop_image(fb, ROI_X_MIN, ROI_Y_MIN, ROI_X_MAX, ROI_Y_MAX);
if (!cropped_fb) {
Serial.println("Failed to crop image");
httpd_resp_send_500(req);
esp_camera_fb_return(fb);
setFlashIntensity(0);
return ESP_FAIL;
}
// Redimensionner l'image à la taille souhaitée (par exemple, 28x28 pixels)
int desiredWidth = 28;
int desiredHeight = 28;
uint8_t* resizedImage = resizeImageBilinear(cropped_fb->buf, cropped_fb->width, cropped_fb->height, desiredWidth, desiredHeight);
// Envoyer l'image redimensionnée en tant que réponse HTTP
httpd_resp_set_type(req, "image/jpeg");
httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=captured.jpg");
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
esp_err_t res = httpd_resp_send(req, (const char *)resizedImage, desiredWidth * desiredHeight);
// Libérer la mémoire allouée pour les images
esp_camera_fb_return(fb);
esp_camera_fb_return(cropped_fb);
free(resizedImage); // Libérer la mémoire de l'image redimensionnée
delay(5);
setFlashIntensity(0);
return res;
}
// Start the camera server
void startCameraServer() {
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.server_port = 80;
if (httpd_start(&stream_httpd, &config) == ESP_OK) {
httpd_uri_t capture_uri = {
.uri = "/capture",
.method = HTTP_GET,
.handler = capture_handler,
.user_ctx = NULL
};
httpd_register_uri_handler(stream_httpd, &capture_uri);
} else {
Serial.println("Error starting stream server");
}
}
// Setup function
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
Serial.begin(115200);
Serial.setDebugOutput(false);
pinMode(LED_GPIO_NUM, OUTPUT);
setupFlashPWM();
setFlashIntensity(defaultFlashIntensity);
// Camera configuration
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_SVGA;
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(50);
Serial.print(".");
}
Serial.println();
Serial.println("WiFi connected");
Serial.print("Camera Stream Ready! Go to: http://");
Serial.print(WiFi.localIP());
startCameraServer();
lastCaptureTime = millis();
}
// Loop function
void loop() {
unsigned long currentTime = millis();
if (currentTime - lastCaptureTime < captureInterval) {
delay(10);
return;
}
String captureUrl = "http://" + WiFi.localIP().toString() + "/capture";
HTTPClient http;
http.begin(captureUrl);
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
Serial.println("Capture d'image réussie");
} else {
Serial.printf("Échec de la capture d'image, code HTTP: %d\n", httpCode);
}
http.end();
lastCaptureTime = currentTime;
delay(10);
}

