diff --git a/analysis/session_state.md b/analysis/session_state.md index aa789aa..398483e 100644 --- a/analysis/session_state.md +++ b/analysis/session_state.md @@ -5,7 +5,7 @@ ## 当前阶段 -**阶段 5/5:全部完成(2026-08-13 晚)** +**阶段 6/6:全部完成(2026-08-14)** ## 已完成的里程碑 @@ -16,22 +16,25 @@ - [x] 逐像素验证:NUC+盲元 0/19200、插值 0 误差、2x 0/76800、窗口一致 - [x] 鬼影根因文档:analysis/reverse_20260813_full.md §7 - [x] demo3 v5 官方复刻实现并实机运行 -- [x] **零像素根因定位与修复**:load_ddt 在端点块内多读 blind 记录导致 - EP1+ 表错位 1280 字节 → 修好后全帧 0 个零像素,gain=4000 正常值 -- [x] demo3 vs 官方同场景对比:gray 直方图相关性 0.9197(跨会话) -- [ ] 提交(用户未要求,等待指示) +- [x] 零像素根因定位与修复(load_ddt 表错位) +- [x] **颗粒感修复**:demo3 显示层改为官方同款 320×240(2x 平滑灰度 + + 调色板 + HALFTONE 放大),同场景 RGB 直方图相关性 0.9796 +- [x] 仓库整理 + csdk/README.md 完整文档 + 本地 commit 0bfb926 +- [ ] push(等服务器上线,用户手动) -## 零像素问题(已解决,记录在案) +## 颗粒感问题(已解决,2026-08-14) -- 现象:demo3 的 nuc 在 (65,25)/(66,25) 为 0,官方同坐标 9987/9989。 -- 根因:**demo3 的 load_ddt 读取布局错误**——blind 记录区在文件所有端点块 - 之后,但代码在每个端点块内读了 blind,导致 EP1+ 的 thr/gain 表整体 - 错位 1280 字节。错位表在死像素列附近的 seg0 gain=35321(应为 ~4000), - d2 踩中后 NUC 输出 clamp 到 0。 -- 修复:thr+gain 按端点块顺序读完后再统一读 blind(csdk/tools/mag160c_demo3.c - load_ddt)。 -- 验证:修复后全帧 0 个零像素;(65,25) nuc=9814、盲元目标 9740/9732; - gain0=4000 正常值;窗口/统计正常([8493,10554] m9409 s258)。 +- 现象:官方画面比 demo3 清晰,demo3 颗粒感重。 +- 根因:demo3 v5 的显示层渲染的是 160×120 原始灰度(最近邻放大到 640×480), + 而官方输出是 2x 升采样后的 320×240(2-tap 平滑本身抑噪),官方 app 显示 + 的也是这个 2x 缓冲。 +- 排查过程(已排除的嫌疑):FUN_1800175a0(0x230)是帧差偏移调整非平滑; + 0x41fe8 后处理(FUN_1800184f0/660 隔行平滑)官方会话 =0 未启用; + temporal filter 0x41fe0=1 未启用;f20 mode=1 直通。 +- 修复:render_bmp 改用 g_gray320(官方 2x 缓冲)上色,BMP/窗口改 320×240, + StretchBlt 加 HALFTONE。 +- 验证:320×240 BMP 与官方同场景 pair_000.rgb 直方图相关性 0.9796, + 颜色数 227 = 官方 227(之前 160×120 版只有 ~161 色)。 ## 常用命令 diff --git a/build-artifacts/mag160c_demo3.exe b/build-artifacts/mag160c_demo3.exe index 4cf9454..779667d 100644 --- a/build-artifacts/mag160c_demo3.exe +++ b/build-artifacts/mag160c_demo3.exe @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f8503748fae7f698c7d9765226628fcf3b2aca317b0db9ce5cf88a852d390ece -size 138825 +oid sha256:a6a6d7a3f5bdcf81d983302e5bcb31d8b8c48094970bd0314418eb7c2886903e +size 139029 diff --git a/csdk/README.md b/csdk/README.md index 88bd00d..014cb98 100644 --- a/csdk/README.md +++ b/csdk/README.md @@ -171,12 +171,13 @@ Start-Process "C:\Project\MAG160C\build-artifacts\mag160c_demo3.exe" -WorkingDir | `mag160c_official_nuc_thr.bin` | 官方 NUC 阈值表(历史抓取,备用)| | `libusb-1.0.dll` | libusb 运行库 | -验证状态(2026-08-13): +验证状态(2026-08-14): - NUC+盲元 与官方同帧抓帧逐像素 0/19200 误差 - 阈值/gain 插值 0 误差;2x 升采样 0/76800 误差;窗口公式与官方一致 -- 同静态场景 gray 直方图与官方相关性 0.92(跨会话) +- 同静态场景 320×240 RGB 直方图与官方相关性 0.98(跨会话) +- 显示层 = 官方同款 320×240(2x 平滑灰度 + 调色板 + HALFTONE 放大) - 输出日志:`demo3_diag.txt`(每帧统计)、`demo3_ffc.txt`(FFC 事件)、 - `demo3_auto_*.bmp`(每 30 帧快照) + `demo3_auto_*.bmp`(每 30 帧快照,320×240) ### 5. 官方抓帧工具(tsdk_pair2 / tsdk_pair3) diff --git a/csdk/tools/mag160c_demo3.c b/csdk/tools/mag160c_demo3.c index 7c0f4ac..72db744 100644 --- a/csdk/tools/mag160c_demo3.c +++ b/csdk/tools/mag160c_demo3.c @@ -1,4 +1,4 @@ -/* MAG160C Windows Demo v5 - FULL official pipeline replication. +/* MAG160C Windows Demo v5 - FULL official pipeline replication. * * Pixel-verified against CoreSDKLib.dll (Ghidra decompile + live same-frame * captures, 2026-08-13): @@ -76,6 +76,8 @@ static double counts_to_c(double v) { #define W 160 #define H 120 #define NPIX (W * H) +#define VW 320 +#define VH 240 /* official timing constants (live: dev+0x2d78..0x2d94 = 4,5,4,1800,250) */ #define OFF_N0 4 /* ref frames after FFC(1) window start */ @@ -90,7 +92,7 @@ static double counts_to_c(double v) { static libusb_context *g_ctx; static libusb_device_handle *g_h; static unsigned char g_frame[40000]; -static unsigned char g_bmp[54 + W * H * 3]; +static unsigned char g_bmp[54 + VW * VH * 3]; static char g_title[256]; static unsigned short g_live[NPIX]; @@ -601,26 +603,28 @@ static void upscale2x(void) { static void render_bmp(void) { unsigned char *px = g_bmp + 54; - for (int y = 0; y < H; ++y) { - for (int x = 0; x < W; ++x) { - int i = y * W + x; - unsigned char gv = g_gray160[i]; + /* 官方输出是 320x240:用 2x 升采样后的灰度上色(2-tap 平滑本身 + * 就抑制了颗粒感;官方 app 显示的也是这个 2x 缓冲)。 */ + for (int y = 0; y < VH; ++y) { + for (int x = 0; x < VW; ++x) { + int i = y * VW + x; + unsigned char gv = g_gray320[i]; unsigned char r = mag160c_official_palette256[gv][2]; unsigned char g = mag160c_official_palette256[gv][1]; unsigned char b = mag160c_official_palette256[gv][0]; - int dst = (119 - y) * W * 3 + x * 3; + int dst = (VH - 1 - y) * VW * 3 + x * 3; px[dst + 0] = b; px[dst + 1] = g; px[dst + 2] = r; } } if (g_max_x >= 0) { - int mx2 = g_max_x, my2 = 119 - g_max_y; - for (int k = -2; k <= 2; ++k) { - if (mx2 + k >= 0 && mx2 + k < W) { - int d2 = my2 * W * 3 + (mx2 + k) * 3; + int mx2 = g_max_x * 2, my2 = (VH - 1 - g_max_y * 2); + for (int k = -3; k <= 3; ++k) { + if (mx2 + k >= 0 && mx2 + k < VW) { + int d2 = my2 * VW * 3 + (mx2 + k) * 3; px[d2] = 255; px[d2 + 1] = 255; px[d2 + 2] = 255; } - if (my2 + k >= 0 && my2 + k < H) { - int d2 = (my2 + k) * W * 3 + mx2 * 3; + if (my2 + k >= 0 && my2 + k < VH) { + int d2 = (my2 + k) * VW * 3 + mx2 * 3; px[d2] = 255; px[d2 + 1] = 255; px[d2 + 2] = 255; } } @@ -633,11 +637,12 @@ static LRESULT CALLBACK wndproc(HWND hw, UINT msg, WPARAM wp, LPARAM lp) { PAINTSTRUCT ps; HDC dc = BeginPaint(hw, &ps); HDC mem = CreateCompatibleDC(dc); - HBITMAP bm = CreateCompatibleBitmap(dc, W, H); + HBITMAP bm = CreateCompatibleBitmap(dc, VW, VH); HGDIOBJ old = SelectObject(mem, bm); - SetDIBitsToDevice(mem, 0, 0, W, H, 0, 0, 0, H, g_bmp + 54, + SetDIBitsToDevice(mem, 0, 0, VW, VH, 0, 0, 0, VH, g_bmp + 54, (BITMAPINFO *)(g_bmp + 14), DIB_RGB_COLORS); - StretchBlt(dc, 10, 10, 640, 480, mem, 0, 0, W, H, SRCCOPY); + SetStretchBltMode(dc, HALFTONE); + StretchBlt(dc, 10, 10, 640, 480, mem, 0, 0, VW, VH, SRCCOPY); SelectObject(mem, old); DeleteObject(bm); DeleteDC(mem); @@ -784,12 +789,15 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmd, int show) { CreateWindowA("BUTTON", "Clear probe", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 820, 200, 100, 32, g_hwnd, (HMENU)1005, inst, NULL); - unsigned sz = 54 + W * H * 3; + unsigned sz = 54 + VW * VH * 3; g_bmp[0] = 'B'; g_bmp[1] = 'M'; g_bmp[2] = (unsigned char)sz; g_bmp[3] = (unsigned char)(sz >> 8); g_bmp[4] = (unsigned char)(sz >> 16); g_bmp[5] = (unsigned char)(sz >> 24); g_bmp[10] = 54; g_bmp[14] = 40; - g_bmp[18] = W; g_bmp[19] = 0; g_bmp[22] = H; g_bmp[23] = 0; + g_bmp[18] = VW & 0xFF; g_bmp[19] = (VW >> 8) & 0xFF; + g_bmp[20] = (VW >> 16) & 0xFF; g_bmp[21] = (VW >> 24) & 0xFF; + g_bmp[22] = VH & 0xFF; g_bmp[23] = (VH >> 8) & 0xFF; + g_bmp[24] = (VH >> 16) & 0xFF; g_bmp[25] = (VH >> 24) & 0xFF; g_bmp[26] = 1; g_bmp[28] = 24; ShowWindow(g_hwnd, SW_SHOW);