android: fix real-device defects (temp double-conversion, FFC temp jump, image orientation per grip, remote raw+metadata stream)

This commit is contained in:
ZXCLI
2026-09-11 03:01:13 +08:00
parent 0919f5e186
commit 464cfcb5e8
26 changed files with 1938 additions and 277 deletions
+42 -15
View File
@@ -66,8 +66,9 @@ android/app/src/main/kotlin/com/mag160c/thermal/
│ ├─ LiveScreen.kt 稳定单分支相机布局:顶栏5控制项(FFC/变倍/追踪/调色板/画中画)
│ │ +底部快门区(相册/拍照/录像)+SurfaceView+PIP浮层
│ ├─ PipCameraView.kt 可见光PIP相机引擎(Camera2最小实现,异常只记日志)
LiveRenderer.kt SurfaceView 软件渲染:图像恒90°旋转(3:4竖)钉在竖屏框架固定区域,
文字恒屏幕水平;色标条/圆圈标记/中心温OSD
ImageTransform.kt 方向/翻转/letterbox/缩放几何 + 传感器↔屏幕互逆映射(纯 Kotlin,单测覆盖)
└─ LiveRenderer.kt SurfaceView 软件渲染:图像按握持角补偿旋转(rot=90-φ)、
│ letterbox+变倍、色标条/圆环标记/中心温OSD(标签按旋转包围盒定位)
├─ ui/gallery/ 相册页(MediaStore DCIM/MAG160C 扫描 + 内嵌JPEG缩略图 + 运行时媒体权限)
├─ ui/analyze/ MDT 离线分析(缩放/调色板重渲染/温度条+点击测温/备注回写/PDF报告)
├─ ui/settings/ 设置页(默认调色板/发射率/报警温度/语言/云同步/远程预览两行/关于)
@@ -129,25 +130,39 @@ res/drawable/*.xml 自绘矢量图标(双弧圆等可靠几何图形
- 旧 analysis/protocol_spec.md 的 66f/670 描述("prepare/version query")不准,
以 magcx_official_flow.md 为准。
### 4.3 实时画面渲染(最终约定:构图钉死竖屏框架
### 4.3 实时画面渲染(2026-09-11 修订:图像内容随握持朝向补偿
- **Activity 锁定竖屏**manifest `screenOrientation="portrait"`):屏幕相对手机框架
永不旋转。顶栏、热像区域、底栏的**绝对位置**永远贴着手机竖屏的物理顶边(挖孔侧)、
中间、物理底边;手机怎么物理旋转,构图都不动(用户明确要求:屏显方向必须始终
与热像镜头实际方向对应,横屏后显示区域跟着屏幕转是错的)。**不要改回 fullSensor。**
- **图标/文字按物理持机朝向补偿旋转**(第六轮定稿):`ui/DeviceOrientation.kt` 用
加速度计得出手机相对竖屏的顺时针物理转角 φ(0/90/180/270,带滞回;竖屏锁定下
Display.rotation 恒 0 不可用)。顶栏/底部导航条目 `graphicsLayer rotationZ=-φ`
原位预旋转;渲染器 OSD 文字 `canvas.rotate(-φ)` 绕锚点旋转(标记圆点、色标条
几何仍钉死在图像上)。对话框与其他页签暂不补偿
中间、物理底边;手机怎么物理旋转,构图都不动。**不要改回 fullSensor。**
- **图像内容补偿旋转(本轮修订,勿回退)**:构图不动,但**画面内容**必须随握持朝向
反向补偿,否则转身后场景跟着转、与镜头实际指向脱节。规则:
`rot = 90 - φ`(φ = DeviceOrientation 握持角 0/90/180/270)。
依据官方实现:`MainActivity.windowOrientationListener` 把 Display.rotation
(0/1/2/3) 映射成图像 90/0/270/180,再经 `ImageViewer.drawImage` 的
`matrix.postRotate` 应用;官方**窗口会随传感器旋转**,故可见结果恒为 90
我们锁了窗口,图像就要承担全部差值 → `90 - φ`。
显示旋转索引→角度约定取自官方自家相机代码
`VisibleCameraHelper.setPreviewOrientation`case 0→0/1→90/2→180/3→270
即索引 N = 顺时针 N×90)。
- **手工修正项**(官方"旋转USB画面/水平翻转/竖直翻转"同款):设置页三项,
叠加在自动补偿之上,持久化于 `AppSettings.imageRotateDeg/imageFlipH/imageFlipV`
用于传感器安装方向特殊的机器。翻转在像素拷贝阶段完成,保证几何映射可测。
- **图标/文字按握持角补偿**`graphicsLayer rotationZ=-φ`Compose/
`canvas.rotate(-φ)`(Canvas)。**标签一律按旋转后包围盒定位**(
`ImageTransform.rotatedBoxHalfExtents` + `drawGripText`),不要再拿基线锚点
摆位——色标条两端数字曾因此与色条错位。
- **几何单一来源**`ui/live/ImageTransform.kt`(纯 Kotlin,可单测)同时供
渲染器与点击/探针映射使用(`sensorToScreen` / `screenToSensor` 互为逆映射,
已对 0/90/180/270 × 翻转组合做往返测试)。渲染与取温映射**必须**用同一套参数,
否则标记会落到错误像素上。
- **加速度计符号约定(第七轮教训,勿改回)**:真机 TYPE_ACCELEROMETER 静止读数
指向世界上方(竖屏正持 y=+9.81);模拟器虚拟传感器是反的重力约定(y=-9.81)。
DeviceOrientation 映射按**真机约定**写,模拟器测试须用反号值驱动
(φ=0→`adb emu sensor set acceleration 0:9.81:0`)。
- **图像恒 90°CW 绘制为 3:4 竖向**,填满可用区域(顶栏下~底导航上)。
- **文字/图标恒屏幕水平**(可读);标记文字位置自动跟随(probeToScreen 固定 90° 映射:
`fx=1-sy/120, fy=sx/160`
- 顶栏=4 个相机控制项(FFC / 变倍×N / 追踪·开 / 调色板+名称),恒在顶部并带小字
标签,`safeDrawing` 顶部inset 适配挖孔屏。
- **追踪开关控制最高+最低两个标记**(此前只关最高,最低永远画着)。
- 顶栏=5 个控制项(FFC / 变倍×N / 追踪·开 / 调色板+名称 / 画中画),恒在顶部并带
小字标签,`safeDrawing` 顶部 inset 适配挖孔屏
- 底部(仅实时页)导航栏上方为**相机快门区**:相册快捷入口 / 大快门拍照 /
录像-停止;快门区高度并入 `vm.uiBottomPx`= 导航高+快门区高)。
- 顶栏高度经 `onSizeChanged`→`vm.uiTopPx`**必须挂在 safeDrawing inset 之前**
@@ -158,6 +173,18 @@ res/drawable/*.xml 自绘矢量图标(双弧圆等可靠几何图形
`connect()` 置 `status="no_device"`LiveScreen 显示占位文案"未检测到热像仪,
请插入MAG160C",渲染器无帧黑底;startDemo/合成帧代码已删除。
### 4.3.1 温度显示(2026-09-11 修复三个真机缺陷)
- **中心温度**`probeTemp()` 返回的**已经是毫度**,界面只许除以 1000 一次。
旧代码又调了一次 `countsToTempMc()`,真机显示 108.7℃(实际约 24℃)。
- **FFC 期间的温度跳变**:FFC 参考帧过去被解码进 `nuc`OSD 采样的同一缓冲区),
未补偿的原始 counts 直接参与显示 → 最高/最低短暂跳到 ~150℃。
现在参考帧走独立 `refScratch` 缓冲。
- **上电/未就绪**:首帧渲染前 `nuc` 全 0,而 `countsToTempMc(0) = -161.0℃`
(看着像合法读数)。`RenderPipeline.tempsReady()` / `IrSession.tempsReady()`
门控 UI 取温;远程页同样门控。
- 回归测试:`core/PipelineTemperatureStateTest.kt`。
### 4.4 温度
- 温度 = 毫度 int(÷1000 = ℃)。`counts_to_temp_mc`(NUC域→T2E逆映射)用于探针/OSD,
在真机上需按 DDT 标定核对绝对值(待办)。