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

I've been experimenting with the ov2640 sensor and found some nice things #203

Closed
raduprv opened this issue Nov 12, 2020 · 10 comments
Closed

Comments

@raduprv
Copy link

raduprv commented Nov 12, 2020

First, use this code, it has the set_reg() actually implemented: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json

Most of my changes only make sense if you want to capture still pictures. For video, the default settings are more or less ok.

So, I identified a few problems with the default settings:

  1. The image is too noisy (short exposure time, light is done by increasing gain, not exposure time).
  2. The noise is sharpened
  3. Images at a somewhat high quality (under 5 or so) will sometimes go over ~350 kb and would cause the image to be corrupted and unusable.

So here are the solutions. Btw, one problem with capturing still images is that after changing the sensor settings you must use a small delay (over 1 second for longer exposures) before getting the frame buffer.

  1. I borrowed and adapted the code from here: http://read.pudn.com/downloads135/sourcecode/embed/573503/ov2640/ov2640.c__.htm

typedef unsigned char u8;

#define OV2640_MAXLEVEL_SHARPNESS 6

const static u8 OV2640_SHARPNESS_AUTO[]=
{
0xFF, 0x00, 0xff,
0x92, 0x01, 0xff,
0x93, 0x20, 0x20,
0x00, 0x00, 0x00
};

const static u8 OV2640_SHARPNESS_MANUAL[]=
{
0xFF, 0x00, 0xff,
0x92, 0x01, 0xff,
0x93, 0x00, 0x20,
0x00, 0x00, 0x00
};

const static u8 OV2640_SHARPNESS_LEVEL0[]=
{
0xFF, 0x00, 0xff,
0x92, 0x01, 0xff,
0x93, 0xc0, 0x1f,
0x00, 0x00, 0x00
};
const static u8 OV2640_SHARPNESS_LEVEL1[]=
{
0xFF, 0x00, 0xff,
0x92, 0x01, 0xff,
0x93, 0xc1, 0x1f,
0x00, 0x00, 0x00
};
const static u8 OV2640_SHARPNESS_LEVEL2[]=
{
0xFF, 0x00, 0xff,
0x92, 0x01, 0xff,
0x93, 0xc2, 0x1f,
0x00, 0x00, 0x00
};
const static u8 OV2640_SHARPNESS_LEVEL3[]=
{
0xFF, 0x00, 0xff,
0x92, 0x01, 0xff,
0x93, 0xc4, 0x1f,
0x00, 0x00, 0x00
};
const static u8 OV2640_SHARPNESS_LEVEL4[]=
{
0xFF, 0x00, 0xff,
0x92, 0x01, 0xff,
0x93, 0xc8, 0x1f,
0x00, 0x00, 0x00
};
const static u8 OV2640_SHARPNESS_LEVEL5[]=
{
0xFF, 0x00, 0xff,
0x92, 0x01, 0xff,
0x93, 0xd0, 0x1f,
0x00, 0x00, 0x00
};
const static u8 OV2640_SHARPNESS_LEVEL6[]=
{
0xFF, 0x00, 0xff,
0x92, 0x01, 0xff,
0x93, 0xdf, 0x1f,
0x00, 0x00, 0x00
};

const static u8 *OV_SETTING_SHARPNESS[]=
{
OV2640_SHARPNESS_LEVEL0,
OV2640_SHARPNESS_LEVEL1,
OV2640_SHARPNESS_LEVEL2,
OV2640_SHARPNESS_LEVEL3,
OV2640_SHARPNESS_LEVEL4,
OV2640_SHARPNESS_LEVEL5,
OV2640_SHARPNESS_LEVEL6
};

static int table_mask_write(const u8* ptab)
{
u8 address;
u8 value,orgval;
u8 mask;
const u8 *pdata=ptab;

if ( NULL==pdata )   
    return -1;

sensor_t * s = esp_camera_sensor_get();

while(1)   
{   
    address =*pdata++;   
    value = *pdata++;   
    mask = *pdata++;           
    if ( (0==address) && (0==value) &&(0==mask) )   
    {   
        break;   
    }   
    
    s->set_reg(s,address,mask,value);
}   

return 0;   

}

int change_sharpness( int sharpness )
{
if ( sharpness > OV2640_MAXLEVEL_SHARPNESS)
{
return -1;
}

if( sharpness <0 )   
{   
    table_mask_write(OV2640_SHARPNESS_AUTO);       
}   
else   
{   
    table_mask_write(OV2640_SHARPNESS_MANUAL);     
    table_mask_write(OV_SETTING_SHARPNESS[sharpness]);   
}      

return 0;   

}

After you initialize the camera, simply call: change_sharpness(0);

@raduprv
Copy link
Author

raduprv commented Nov 12, 2020

Longer exposure. This one is easy:
s->set_reg(s,0xff,0xff,0x01);//banksel
s->set_reg(s,0x11,0xff,01);//frame rate
This is about 1 second.

And finally, being able to save high quality images (if the image is getting corrupted)
s->set_reg(s,0xff,0xff,0x00);//banksel
s->set_reg(s,0xd3,0xff,5);//jpg clock

Another interesting discovery is that this DSP register changes the quality of the image. Only the first 7 bits matter, not sure what the 8th bit is used for. very low numbers will give you very bad quality, but at higher numbers I couldn't find any noticable differences in quality.

s->set_reg(s,0xff,0xff,0x00);//banksel
s->set_reg(s,0x42,0xff,0x4f)

@raduprv
Copy link
Author

raduprv commented Nov 12, 2020

And here are my complete settings for best quality picture (YMMV, feel free to experiment)

s->set_brightness(s, 0); // -2 to 2
s->set_contrast(s, -2); // -2 to 2
s->set_saturation(s, -2); // -2 to 2
s->set_special_effect(s, 0); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
s->set_whitebal(s, 1); // 0 = disable , 1 = enable
s->set_awb_gain(s, 1); // 0 = disable , 1 = enable
s->set_wb_mode(s, 0); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
s->set_exposure_ctrl(s, 1); // 0 = disable , 1 = enable
s->set_aec2(s, 0); // 0 = disable , 1 = enable
//s->set_ae_level(s, 2); // -2 to 2
//s->set_aec_value(s, 400); // 0 to 1200
s->set_gain_ctrl(s, 0); // 0 = disable , 1 = enable
s->set_agc_gain(s, 0); // 0 to 30
s->set_gainceiling(s, (gainceiling_t)6); // 0 to 6
s->set_bpc(s, 1); // 0 = disable , 1 = enable
s->set_wpc(s, 1); // 0 = disable , 1 = enable
s->set_raw_gma(s, 1); // 0 = disable , 1 = enable (makes much lighter and noisy)
s->set_lenc(s, 0); // 0 = disable , 1 = enable
s->set_hmirror(s, 0); // 0 = disable , 1 = enable
s->set_vflip(s, 0); // 0 = disable , 1 = enable
s->set_dcw(s, 0); // 0 = disable , 1 = enable
s->set_colorbar(s, 0); // 0 = disable , 1 = enable

change_sharpness(0);

s->set_reg(s,0xff,0xff,0x01);//banksel 
s->set_reg(s,0x11,0xff,01);//frame rate

s->set_reg(s,0xff,0xff,0x00);//banksel 
s->set_reg(s,0x86,0xff,1);//disable effects

s->set_reg(s,0xd3,0xff,5);//clock

s->set_reg(s,0x42,0xff,0x4f);//image quality (lower is bad)
s->set_reg(s,0x44,0xff,1);//quality

delay(1200);

@BrianPugh
Copy link

I'm super excited to try these modifications. As for your post, I was wondering if you could fix the formatting for easier viewing? Thanks!

@raduprv
Copy link
Author

raduprv commented Nov 12, 2020

Unfortunately, I can't edit posts with my browser (Waterfox). Either way, I just wanted to post the information, hoping that others find it useful and maybe some of it ends up in the official version, if the developers think it's useful.
One thing though, my program is meant for timelapse, taking single pictures at high quality and then sleeping a bit. Not sure if the long exposure thing is very useful in videos :)
But let me know if it works for you too.

@BrianPugh
Copy link

so far I think this both drastically improved my quality and solved my buffer issues. Thank you so much! I'll be testing more this week and post some nicely formatted code.

@jensdk3
Copy link

jensdk3 commented Apr 2, 2021

@raduprv , Thanks a lot for sharing the results of your big investigation!

I can confirm your settings in this post #203 (comment) works well for me also. I have a ESP32CAM AI thinker type with a OV2640 sensor and 4MB psram.
I get much less noise in image and a jpg image size of about 500kB with 1600x1200 resolution. However my images also gets darker. But this is as expected I guess.

I have two questions that I hope you have time to answer. Maybe they can also be of interest to others.

Can you elaborate a bit on how to change exposure time?
I assume these two lines control exposure time:

s->set_reg(s,0xff,0xff,0x01);//banksel 
s->set_reg(s,0x11,0xff,01);//frame rate

But I did not get darker images by changing frame rate 01->02
Any formula you can share if I want exposure time of for example: 0.5s, 1.0s, 2.0s?

What is the link between the value 1 in camera config:

config.jpeg_quality = 1; (higher is bad)

and the value 1 in your solution

  s->set_reg(s,0x42,0xff,0x4f);//image quality (lower is bad)
  s->set_reg(s,0x44,0xff,1);//quality

which one takes precedence? Would putting a 5 in your solution increase quality?

@raduprv
Copy link
Author

raduprv commented Apr 2, 2021

Hi. Setting the config.jpeg_quality = 1; at the beginning tells the camera library to allocate a large buffer (if quality is under 4). The second part is the actual quality. For your other questions, take a look at my timelapse program here: https://github.com/raduprv/esp32-cam_ov2640-timelapse I set the exposure based on the lightmeter register. I do it by empirically observing which is the best value depending on the value of that register.

@nilspupils
Copy link

@raduprv: Thank you for providing the code, which has been very helpful in getting a decent image quality from the camera. Have you been able to find out anything about the noise reduction also?
I have been looking for information in the internet and found two additional files, the hardware application and the software application notes. Are you aware of these?
Thanks again and greetings!

@raduprv
Copy link
Author

raduprv commented Aug 18, 2021

Check out my timelapase program, it has all the settings that I know of:
https://github.com/raduprv/esp32-cam_ov2640-timelapse
I don't find any noticeable noise reduction artifacts. The only thing I don't know how to solve is reducing the exposure time beyond certain values, so bright scenes always get overexposed. Maybe it's a hardware limit?

And yes, I looked at all the files I could find for that camera :)

@github-actions
Copy link

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

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

No branches or pull requests

4 participants