如何实现强光下自由降低曝光阶?修改stats数据是否有效?如何动态修改曝光权重?

修改stats数据

存在的问题

为了注重强光,需要降低亮度,如果增加亮区的亮度,原来可能已经饱和了,现在如果继续增加,就没有效果了。

解决方案

如果调整stats数据中RGB的最大值,防止超出最大值,是否有用。

实验发现调整rbg_max大小是没有用的,ISP输出数据深度,超过这个深度之后无效,因此增大亮区的统计亮度基本没有效果;如果改成增大暗区的统计亮度来降低曝光,就失去了背光补偿的意义

修改曝光权重

分析

曝光权重是从chromatix直接导入的,流经stats模块,进入了aec_port,我们可以在stats模块中截取进行修改;也可以再aec模块传入的地方进行修改。

STATS模块尝试

如果在stats模块传递到的位置进行修改,因为先要由统计数据去调整曝光权重,而程序是先执行调整曝光权重再去判断统计数据的话,理论上曝光调整会延迟一帧进行。因此最好在stats模块中能获取到stats和曝光权重的时候一并修改。但是可能存在一个问题,在stats模块修改了chromatix中的数据,aec模块可能不会重新加载。

stats_port_event函数中的IPC_KEDA_AE_CONTROL下方对private中的chromatix参数进行修改

首先确认这是否是每帧都会运行的地方,加打印,看打印时间相隔多少,如果是33ms就是每帧都会打印;却发现没有打印,因此需要重新找到将stats数据和测光权重传递给aec的事件

从名称中定位到可能的事件,并查看处理方法:MCT_EVENT_CONTROL_SET_PARM (不是每帧都执行)MCT_EVENT_MODULE_ISP_STATS_INFO(用来debug的,排除) MCT_EVENT_CONTROL_PARM_STREAM_BUF (stats模块控制,排除)MCT_EVENT_CONTROL_SOF(控制帧开头,用来插入JPEG信息,排除) MCT_EVENT_MODULE_STATS_DATA(平均20ms执行一次是不是有点过快了)

AEC模块尝试

由于stats模块在aec模块的上游,因此我们在aec模块中进行修改,可以更简单快捷的控制AEC模块的输入。

思考过程

  1. 我们要找到chromatix导入的位置:aec_biz_map_init_chromatix_sensor函数

  2. 可以看到chromatix数据是从chromatix_3a_parms_wrapper导入到aec_set_parameter_init_t结构体中,chromatix_3a_parms_wrapper在stats模块中也可以进行修改,但是为了防止修改了chromatix参数,如果仅在初始化时进行修改,那么不会生效

  3. 接下来找到aec_biz_map_init_chromatix_sensor函数调用位置:aec_biz_set_param,仅有在param->type == AEC_SET_PARAM_INIT_CHROMATIX_SENSOR时,才会进行调用。因此我们之前可能不生效的猜测是正确的,只有在初始化的时候才会加载。

  4. 两个解决方案:一是我们在最后必须经过的地方,对chromatix中的值进行修改;二是增加一个自定义的param->type类型。 相比较来说,肯定是第二种方法比较好,在一帧里不会重复的对chromatix中的值进行修改。

  5. 还有一个问题值得注意:aec_biz_set_param函数也不是每帧都会执行的,只有当设置参数的时候才会执行。经过打印实验,相比于set_params,函数aec_biz_process是会每帧执行的。我们的背光补偿算法以及对测光权重的处理需要放在一个一直执行的地方。

  6. 很遗憾的是aec_biz_map_init_chromatix_sensor是对数据set_param进行操作的,而set_param只能由函数set_parameters进行设置,不能在函数aec_biz_process中进行处理。

  7. 下面有三种解决方案:

    • 一种是在process中调用set_parameters函数(但是不知道形参)

    • 一种是在set_parameters中增加自定义的处理

    • 最后一种是定义一个全局变量backlight_set_parameter指向aec_set_parameter_t的地址,当运行aec_biz_set_param时更新全局变量,然后在process中处理。

    • 但是这个时候第二种方法就不如第三种方法来的简单,第二种方法会导致开启背光补偿后每帧都需要执行aec_biz_set_param函数,第三种方法更加简单,但是相当于给set_param和process之间开了一个后门,可能会有安全隐患?

  8. 为了可以修改送入AEC算法的stats数据,以及需要在aec算法之前操作,我们将背光补偿算法放在函数aec_biz_process中,位于函数aec_biz_stats_mapaec_algo_ops.process之前。

  9. 但是发现函数process不能使用aec_set_parameter_t这个数据,一个方法是定义一个全局变量backlight_set_parameter指向aec_set_parameter_t的地址,当运行aec_biz_set_param时更新全局变量,然后在process中处理。这样相当于开了一个后门

  10. 由于aec_core_set_param_type是union类型,因此需要在一个合适的时候讲

  11. 定义一个static的变量,将设置的值保存起来

发现问题:

  1. 死机,摄像头加载不了

    解决方法:将指针改成了结构体变量,防止由于set_param变量的变化而变化

  2. 打印修改的测光权重,发现不对应

    解决方法:调整stats分块数据和测光权重表的对应关系

  3. 想要更新AEC的参数,只能通过set_param函数进行修改,传入的值是一个union类型加一个type,也就是说每次传入的参数是跟type有关系的。而aec模块的输入是封闭的,需要你有对应的set_param类型,通过命名很明显的发现没有

    解决方法只能是init_chromatix的方法对测光权重进行修改

  4. 难道说这里的aec_algo_ops一直指的是aec_biz,而不是aec的算法,也就是说它会经常调用自身,那我之前的推导岂不是全部乱掉了

    解决方法:重新对结构体进行了分析 ,param->type 的枚举值里有PARAM,而set_param里面没有,按照set_param的函数的执行顺序,是将类型aec_set_parameter_t转换成了aec_core_set_param_type的类型,发现之前的推论没有问题。

    • 写入chromatix文件无效,尝试读取权重表,自己计算current luma,将两者的current luma进行对比,确实将曝光权重进行了修改,但是参数不生效。

    • 无论写入什么值或者不写入值,图像曝光都不够,cur_luma一直很低:是否是chromatix参数传递有问题?将chromatix打印出来没有问题啊。是否可能是因为调用memcpy函数之前没有对目标指针进行初始化?

    • 时间一长设备会挂掉,是否因为哪个地方内存泄露?或者越界访问了?

    • 解决方法:将设置参数类型改为AEC_SET_COPY_CHROMATIX_SENSOR,,参数生效,但是好像过一段时间会再次不生效

      AE容易闪烁

解决方法:因为增大了亮区的权重,会对亮的地方很敏感,我测试的场景下有个会动的显示屏,所以出现闪烁的情况也是正常现象。

  1. 如果用ADRC的方法实现,由于set_param只有enable这个选项,也需要进行chromatix参数的改写。也可以利用这个方法对chromatix参数进行改写。

代码实现

在aec_biz.c中进行修改

  1. 首先定义局部静态变量,储存chromatix数据以及初始化时的测光权重数据。
static aec_core_set_param_type backlight_set_parameter;
static float keda_exposure_weight[NUM_AEC_STATS][NUM_AEC_STATS]={0};
  1. 在初始化时读取chromatix数据,并存储测光权重表
//放在代码pthread_rwlock_rdlock(&chromatix->lock)之后
//在对读写锁进行读操作之后
#ifdef IPC_BACKLIGHT_CONTROL
backlight_set_parameter.type = AEC_SET_COPY_CHROMATIX_SENSOR;
STATS_MEMCPY(&backlight_set_parameter.u.init_param, sizeof(aec_set_parameter_init_t),
¶m->u.init_param, sizeof(aec_set_parameter_init_t));

for(int i=0;i < NUM_AEC_STATS; i++)
for(int j=0;j < NUM_AEC_STATS; j++)
keda_exposure_weight[i][j] =
chromatix->AEC.aec_metering_tables.AEC_weight_center_weighted[i][j];
#endif
  1. 在aec_biz_process函数之前定义处理自定义处理函数
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;

chromatix_3a_parms_wrapper *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 * 0.6 /4;
chromatix = (chromatix_3a_parms_wrapper*)
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;
int grid_v_num, grid_h_num;
int weight_grid_count[NUM_AEC_STATS][NUM_AEC_STATS] = {0};
if (property_get("persist.liqinxing.backlight", value, "") > 0){
aec_ratio = atoi(value)/1000.0;
//reduce traverse times and prevent data overflow
int grid_count = q3a_bg_stats->bg_region_h_num * q3a_bg_stats->bg_region_v_num;
//find the position of bright region

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){
grid_v_num = i/q3a_bg_stats->bg_region_h_num/v_step;//calculate current position v
grid_h_num = i%q3a_bg_stats->bg_region_h_num/h_step;//calculate current position h
weight_grid_count[grid_v_num][grid_h_num]++;
}
}
//adjust metering ratio in the bright region
pthread_rwlock_wrlock(&chromatix->lock);
backlight_set_parameter.type = AEC_SET_COPY_CHROMATIX_SENSOR;
for(int i=0; i < NUM_AEC_STATS; i++){
for(int j=0; j < NUM_AEC_STATS; j++){
if(weight_grid_count[i][j] > 3){
chromatix->AEC.aec_metering_tables.AEC_weight_center_weighted[i][j] =
keda_exposure_weight[i][j] * aec_ratio;
}
}
}
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;

}
}
return TRUE;
}
  1. 在aec_biz_process函数中的ret = aec_biz_stats_map(aec, stats);上方调用我们的函数:keda_backlight_algo(aec,stats);