Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect frame width and height returned by esp_camera_fb_get() when using sensor->set_res_raw() #669

Open
jksemple opened this issue Jul 16, 2024 · 1 comment

Comments

@jksemple
Copy link

Using sensor->set_res_raw allows a custom image size/offset to be specified allowing e.g. a square JPEG to be returned by esp_camera_fb_get(). However, the implementation of esp_camera_fb_get (lines 369-370) sets frame.width and frame.height by indexing into the resolution[] array which is not always appropriate.

It should return the actual width and height of the returned JPEG to simplify further image processing.

Steps to reproduce. Run this sketch (configured for ESP32S3 XIAO):

#include <esp_camera.h>
#include <JPEGDEC.h>

camera_config_t config;
camera_fb_t* fb = NULL;

JPEGDEC decoder;

void setup() {

#define PWDN_GPIO_NUM  -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM  10
#define SIOD_GPIO_NUM  40
#define SIOC_GPIO_NUM  39

#define Y9_GPIO_NUM    48
#define Y8_GPIO_NUM    11
#define Y7_GPIO_NUM    12
#define Y6_GPIO_NUM    14
#define Y5_GPIO_NUM    16
#define Y4_GPIO_NUM    18
#define Y3_GPIO_NUM    17
#define Y2_GPIO_NUM    15
#define VSYNC_GPIO_NUM 38
#define HREF_GPIO_NUM  47
#define PCLK_GPIO_NUM  13

  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_sccb_sda = SIOD_GPIO_NUM;
  config.pin_sccb_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; //YUV422,GRAYSCALE,RGB565,JPEG
  config.grab_mode = CAMERA_GRAB_LATEST;

  config.frame_size = FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
  config.jpeg_quality = 10; //0-63 lower number means higher quality
  config.fb_count = 1;
  
  // Initialize the Camera
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }
  auto sensor = esp_camera_sensor_get();
  sensor->set_res_raw(sensor, 0, 0, 0, 0, 200, 0, 1200, 1200, 1200, 1200, false, false);
}

void loop() {
  while(1) {
    if (fb) {
      esp_camera_fb_return(fb);
    }
    fb = esp_camera_fb_get();
    log_i("Driver:  Width=%d Height=%d", fb->width, fb->height);
    decoder.openRAM(fb->buf, fb->len, NULL);
    log_i("JPEGDEC: Width=%d Height=%d", decoder.getWidth(), decoder.getHeight());

    delay(2000);
  }
}

Example output:
[ 4927][I][SetResRawBug.ino:71] loop(): Driver: Width=1600 Height=1200
[ 4934][I][SetResRawBug.ino:73] loop(): JPEGDEC: Width=1200 Height=1200

@jksemple
Copy link
Author

jksemple commented Aug 9, 2024

While this bug remains outstanding it is possible to patch the erroneous width/height in the camera buffer as the following code demonstrates:

#include <esp_camera.h>
#include "img_converters.h"
camera_config_t config;
camera_fb_t* fb = NULL;

#define PWDN_GPIO_NUM  -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM  10
#define SIOD_GPIO_NUM  40
#define SIOC_GPIO_NUM  39

#define Y9_GPIO_NUM    48
#define Y8_GPIO_NUM    11
#define Y7_GPIO_NUM    12
#define Y6_GPIO_NUM    14
#define Y5_GPIO_NUM    16
#define Y4_GPIO_NUM    18
#define Y3_GPIO_NUM    17
#define Y2_GPIO_NUM    15
#define VSYNC_GPIO_NUM 38
#define HREF_GPIO_NUM  47
#define PCLK_GPIO_NUM  13

typedef struct {
        uint16_t width;
        uint16_t height;
        uint16_t data_offset;
        const uint8_t *input;
        uint8_t *output;
} jpg_decoder;

// JPG reader
static unsigned int _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len)
{
    jpg_decoder * jpeg = (jpg_decoder *)arg;
    if(buf) {
        memcpy(buf, jpeg->input + index, len);
    }
    return len;
}

static bool _jpg_write(void* arg, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t* data)
{
    jpg_decoder * jpeg = (jpg_decoder *)arg;
    if(!data){
        if(x == 0 && y == 0){
            jpeg->width = w;
            jpeg->height = h;
        }
    }
    return true;
}

static void extractJpegSize(uint8_t* buffer, size_t len, size_t& width, size_t& height) {
  jpg_decoder jpeg;
  jpeg.input = buffer;
  jpeg.data_offset = 0;
  esp_jpg_decode(len, JPG_SCALE_NONE, _jpg_read, _jpg_write, (void*)&jpeg);
  width = jpeg.width;
  height = jpeg.height;
}

static void fixCameraBuffer(camera_fb_t* fb) {
  if (!fb) return;

  if (!fb->buf) return;

  extractJpegSize(fb->buf, fb->len, fb->width, fb->height);
}

void setup() {

  Serial.begin(115200);
  while (!Serial) ;

  Serial.println("Starting");

  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_sccb_sda = SIOD_GPIO_NUM;
  config.pin_sccb_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; //YUV422,GRAYSCALE,RGB565,JPEG
  config.grab_mode = CAMERA_GRAB_LATEST;

  config.frame_size = FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
  config.jpeg_quality = 10; //0-63 lower number means higher quality
  config.fb_count = 1;
  
  // Initialize the Camera
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }
  auto sensor = esp_camera_sensor_get();
  sensor->set_res_raw(sensor, 0, 0, 0, 0, 200, 0, 1200, 1200, 1200, 1200, false, false);
  log_i("Sensor PID=%4x", sensor->id.PID);
  Serial.println("Finished setup");
}

void loop() {
  while(1) {
    if (fb) {
      esp_camera_fb_return(fb);
    }
    Serial.println("Capture");
    fb = esp_camera_fb_get();
    log_i("Driver   : Width=%d Height=%d", fb->width, fb->height);
    fixCameraBuffer(fb);
    log_i("After fix: Width=%d Height=%d", fb->width, fb->height);

    delay(2000);
  }
}

Example output:
11:47:59.764 -> Capture
11:47:59.857 -> [408002][I][SetResRawBugWorkaround.ino:123] loop(): Driver : Width=1600 Height=1200
11:48:00.638 -> [408785][I][SetResRawBugWorkaround.ino:125] loop(): After fix: Width=1200 Height=1200

Note that the JPEGDEC library is not now required. The fix code just uses the esp_jpg_decode already present in the driver.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant