diff --git a/android/app/src/main/kotlin/com/mag160c/thermal/ui/live/LiveRenderer.kt b/android/app/src/main/kotlin/com/mag160c/thermal/ui/live/LiveRenderer.kt index 6044275..99ed116 100644 --- a/android/app/src/main/kotlin/com/mag160c/thermal/ui/live/LiveRenderer.kt +++ b/android/app/src/main/kotlin/com/mag160c/thermal/ui/live/LiveRenderer.kt @@ -62,6 +62,18 @@ class LiveRenderer( private var bitmap = Bitmap.createBitmap(320, 240, Bitmap.Config.ARGB_8888) private var bufW = 320 private var bufH = 240 + + // Round 39 verdict (docs/android_app/rendering_diff_phone_20261006.md §17.7): + // the "official background stripes are less visible" gap was NOT the display + // chain at all. A/B on the device: hardware vs software canvas — identical + // dark-region ROW; a Skia 6x pre-scale stage — identical; in-app bitmap dump + // vs screenshot — identical, i.e. nothing between the pipeline output and the + // screen adds alternation. The whole gap was the 7x7 detail enhancement + // running at level 4 on this device (official app NEVER calls + // SetDetailEnhancement — jadx, and round 22 already measured "level 2 makes + // noise larger"). With enhancement off the dark-region ROW drops from + // 0.31-0.46 to 0.14-0.21 x1e-2 arc ≈ the official 0.15. The renderer keeps + // the round-38 single-stage nativeOut draw below. private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { color = Color.WHITE typeface = Typeface.SANS_SERIF @@ -234,13 +246,12 @@ class LiveRenderer( fit.cx - size[0] / 2f, fit.cy - size[1] / 2f, fit.cx + size[0] / 2f, fit.cy + size[1] / 2f, ) - // Single filtered scale buffer -> screen. The official viewer's 640x480 - // offscreen stage was tried (round 38) and REVERTED: in our portrait - // fitRect the second stage lands on a 2.25x non-integer scale, and the - // GPU's rendering of it measurably WORSENED the column-direction - // alternation on the device (0.33 -> 0.55 x1e-2 arc) — an artifact the - // official (landscape, integer 3x second stage) doesn't have. The 160x120 - // -> 1440x1920 single scale is 9x/16x integer, artifact-free. + // Single filtered scale buffer -> screen. Round 39 additionally tried a + // Skia 6x pre-scale stage (second stage an exact integer 2x, unlike the + // round-38 640x480 attempt): the device A/B showed NO display-quality + // gain over this single-stage draw, so it was reverted — the stripe gap + // was the detail enhancement, not any scaling stage (see the class + // comment above). if (src != null) canvas.drawBitmap(bitmap, src, dstRect, paint) else canvas.drawBitmap(bitmap, null, dstRect, paint) canvas.restore() diff --git a/android/app/src/main/kotlin/com/mag160c/thermal/ui/settings/SettingsScreen.kt b/android/app/src/main/kotlin/com/mag160c/thermal/ui/settings/SettingsScreen.kt index 25d31e0..b02e56b 100644 --- a/android/app/src/main/kotlin/com/mag160c/thermal/ui/settings/SettingsScreen.kt +++ b/android/app/src/main/kotlin/com/mag160c/thermal/ui/settings/SettingsScreen.kt @@ -249,8 +249,10 @@ fun SettingsScreen( Text( "加强局部细节(官方同款 7×7 局部映射)。档位越高:①对比度门槛越高," + "只有更强的细节会被增强,弱对比区域(噪声所在处)被排除;" + - "②强边缘的增强幅度更大。所以高档位是「更锐但更挑」," + - "而不是「连噪声一起放大」。", + "②强边缘的增强幅度更大。\n\n" + + "注意:实测开启增强会放大暗场(低温背景)的固定条纹。官方 App 的" + + "实时画面从不开启增强(逆向 jadx 全量搜索无 SetDetailEnhancement 调用)," + + "想要与官方一致的背景观感请选「关闭」。", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(bottom = 8.dp), @@ -262,6 +264,10 @@ fun SettingsScreen( // local contrast dominates the divisor). On flat, noisy areas the // divisor floor holds the amplitude at 128/gain regardless of // level, which is why cranking it up sharpens instead of boiling. + // Round 39: the official app NEVER calls SetDetailEnhancement + // (jadx), and on-device A/B showed level 4 is what inflated the + // dark-region row stripes (0.31-0.46 -> 0.14-0.21 when off) — + // hence no "recommended" tag on any level and the notice above. Column( modifier = Modifier .heightIn(max = 320.dp) @@ -272,7 +278,7 @@ fun SettingsScreen( 1 to "1 级(轻微)", 2 to "2 级", 3 to "3 级", - 4 to "4 级(推荐)", + 4 to "4 级", 6 to "6 级", 8 to "8 级(强)", ).forEach { (lvl, label) -> diff --git a/build-artifacts/mag160c-app-debug.apk b/build-artifacts/mag160c-app-debug.apk index 3b580a5..2dd039c 100644 --- a/build-artifacts/mag160c-app-debug.apk +++ b/build-artifacts/mag160c-app-debug.apk @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b63016e89983bbf38b5fe1dc1459b297a4e4d4121a5ed7c26c269522bb178c46 +oid sha256:a9b371fe437a72677eeb04fb6f163b909418cd1f22a05554e91086c6ffec3470 size 12663188 diff --git a/csdk/README.md b/csdk/README.md index 437d56c..8da4112 100644 --- a/csdk/README.md +++ b/csdk/README.md @@ -305,13 +305,18 @@ build-artifacts\csdk_test_display.exe (SDK 给 App 的位图就是 160×120,App 用 `new Paint(1)`(无 FILTER_BITMAP)最近邻放大), 而 PC 版的双线性会展出一层"全幅/半幅"交错细纹(暗/低温区最明显)。 真机实测:冷区细尺度列条纹比 1.16/1.29(双线性)→ **1.00/0.93**(最近邻)。 - - **38 轮补充(显示链两级结构)**:官方 `ImageViewer` 是**两级**——160×120 SDK - 位图先用 `PaintFlagsDrawFilter + FILTER_BITMAP` 画进 **640×480 离屏**(4× 双线性), - SurfaceView 再放大到屏。中间 4× 对逐行 FPN 图样有 ~0.6× 低通:同一份 NUC 数据 - 走官方两级链复刻 0.12、走单级 12× 复刻 0.32(行交替,×1e-2 弧长)——**官方 - "背景条纹更不明显"的主导机制是显示链**。Kotlin 侧已按此两级实现 - (`RenderPipeline.nativeOut` 输出原生 160×120 + `LiveRenderer` 的 640×480 中间位图); - C 参考路径(`upscale2x`/`upscale_nearest`)保持不变,仅供黄金夹具与 PC 路径。 + - **38 轮补充(显示链两级结构,39 轮已修正结论)**:官方 `ImageViewer` 是**两级** + ——160×120 SDK 位图先用 `PaintFlagsDrawFilter + FILTER_BITMAP` 画进 **640×480 离屏** + (4× 双线性),SurfaceView 再放大到屏。38 轮曾据此把"背景条纹更不明显"归因于 + 显示链;**39 轮真机 A/B 推翻**:硬件/软件画布、单级/Skia 6× 预放大、App 内位图 + dump vs 截屏逐环对照全部无差——显示链(含 SurfaceFlinger 合成与截屏)不添加 + 行交替;真因是**我们的 App 开着 7×7 细节增强(enhanceLevel=4),官方 App 从不 + 调用 SetDetailEnhancement(jadx 全量无此符号)**。关闭后暗区行交替 + 0.31-0.46 → 0.14-0.21(×1e-2 弧长)≈ 官方 0.15。Kotlin 侧维持 38 轮的 + `nativeOut` 单级输出(160×120 → LiveRenderer 单次滤波放大),640×480 中间级 + 已于 38 轮内回退(竖屏 fitRect 二级 2.25× 非整数伪影)、39 轮的 720×960 预放大 + 试验也因无显示收益回退;C 参考路径(`upscale2x`/`upscale_nearest`)保持不变, + 仅供黄金夹具与 PC 路径。 - 顺带(同一条链):**曲线插值温度要加 `BasePara2.fpaTempFix`**。官方 App 传给 SDK 的是 `帧尾 FPA + fpaTempFix`(真机 1699),`SelectCaliCurve`/`GenerateVolatileLens` 用它选/插值每像素标定表;温度换算链路仍用帧尾原值。C 侧**已实现** diff --git a/docs/android_app/rendering_diff_phone_20261006.md b/docs/android_app/rendering_diff_phone_20261006.md index b63778b..6f8cfdb 100644 --- a/docs/android_app/rendering_diff_phone_20261006.md +++ b/docs/android_app/rendering_diff_phone_20261006.md @@ -847,3 +847,48 @@ Kotlin 一致),与 App 自己 `copyNuc` 的 dump 对拍:corr=0.9996,仅 - 现场状态:rotation 锁竖屏(原值 1,**待用户恢复**);我们 App 前台出流; MIUI 的 App 自动启动会在授权后把焦点拉回我们 App,切官方测前先 force-stop 我们,用 `TAB×2+ENTER` 过 USB 弹窗(横屏时 input tap 坐标不可靠)。 + +### 17.7 第三十九轮:GPU 画布假设被真机 A/B 推翻,真因 = 7×7 细节增强(2026-10-07) + +按 §16.3/17.6 交接清单执行(首验 GPU 硬件画布 → 正式修复 → A/B 验收)。全部测量用 +39 轮新脚本 `final39.py/final39b/c/d/e.py`(口径:12px 网格 + UI mask + 弧长反演 + +鲁棒 std;暗区 = sensor 行 0..40 × 列 90..160;官方横屏直接同区;38 轮存档截图全部 +可复现,官方 FULL 0.10/0.28、暗区 0.15 与 17.6 表逐位一致,DARK 对照里照片 JPEG +0.25 也精确复现)。 + +- **首验(软件画布强制 false)**:fps 11.6-12.4(avg 84.6 ms < 15 fps 预算 66 ms), + 软件画布在 160×120 buffer 时代仍不达标(33 轮历史问题重演)。同场景 A/B + (唯一变量 = 画布类型,5 分钟内截屏):暗区 ROW 0.49/0.28/0.19/0.33(软)vs + 0.46/0.31/0.32/0.36(硬)——**无显著差,"GPU 硬件画布添加交替"定案否定**。 + 17.6 遗留的"暗区 ROW 2.07"在所有存档截图 × 所有合理口径(网格/逐像素 × + mask/无 mask × 顶/底暗区)下均无法复现(最高 0.47),该数字是 38 轮会话末 + 一次性计算、无脚本存档,且与其方向性结论一起作废。 +- **备选 a(Skia 6× 预放大 + 硬件整数 2× blit)**:720×960 中间级实现后 fps 达标 + (avg 31.9 ms,15.1 paints),但暗区 ROW 0.43/0.28/0.41/0.43 vs 单级 + 0.46/0.31/0.32/0.36——**无显示收益,已回退**(SF 采样比假设同时否定)。 +- **App 内位图 dump(决定性归属实验,flag 门控调试设施,结论落地后已删)**: + 同帧三方(统一口径)——管线 RGB 输出(bitmap dump)DARK ROW **0.35**、 + Skia 6× 后 **0.21**、同秒截屏 **0.27**;离线复刻(同 NUC 数据走干净显示链) + **0.16**。合成/截屏不添加(dump≈截屏);差源头在**管线 RGB 输出本身** + (0.35 vs 复刻 0.16)。 +- **真因定位**:真机 `mag160c_settings.xml` 实测 `enhanceLevel=4`(strength=32, + 7×7 细节增强**开着**)。`DetailEnhance.enhance` 在灰度图上 `gray += strength·detail/32768` + (detail ∝ center−7×7 局部均值,暗区行 FPN 被同比例放大);而**官方 App 从不 + 调用 SetDetailEnhancement**(jadx 全量无此符号,22 轮已记录"开 2 级噪声反而 + 更大")——**官方背景条纹更不明显的真因 = 我们开着官方从不开启的增强**。 + 38 轮的"显示链添加 ROW +0.146"(复刻 0.13 vs 截图 0.21 的平方和差)与"NUC 域 + 1.6×"疑云同源:复刻链无增强、真机链有。 +- **修复与验收(同场景 A/B,唯一变量 enhanceLevel 4→0)**: + 暗区 ROW 0.31-0.46 → **0.14-0.23**(官方 0.15);全幅(final38 同口径) + ROW 0.29 → **0.15**(官方 0.10,1.5×)、COL 0.54 → **0.34**(官方 0.28,1.2×, + 38 轮已达标)。§16.3 目标"逐行 std ≤ 0.2":nomask 暗区 0.18 达标,mask 口径 + 0.22-0.23 贴线(mask 边缘残差,官方无 UI 不受此影响)。fps 15.1(38 轮同 + 代码实测)。 +- **落点**:`LiveRenderer` 维持 38 轮 nativeOut 单级(预放大/dump 试验代码已回退, + 类注释记录 39 轮定案);`DebugLog.enabled` 改回 false;设置页"图像增强"对话框 + 去掉"4 级(推荐)"标记并注明"开启会放大暗场条纹、官方实时画面从不开启"; + 用户设备 `enhanceLevel` 已恢复 0。`DetailEnhance` 移植本身保留(官方 SDK 功能、 + 单测锁定,用户可选开)。csdk/README.md 的 38 轮段已同步修正。 +- **口径教训(承 17.6)**:38 轮"同帧对照"的截图(sync 系列)与照片对照 + (withphoto/photo.mdt)都**含增强**,而所有"复刻"都**无增强**——两个未解子问题 + (①真机 GPU 为何添加交替 ②NUC 域 1.6× 根源)被同一个混淆变量解释,均结案。 diff --git a/docs/android_app/session_state.md b/docs/android_app/session_state.md index e2f0337..7bed9d0 100644 --- a/docs/android_app/session_state.md +++ b/docs/android_app/session_state.md @@ -489,6 +489,40 @@ "未连接"分支显示,正常出图时用户看不到任何反馈)。 - [x] 单测 66 → **76 项全绿**;debug + release(R8) 双构建通过;APK 已更新。 +## 用户反馈修复 第三十九轮(2026-10-07 凌晨,"官方背景条纹更不明显"结案:GPU 画布假设被真机 A/B 推翻,真因 = 我们开着官方从不开启的 7×7 细节增强) + +按 38 轮交接清单执行(首验软件画布 → 正式修复 → A/B 验收 → NUC 域收尾)。 +完整数据与口径见 `rendering_diff_phone_20261006.md` §17.7。测量脚本 +`%TEMP%\mag160c_re\final39*.py`(38 轮存档数字全部复现:官方 FULL 0.10/0.28、 +暗区 0.15、照片 JPEG 0.25——口径互锁)。 + +- [x] **首验(软件画布)否定 GPU 画布假设**:`useHardwareCanvas=false` 实测 + fps 11.6-12.4(avg 84.6 ms,15 fps 不达标,33 轮历史重演);同场景 A/B + 暗区 ROW 软件 0.49/0.28/0.19/0.33 vs 硬件 0.46/0.31/0.32/0.36——**画布 + 类型无显示差**。38 轮"暗区 ROW 2.07"无脚本存档、所有口径均无法复现 + (≤0.47),随方向性结论一起作废。 +- [x] **备选 a(Skia 720×960 预放大 + 硬件整数 2× blit)无收益**:fps 达标 + (15.1 paints,avg 31.9 ms)但暗区 ROW 与单级无差——已回退。SF 采样比 + 假设同时否定。 +- [x] **App 内位图 dump 定位(决定性)**:同帧三方——管线 RGB 输出 DARK ROW + **0.35**、Skia 6× 后 0.21、同秒截屏 0.27、离线复刻 0.16。合成/截屏不添加; + 差在管线输出(vs 复刻)。真机 settings 实测 **enhanceLevel=4**(7×7 增强 + 开着,strength=32);官方 App **从不调用 SetDetailEnhancement**(jadx 全量 + 无此符号)。38 轮两个未解子问题("显示链添加 +0.146"与"NUC 域 1.6×") + 被同一混淆变量解释:**复刻链无增强、真机链有**——均结案。 +- [x] **修复 + 验收(同场景 A/B,唯一变量增强 4→0)**:暗区 ROW 0.31-0.46 → + **0.14-0.23**(官方 0.15,nomask 0.18 达成 §16.3 的 ≤0.2);全幅 ROW + 0.29 → **0.15**(官方 0.10)、COL 0.54 → **0.34**(官方 0.28)。fps 15.1。 +- [x] **代码落点**:LiveRenderer 维持 nativeOut 单级(试验代码回退,类注释记录 + 定案);DebugLog 改回 false;设置页"图像增强"去掉"4 级(推荐)"标记 + + 注明会放大暗场条纹/官方从不开启;**用户设备 enhanceLevel 已恢复 0**; + csdk/README.md 38 轮段同步修正。DetailEnhance 移植保留(官方 SDK 功能、 + 单测锁定,用户可选)。 +- [x] 123 单测全绿;csdk 无代码变化。 +- 现场状态:我们 App 前台出流(最终版 APK = 仓库提交代码);rotation 仍锁 + 竖屏(**待用户恢复**);增强已关(用户如需锐度可在设置里重开,代价是暗场 + 条纹加重);`%TEMP%\mag160c_re\` 下 39 轮脚本与截图待清理。 + ## 用户反馈修复 第三十八轮(2026-10-07 凌晨,原始帧录制 + 逐级重放全链路定案 + 显示链与官方对齐) 按第三十七轮交接清单执行。用户中途解锁手机并配合真机操作。完整数据表与