Files
MAG160C/docs/android_app/pc_official_compare_20261006.md
T

112 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 官方 PC 端 vs 逆向 SDK:同一帧逐像素对比(2026-10-06)
> 目的:热像仪接 PC 后,用**官方 Windows 软件**采图,与**逆向出来的 SDK/App 管线**
> 渲染同一帧做逐像素对比,找出"照片到底差在哪些细节上"。
> 证据图:`docs/android_app/pc_vs_ours_20261006.png`(官方 / 我们 / 差异×8 / 灰度 ×16)。
## 结论速览
| 环节 | 一致性 | 证据 |
|---|---|---|
| NUC(端点插值 + 增益/阈值表 + 盲元补偿 + ref) | **逐像素完全一致** | 抽检 4 帧:19200/19200 像素相等,max diff = 0 |
| 调色板 | **完全一致** | 官方 `.pal`、官方彩色图颜色集合、我们的 `PALETTE256_ARGB` 三者 256/256 相同 |
| 灰度映射(窗口 + 直方图 LUT + 2× 上采样) | 平均 **+1.3 级**、最大 10 级(/255) | 320×240 灰度逐像素;本轮场景计数 9600–11000 |
| 彩色输出 | 60.9–72 % 字节完全相同,平均 \|Δ\| 1.4–1.9/255,最大 20 | 官方 `.rgb`(BGR24)vs 我们导出的 PNG |
| 细节增强(7×7 DDE) | 官方这份渲染**没开**;我们默认等级 0 也关 | 官方彩色 = palette(官方灰度),逐像素 max diff = 0 |
| 显示窗口 | **下界规则不同** | 官方 20/20 帧 `winHi = 数据max`;12/20 帧 `winLo = 数据min`,其余仅低 1–7 计数。我们 `winLo = mean−500`,本例比数据 min 低 **188** 计数 |
| 温度换算 | **系统差:+0.1 ℃ @27 ℃ → −0.7 ℃ @38 ℃** | 官方 `t32` 是 NUC 计数的精确直线(k=8.539 mC/count,残差 ≤0.5 mC),我们的固定 T2E 表斜率 8.26→7.67 递减 |
**一句话**:渲染管线(NUC/调色板/灰度)已经和官方 PC SDK 对得上,肉眼几乎无差别;
真正还差的是**温度曲线**(最高到 0.7 ℃)和**窗口下界规则**(造成约 1 级亮度偏差)。
## 1. 官方 Windows 侧用谁
| 候选 | 判定 |
|---|---|
| `app/【PC分析软件】…exe`(内部名 **ThermoX 2.5.3.6**,巨哥电子) | 是**网络码流**软件:面板是"指定IP / 启用DHCP Server / 传输混合/纯视频/纯温度码流",`刷新`扫不到 USB 相机,运行期不加载任何 USB 库 → 不吃本机 USB 机芯 |
| `IR_Camera_SDK-1.0.1/windows/windows/app/EloThermal.exe`(官方 SDK 演示,C# 源码在仓库) | 能连 USB 出图(CameraSDK/ThermalSDK + libusb0),但**没有任何存图功能**(全源码无 Encoder/Save)→ 只能截屏 |
| `build-artifacts/tsdk_pair3.exe`(本仓库工具,调用官方 `ThermalSDK.dll` + `CoreSDKLib.dll`) | ✅ **采用**:一次采集给出官方渲染图、原始帧、温度、调色板、LUT、DDT 全套 |
## 2. 方法(可复现)
```
# 1) 设备驱动(本机原来没装,CM_PROB_FAILED_INSTALL)
# 厂商 INF:IR_Camera_SDK-1.0.1/windows/windows/drivers/amd64/ThermoCube_stream.inf
# (libusb-win32 1.2.6.0,cat 是 libwdi 自签证书) → 需先把该证书导入
# "受信任的根证书颁发机构" + "受信任的发布者",再:
pnputil /add-driver ThermoCube_stream.inf /install # 设备变 OK,libusb0 服务 RUNNING
# 2) 官方基准采集(输出目录用第 1 个参数,勿落在仓库里)
cd <temp>
tsdk_pair3.exe <temp>\off20 20 6000 > off20_stdout.txt
# 每帧产出 19 个文件:gray / rgb / cbrgb(BGR24) / raw / f20 / ref / t32 / pal /
# lut / h1024 / cdf / hist / thr0 / thr1 / thrw / g0 / g1 / gw / blind (+official.ddt)
# 3) 用我们的管线离线渲染同一批帧(临时 JVM 单测,用完已删)
# RenderPipeline.loadDdt(official.ddt) 后:
# repeat(4){ frameRemote(pair.ref, phase=2, shutter) } // ref = 官方的参考帧
# frameRemote(pair.f20, phase=0, shutter) // 渲染
# 产出 our_XXX.png / .nuc / .t32,再用 Python 与官方逐像素比
```
**新解码出的官方内部缓存语义**(供后续逆向用):
| 缓冲 | 地址 | 含义 |
|---|---|---|
| 帧输入 | dev+0x41f20 | 官方喂给 NUC 的帧数据(= USB 帧 payload 域,均值 ~12000) |
| 参考帧 | dev+0x41ef0 | NUC 用的 ref |
| 传感器温度 | dev+0x54 | 端点插值用的 FPA 温度(与 DDT 锚点同量纲,m℃) |
| NUC 输出 | dev+0x270 | 校正后计数(均值 ~9600–11000);`MAG_GetTemperatureData_Raw` 的输入 |
| 端点选择 | dev+0x41840 | 官方的 `sel`,与我们的 `endpoint()` 取值完全一致 |
> 关键验证:把 `dev54` 当帧尾 FPA 温度喂进我们的管线,NUC 输出与官方
> `pair_*.raw` **19200/19200 像素逐位相等**——这同时锁定了帧尾 FPA 字段的语义。
## 3. 温度:官方是"每设备线性响应",我们用的是固定曲线
官方 `t32`(19200 像素 × int32 毫度)与 NUC 计数之间是**精确直线**:
```
t_mC = 8.539 * counts - 55688 # 残差 ≤ 0.5 mC;逐帧斜率 8.5358–8.5424(≈常数)
```
同一计数下不同像素的温度离散为 **0 mC**(无逐像素差异)→ 说明官方对"已校正计数"
只做一次全局线性换算(对应 `ConvertResponse2Temperature` 一族,csdk 里已有实现
`mag160c_temp_convert_response`,但安卓 App 未使用)。
我们的 `TempMath.countsToTempMc` 是固定 T2E 反查表,局部斜率随计数**递减**:
| 计数区间 | 官方斜率 | 我们的斜率 | 比值 |
|---|---|---|---|
| 9600–9900 | 8.5390 | 8.2581 | 1.034 |
| 9900–10200 | 8.5391 | 8.1993 | 1.041 |
| 10200–10600 | 8.5390 | 7.9505 | 1.074 |
| 10600–11000 | 8.5390 | 7.6706 | 1.113 |
→ 温度读数偏差:**≈27 ℃ 处 +0.1 ℃,≈38 ℃ 处 −0.62 ℃**(累计最大 −0.70 ℃)。
## 4. 建议的下一步
1. **温度**(优先):安卓端改用设备响应换算。官方 PC 侧对已校正计数是线性映射,
系数随设备(本机实测 k=8.539 mC/count、b≈−55.7 k mC,随 FPA 温度微动 ~±50 mC)。
需要先定位系数的来源(DDT / BasePara 里的标定字段),再按官方公式实现;
`csdk/src/mag160c_temp.c::mag160c_temp_convert_response` 已是同款算法,可作参照。
2. **窗口**:把 `statsWindow()` 从"`mean ± (dev24*1000>>dev4cShift)/2` 向外扩"改成官方行为
(上界 = 数据 max、下界 = 数据 min,20 帧实测 20/20 与 12/20 如此,其余差 ≤7 计数)。
这一项解释了灰度上 ~1.3 级的系统性偏差。
3. **手机侧对照照(未完成)**:本轮只覆盖"渲染",没覆盖官方**手机 App 的拍照路径**。
建议:同一静止场景,官方手机 App 与我们的 App 各拍一张同调色板照片;
官方 MDT 里存的就是 NUC 后帧,可以直接喂我们的显示阶段逐像素比
(需要给管线加一个"只跑显示阶段"的离线入口)。
4. **PC 端 C 工具链**:本机 libusb-1.0 1.0.19 对 libusb0.sys 绑定**不可用**
(`claim_interface` → Win32 错误 87;官方走 libusb0.dll API 正常)。
若要在这台机器跑 csdk 的 demo3/render_offline,需要换 libusb-1.0 版本,
或把设备改绑 WinUSB/libusbK。
## 5. 复现清单(临时文件不在仓库)
- 官方基准数据(20 帧全套,35 MB)与差异图在
`%TEMP%\mag160c_wincmp\off20\`(`compare_visual.png` / `compare_tempdiff.png` / `compare.txt`);
仓库内只保留 `docs/android_app/pc_vs_ours_20261006.png` 一张证据图。
- 驱动安装脚本痕迹:`%TEMP%\mag160c_wincmp\pnputil*.log`、`cert.log`。