# 官方 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 tsdk_pair3.exe \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`。