diff --git a/src/io_pipeline.c b/src/io_pipeline.c index 05a8b25..e7d6906 100644 --- a/src/io_pipeline.c +++ b/src/io_pipeline.c @@ -24,6 +24,12 @@ static MPPipeline *pipeline; static GSource *capture_source; static bool pipeline_changed = true; +typedef struct invoke_set_control { + uint32_t control; + int32_t int_value; + bool bool_value; +} invoke_set_control; + static void setup(MPPipeline *pipeline, const void *data) { @@ -124,6 +130,23 @@ mp_io_pipeline_focus() mp_pipeline_invoke(pipeline, focus, NULL, 0); } +static void +set_control_int32(MPPipeline *pipeline, const invoke_set_control *data) +{ + mp_camera_control_set_int32(state_io.camera, data->control, data->int_value); +} + +void +mp_io_pipeline_set_control_int32(uint32_t control, uint32_t value) +{ + invoke_set_control data = { 0 }; + data.control = control; + data.int_value = value; + + mp_pipeline_invoke( + pipeline, set_control_int32, &data, sizeof(invoke_set_control)); +} + static void capture(MPPipeline *pipeline, const void *data) { @@ -256,13 +279,45 @@ update_controls() static void do_aaa() { - if (!state_io.exposure.manual && state_io.exposure.auto_control == 0) { - int step = state_io.gain.value / 16; - if (step < 1) { - step = 1; + bool auto_exposure = + !state_io.exposure.manual && state_io.exposure.auto_control == 0; + + if (auto_exposure) { + int direction = state_io.stats.exposure; + int step = 0; + if (direction > 0) { + // Preview is too dark + + // Try raising the exposure time first + if (state_io.exposure.value < state_io.exposure.max) { + step = state_io.exposure.value / 16; + state_io.exposure.value_req = + state_io.exposure.value + (step * direction); + printf("Expose + %d\n", state_io.exposure.value_req); + } else { + // Raise sensor gain if exposure limit is hit + step = state_io.gain.value / 16; + state_io.gain.value_req = + state_io.gain.value + (step * direction); + printf("Gain + %d\n", state_io.gain.value_req); + } + } else if (direction < 0) { + // Preview is too bright + + // Lower the sensor gain first to have less noise + if (state_io.gain.value > 0) { + step = state_io.gain.value / 16; + state_io.gain.value_req = + state_io.gain.value + (step * direction); + printf("Gain - %d\n", state_io.gain.value_req); + } else { + // Shorten the exposure time to go even darker + step = state_io.exposure.value / 16; + state_io.exposure.value_req = + state_io.exposure.value + (step * direction); + printf("Expose - %d\n", state_io.exposure.value_req); + } } - state_io.gain.value_req = state_io.gain.value; - state_io.gain.value_req += step * state_io.stats.exposure; } } diff --git a/src/io_pipeline.h b/src/io_pipeline.h index 575b800..21ca6d8 100644 --- a/src/io_pipeline.h +++ b/src/io_pipeline.h @@ -10,6 +10,7 @@ void mp_io_pipeline_stop(); void mp_io_pipeline_focus(); void mp_io_pipeline_capture(); +void mp_io_pipeline_set_control_int32(uint32_t control, uint32_t value); void mp_io_pipeline_release_buffer(uint32_t buffer_index); diff --git a/src/process_pipeline.c b/src/process_pipeline.c index 9972aaa..56d0520 100644 --- a/src/process_pipeline.c +++ b/src/process_pipeline.c @@ -39,6 +39,7 @@ static int output_buffer_width = -1; static int output_buffer_height = -1; static bool flash_enabled; +static int framecounter = 0; static char capture_fname[255]; @@ -154,6 +155,10 @@ setup(MPPipeline *pipeline, const void *data) libdng_init(); settings = g_settings_new(APP_ID); prctl(PR_SET_NAME, "megapixels-pr", NULL, NULL, NULL); + + state_proc.mode_balance = AAA_BY_POST; + state_proc.mode_exposure = AAA_BY_V4L2_CONTROLS; + state_proc.mode_focus = AAA_DISABLED; } void @@ -340,6 +345,122 @@ clamp_float(float value, float min, float max) return value; } +static void +clamp_control(controlstate *control) +{ + if (control->value_req > control->max) { + control->value_req = control->max; + } +} + +static void +process_aaa() +{ + bool auto_exposure = + !state_proc.exposure.manual && state_proc.exposure.auto_control == 0; + bool auto_focus = + !state_proc.focus.manual && state_proc.focus.auto_control == 0; + bool auto_balance = TRUE; + if (!auto_exposure && !auto_focus && !auto_balance) { + return; + } + + int width = output_buffer_width; + int height = output_buffer_height / 3; + uint32_t *center = g_malloc_n(width * height * sizeof(uint32_t), 1); + glReadPixels(0, height, width, height, GL_RGBA, GL_UNSIGNED_BYTE, center); + + libmegapixels_aaa_set_matrix(&state_proc.stats, + state_proc.calibration.color_matrix_1, + state_proc.calibration.color_matrix_2); + libmegapixels_aaa_software_statistics( + &state_proc.stats, center, width, height); + + state_proc.blacklevel -= (float)state_proc.stats.blacklevel * 0.001f; + state_proc.blacklevel = clamp_float(state_proc.blacklevel, 0.0f, 0.07f); + + if (auto_exposure) { + int direction = state_proc.stats.exposure; + int step = 0; + if (direction > 0) { + // Preview is too dark + + // Try raising the exposure time first + if (state_proc.exposure.value < state_proc.exposure.max) { + step = state_proc.exposure.value / 4; + step = step < 4 ? 4 : step; + state_proc.exposure.value_req = + state_proc.exposure.value + + (step * direction); + printf("Expose + %d\n", + state_proc.exposure.value_req); + } else { + // Raise sensor gain if exposure limit is hit + step = state_proc.gain.value / 4; + step = step < 4 ? 4 : step; + state_proc.gain.value_req = + state_proc.gain.value + (step * direction); + printf("Gain + %d\n", state_proc.gain.value_req); + } + } else if (direction < 0) { + // Preview is too bright + + // Lower the sensor gain first to have less noise + if (state_proc.gain.value > 0) { + step = state_proc.gain.value / 4; + state_proc.gain.value_req = + state_proc.gain.value + (step * direction); + printf("Gain - %d\n", state_proc.gain.value_req); + } else { + // Shorten the exposure time to go even darker + step = state_proc.exposure.value / 4; + state_proc.exposure.value_req = + state_proc.exposure.value + + (step * direction); + printf("Expose - %d\n", + state_proc.exposure.value_req); + } + } + + clamp_control(&state_proc.gain); + clamp_control(&state_proc.exposure); + mp_io_pipeline_set_control_int32(state_proc.gain.control, + state_proc.gain.value_req); + mp_io_pipeline_set_control_int32(state_proc.exposure.control, + state_proc.exposure.value_req); + state_proc.gain.value = state_proc.gain.value_req; + state_proc.exposure.value = state_proc.exposure.value_req; + } + + if (auto_balance) { + float r = state_proc.stats.avg_r; + float g = state_proc.stats.avg_g; + float b = state_proc.stats.avg_b; + + // Revert the current gains set on the preview + b /= state_proc.red; + b /= state_proc.blue; + + float t = 2.0f; + if (r < t && g < t && b < t) { + // Don't try to AWB on very dark frames + } else { + // Calculate the new R/B gains based on the average color of + // the frame + float new_r = g / clamp_float(r, 1.0f, 999.0f); + float new_b = g / clamp_float(b, 1.0f, 999.0f); + + state_proc.red = clamp_float(new_r, 0.01f, 4.0f); + state_proc.blue = clamp_float(new_b, 0.01f, 4.0f); + } + } + + gles2_debayer_set_shading(gles2_debayer, + state_proc.red, + state_proc.blue, + state_proc.blacklevel); +} + static GdkTexture * process_image_for_preview(const uint8_t *image) { @@ -412,48 +533,9 @@ process_image_for_preview(const uint8_t *image) mp_process_pipeline_buffer_ref(output_buffer); mp_main_set_preview(output_buffer); - if (!state_proc.exposure.manual && state_proc.exposure.auto_control == 0) { - int width = output_buffer_width; - int height = output_buffer_height / 3; - uint32_t *center = g_malloc_n(width * height * sizeof(uint32_t), 1); - glReadPixels( - 0, height, width, height, GL_RGBA, GL_UNSIGNED_BYTE, center); - - libmegapixels_aaa_set_matrix(&state_proc.stats, - state_proc.calibration.color_matrix_1, - state_proc.calibration.color_matrix_2); - libmegapixels_aaa_software_statistics( - &state_proc.stats, center, width, height); - - state_proc.blacklevel -= (float)state_proc.stats.blacklevel * 0.001f; - state_proc.blacklevel = - clamp_float(state_proc.blacklevel, 0.0f, 0.07f); - - float r = state_proc.stats.avg_r; - float g = state_proc.stats.avg_g; - float b = state_proc.stats.avg_b; - - // Revert the current gains set on the preview - b /= state_proc.red; - b /= state_proc.blue; - - float t = 2.0f; - if (r < t && g < t && b < t) { - // Don't try to AWB on very dark frames - } else { - // Calculate the new R/B gains based on the average color of - // the frame - float new_r = g / clamp_float(r, 1.0f, 999.0f); - float new_b = g / clamp_float(b, 1.0f, 999.0f); - - state_proc.red = clamp_float(new_r, 0.01f, 4.0f); - state_proc.blue = clamp_float(new_b, 0.01f, 4.0f); - } - - gles2_debayer_set_shading(gles2_debayer, - state_proc.red, - state_proc.blue, - state_proc.blacklevel); + if (framecounter++ == 2) { + framecounter = 0; + process_aaa(); } // Create a thumbnail from the preview for the last capture @@ -1022,12 +1104,14 @@ update_state(MPPipeline *pipeline, const mp_state_proc *new_state) .gain.control = state_proc.gain.control, .gain.auto_control = state_proc.gain.auto_control, .gain.value = state_proc.gain.value, + .gain.value_req = state_proc.gain.value_req, .gain.max = state_proc.gain.max, .gain.manual = state_proc.gain.manual, .exposure.control = state_proc.exposure.control, .exposure.auto_control = state_proc.exposure.auto_control, .exposure.value = state_proc.exposure.value, + .exposure.value_req = state_proc.exposure.value_req, .exposure.max = state_proc.exposure.max, .exposure.manual = state_proc.exposure.manual, diff --git a/src/state.h b/src/state.h index 62feebf..705c7f5 100644 --- a/src/state.h +++ b/src/state.h @@ -69,6 +69,13 @@ typedef struct state_io { int device_rotation; } mp_state_io; +typedef enum aaa_mode { + AAA_DISABLED, + AAA_BY_SENSOR, + AAA_BY_V4L2_CONTROLS, + AAA_BY_POST, +} aaa_mode; + typedef struct state_proc { libmegapixels_devconfig *configuration; libmegapixels_camera *camera; @@ -98,4 +105,8 @@ typedef struct state_proc { float red; float blue; float blacklevel; + + int mode_balance; + int mode_exposure; + int mode_focus; } mp_state_proc; \ No newline at end of file