修复 demo3 颗粒感:显示层改用官方同款 320x240 2x 平滑输出

- 根因:demo3 之前渲染 160x120 原始灰度并最近邻放大,官方输出是
  2x 升采样后的 320x240(2-tap 平滑本身抑噪)
- 已排除:0x230(FUN_1800175a0 帧差调整)、0x41fe8 隔行平滑(=0)、
  temporal filter 0x41fe0(=1) 均未启用,f20 mode=1 直通
- 修复:render_bmp 改用 g_gray320 上色,BMP/窗口改 320x240,
  StretchBlt 加 HALFTONE,BMP 头支持 >255 尺寸
- 验证:同场景与官方 pair_000.rgb 直方图相关性 0.9796,颜色数 227=官方
This commit is contained in:
ZXCLI
2026-08-14 16:16:10 +08:00
parent 0bfb926892
commit 03ed936a5f
4 changed files with 50 additions and 38 deletions
+4 -3
View File
@@ -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
+26 -18
View File
@@ -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);