espressif / esp32-camera

Apache License 2.0
1.85k stars 632 forks source link

Photos with green/blue dark tint after restart? #314

Closed knilft closed 2 years ago

knilft commented 3 years ago

I've recently set up my ESP32-CAM to take a jpeg every 5 minutes and send to a webservice. Additionally, I use ESP.restart() once every 24 hours as I have found that it helps keep the thing from hanging up (I have not yet found the root cause for hanging up).

The issue I'm having is that after restarting, the pictures will sometimes have a green/blue tint to them. This does not happen every time a restart occurs, but if it does, the tint will persist until at least another restart happens. Pictures below for a comparison.

Am I missing something crucial in the camera configuration that controls this? Something with the sensor?

Also, I have noticed this with several different ESP32 cams, so I know it's not a faulty module.

BEFORE RESTART: BeforeRestart

AFTER RESTART: AfterRestart

me-no-dev commented 3 years ago
CarlosGS commented 2 years ago

Found some clues, hope these can help: a) the green tint appears only when there is a really bright area (i.e. bright sky). b) lowering the resolution reliably stops the tint (@knilft please verify) c) it is not fixed by restarting nor unplugging the cameras. in my case the tint disappears on its own when the scene goes less luminous d) it doesn't depend on compression / frame length:

Setup info: Ai-Thinker ESP32-CAM w/ OV2640, ESPHome 2021.9.0, config:

esp32_camera:
  external_clock:
    pin: GPIO0
    frequency: 20MHz
  i2c_pins:
    sda: GPIO26
    scl: GPIO27
  data_pins: [GPIO5, GPIO18, GPIO19, GPIO21, GPIO36, GPIO39, GPIO34, GPIO35]
  vsync_pin: GPIO25
  href_pin: GPIO23
  pixel_clock_pin: GPIO22
  power_down_pin: GPIO32
  name: Camara
  resolution: 1600x1200
  jpeg_quality: 10
  max_framerate: 1 fps
CarlosGS commented 2 years ago

A couple images to illustrate the problem. Both taken within seconds of each other, changing the resolution only. (image sections have been cropped and scaled to match each other. no color adjustments) 1600x1200 q=10 image 1280x1024 q=10 image You can see it is not only a green tint, the contrast seems to be affected too.

me-no-dev commented 2 years ago

Is it possible that you expect the first frames to be good? when the camera is initialized, the first few frames will be bad, until WB and Gain can be adjusted by the sensor.

CarlosGS commented 2 years ago

Nope, the green tint/contrast persists in time, seemingly until the sky is less bright.

And it very consistently doesn't happen when using a lower resolution.

github-actions[bot] commented 2 years ago

This issue appears to be stale. Please close it if its no longer valid.

CarlosGS commented 2 years ago

Let me know if I can do any more testing, I'd love to be able to use the maximum resolution without having these tint issues.

Stepnolab commented 2 years ago

Hello CarlosGS. I have exactly the same problem, that arises when in presence of a cloudy day with sudden variation of sky brilliance. Here is a sequence, as you can see as soon as the sky becomes less cloudy (top left), the image worses, and then goes back to normal image (Pic1) image (Pic2 - 30' later) image (Pic3 - 30' later - sun is appearing and the image became dark and green) image (Pic4 - 30' later again - no more sun, image quite OK)

I wasn't able to manage it through normal register settings, but I'm now trying to overcame the issue using manual exposure and an external luxometer to get the "average" luminosity of the scene, instead of leveraging on the 0x2F Luminance Average register (note: Figure 18 of AV2640 specs: the light response to different RGB wavelenghts shows very different curves, and the channel average formula is poorly weighted in my opinion) I'll be back with some data asap

Stepnolab commented 2 years ago

Hello again CarlosGS. It took some time to arrange a test, but here is what I discovered: the Automatic Exposure Control AEC and the Automatic Gain Control AGC are highly related to the calculation of the "Luminance Average" YAVG, the last being auto updated by the OV2640 and written to register 2F. In particular, the auto-update starts immediately after a call to _esp_err_t err=esp_camerainit(&config) (I use Arduino IDE) If you start to read the register immediately after a "restart" (as in my case , using deep_sleep) it appears that the register updates every 500ms, starting from a value usually higher than the one you can get after some seconds of stabilisation. The time to stabilize is related to the resolution (higher the resolution, longer the time) and to the luminance of the scene (stronger the brilliance, longer the time) and to the air temperature (longer the time if closer to the lower 0°C limit of operation). During a winter cloudy day, it took from 1.2 up to 4.8 seconds to get a constant YAVG value at SVGA resolution, less than 1 second@CIF, around 6 seconds@UXGA. If I wait sufficient time to let the YAVG value stabilize before _esp_camera_fbget, than the photo is almost perfect and I do not suffer any more of the "dark tint after restart". Another interesting thing: while the AEC value is more or less inversely related to YAVG, the AGC varies a lot: in the following two pictures I got almost the same YAVG (62 in Decimal) and AEC(3 in Decimal) but two completely different values for AGC (8bit): In the first picture AGC is 00000011 (it should mean around 1.2x), in the second AGC is 00001110 (around 2x). Again, if I wait a sufficient time for YAVG, also the AGC register value stabilizes itself. picture180_AUTO_WHILE_1 60 sec_62_3_3 picture204_AUTO_WHILE_1 80 sec_61_1_14 I hope these observations useful for you! Stepno

github-actions[bot] commented 2 years ago

This issue appears to be stale. Please close it if its no longer valid.

knilft commented 2 years ago

Hi Stepnolab and CarlosGS, Thanks for the extrememly thorough and useful results after investigating this issue. I unfortunately had to deploy my cameras and code long before y'all were able to dig into this. But, I'll have a solution to try the next time I set one of these up. All the best.

CarlosGS commented 2 years ago

Thanks Knilft for reporting and Stepno for the very defailed insight! :pray:

@Stepnolab I did keep trying, increasing the intervals between frames (+ testing soo many settings) but so far couldn't use 1600x1200 reliably. Would you mind sharing your working sketch? It would really help me pinpoint the differences :smiling_face_with_tear:

KungPhoo commented 1 year ago

@Stepnolab how do you wait for the AGC to stabilize? I didn't find out how to read the current AGC value. Also - what difference value can be considered as "stabilized"?

TheLongRunSmoke commented 1 year ago

Well, I stumble upon this issue are few days ago. My workaround look like this:

camera_fb_t* frameBuffer = nullptr;
// Skip first N frames.
for (int i = 0; i < 3; i++) {
    frameBuffer = esp_camera_fb_get();
    esp_camera_fb_return(frameBuffer);
    frameBuffer = nullptr;
}

Not entirely sure it is correct, but after 3-5 frames green tint is basically gone, for JPEG 1600x1200. However, it's still greenish in bad conditions, like in a very dim light.

Not planning to further investigate issue, because have no use to encoded JPEG in my project.

mark-hahn commented 8 months ago

I am designing a camera that needs to take a picture within one second after power-up. Is there a way to overcome this agc calculating delay, maybe by using a manual setting? If not, is there some alternative to the ov2640 that doesn't have this delay?

logan893 commented 8 months ago

I'm also having these green tint problems with an OV2640 based TTGO mini with certain scenarios and settings. Indoor setting, fairly low light scenario. White balance left at auto or set to home doesn't seem to matter. Tests below has white balance left at auto.

All manual gain modes are consistent in their image tint from this basic testing in unchanging lighting conditions. I have not moved the camera or changed the lighting between the images saved. Only the first frame of auto gain and manual gain set to 0 gives green tint.

In this test, the manual gain set to 4 gives a seeming equal brightness to automatic gain (ceiling 2x), at significantly lower image quality (much more noise).

Image size is very interesting also.

Automatic gain (default settings; agc_mode: auto, agc_gain_ceiling: 2x) from frame 2+. So far the green tint seems to happen at least for the first frame with automatic gain control active, after which it seems to be back to normal proper colors. Image size: 98 kB TTGO_Camera Mini 1_2024-01-04T15_39_25 389Z_gainauto

Manual gain (agc_mode: manual, agc_value: 0). With manual gain set to 0 the green tint is continuous. Brightness is perhaps slightly lower than the stable-state with automatic gain. Image size: 59 kB TTGO_Camera Mini 1_2024-01-04T15_34_51 797Z_gain0_and_autofirstframe

Manual gain (agc_mode: manual, agc_value: 1). With manual gain set to 1 the image is significantly darker than both gain 0 and auto gain, with a slight blue tint. The image quality and hue is continuous. Slight increase in noise. Image size: 90 kB TTGO_Camera Mini 1_2024-01-04T15_44_22 491Z_gain1

Manual gain (agc_mode: manual, agc_value: 2). With manual gain set to 2 there is still a hint of blue tint, it is continuous, and image quality is deteriorating further with additional noise. Image size: 138 kB TTGO_Camera Mini 1_2024-01-04T15_48_12 717Z_gain2

Manual gain (agc_mode: manual, agc_value: 3). Gain at 3 still has a blue tint, and more noise. Image size: 156 kB TTGO_Camera Mini 1_2024-01-04T15_51_42 309Z_gain3

Manual gain (agc_mode: manual, agc_value: 4). Gain at 4 and the blue tint is finally gone. Brightness is seemingly on bar with the auto gain setting, with much more noise in comparison. Image size: 173 kB TTGO_Camera Mini 1_2024-01-04T15_58_17 670Z_gain4

logan893 commented 8 months ago

Additionally, size of first vs subsequent images generated after a camera restart, with gain set to automatic, are as follows.

[19:53:29][D][api:102]: Accepted x.x.x.x
[19:53:29][D][api.connection:1121]: Home Assistant 2023.12.3 (x.x.x.x): Connected successfully
[19:53:57][D][esp32_camera:196]: Got Image: len=49933
[19:54:18][D][esp32_camera:196]: Got Image: len=97527
[19:54:28][D][esp32_camera:196]: Got Image: len=97725
[19:54:38][D][esp32_camera:196]: Got Image: len=97613
[19:54:48][D][esp32_camera:196]: Got Image: len=97829
[19:54:58][D][esp32_camera:196]: Got Image: len=97711
[19:55:08][D][esp32_camera:196]: Got Image: len=97713
ymich9963 commented 6 months ago

If anyone is still struggling with this, if you disable all of the auto functions except lenc, and set the sensor settings after initialisation to white balance mode 3 or 4, it should get rid of the green tint. The sensor initialises with AGC and AWB enabled so you need to disable them and fix the white balance before taking a picture.

jamienz commented 4 months ago

That sounds promising @ymich9963, do you have a code snippet showing the specific settings?

TheLongRunSmoke commented 4 months ago

I tried @ymich9963's solution but it didn't solve the problem. It seems to work, but only because he deliberately set the awb mode to 3 (office) or 4 (home), which slightly balances out the blue tint.

ymich9963 commented 4 months ago

@jamienz yes of course. The WULPSC repo on my account uses the esp-camera driver

ymich9963 commented 4 months ago

@TheLongRunSmoke you can try setting wb mode to 0 instead, or at least set it to another mode and then back to 0 to refresh the white balance. Have you tried refreshing the image by capturing and returning the frame buffer?

TheLongRunSmoke commented 4 months ago

@ymich9963 yes. Your code used same trick with recapturing that I mentioned early. I look at WULPSC and there few other interesting things, like you initialy set aec to a small value.

I'm linking your code here, it might be helpful. https://github.com/ymich9963/WULPSC/blob/42a5a65305e34a70e2f2345e115effba87407ef0/MCC/main/main.c#L14 https://github.com/ymich9963/WULPSC/blob/42a5a65305e34a70e2f2345e115effba87407ef0/MCC/main/source/mcc_config.c#L16

ymich9963 commented 4 months ago

@TheLongRunSmoke it's set to 20 since that was found to be best for room light level pictures. Maybe try my settings and see what you find.

jamienz commented 4 months ago

@jamienz yes of course. The WULPSC repo on my account uses the esp-camera driver

Thanks for the pointer @ymich9963. That did indeed resolve the issue!

I know @TheLongRunSmoke pasted links to code above, but for the convenience of those using Rui Santos's ESP32Cam code samples (or those unknowingly using derivatives of them), below is a snippet of code showing how the fix by @ymich9963 would fit into that. Note that he sets those settings prior to each image captured.

Also note that because most of the camera's auto settings are now disabled, set_aec_value(..) becomes very important - set this according to the amount of light the camera receives. Inside on a dull rainy day with the lights off, 400 worked for me. @ymich9963 had this set to 20, so he may have been sitting next to a supernova.

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

  // start: fix for dark green/blue issue
  sensor_t *s = esp_camera_sensor_get();
  s->set_brightness(s, 0);
  s->set_contrast(s, 0);
  s->set_saturation(s, 0);
  // s->set_sharpness(s, 0); not supported by OV2640
  // s->set_denoise(s, 0); not supported by OV2640
  s->set_special_effect(s, 0);
  s->set_wb_mode(s, 3);
  s->set_ae_level(s, 0);
  s->set_awb_gain(s, 1);
  s->set_wb_mode(s, 3); // 0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home
  s->set_aec_value(s, 400); // the brighter your scene the lower this value should be (0-1200)
  s->set_agc_gain(s, 2);
  s->set_gainceiling(s, GAINCEILING_4X);
  s->set_lenc(s, true);
  s->set_gain_ctrl(s, false);
  s->set_exposure_ctrl(s, false);
  s->set_hmirror(s, false);
  s->set_vflip(s, false);
  s->set_aec2(s, true);
  s->set_bpc(s, true);
  s->set_wpc(s, true);
  // end: fix for dark green/blue issue

  camera_fb_t *fb = NULL;
  fb = esp_camera_fb_get();
ymich9963 commented 4 months ago

@jamienz lol yeah these cameras are very sensitive. Glad to hear your issue has been resolved. WB Mode 3 can add a bit of a blue tint so you could try taking a frame buffer really quickly with one set of settings to help it adjust and get a clearer image on the next frame. This type of procedure would only be needed as soon as the camera would be initialised and then it should be good for subsequent pictures.

TheLongRunSmoke commented 4 months ago

I returned to this issue and check @ymich9963 solution as its used in WULPSC…and it works great!

To make it work.

  1. Disable auto exposure.
  2. Set aec to some small value. 50-500 work ok.
  3. Skip one frame.
  4. Enable auto exposure.
  5. Skip frame or two.

Next captured frame has almost no tint in almost any conditions.

Snippet from my tests.


init(){
     …
    Normal init section finished with esp_camera_init()
    …
    sensor_t* s = esp_camera_sensor_get();
    s->set_brightness(s, 1);               // I want image about 1 stop brighter (-2 to 2)
    s->set_contrast(s, 0);
    s->set_saturation(s, 0);
    s->set_gain_ctrl(s, 0);                // Auto gain off
    s->set_exposure_ctrl(s, 0);            // Auto exposure off
    s->set_aec_value(s, 100);              // Set aec to a small initial value.
    s->set_awb_gain(s, 1);                 // Auto White Balance enable (0 or 1)
    s->set_wb_mode(s, 0);                  // If awb_gain enabled (0-Auto,1-Sunny,2-Cloudy,3-Office,4-Home)
    s->set_aec2(s, 1);                     // AEC DSP
    s->set_bpc(s, 1);                      // Black point control
    s->set_wpc(s, 1);                      // White point control
    s->set_lenc(s, 1);                     // Lens correction
    // Due board position, rotate image 180 degrees.
    s->set_hmirror(s, 1);
    s->set_vflip(s, 1);
}

camera_fb_t* capture() {
    if (!ok) return nullptr;
    // Skip first frame.
    esp_camera_fb_return(_frameBuffer);
    _frameBuffer = esp_camera_fb_get();
    esp_camera_fb_return(_frameBuffer);
    // Enable auto exposure.
    sensor_t* s = esp_camera_sensor_get();
    s->set_gain_ctrl(s, 1);                 // Auto gain on
    s->set_exposure_ctrl(s, 1);             // Auto exposure on
    // Skip 2 frames.
    for (int i = 0; i < 2; i++) {
        _frameBuffer = esp_camera_fb_get();
        esp_camera_fb_return(_frameBuffer);
    }
    return esp_camera_fb_get();
}
ymich9963 commented 4 months ago

@TheLongRunSmoke Very interesting solution! I wonder why you need to execute those processes in that sequence but it's good to hear that it works! I would think skipping steps 4 and 5 entirely would also work but I guess it may depend on your implementation.

AxelLin commented 3 months ago

Is it possible that you expect the first frames to be good? when the camera is initialized, the first few frames will be bad, until WB and Gain can be adjusted by the sensor.

@me-no-dev If the first few frames will be bad, how to know which frame begins to be good? (Is there any logic can be used to check if a frame is good?)

me-no-dev commented 3 months ago

@AxelLin I'm not sure exactly, I think it depends on the settings and actual environment. Maybe you can use some "safe" value, like skip 10 frames?

davefes commented 1 month ago

Thank you for the "10 frame" suggestion. I have been struggling for over a year to get a snapshot after coming out of deepsleep or lightsleep using the firmware at: https://github.com/shariltumin/esp32-cam-micropython-2022/tree/main/X23/esp32-aiThinker/firmwares without many of the problems mentioned above.

step135 commented 2 weeks ago

I was taking photos in a loop of sequence of 4, then changed to 15 and it didn't make any difference. The first 4 photos that I saved after the sequence were bad. I believe it is question of time and it can be enough to take 4 photos with 2 seconds delays between each other, then the following images will be good.

davefes commented 2 weeks ago

After a month's testing taking 5 frames with 100ms delays works much better. 4 * 2 seconds the intruder is long-gone!