嵌入式图像处理(五):整合 —— 从单一帧到完整质量报告

嵌入式图像处理(五):整合 —— 从单一帧到完整质量报告 前几篇的工具各自跑各自的每次要手动输命令、记参数、对比输出。太散了。这篇把所有模块拼成一个大号工具——iq_analyzer输入一帧 YUYV一次跑完亮度、曝光、色彩、噪点、清晰度五维分析直接出综合评分。外加 JSON 导出。1. 文件结构六个源文件 一个头文件Makefile 管编译iq_analyzer.h— 四个 report 结构体所有模块共享iq_analyzer.c— main()参数解析 → 读帧 → 调各模块 → 出报告luminance.c— 亮度统计直方图/均值/std/median/曝光color.c— 色彩分析U/V 均值 → 偏色方向noise.c— 噪点评估空间域相邻像素差方差sharpness.c— 清晰度Sobel 3×3 边缘能量新算法report.c— 加权评分 终端输出 JSON 导出typedefstruct{doublemean,stddev;intmedian,min,max,hist[256];doubleover_pct,under_pct;intdyn_range;constchar*status;}LumReport;typedefstruct{doubleu_mean,v_mean;constchar*cast;}ColorReport;typedefstruct{doubley_noise_var,uv_noise_var;constchar*y_level,*uv_level;}NoiseReport;typedefstruct{doubleedge_energy;constchar*level;}SharpReport;分析的对象就是之前拍的那帧——关掉 AWB 和锐化后的原始画面2. 亮度统计和之前 frame_analyzer 不太一样——这里所有统计从直方图算不是从原始像素数组。先建一次 256 桶 histogram后续全是 O(256)/* 均值加权求和 */sum0.0;for(i0;i256;i)sumi*hist[i];r.meansum/total;/* 标准差同样从直方图算 */sum0.0;for(i0;i256;i){doublediffi-r.mean;sumdiff*diff*hist[i];}r.stddevsqrt(sum/total);/* 中位数累加直方图到 halfway */sum0.0;for(i0;i256;i){sumhist[i];if(sumtotal/2){r.mediani;break;}}曝光用累积分布的 P1 和 P99 间距作为动态范围比之前简单的 0-255 范围更鲁棒。3. 偏色检测YUYV 里每 4 字节有 1 个 U 和 1 个 V。遍历取均值后看 UV 偏离 128 的方向和幅度duu_mean-128.0;dvv_mean-128.0;if(fabs(du)3fabs(dv)3)r.castneutral;elseif(dv5)r.castwarm;elseif(dv-5)r.castcool;elseif(du5)r.castmagenta;elseif(du-5)r.castgreen;elser.castslight cast;阈值是试出来的——3 以内肉眼基本看不出偏色5 以上方向明确。4. 噪点评估用最简单的水平相邻像素差方差。值越小说明相邻像素越接近画面越干净for(y_idx0;y_idxh;y_idx){for(x0;xw-1;x){diffy_plane[y_idx*wx]-y_plane[y_idx*wx1];y_sumdiff*diff;y_cnt;}}r.y_noise_vary_sum/y_cnt;UV 噪点用同样逻辑但每 2 行 2 列采样一次——色度通道天然低分辨率全采样没意义。5. 清晰度Sobel 边缘能量这是前面没涉及的新模块。思路很直觉清晰图像的边缘锐利、亮度落差大模糊图像边缘平缓、落差小。Sobel 算子用两个 3×3 核分别测水平和垂直梯度Sobel X: Sobel Y: [-1 0 1] [-1 -2 -1] [-2 0 2] [ 0 0 0] [-1 0 1] [1 2 1]对每个内部像素跳过边界 1px算 gx 和 gy梯度幅值mag sqrt(gx² gy²)全体取均值for(y_idx1;y_idxh-1;y_idx){for(x1;xw-1;x){gx-1*y[(y_idx-1)*w(x-1)]1*y[(y_idx-1)*w(x1)]-2*y[y_idx*w(x-1)]2*y[y_idx*w(x1)]-1*y[(y_idx1)*w(x-1)]1*y[(y_idx1)*w(x1)];gy-1*y[(y_idx-1)*w(x-1)]-2*y[(y_idx-1)*wx]-1*y[(y_idx-1)*w(x1)]1*y[(y_idx1)*w(x-1)]2*y[(y_idx1)*wx]1*y[(y_idx1)*w(x1)];magsqrt(gx*gxgy*gy);summag;count;}}r.edge_energysum/count;edge_energy 15 判 soft 30 判 normal以上 sharp。6. 综合评分五项加权满分 10。亮度占 30%最重要——人眼先看亮度曝光和色彩各 20%噪点和清晰度各 15%lum_sclamp(1.0-fabs(l.mean-120)/80,0.0,1.0);exp_sclamp(1.0-(l.over_pctl.under_pct)/25,0.0,1.0);col_sstrcmp(c.cast,neutral)0?1.0:0.5;noise_sstrcmp(n.y_level,clean)0?1.0:0.5;sharp_sstrcmp(s.level,normal)0?1.0:0.5;score(lum_s*0.30exp_s*0.20col_s*0.20noise_s*0.15sharp_s*0.15)*10;权重和阈值是我自己拍的——没有标准参考纯凭调试感觉。生产中的 ISP 评分系统远比这个复杂但核心思路一样多维打分、加权综合。白平衡加增强后的实际输出 Image Quality Analysis Report LUMINANCE Mean: 169.7 (normal) StdDev: 69.8 Median: 184 Min/Max: 17 / 255 EXPOSURE Over-exposed: 28.0% Dynamic Range: 209 COLOR U Mean: 124.2 Cast: cool NOISE Y Noise Var: 44.2 (heavy) UV Noise Var: 131.4 (heavy) SHARPNESS Edge Energy: 30.3 (sharp) OVERALL SCORE: 3.6 / 107. 完整管线三个工具串成管线原始帧 → auto_wb (Gray World) → image_enhance → iq_analyzer → JSON 报告三步处理后原始帧从偏绿、噪声 24.5 变成白平衡校正 均衡化 Gamma 模糊的综合增强结果full_pipeline.sh一行跑完。完整代码github.com/cjh1230/learn-embedded-linux-video