Fix V-face deformation with lower-jaw landmark pins; pass regression

这个提交包含在:
dayu 于 2026-10-22 16:26:42 +08:00 提交
1 父节点 58f09e5756
当前提交 7fa3298e02
19 个文件已更改
+566 -36

没有与搜索条件相匹配的文件

+42
查看文件
@@ -0,0 +1,42 @@
# V 脸下颌区域修复
2026-10-22,针对用户报告「V 脸作用到眼睛,应作用于下颌」。目标为本机 MSE18V2 2.4.0(20260724150000),产品修复版本为 1.3.1(16)。
## 原因与修改
1.3.0 仍使用脸框上的宽范围高斯带及纵向位移近似 V 脸。它不依赖真实下颌轮廓,在真实人像上能测到眼区变化。不是面板标签或参数 ID 接错;本次只替换 V 脸几何场。
新版目标的 AIR 按 `69→39、65→35、70→26、64→34` 顺序执行四组局部逆向采样。起点都是下颌轮廓,锚点都是外唇。每步使用前一步更新后的 q:
```text
r = length(anchor − origin)
if r > 0.0001:
weight = clamp(1 − length(q − origin) / r, 0, 1)
q = clamp(q − (anchor − origin) × ratio × weight, 0, 1)
```
CPU 函数 `0x10002fb74` 从参数 `+0x90` 读取 V 值,乘位于 `0x100085ea0` 的 double `0.065` 和公共强度,再转 float 写入 shape 结构的 `+8`。新版三个 shader helper 都没有旧版的 `280/尺寸` 增益;本项目 V 使用 `clamp(faceV,0,1)×0.065`,整体混合沿用现有产品链。公共强度各调用方的完整来源尚未追踪,不将整体混合宣称为原版该参数的等价重建。
生产实现复用局部 pin 核心,四步严格顺序更新,与图像/四张支持蒙版共享同一坐标场;完整 76 点缺失或不合法时安全直通。Core Image 坐标和关键点一起转换到 Y 向下的全图 UV,结束后一起转回,支持非零图像原点、横竖画幅和歪头。
## 静态证据与复现
原始 APP、metallib、完整 AIR 与用户帧仅留本机 build,不是构建依赖。源码没有嵌入目标二进制、私有模型或授权逻辑。
- 原 APP 嵌套 Mach-O(fat)SHA-256:`577303e0de72e07feb576c882e82382b3f056b023a7e987d990ffb37f6b16109`。
- arm64 切片 SHA-256:`afc71d0d21fd3c1ca95ca745ddafcd729e36ad95f17d8638009f2e45d9b7e8df`。
- metallib SHA-256:`856849c45cea2c8f083eeba06a9e318c450f4a56694b438d1a5ba8ef752a0ee0`。
- `xcrun metal-objdump --metallib --disassemble` 输出 SHA-256:`1d5bd8d5a3917afbb3b4fe1c40b44480760614102685ac826e84ad2e30a450f9`;V 分支 3364–3431 行,四组点索引在 3374、3376、3378、3380 行。
- 精简 AIR 摘录:[vface-air.txt](vface-air.txt);CPU 字段/系数与分析条件:[vface-findings.json](vface-findings.json);REA CPU 汇编和常量证据:[rea-vface-evidence.json](rea-vface-evidence.json)。REA 操作为只读,未运行原 APP 或原 shader。
## 验证
修改前隔离 V 测试复现:NASA 人像瞳孔 6 的位移约 0.031493 像素;宿主物理行序测试四种原点组合的 V 输出与独立 upright 参考均不符。原宿主输出两眼区像素变化量分别为 12824、19304,下颌为 195206。
修改后瞳孔检查通过;宿主四种原点组合的 V 眼区像素变化量均为 0,下颌为 39246,独立 upright 对照最大差 1/255、均差 0.000124/255。数值场覆盖 4 种画幅、正侧脸和歪头、半强度、小脸不带旧增益、顺序组合、缺点/坏点以及四张蒙版同步。独立的真实解剖位置检查要求下颌有位移且两瞳孔位移小于 0.002 像素,避免只用复制实现的数值公式验收。
完整自动回归:1901 项通过(57 Beauty、116 Face、1033 几何、370 Host ASan、98 OSC ASan、227 Inspector ASan)。真实用户帧专项另记交付验证;与基础用例重叠,不相加。正式安装和原生宿主 V 脸视觉验收状态见 [验证记录](../验证记录.md)。
## 范围
V 脸现在加入已恢复的五项局部控制。瘦脸、下巴、短脸、下颌骨沿用此前版本的尺寸增益;未将新版目标的其他差异混入本次修复。大眼、窄脸、颧骨和小头仍为近似,整条混合控制链的数学、顺序和最终像素尚未全部对齐。静态重建、离屏/接口检查不等于原版 shader 的运行时逐像素比较,也不等于 Motion/FCP 原生视觉验收。
+1
查看文件
@@ -0,0 +1 @@
{"artifacts":[{"architecture":"arm64","digest":{"sha256":"afc71d0d21fd3c1ca95ca745ddafcd729e36ad95f17d8638009f2e45d9b7e8df"},"format":"mach-o"}],"captures":[],"environments":[],"providers":[{"id":"ghidra","name":"Ghidra","version":"12.1.4"}],"records":[{"analysis_profile":{"digest":"20565088524a2d0359b9b96f46ac48e679d774c7b0092c56abc06de8fdbec11c","parameters":{"analyzer_preset":"ghidra-default","annotation_policy":"atomic-function-entry-metadata-v1","architecture":"arm64","available_architectures":["arm64"],"compiler_spec_id":"auto-default","decompiler_jump_loads":true,"function_body_evidence":"complete-inclusive-ranges-v1","function_boundary_observations":"ghidra-terminal-call-limitations-v1","function_references":"complete-body-and-entry-reference-manager-v2","import_mode":"ephemeral-source-immutable","instruction_flow_evidence":"decoded-return-pcode-v1","jump_table_evidence":"typed-case-default-blocks-v1","language_id":"auto-from-header","load_image_observations":"source-mappings-entry-context-v2","loader":"auto-from-header","location_resolution":"explicit-address-exact-entry-symbol-first-v3","no_return_repair":"returning-imports-decoded-return-v2","process_launch":"inspected-jvm-launch-support-v1","string_inventory_evidence":"defined-data-coverage-v1","target_format":"mach-o","target_kind":"executable"},"provider":{"id":"ghidra","name":"Ghidra","version":"12.1.4"}},"authority":"shipped-artifact","confidence":"observed","environment":null,"evidence_id":"ev_32b6ee46d4fb5eab563883ba73e35a458d5da21229b8977fddcb4df656517152","evidence_links":[],"limitations":["The session serves operations only after Ghidra reports default auto-analysis complete; incomplete analysis does not expose partial results.","The imported Program and temporary project are ephemeral and deleted on close. Annotation operations edit session database metadata; original executable bytes are never written.","Cancelling an active Ghidra request terminates its ephemeral database and discards session annotations. The selected target remains open; the next provider query imports the original artifact again.","Default-space addresses use lowercase 0x-prefixed hexadecimal; other address spaces use <percent-encoded-space>:0x<hex>.","Bytes reflect Ghidra's initialized memory, including loader fixups. Reads stop at an unmapped or uninitialized byte and report completeness explicitly.","macOS sessions require a matching executable Ghidra native decompiler; REA checks for it but does not build native components or change Gatekeeper quarantine state."],"locations":[],"normalized_result":{"address":"0x100085ea0","bytes_hex":"a4703d0ad7a3b03f9a9999999999a93f","complete":true,"requested_bytes":16,"returned_bytes":16},"operation":"read_bytes","parameters":{"address":"0x100085ea0","length":16},"predicate_type":"rea.analysis","provider":{"id":"ghidra","name":"Ghidra","version":"12.1.4"},"raw_result":{"address":"0x100085ea0","bytes_hex":"a4703d0ad7a3b03f9a9999999999a93f","complete":true,"requested_bytes":16,"returned_bytes":16},"subject":{"architecture":"arm64","digest":{"sha256":"afc71d0d21fd3c1ca95ca745ddafcd729e36ad95f17d8638009f2e45d9b7e8df"},"format":"mach-o","local_path":"/Users/dayu/Documents/PaintFCP/build/body-analysis/MSE18V2-arm64","name":"MSE18V2-arm64"}},{"analysis_profile":{"digest":"20565088524a2d0359b9b96f46ac48e679d774c7b0092c56abc06de8fdbec11c","parameters":{"analyzer_preset":"ghidra-default","annotation_policy":"atomic-function-entry-metadata-v1","architecture":"arm64","available_architectures":["arm64"],"compiler_spec_id":"auto-default","decompiler_jump_loads":true,"function_body_evidence":"complete-inclusive-ranges-v1","function_boundary_observations":"ghidra-terminal-call-limitations-v1","function_references":"complete-body-and-entry-reference-manager-v2","import_mode":"ephemeral-source-immutable","instruction_flow_evidence":"decoded-return-pcode-v1","jump_table_evidence":"typed-case-default-blocks-v1","language_id":"auto-from-header","load_image_observations":"source-mappings-entry-context-v2","loader":"auto-from-header","location_resolution":"explicit-address-exact-entry-symbol-first-v3","no_return_repair":"returning-imports-decoded-return-v2","process_launch":"inspected-jvm-launch-support-v1","string_inventory_evidence":"defined-data-coverage-v1","target_format":"mach-o","target_kind":"executable"},"provider":{"id":"ghidra","name":"Ghidra","version":"12.1.4"}},"authority":"shipped-artifact","confidence":"observed","environment":null,"evidence_id":"ev_6a88310573f4dfd2f23feef7d12c3f58eba366db1717861770656d55375e07a1","evidence_links":[],"limitations":["The session serves operations only after Ghidra reports default auto-analysis complete; incomplete analysis does not expose partial results.","The imported Program and temporary project are ephemeral and deleted on close. Annotation operations edit session database metadata; original executable bytes are never written.","Cancelling an active Ghidra request terminates its ephemeral dataLine truncated
+71
查看文件
@@ -0,0 +1,71 @@
MSE18V2 2.4.0 AIR excerpt; original complete-text line numbers.
SHA256: 1d5bd8d5a3917afbb3b4fe1c40b44480760614102685ac826e84ad2e30a450f9
3364: 158: ; preds = %109, %85
3365: %159 = phi <2 x float> [ %155, %109 ], [ %86, %85 ]
3366: %160 = tail call fast float @air.fast_fabs.f32(float %5) #2
3367: %161 = fcmp fast ogt float %160, 0x3EB0C6F7A0000000
3368: br i1 %161, label %162, label %209
3369:
3370: 162: ; preds = %158
3371: %163 = bitcast [4 x <2 x i32>]* %17 to i8*
3372: call void @llvm.lifetime.start.p0i8(i64 32, i8* nonnull %163) #10
3373: %164 = getelementptr inbounds [4 x <2 x i32>], [4 x <2 x i32>]* %17, i64 0, i64 0
3374: store <2 x i32> <i32 69, i32 39>, <2 x i32>* %164, align 8
3375: %165 = getelementptr inbounds [4 x <2 x i32>], [4 x <2 x i32>]* %17, i64 0, i64 1
3376: store <2 x i32> <i32 65, i32 35>, <2 x i32>* %165, align 8
3377: %166 = getelementptr inbounds [4 x <2 x i32>], [4 x <2 x i32>]* %17, i64 0, i64 2
3378: store <2 x i32> <i32 70, i32 26>, <2 x i32>* %166, align 8
3379: %167 = getelementptr inbounds [4 x <2 x i32>], [4 x <2 x i32>]* %17, i64 0, i64 3
3380: store <2 x i32> <i32 64, i32 34>, <2 x i32>* %167, align 8
3381: %168 = insertelement <2 x float> undef, float %5, i64 0
3382: %169 = shufflevector <2 x float> %168, <2 x float> undef, <2 x i32> zeroinitializer
3383: br label %171
3384:
3385: 170: ; preds = %205
3386: call void @llvm.lifetime.end.p0i8(i64 32, i8* nonnull %163) #10
3387: br label %209
3388:
3389: 171: ; preds = %205, %162
3390: %172 = phi <2 x float> [ %159, %162 ], [ %206, %205 ]
3391: %173 = phi i32 [ 0, %162 ], [ %207, %205 ]
3392: %174 = zext i32 %173 to i64
3393: %175 = getelementptr inbounds [4 x <2 x i32>], [4 x <2 x i32>]* %17, i64 0, i64 %174
3394: %176 = load <2 x i32>, <2 x i32>* %175, align 8
3395: %177 = extractelement <2 x i32> %176, i64 0
3396: %178 = add nsw i32 %177, %2
3397: %179 = sext i32 %178 to i64
3398: %180 = getelementptr inbounds %struct.MSSFaceThinPoint, %struct.MSSFaceThinPoint addrspace(2)* %1, i64 %179, i32 0
3399: %181 = load <2 x float>, <2 x float> addrspace(2)* %180, align 8, !tbaa !74
3400: %182 = extractelement <2 x i32> %176, i64 1
3401: %183 = add nsw i32 %182, %2
3402: %184 = sext i32 %183 to i64
3403: %185 = getelementptr inbounds %struct.MSSFaceThinPoint, %struct.MSSFaceThinPoint addrspace(2)* %1, i64 %184, i32 0
3404: %186 = load <2 x float>, <2 x float> addrspace(2)* %185, align 8, !tbaa !74
3405: %187 = fsub fast <2 x float> %181, %186
3406: %188 = tail call fast float @air.dot.v2f32(<2 x float> %187, <2 x float> %187) #2
3407: %189 = tail call fast float @air.fast_sqrt.f32(float %188) #2
3408: %190 = fcmp fast ugt float %189, 0x3F1A36E2E0000000
3409: br i1 %190, label %191, label %205
3410:
3411: 191: ; preds = %171
3412: %192 = fsub fast <2 x float> %186, %181
3413: %193 = fsub fast <2 x float> %172, %181
3414: %194 = tail call fast float @air.dot.v2f32(<2 x float> %193, <2 x float> %193) #2
3415: %195 = tail call fast float @air.fast_sqrt.f32(float %194) #2
3416: %196 = fdiv fast float %195, %189
3417: %197 = fsub fast float 1.000000e+00, %196
3418: %198 = tail call fast float @air.fast_clamp.f32(float %197, float 0.000000e+00, float 1.000000e+00) #2
3419: %199 = insertelement <2 x float> undef, float %198, i64 0
3420: %200 = shufflevector <2 x float> %199, <2 x float> undef, <2 x i32> zeroinitializer
3421: %201 = fmul fast <2 x float> %192, %169
3422: %202 = fmul fast <2 x float> %201, %200
3423: %203 = fsub fast <2 x float> %172, %202
3424: %204 = tail call fast <2 x float> @air.fast_clamp.v2f32(<2 x float> %203, <2 x float> zeroinitializer, <2 x float> <float 1.000000e+00, float 1.000000e+00>) #2
3425: br label %205
3426:
3427: 205: ; preds = %191, %171
3428: %206 = phi <2 x float> [ %204, %191 ], [ %172, %171 ]
3429: %207 = add nuw nsw i32 %173, 1
3430: %208 = icmp eq i32 %207, 4
3431: br i1 %208, label %170, label %171, !llvm.loop !89
+258
查看文件
@@ -0,0 +1,258 @@
{
"authority": "Static shipped Apple AIR and Mach-O byte/assembly inspection; target app and shaders not executed",
"sources": {
"metallib": "/Users/dayu/Downloads/MSE18V2.app/Contents/PlugIns/MSE18V2.pluginkit/Contents/Resources/default.metallib",
"metallib_sha256": "856849c45cea2c8f083eeba06a9e318c450f4a56694b438d1a5ba8ef752a0ee0",
"command": [
"xcrun",
"metal-objdump",
"--metallib",
"--disassemble",
"/Users/dayu/Downloads/MSE18V2.app/Contents/PlugIns/MSE18V2.pluginkit/Contents/Resources/default.metallib"
],
"air_sha256": "1d5bd8d5a3917afbb3b4fe1c40b44480760614102685ac826e84ad2e30a450f9",
"binary": "/Users/dayu/Downloads/MSE18V2.app/Contents/PlugIns/MSE18V2.pluginkit/Contents/MacOS/MSE18V2",
"binary_arm64_sha256": "afc71d0d21fd3c1ca95ca745ddafcd729e36ad95f17d8638009f2e45d9b7e8df",
"double065_file_offsets_in_arm64_slice": [
548512
]
},
"new_version_vface": {
"struct_field": "MSSFaceShapeParams.vlian offset8; helper parameter5",
"render_params_field": "faceV double offset0x90",
"cpu_function": "0x10002fb74",
"cpu_coefficient_address": "0x100085ea0",
"cpu_coefficient": 0.065,
"cpu_ratio": "float(clamp(faceV,0,1)*0.065*double(float(clamp(commonStrength,0,1))))",
"pin_order": [
[
69,
39
],
[
65,
35
],
[
70,
26
],
[
64,
34
]
],
"pin_fields": [
"origin",
"direction/radius anchor"
],
"coordinate_system": "full-image normalized UV; q and points must share the same vertical axis",
"radius": "length(Panchor-Porigin), unscaled UV metric; skip radius<=0.0001",
"weight": "clamp(1-length(q-Porigin)/radius,0,1)",
"step": "q=clamp(q-(Panchor-Porigin)*ratio*weight,0,1)",
"sequential": true,
"ratio_threshold": 1e-06,
"shader_lines": {
"helper": 3159,
"vface_gate": 3364,
"pins": [
3374,
3376,
3378,
3380
],
"loop": 3389,
"radius": 3405,
"offset": 3412,
"clamp": 3424,
"count": 3430,
"metadata": 4132
},
"order": "thin17 -> narrow16 -> V4 -> jaw6 -> cheek6 -> chin3 -> short -> smallHead -> bigEye",
"helper_copies": [
{
"start_line": 3159,
"v_pins": [
[
69,
39
],
[
65,
35
],
[
70,
26
],
[
64,
34
]
],
"has_old_pixel_gain_constant": false
},
{
"start_line": 6840,
"v_pins": [
[
69,
39
],
[
65,
35
],
[
70,
26
],
[
64,
34
]
],
"has_old_pixel_gain_constant": false
},
{
"start_line": 8682,
"v_pins": [
[
69,
39
],
[
65,
35
],
[
70,
26
],
[
64,
34
]
],
"has_old_pixel_gain_constant": false
}
]
},
"version_difference": {
"old_air": "build/pose-analysis/face-thin.air.txt",
"old_gain_lines": [
592,
633
],
"old_vface_lines": [
830,
897
],
"old_cpu_coefficient_address": "0x10005f9a0",
"coefficient_unchanged": true,
"old_extra_gain": "clamp(280/max(length((P75-P59)*pixelSize),2*length((P67-(P74+P60)/2)*pixelSize),1),1,2.25)",
"new_extra_gain": "Absent from all3 new shader helper copies and the complete new AIR text; CPU helper is plain clamped-control*.065*commonStrength",
"anatomy": "Origin indices64,65,69,70 are lower contour; anchor indices26,34,35,39 are outer lips; eyes0..13 are not controls."
},
"eye_support_probe": {
"authority": "Derived from existing local public-API Vision probe; no new image coordinates or original plug-in execution",
"settings": {
"vface": 1,
"common_strength": 1,
"coefficient": 0.065,
"pins": [
[
69,
39
],
[
65,
35
],
[
70,
26
],
[
64,
34
]
]
},
"samples": [
{
"seconds": 0,
"minimum_eye_distance_over_pin_radius": 2.86112178668839,
"max_eye_weight": 0,
"max_sequential_eye_displacement_pixels": 0
},
{
"seconds": 2,
"minimum_eye_distance_over_pin_radius": 2.7386373955689196,
"max_eye_weight": 0,
"max_sequential_eye_displacement_pixels": 0
},
{
"seconds": 4,
"minimum_eye_distance_over_pin_radius": 2.0723834235969316,
"max_eye_weight": 0,
"max_sequential_eye_displacement_pixels": 0
},
{
"seconds": 6,
"minimum_eye_distance_over_pin_radius": 2.7994186406360964,
"max_eye_weight": 0,
"max_sequential_eye_displacement_pixels": 0
},
{
"seconds": 8,
"minimum_eye_distance_over_pin_radius": 2.720538093620307,
"max_eye_weight": 0,
"max_sequential_eye_displacement_pixels": 0
},
{
"seconds": 10,
"minimum_eye_distance_over_pin_radius": 2.7459899006401245,
"max_eye_weight": 0,
"max_sequential_eye_displacement_pixels": 0
},
{
"seconds": 12,
"minimum_eye_distance_over_pin_radius": 2.8522571566241655,
"max_eye_weight": 0,
"max_sequential_eye_displacement_pixels": 0
},
{
"seconds": 14,
"minimum_eye_distance_over_pin_radius": 2.7849986024852744,
"max_eye_weight": 0,
"max_sequential_eye_displacement_pixels": 0
},
{
"seconds": 16,
"minimum_eye_distance_over_pin_radius": 2.86097776991491,
"max_eye_weight": 0,
"max_sequential_eye_displacement_pixels": 0
},
{
"seconds": 18,
"minimum_eye_distance_over_pin_radius": 2.4994391012873294,
"max_eye_weight": 0,
"max_sequential_eye_displacement_pixels": 0
},
{
"seconds": 20,
"minimum_eye_distance_over_pin_radius": 2.3076941598447127,
"max_eye_weight": 0,
"max_sequential_eye_displacement_pixels": 0
}
]
},
"limits": [
"No reference shader runtime or pixel comparison performed.",
"The commonStrength caller-source semantics are not fully traced; formula reports the helper input.",
"CPU prechecks/license/module gates are observed but are outside the independently authored geometry reconstruction.",
"Eye support results apply to the11 existing saved Vision samples, not every possible pose.",
"No product sources, app build, host session or Git were changed."
]
}