8.0 KiB
8.0 KiB
MAG160C 热像仪渲染管线交接文档 v5
项目:MAG160C 热像仪(160x120, USB, VID 0x833C)自实现渲染管线 demo3 目标:复刻官方 CoreSDKLib.dll 渲染效果 状态:颗粒 ✓ / FFC 颜色 ✓ / 蒙层 ✓ / 黑洞·黑拖影 ✗(当前阻塞)
1. 已解决问题(历史)
| 问题 | 根因 | 修复 |
|---|---|---|
| 画面颗粒重 | NUC 斜率 3 过大(官方 0.19) | NUC_GAIN=19(0.19) |
| FFC 后颜色跳变 | FFC 后重采集 ref 吸收场景 | REF_REINIT_AFTER_FFC=0:FFC(1) 后只 rebase 全局偏移 |
| 物体显示破碎/椒盐 | 固定窗口 ±312,物体 nuc 超窗口饱和 | 自适应 P2..P98 百分位窗口(下限 624,官方实测) |
| 画面"蒙一层东西" | 常数 ref 不做平场,mura(空间 std 3560)直接显示 | 启动采集每像素平场(median,运动门控) |
| 黑拖影/黑洞 | ref 冻结 + 物体移动 → 旧位置 d = live-ref < 0 → nuc 低 → 黑 | 未解决(见 §3) |
2. 官方逆向成果(已验证,disasm 证据)
2.1 渲染数据流
USB帧(type=0)
→ 平滑器 0x41f10(模式1: 首帧 memcpy + EMA) → f20 (dev+0x41f20)
→ NUC 查表 0x180017200: d2=(f20-ref)>>1, 段查表, clamp[0,65535]
→ 3x3 加权 0x180017330(写回) → dev+0x270 (raw/NUC输出)
→ 窗口 0x4202c/0x42030 (hi/lo, 动态) → gray (dev+0x220, 2x 升采样 320x240)
→ 调色板 256 色 (pal[0]=黑, pal[255]=白) → RGB
2.2 NUC 查表(0x180017200, 完整逆向)
每像素:
d2 = (f20[i] - ref[i]) >> 1 (sar)
段选择: for(edx=0; edx<nsegs-1; edx++)
if (d2 <= s16 thresh[edx]) break; (thresh 在 dev+0x41858, 值 [3136, 2484, 644])
偏移 = edx * 19200 * 2
nuc = (u16 gain[段][i] * d2) >> (shift=0x11c=12) + u16 off[段][i]
clamp [0, 65535]
→ dev+0x270
- gain/off 表:dev+0x41870,nsegs=dev+0x41554=3,每段 19200 像素 × (gain u16, off u16)
- 注意:d2 全负(正常场景,ref≈f20+3800)→ 段0。段1/2 用于大 d(物体),官方对物体响应待验证
2.3 参考帧 ref(dev+0x41ef0)
- 来源:平滑器 0x41ee0(模式4 = 累加平均 acc+=in, out=acc>>2)
- 采集:启动帧 6-9(仅 4 次调用 0x18000c620),之后冻结
- FFC 后:只全局偏移变化(-145/-214 counts),空间结构不变(不重采集)
- 与 f20 同源 mura(corr 0.998),精确抵消 mura → d2 空间 std 仅 ~114
2.4 FFC 触发(0x18000a1d2, 0x180002f50)
- 条件 A:帧号 ≥ r9 + [0x2d90](0x2d90=1800 帧 ≈ 120s 周期)
- 条件 B:帧号 ≥ r9 + r8(30/100 冷却)且 |sensor_temp - last| > [0x2d94]=250
- 动作:发 FFC(0) (0x6bb6b672, param=0),更新温度记录(0x54/0x58),重置计数(0x49c)
- FFC(1) 在 9 帧后(0x18000c620 链路),type=1 帧期间不渲染(官方冻结画面)
2.5 温度
- NUC 输出与温度完全正相关(corr=1.0, 热=亮)
- temp_mC = T2E 查表(3×nuc - C, C~5797, 表 0x249f0 645 项)
- 平滑器 0x41f40(模式?)→ 0x180010780(温度)
2.6 关键结论
- 官方 ref 冻结 → 官方同样会黑洞/拖影(移动物体),官方无场景检测
- 官方 FFC 周期 120s,比 demo3 早期 27s 长得多
- 官方窗口动态(无手 span≈624,有手 span≈1381)
3. 当前阻塞:黑洞/黑拖影
3.1 现象
用户敲键盘(手在镜头前小幅移动):手移动后,手旧位置/背景最热处显示为黑色(d 负 → nuc 低 → 黑),拖影明显。
3.2 物理机制
ref 冻结(平场) → 物体移动 → 旧位置:
d = live(当前背景) - ref(启动背景)
若背景温度漂移/场景变化 → d < 0 → nuc < comp → 黑
3.3 已试方案与结果
| 方案 | 结果 | 失败原因 |
|---|---|---|
| 固定窗口 ±312 | 物体破碎 | 窗口太窄 |
| FFC 后重采集 ref | 颜色跳变 | 吸收场景 |
| 冻结 median 平场 | 黑洞 | ref 冻结 |
| 常数 ref(每帧全局最低) | 无黑洞但蒙层 | 不平场 |
| 平场+min 平移 | 蒙层✓(修复采集 bug 后) 黑洞回归 | ref 仍冻结 |
| SBNUC 条件更新(df<50 且 dd<80, α=1/64) | 蒙层✓ 黑洞仍存 | 见 3.4 |
3.4 SBNUC 失效根因(diag 实测 d=(-467±1479))
- df 条件(帧差<50):手快速移动时,手经过的像素帧差大 → 永不满足 → 永不吸收 → 拖影持续
- dd 阈值 80 太严:背景 d 空间 std ~458 → 大部分背景像素 |d-med|>80 → 吸收率极低
- α=1/64 太慢:完整吸收需 64 帧(4s),拖影长
- 背景漂移:场景整体漂移(实测 d mean 从 +1734 → -467,场景变冷)→ d 负 → 黑
3.5 下一步建议(按优先级)
- 改 SBNUC 参数:去掉 df 条件(或放宽到 200),dd 阈值 ~120,α=1/16(1s 吸收)
- 理由:只用 dd(物体距离)判断,手经过区域(背景没变,d≈med)→ 立即吸收
- 每像素时域 d 方差检测(Hardie Kalman SBNUC, IEEE TIP 1998):
- 维护 d[i] 短窗方差:背景稳定(方差小)→ 吸收;物体经过(方差大)→ 不吸收
- 双缓冲 ref:背景 ref(条件更新)+ 物体区域冻结保护
- 验证指标(demo3_diag.txt):d mean → 0,d 空间 std 应 < 300,物体移动后旧位置 d 快速回 0
- 若参数方案有效,复刻官方查表(0.19 线性 → 官方分段)对比官方画面
4. 代码结构与关键参数(demo3.c)
常量:
CONSTANT_REF=1 常数/平场+SBNUC 模式
NUC_GAIN=19 (0.19)
FFC_PERIOD=1800 官方周期(120s)
FFC_GAP=9
REF_INIT_N=12 平场采集帧数
REF_REINIT_AFTER_FFC=0 FFC 后不重采集
WIN_HALF=312 窗口下限半宽(P2-P98 自适应)
平场采集:g_ref_phase==1(启动 nread>=60 后),运动门控(帧差>30 或 4% 像素动 → 重置窗口)
SBNUC 块:CONSTANT_REF && g_ref_mura_ready 分支(行 ~885):
med = 直方图中值(live - g_ref_mura)
if (|live-prev|<50 && |(live-mura)-med|<80) mura += (live-mura)/64
平移: ref = mura - min(mura) + min(live)
关键数组:g_ref_mura[NPIX] 平场, g_reference[NPIX] 渲染用
编译
gcc -O2 -w -DMAG160C_STATIC -I"C:\Project\MAG160C\csdk\third_party\libusb\win64" -I"C:\Project\MAG160C\csdk\include" -I"C:\Project\MAG160C\csdk\src" -o "C:\Project\MAG160C\build-artifacts\mag160c_demo3.exe" "C:\Project\MAG160C\csdk\tools\mag160c_demo3.c" "C:\Project\MAG160C\csdk\src\mag160c_display.c" "C:\Project\MAG160C\csdk\src\mag160c_error.c" "C:\Project\MAG160C\csdk\third_party\libusb\win64\libusb-1.0.x64.a" -lgdi32 -luser32
运行/测试
Stop-Process -Name mag160c_demo3 -Force
Start-Process "C:\Project\MAG160C\build-artifacts\mag160c_demo3.exe" -WorkingDirectory "C:\Project\MAG160C\build-artifacts"
日志:demo3_diag.txt(每帧 ref/live/nuc/窗口/d), demo3_auto_*.bmp(每 30 帧自动截图)
用户测试:启动 → 等平场(~5s) → 手放镜头前小幅移动(敲键盘) → 观察黑拖影
工具
build-artifacts/tsdk_pair2.exe <dir> <npairs> <delay>:官方 SDK 抓帧(需先停 demo3;若 "no channel" 需重插设备)build-artifacts/mag160c_frame_stats.exe:libusb 直连抓帧- 反汇编:
analysis/disasm/coresdk_windows_disasm.txt(UTF-16,193k 行) - 官方数据:
analysis/pairs_nuc2/pair_000.*(f20/ref/raw=nuc/gray/rgb/cbrgb/t32/thr/gain)
5. 方法参考(专利/开源)
- Scribner et al., "Adaptive nonuniformity correction for IR focal-plane arrays using neural networks", SPIE 1541, 1991/1993 — 最经典 SBNUC
- Hardie et al., "Scene-based nonuniformity correction with reduced ghosting using a recursive Kalman filter", IEEE Trans. Image Process. 7(10), 1998 — 专治移动物体鬼影,与当前问题最相关
- FLIR/ULIS 自适应 FFC 专利(如 US 7,940,301 系列)— 静止场景才刷新校正表
- 开源:seek-thermal-suite(madsci1016)、Melexis MLX90640 API(无快门自校正)
- 核心思想:背景像素"静止且接近参考"才更新;物体区域冻结
6. 未完成逆向
- 官方查表段 1/2(大 d 物体)的增益/偏移值与物体响应(线性 0.19 vs 官方分段)
- 官方窗口(0x4202c/0x42030)更新机制(动态 span 怎么算)
- 0x180016ae0 菜单表初始化(0x417xx/DDT)
- FFC 状态机 0x73bf4(帧 75/130)
- 官方输出 2x 升采样算法(0x220 38400 → RGB)