如何实现强光场景的识别?如何自动确定对强光区域权重的最优增益?
实验数据获取 控制变量,尽量保持场景不变,寻找随着曝光阶的改变,过曝的场景和普通场景的区别。发现问题1 :当曝光阶为0时,如果场景没有什么大变化的话,再去动测光权重没有效果 –>调整拍摄顺序,在背光算法中注意控制曝光阶不为0发现问题2 :相同的增益不一定对应同一个曝光阶,增益经过调节之后,调回去曝光阶和之前不一样 注意修改测光权重表,门禁设备的测光权重只有中间的一小块有效;改成默认的中心权重曝光
从没有一点强光到有强光场景拍十组照片,用来识别强光场景下的特点
从过曝的场景到不过曝的场景拍十组数据,用来选取强光场景下的最优增益
强光场景识别
从直方图亮度饱和的比例来看:当亮点比较小,或者欠曝的时候很难进行区分
从直方图的亮暗区间的区分来看,一般亮的场景会区分比较大,但是如果
分块数据来看
识别特征:
1、当前对亮区测光权重的增益;如果已经比较大,说明已经识别为背光场景
2、静态亮区 vs 动态亮区
一般来说,运动的车灯是动态亮区,而路灯等是稳定不动的,我们通过调节brightness就可以控制路灯等静态亮区,而动态亮区的权重我们可以粗略的当做车灯。或者静态亮区和动态亮区给予不同的增益。
当检测到某个亮区的亮度相比于前一帧的相同位置有变化,将它称作动点,一个车灯的动点只有边缘区域,而中心的区域可能没有动,因此我们可以将和动点连通的亮区作为动态亮区。如何标记动点连通的亮区,我采用的是递归的方法。
void search_dynamic_region (char *array , int x, int y,int row,int col) { int i,j; for (i = -1 ; i < 2 ; i++) { for (j = -1 ; j < 2 ; j++) { if (x + i >= 0 && x + i < row && y + j >= 0 && y + j < col && (x || y == 1 ) && array [(x + i)*col + y + j] == 1 ) { array [(x + i)*col + y + j] = 2 ; search_dynamic_region(array , x + i, y + j, row, col); } } } }
强光场景最优增益
获取9组数据,第5-6组是最优组
问题:为什么曝光行差距很大,但是Bayer域的统计数据却差不多?
背光补偿代码bug 发现一个背光补偿测光权重初始化的bug,如果在途中对chromatix进行了重新加载,测光表的初始值就改变了,可能会导致图片 –>通过验证,并不会发生这种问题
长时间将亮区的测光权重增大,会导致图像整体变亮 ->
设置一个固定的权重增益,理论上说用手遮住亮区再拿开,场景不变,曝光行应该回到正常场景,但是它在增大
就算不懂动,曝光行好像也在缓慢的增大,会不会由于第一个原因引起的?
曝光行在不断的增加,说明亮区权重在降低,或者暗区权重在增加
初始化的时候就会还原
实验证明,亮区的检测并没有发生改变,依旧改的是那几个区域,测光权重表是修改正确的
解决方案 :将测光权重完全指定 :说明了AEC算法内部会对测光权重进行修改
如果调低亮度,容易闪烁:调低亮度之后,亮区就会变少,整体亮度会提高;而提高亮度后,亮区有变多,整体亮度会变暗 遮挡一个亮区后,会闪烁:亮区的权重很大,亮区有轻微的改变就会影响AE,而我的视野里有一个闪动的屏幕,因此有闪烁属于正常,最后稳定阶段在进行优化
不同平台的实现 660平台 经过验证,可以实现对运动的强光进行抑制。
int v_step = q3a_bg_stats->bg_region_v_num/NUM_AEC_STATS;int h_step = q3a_bg_stats->bg_region_h_num/NUM_AEC_STATS;static uint32_t bg_sum[MAX_BG_STATS_NUM]={0 };char grid_region[MAX_BG_STATS_NUM] ={0 };int grid_v_num, grid_h_num;int weight_grid_count[NUM_AEC_STATS][NUM_AEC_STATS] = {0 };uint32_t bg_count=0 ;if (property_get("persist.liqinxing.backlight" , value, "" ) > 0 ){ aec_dynamic_ratio = atoi(value)/1000.0 ; int grid_count = q3a_bg_stats->bg_region_h_num * q3a_bg_stats->bg_region_v_num; for (int i = 0 ; i < grid_count; i++){ if (q3a_bg_stats->bg_r_sum[i] > threhold_h || q3a_bg_stats->bg_gr_sum[i] > threhold_h || q3a_bg_stats->bg_b_sum[i] > threhold_h){ bg_count = q3a_bg_stats->bg_r_sum[i]+q3a_bg_stats->bg_gr_sum[i]+ q3a_bg_stats->bg_gb_sum[i]+q3a_bg_stats->bg_b_sum[i]; if (bg_count*10 /bg_sum[i] > 12 ) grid_region[i] = 2 ; else grid_region[i] = 1 ; } bg_sum[i] = q3a_bg_stats->bg_r_sum[i]+q3a_bg_stats->bg_gr_sum[i]+ q3a_bg_stats->bg_gb_sum[i]+q3a_bg_stats->bg_b_sum[i]; } for (int i=0 ; i < grid_count; i++){ if (grid_region[i] == 2 ){ search_dynamic_region(grid_region, i/q3a_bg_stats->bg_region_h_num, i%q3a_bg_stats->bg_region_h_num, q3a_bg_stats->bg_region_v_num, q3a_bg_stats->bg_region_h_num); } } for (int i=0 ; i<grid_count; i++){ if (grid_region[i] != 0 ){ grid_v_num = i/q3a_bg_stats->bg_region_h_num/v_step; grid_h_num = i%q3a_bg_stats->bg_region_h_num/h_step; if (grid_region[i] == 2 ) weight_grid_count[grid_v_num][grid_h_num] = 2 ; else if (weight_grid_count[grid_v_num][grid_h_num] != 2 ) weight_grid_count[grid_v_num][grid_h_num] = 1 ; } } pthread_rwlock_wrlock(&chromatix->lock); for (int i=0 ; i < NUM_AEC_STATS; i++){ for (int j=0 ; j < NUM_AEC_STATS; j++){ if (weight_grid_count[i][j] == 2 ){ chromatix->AEC.aec_metering_tables.AEC_weight_center_weighted[i][j] = keda_exposure_weight[i][j] * aec_dynamic_ratio; } else if (weight_grid_count[i][j] == 1 ) chromatix->AEC.aec_metering_tables.AEC_weight_center_weighted[i][j] = keda_exposure_weight[i][j] * aec_static_ratio; else chromatix->AEC.aec_metering_tables.AEC_weight_center_weighted[i][j] = keda_exposure_weight[i][j]; } } pthread_rwlock_unlock(&chromatix->lock); if (aec->aec_algo_ops.set_parameters) { aec->aec_algo_ops.set_parameters(aec->handle, &backlight_set_parameter); } else { return FALSE; } }
8056平台的移植 由于部分参数的类型不同,以及编译规则的不同,对keda_backlight_algo函数进行修改。
static boolean keda_backlight_algo (aec_biz_t *aec, const stats_t * stats) { if (!aec || !stats) { AEC_ERR("Invalid input: %p,%p" ,aec, stats); return FALSE; } char value[PROPERTY_VALUE_MAX]; float aec_ratio = 1 ; float aec_dynamic_ratio=1000 ,aec_static_ratio=1 ; chromatix_3a_parms_type *chromatix; const q3a_bg_stats_t * q3a_bg_stats = stats->bayer_stats.p_q3a_bg_stats; uint32_t threhold_h = q3a_bg_stats->rMax * q3a_bg_stats->region_pixel_cnt * BRIGHT_REGION_RANGE /4 ; chromatix = (chromatix_3a_parms_type*)backlight_set_parameter.u.init_param.chromatix; if (!chromatix) { AEC_ERR("Invalid chromatix: %p" , chromatix) return FALSE; } int v_step = q3a_bg_stats->bg_region_v_num/NUM_AEC_STATS; int h_step = q3a_bg_stats->bg_region_h_num/NUM_AEC_STATS; static uint32_t bg_sum[MAX_BG_STATS_NUM]={0 }; char grid_region[MAX_BG_STATS_NUM] ={0 }; int grid_v_num, grid_h_num; int weight_grid_count[NUM_AEC_STATS][NUM_AEC_STATS] = {{0 }}; uint32_t bg_count=0 ; if (property_get("persist.liqinxing.backlight" , value, "" ) > 0 ){ aec_static_ratio = atoi(value)/1000.0 ; int grid_count = q3a_bg_stats->bg_region_h_num * q3a_bg_stats->bg_region_v_num; int i,j; for (i = 0 ; i < grid_count; i++){ if (q3a_bg_stats->bg_r_sum[i] > threhold_h || q3a_bg_stats->bg_gr_sum[i] > threhold_h || q3a_bg_stats->bg_b_sum[i] > threhold_h){ bg_count = q3a_bg_stats->bg_r_sum[i]+q3a_bg_stats->bg_gr_sum[i]+ q3a_bg_stats->bg_gb_sum[i]+q3a_bg_stats->bg_b_sum[i]; if (bg_count*10 /bg_sum[i] > 12 ) grid_region[i] = 2 ; else grid_region[i] = 1 ; } bg_sum[i] = q3a_bg_stats->bg_r_sum[i]+q3a_bg_stats->bg_gr_sum[i]+ q3a_bg_stats->bg_gb_sum[i]+q3a_bg_stats->bg_b_sum[i]; } for (i=0 ; i < grid_count; i++){ if (grid_region[i] == 2 ){ search_dynamic_region(grid_region, i/q3a_bg_stats->bg_region_h_num, i%q3a_bg_stats->bg_region_h_num, q3a_bg_stats->bg_region_v_num, q3a_bg_stats->bg_region_h_num); } } for (i=0 ; i<grid_count; i++){ if (grid_region[i] != 0 ){ grid_v_num = i/q3a_bg_stats->bg_region_h_num/v_step; grid_h_num = i%q3a_bg_stats->bg_region_h_num/h_step; if (grid_region[i] == 2 ) weight_grid_count[grid_v_num][grid_h_num] = 2 ; else if (weight_grid_count[grid_v_num][grid_h_num] != 2 ) weight_grid_count[grid_v_num][grid_h_num] = 1 ; } } for (i=0 ; i < NUM_AEC_STATS; i++){ for (j=0 ; j < NUM_AEC_STATS; j++){ if (weight_grid_count[i][j] == 2 ){ chromatix->AEC_algo_data.aec_metering_tables.AEC_weight_center_weighted[i][j] = keda_exposure_weight[i][j] * aec_dynamic_ratio; } else if (weight_grid_count[i][j] == 1 ) chromatix->AEC_algo_data.aec_metering_tables.AEC_weight_center_weighted[i][j] = keda_exposure_weight[i][j] * aec_static_ratio; else chromatix->AEC_algo_data.aec_metering_tables.AEC_weight_center_weighted[i][j] = keda_exposure_weight[i][j]; } } if (aec->aec_algo_ops.set_parameters) { aec->aec_algo_ops.set_parameters(aec->handle, &backlight_set_parameter); } else { return FALSE; } } return TRUE; }
Tips
aec_biz.c中的aec_biz_initialize_custom_tuning_param函数,可以在chromatix初始化之后对chromatix重新改写。但是只有当init时才可以启动
优化静态变量backlight_set_parameter:可以在aec_biz_init时创建,在aec_biz_destroy时进行销毁。
void*指针变量在结构体中定义,难道不需要进行内容大小的定义么?就可以直接对结构体memcpy了?因为指针大小是确定的,因此memcpy是没有问题的,指针的类型只是定义了指向数据的类型,也就是决定了指针运算的逻辑。
注意换了个镜头,要把膜撕掉!否则因为膜是棕色的,会导致白平衡异常,拍出来的是绿色的。
aec_biz是最接近aec模块的控制,aec_port是处理模块间的交流,相当于直接和aec_biz进行通信。
python注意点:for i in a:对i的值进行改变,不会影响a的值