双目拼接亮度差优化

双目拼接亮度差优化 一、亮度差消除优化点优化线性自适应步长逻辑解决OFFSET_MAX_STEP4收敛慢、作用弱问题提供可放大的量程配置保留全部鲁棒逻辑硬件直方图两级阈值统计、无有效像素多级兜底、双目互相借用亮度、滞回死区、线性插值自适应变步长、帧低通平滑、寄存器限速刷新全整数无浮点适配 ISP 嵌入式线性插值区间拉大最大步长可灵活配置默认给到 8兼顾收敛速度与防闪烁同时保留最小步长、滞回约束。二、代码#include stdint.h // 量产可调宏配置 // 直方图亮度过滤阈值 #define Y_VALID_MIN 16 #define Y_VALID_MAX 248 #define Y_RELAX_MIN 8 #define Y_RELAX_MAX 252 // Offset自适应线性步长核心配置解决原MAX_STEP4收敛弱问题 #define HYSTERESIS_THR 2 // 滞回死区差值小于阈值不更新 #define OFFSET_MIN_STEP 1 // 单帧最小调整幅度保证不会无限微调整 #define OFFSET_MAX_STEP 8 // 放大最大步长提升大差异场景收敛速度 #define OFFSET_LINEAR_MAX_DIFF 30 // 线性映射差值区间差值≥30直接使用MAX_STEP #define OFFSET_MIN -40 // Offset整体上下限同步放大匹配大步长 #define OFFSET_MAX 40 #define OFFSET_FILTER_SHIFT 4 // 低通系数 15/16抑制晃动闪烁 // Ygain固定1.0全程不做动态更新 #define Q8_BASE 256 #define FIX_YGAIN_Q8 Q8_BASE // 运动、无效帧配置 #define MOTION_VAR_THR 1500 #define INVALID_FADE_STEP 1 // // 全局状态缓存 static int16_t g_last_offset 0; static uint8_t g_reg_update_cnt 0; static uint8_t g_last_valid_master 128; static uint8_t g_last_valid_slave 128; static uint16_t g_master_invalid_cnt 0; static uint16_t g_slave_invalid_cnt 0; /** * brief 线性自适应步长插值函数 * 差值越小步长越小、差值越大步长线性放大差值超过阈值直接满步长 * 解决原MAX_STEP4收敛力度不足问题 */ static int32_t GetAdaptiveStep_Linear(int32_t diff_raw) { int32_t abs_diff (diff_raw 0) ? diff_raw : -diff_raw; // 超出死区的有效差值区间 int32_t range_diff abs_diff - HYSTERESIS_THR; if (range_diff 0) range_diff 0; // 线性映射range_diff [0 ~ OFFSET_LINEAR_MAX_DIFF] → step [MIN_STEP ~ MAX_STEP] int32_t step_range OFFSET_MAX_STEP - OFFSET_MIN_STEP; int32_t step OFFSET_MIN_STEP (range_diff * step_range) / OFFSET_LINEAR_MAX_DIFF; // 限制最大步长 if (step OFFSET_MAX_STEP) step OFFSET_MAX_STEP; // 恢复正负符号 if (diff_raw 0) step -step; // 防过冲单次调整不超过真实差值避免来回震荡 if (abs_diff abs(step)) step diff_raw; return step; } /** * brief 直方图转有效平均亮度 * 两级阈值筛选 连续无效帧缓慢向128衰减兜底 */ static uint8_t Hist2Luma(uint32_t y_hist[256], uint32_t hist_pixel_cnt, uint8_t *last_valid, uint16_t *invalid_cnt) { // 内部计算指定阈值区间均值返回-1代表无有效像素 auto InnerCalc [](uint8_t ymin, uint8_t ymax)-int32_t { uint32_t sum 0; uint32_t cnt 0; for (int y ymin; y ymax; y) { sum (uint32_t)y * y_hist[y]; cnt y_hist[y]; } if (cnt 0) return -1; return sum / cnt; }; // 1.标准严格区间 [16, 248] int32_t mean_std InnerCalc(Y_VALID_MIN, Y_VALID_MAX); if (mean_std ! -1) { *last_valid (uint8_t)mean_std; *invalid_cnt 0; return (uint8_t)mean_std; } // 2.标准区间无像素放宽阈值二次统计 [8,252] int32_t mean_relax InnerCalc(Y_RELAX_MIN, Y_RELAX_MAX); if (mean_relax ! -1) { *last_valid (uint8_t)mean_relax; *invalid_cnt 0; return (uint8_t)mean_relax; } // 3.两级区间全部无有效像素多帧缓慢衰减至128 (*invalid_cnt); int32_t val *last_valid; if (val 128) val - INVALID_FADE_STEP; else if (val 128) val INVALID_FADE_STEP; return (uint8_t)val; } /** * brief 核心Offset计算逻辑 * 公式Yout Yin * FIX_YGAIN - Offset * 内置滞回死区 线性自适应变步长 一阶低通平滑 上下限钳位 */ static int16_t CalcSmoothOffset(uint8_t Ym, uint8_t Ys) { // Ygain固定1.0不再动态调整 int32_t Ys_gain ((uint32_t)Ys * FIX_YGAIN_Q8) / Q8_BASE; int32_t offset_raw Ys_gain - Ym; int32_t diff_raw offset_raw - g_last_offset; // 滞回死区微小差值直接保持原值消除高频微小抖动 if (diff_raw -HYSTERESIS_THR diff_raw HYSTERESIS_THR) { return g_last_offset; } // 获取线性自适应调整步长 int32_t adaptive_delta GetAdaptiveStep_Linear(diff_raw); // 定点一阶低通滤波old * 15/16 new * 1/16大幅抑制帧跳变 int32_t temp_off g_last_offset adaptive_delta; temp_off ((g_last_offset * 15) temp_off) OFFSET_FILTER_SHIFT; // Offset全局上下限保护 if (temp_off OFFSET_MIN) temp_off OFFSET_MIN; if (temp_off OFFSET_MAX) temp_off OFFSET_MAX; g_last_offset (int16_t)temp_off; return g_last_offset; } /** * brief 双目亮度对齐顶层入口每帧调用 * 固定Ygain不更新仅输出平滑后的Offset * return 1:需要写入ISP寄存器 0:暂不更新 */ uint8_t BinocularLumaAlign(uint32_t master_hist[256], uint32_t slave_hist[256], uint32_t master_pix, uint32_t slave_pix, uint32_t motion_var, int16_t *out_offset) { // 1.分别计算两路稳健平均亮度自带无效像素兜底衰减 uint8_t Ym Hist2Luma(master_hist, master_pix, g_last_valid_master, g_master_invalid_cnt); uint8_t Ys Hist2Luma(slave_hist, slave_pix, g_last_valid_slave, g_slave_invalid_cnt); // 双目专属兜底单路连续多帧无有效像素借用另一路亮度同步对齐 if (g_master_invalid_cnt 3) Ym Ys; if (g_slave_invalid_cnt 3) Ys Ym; // 2.线性自适应计算平滑OffsetYgain全程固定1.0 *out_offset CalcSmoothOffset(Ym, Ys); // 3.寄存器刷新限速晃动场景减少硬件改写频次抑制闪烁 g_reg_update_cnt; uint8_t update_flag 0; uint8_t is_static (motion_var MOTION_VAR_THR) ? 1 : 0; if (is_static) { // 静态3帧更新一次 if (g_reg_update_cnt 3) { g_reg_update_cnt 0; update_flag 1; } } else { if (motion_var MOTION_VAR_THR * 3) { // 剧烈镜头晃动暂停更新寄存器防止大幅度明暗跳动 update_flag 0; } else { // 轻微运动2帧更新一次 if (g_reg_update_cnt 2) { g_reg_update_cnt 0; update_flag 1; } } } return update_flag; } // 业务调用示例 /* void FrameProcessTask(void) { uint32_t master_y_hist[256], slave_y_hist[256]; uint32_t m_pix_cnt, s_pix_cnt; uint32_t frame_motion_variance; int16_t send_offset; // 读取ISP两路硬件直方图硬件已过滤Y16/Y248 ISP_ReadHistData(ISP_MASTER, master_y_hist, m_pix_cnt); ISP_ReadHistData(ISP_SLAVE, slave_y_hist, s_pix_cnt); frame_motion_variance ISP_GetMotionVariance(); uint8_t need_update BinocularLumaAlign(master_y_hist, slave_y_hist, m_pix_cnt, s_pix_cnt, frame_motion_variance, send_offset); if (need_update) { ISP_RegWrite(REG_YUVEFFECT_YGAIN, FIX_YGAIN_Q8); ISP_RegWrite(REG_YUVEFFECT_OFFSET, send_offset); } } */