Analyze contour-based reference slimming and profile deformation gap; preserve installed 1.0.4

这个提交包含在:
dayu 于 2026-10-21 19:02:24 +08:00 提交
1 父节点 302dc20c0a
当前提交 2a856c669a
7 个文件已更改
+592 -1

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

@@ -71,3 +71,7 @@ REA 直接打开内嵌插件。原包为 arm64/x86_64 通用 Mach-O,Ghidra 无
本机原生观察:Motion中使用同一蓝发视频,原版UI默认多人关闭(单人)、防跳180%;启用检测框并调瘦脸/大眼后,播放时自动更新人脸框,没有先执行离线分析。此观察只覆盖该单人片段,不能替代多人遮挡与所有参数对照。
1.0.4已在Motion实际加载与不同播放头位置观察默认自动跟随输出;用户随后接管Motion/FCP自测,FCP该版自动跟随及显式OSC原生点击尚未完成自动验收。已完成范围与剩余验证见 [验证记录](../验证记录.md)。
## 最大瘦脸与转脸差异(用户复测反馈)
用户反馈跟踪正常,但最大瘦脸与原版不一致,侧脸出现异常变形。补充REA与Metal AIR分析确认,原版瘦脸以76点加中心点输入,执行17组轮廓到鼻部/瞳孔锚点的局部逆向采样;当前复刻采用矩形框水平对称场,两者没有对齐。已检查的GPU瘦脸路径没有显式yaw角度切换或侧脸衰减,不能将局部二维点自适应描述为完整3D姿态识别。详见 [脸型变形与转脸分析](脸型变形与转脸分析.md) 及 [结构报告](slim-pose-report.json)。本轮未修改安装版的变形代码。
+1
查看文件
@@ -0,0 +1 @@
{"artifacts":[{"architecture":"arm64","digest":{"sha256":"f01e8c0b7e2676b2961950b0e48efdd2589af218e4b36de7d39f5d1c76852bfb"},"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_02f734a0d72f1b7b77a3112d1de297b9e99ea43d5377349e6eebb71ab017d650","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>.","Pseudocode is Ghidra decompiler output, not original source and not text-equivalent to Hopper output.","External functions and functions without an analyzable body return null; other decompiler failures remain explicit.","Ghidra Listing reports a terminal call at 0x100016b84 to 0x10005c880 objc_stub::setUsesCPUOnly: (external=false, thunk=false, hasNoReturn=false); fallthrough is excluded by Ghidra's flow model. The reason for the terminal flow is not established by this observation.","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":"\nvoid FUN_100016a3c(ID param_1,ID param_2,ID param_3)\n\n{\n undefined8 uVar1;\n int iVar2;\n ID IVar3;\n ID IVar4;\n ID IVar5;\n \n if (param_1 == 0) {\n return;\n }\n if ((param_2 == 0) || (IVar3 = objc_stub::revision(param_2), IVar3 == 0)) {\n IVar5 = VNDetectFaceLandmarksRequest::supportedRevisions\n ((ID)&_OBJC_CLASS___VNDetectFaceLandmarksRequest);\n IVar3 = 3;\n IVar4 = VNDetectFaceLandmarksRequest::containsIndex_(IVar5,3);\n if ((IVar4 & 1) == 0) {\n IVar5 = VNDetectFaceLandmarksRequest::containsIndex_(IVar5,2);\n IVar3 = 1;\n if ((int)IVar5 != 0) {\n IVar3 = 2;\n }\n }\n }\n else {\n IVar3 = objc_stub::revision(param_2);\n }\n IVar5 = VNDetectFaceLandmarksRequest::supportedRevisions\n ((ID)&_OBJC_CLASS___VNDetectFaceLandmarksRequest);\n IVar5 = VNDetectFaceLandmarksRequest::containsIndex_(IVar5,IVar3);\n if ((IVar5 & 1) == 0) {\n IVar3 = VNDetectFaceLandmarksRequest::supportedRevisions\n ((ID)&_OBJC_CLASS___VNDetectFaceLandmarksRequest);\n IVar5 = VNDetectFaceLandmarksRequest::containsIndex_(IVar3,3);\n if ((int)IVar5 == 0) {\n IVar3 = VNDetectFaceLandmarksRequest::containsIndex_(IVar3,2);\n uVar1 = 1;\n if ((int)IVar3 != 0) {\n uVar1 = 2;\n }\n objc_stub::setRevision_(param_1,uVar1);\n goto LAB_100016b4c;\n }\n objc_stub::setRevision_(param_1,3);\n }\n else {\n objc_stub::setRevision_(param_1,IVar3);\n if (IVar3 != 3) goto LAB_100016b4c;\n }\n IVar3 = VNDetectFaceLandmarksRequest::revision_supportsConstellation_\n ((ID)&_OBJC_CLASS___VNDetectFaceLandmarksRequest,3,2);\n if ((int)IVar3 != 0) {\n objc_stub::setConstellation_(param_1,2);\n }\nLAB_100016b4c:\n IVar3 = objc_stub::count(param_3);\n iVar2 = 0;\n if (IVar3 != 0) {\n IVar3 = objc_stub::setInputFaceObservations_(param_1,param_3);\n iVar2 = (int)IVar3;\n }\n FUN_10003b084();\n if (iVar2 == 0) {\n return;\n }\n objc_stub::setUsesCPUOnly_(param_1,1Line truncated
+385
查看文件
@@ -0,0 +1,385 @@
{
"schema": 1,
"date": "2026-10-21",
"request": "最大瘦脸差异与人物转脸时的变形机制",
"reference": {
"plugin_sha256": "6c5d74bc844a5c5f745718b48ea10ec6635d08aa08a5b6dc6e62653980849f8a",
"arm64_sha256": "f01e8c0b7e2676b2961950b0e48efdd2589af218e4b36de7d39f5d1c76852bfb",
"metallib_sha256": "4e8c77db8be94ae169337a2e646407f69520b63afb248faa653e0e068237aaae"
},
"air_observation": {
"authority": "static Apple metal-objdump AIR disassembly; not observed shader execution",
"command": "xcrun metal-objdump --metallib --disassemble-symbols=faceThinFragmentShader <reference default.metallib>",
"full_output_sha256": "f97705b88f5491057df2117aef457ffca84e11020574cd57742b0670db4484d8",
"full_output_bytes": 274496,
"local_output": "build/pose-analysis/face-thin.air.txt",
"module": "faceThinFragmentShader",
"module_metallib_offset": "0x5d5e",
"function": "mssFaceShapePinCoordinate",
"active_branch": "faceShapeParamsActive selects Shape helper; inactive branch alone selects legacy Thin helper",
"local_pin_triplets": [
[
75,
74,
6
],
[
59,
60,
13
],
[
74,
73,
47
],
[
60,
61,
47
],
[
73,
72,
47
],
[
61,
62,
47
],
[
72,
71,
48
],
[
62,
63,
48
],
[
71,
70,
48
],
[
63,
64,
48
],
[
70,
69,
49
],
[
64,
65,
49
],
[
69,
68,
49
],
[
65,
66,
49
],
[
68,
67,
52
],
[
66,
67,
52
],
[
67,
65,
47
]
],
"triplet_usage": "first=origin, third=anchor; second unused in thin loop",
"thin_cpu_scale": 0.025,
"size_gain": "clamp(280/max(length((P75-P59)*imageSize),2*length((P67-(P74+P60)/2)*imageSize),1),1,2.25)",
"effective_pin_gain": "clamp(UI thin,0,1) * strengthMultiplier * 0.025 * size_gain",
"radius": "length(origin-anchor) in normalized image UV",
"falloff": "clamp(1-length(currentSampleCoordinate-origin)/radius,0,1)",
"inverse_sample_step": "q = clamp(q-(anchor-origin)*effective_pin_gain*falloff,0,1)",
"iteration": "17 sequential updates; not 17 independent offsets evaluated at one original coordinate",
"bounded_negative_findings": [
"No yaw/roll/pitch input to the checked GPU helpers",
"No explicit side-view angle gate in the checked thin branch",
"No explicit eye/nose restore mask in the checked thin branch; each single pin has zero weight at its own anchor"
],
"limits": [
"This is a 2D local landmark deformation, not proof of 3D head pose estimation",
"Static paths are not a capture of the exact runtime branch used in the supplied screenshots",
"Other contour controls include fixed horizontal offsets; not all controls share rotation-equivariant behavior"
]
},
"vision_probe": {
"authority": "Independent public Apple Vision / AVFoundation invocation; no reference app or plug-in executed",
"script": "scripts/inspect-face-geometry.m",
"revision": 3,
"constellation": 76,
"maximum_dimension": 1280,
"coordinate_system": "full-image normalized, y up",
"region_indices": {
"contour": [
59,
60,
61,
62,
63,
64,
65,
66,
67,
68,
69,
70,
71,
72,
73,
74,
75
],
"innerLips": [
42,
40,
43,
45,
41,
44
],
"leftEye": [
0,
4,
5,
1,
3,
2
],
"leftPupil": [
6
],
"nose": [
47,
56,
54,
53,
52,
51,
50,
55
],
"noseCrest": [
46,
47,
48,
49,
58,
57
],
"outerLips": [
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37,
38,
39,
26
],
"rightEye": [
7,
11,
12,
8,
10,
9
],
"rightPupil": [
13
]
},
"frames": [
{
"time": 0,
"face_count": 1,
"all_point_counts": [
76
],
"yaw_radians": [
0
],
"pitch_radians": [
null
]
},
{
"time": 2,
"face_count": 1,
"all_point_counts": [
76
],
"yaw_radians": [
0.7853981852531433
],
"pitch_radians": [
null
]
},
{
"time": 4,
"face_count": 1,
"all_point_counts": [
76
],
"yaw_radians": [
1.5707963705062866
],
"pitch_radians": [
null
]
},
{
"time": 6,
"face_count": 1,
"all_point_counts": [
76
],
"yaw_radians": [
0
],
"pitch_radians": [
null
]
},
{
"time": 8,
"face_count": 1,
"all_point_counts": [
76
],
"yaw_radians": [
0
],
"pitch_radians": [
null
]
},
{
"time": 10,
"face_count": 1,
"all_point_counts": [
76
],
"yaw_radians": [
0
],
"pitch_radians": [
null
]
},
{
"time": 12,
"face_count": 1,
"all_point_counts": [
76
],
"yaw_radians": [
0
],
"pitch_radians": [
null
]
},
{
"time": 14,
"face_count": 1,
"all_point_counts": [
76
],
"yaw_radians": [
0
],
"pitch_radians": [
null
]
},
{
"time": 16,
"face_count": 1,
"all_point_counts": [
76
],
"yaw_radians": [
0
],
"pitch_radians": [
null
]
},
{
"time": 18,
"face_count": 1,
"all_point_counts": [
76
],
"yaw_radians": [
1.5707963705062866
],
"pitch_radians": [
null
]
},
{
"time": 20,
"face_count": 1,
"all_point_counts": [
76
],
"yaw_radians": [
0.7853981852531433
],
"pitch_radians": [
null
]
}
],
"result": "All 11 sampled frames returned one face and 76 points; 4s/18s reported yaw approx pi/2; pitch unavailable",
"privacy": "Source frames, complete landmarks and source path remain ignored under build/pose-analysis/vision and are not included in Git"
},
"reconstruction_baseline": {
"commit": "e03e460a9b3e5ad489aad24095485ba10ceb987d",
"installed_version": "1.0.4 (6)",
"root_causes": [
"thin deformation uses bounding box x/y/size rather than saved contour/nose points",
"horizontal symmetric inverse warp is applied at full strength for any valid box",
"no contour/landmark completeness guard or pose metadata preservation",
"fixed coefficients do not match reference local-pin scaling"
],
"direction_check": "Positive thin samples outward and moves image features inward; the sign is not reversed",
"test_gap": "Existing geometry tests establish pixel changes, extent and persistence, not reference likeness or profile shape preservation"
},
"checklist": {
"why_maximum_slim_differs": "answered: different geometric model, locality and effective strength",
"how_profile_is_handled": "answered within checked active thin path: projected 2D landmarks, local directions/radii; no established explicit yaw gate",
"full_native_visual_equivalence": "unverified; screenshots are not a synchronized isolated-parameter capture",
"product_fix": "not changed by this analysis; installed 1.0.4 retains the reported issue"
},
"host_boundary": "User controls Motion/FCP; no UI interaction, host shutdown, app install or plug-in replacement in this analysis."
}
@@ -0,0 +1,80 @@
# 最大瘦脸与转脸变形分析
2026-10-21。用户已反馈自动跟随正常;本次检查四张实测图指出的脸型差异。当前安装版仍为1.0.4(6),本次没有修改产品变形代码、替换APP或操作Motion/FCP。
## 结论
我们的瘦脸按矩形脸框建立左右对称的水平收缩场;原版主要瘦脸路径根据完整二维面部关键点,顺序执行17组局部位移。两者的作用范围、方向和强度定义均不同。侧脸继续使用正面矩形场,是当前鼻尖、下巴和邻近区域异常变形的直接原因;跟踪框跟对人不能解决这个问题。
原版已检查的瘦脸路径没有读取yaw/roll/pitch后切换侧脸算法,也没有专门的侧脸角度衰减。它每帧获得随姿态变化的二维轮廓与鼻部位置,让局部收缩方向和作用半径随这些点变化。这属于二维关键点自适应,不能表述成已恢复完整3D姿态估计。
## 原版证据链
只读目标与上一轮一致:插件原件SHA-256为`6c5d74bc844a5c5f745718b48ea10ec6635d08aa08a5b6dc6e62653980849f8a`;arm64分析切片为`f01e8c0b7e2676b2961950b0e48efdd2589af218e4b36de7d39f5d1c76852bfb`。本次开始后重新计算原件摘要,未发生变化。Metal库摘要为`4e8c77db8be94ae169337a2e646407f69520b63afb248faa653e0e068237aaae`。
|步骤|直接静态观察|REA Evidence|
|---|---|---|
|检测设置|`0x100016a3c`优先配置Vision revision 3及76点constellation;不支持时存在旧revision回退|`ev_02f734a0d72f1b7b77a3112d1de297b9e99ea43d5377349e6eebb71ab017d650`|
|当前帧检测|`cachedFaceObservations…`发起`VNDetectFaceLandmarksRequest`,另有重试与缓存|`ev_61ea8c6e7117f18e47fe6cf820c9ec0a59865dedaf3d9c09813be0a5bd7a79bc`|
|有效点筛选|`0x1000230cc`要求至少76点;`0x100022c70`转换76点并添加第77个中心点;`0x100017cd8`检查有限数值、空间范围及轮廓分布|`ev_63bb7c8fb07dbe456027f4d4e6be52118338b91ab39dbf9ed6620de9090bc260`、`ev_5f27f174c793c94cd45219d6c405e6b54b96e2650c7462ccc5eeaea14f2604c3`、`ev_1c62b4dab3cf8b661eb5c31b034a0b3cadf9f1f6cb512aace8b3b0838bc74b5c`|
|输入几何|`0x1000274d0`打包77个float2,616字节,转换到输出纹理坐标并检查越界点/轮廓尺度|`ev_8a33a7bb27a1a966f9e4ee26380501f1cb009d7d4665ed93b73bd3f630436556`|
|参数缩放|`0x1000279f4`将瘦脸滑块值乘0.025及渲染强度,写入十float脸型结构的第一项|`ev_449e1fc8883b8afe27240f672f98f300d4132ac4994db5e1590a4f7ca4e21278`|
|GPU连接|`FxMTLDeviceCacheItem::initWithDevice:pixelFormat:`将`faceThinFragmentShader`接入瘦脸pipeline;`0x100027d38/0x10002807c`上传点阵、数量及脸型参数到该pipeline|`ev_1520c2d83845004994f2be940c8c5ea3f4a62daf2d8e3e06278f381ffca1a010`、`ev_dc4487de4891c4a663dab370f2e207799bec7e2ad305a98a1ed579e5de6ef263`、`ev_7d2cc9ff3bf8ed3ceea2d91ffb7c2c47ad45337a792f101c1d989272f37023b1`|
完整REA记录见[证据包](rea-slim-pose.json)。伪代码是Ghidra生成的静态分析结果,寄存器参数与CGRect返回类型部分恢复不完整;此处没有将其当作原始源码或实际执行记录。
## Metal瘦脸计算
用本机Apple工具对只读`default.metallib`输出AIR中间代码。它不是字符串猜测:`faceThinFragmentShader`在`faceShapeParamsActive`为真时调用`mssFaceShapePinCoordinate`,只有inactive分支调用旧`mssFaceThinPinCoordinate`。开启瘦脸后第一项脸型参数非零,因此可建立Shape分支的静态路径。原版截图当时具体走哪条宿主渲染分支没有运行捕获。
Shape helper中的瘦脸部分有17组点操作,每组实际读取origin和anchor,三元表中的第二项在这一循环中未使用。origin均为59–75号脸轮廓点,anchor为6、13、47、48、49、52号点;独立Vision区域对应检查确认,6/13是左右瞳孔,47/48/49/52位于鼻部或鼻梁。不能把所有锚点都叫下颌点或鼻部点。
单组逆向采样的结构为:
```text
r = distance(origin, anchor)
w = clamp(1 - distance(q, origin) / r, 0, 1)
q = clamp(q - (anchor - origin) * effectiveStrength * w, 0, 1)
```
17组顺序更新同一个采样坐标q,不能将其视为一次整脸等比缩放。这里的距离在整幅纹理归一化UV中计算;它并非完整按像素各向同性的旋转不变模型。单组在其anchor处权重为零,但其他组仍可能影响该处,不能说整个鼻部或所有五官被锁死。
有效强度另含尺寸补偿:`k=clamp(280/max(轮廓两端像素距离, 下巴到上侧轮廓中点的像素距离×2, 1), 1, 2.25)`,单pin系数约为`滑块×0.025×k×渲染强度`。它对小脸加大效果,**不是侧脸质量衰减**。最大单组系数0.05625也不是最终整脸缩窄百分比。
AIR结构摘要、全部17组索引、尺寸公式、输出摘要和复现命令在[机器可读报告](slim-pose-report.json)。完整AIR文本留在本地`build/pose-analysis/face-thin.air.txt`,未把原版metallib或原版程序加入产品或仓库。
## 为什么转脸时更自然
已证实的机制是:轮廓投影变化→局部origin、anchor间向量变化→各处的收缩方向、半径随之变化;并非先证明“人物转头90度”,再换一套固定对称瘦脸公式。另有上一轮已分析的时间点阵稳定、有效性检查和失检参考保持,这些可减少跳动;它们与瘦脸位移模型是两个层面。
本次检查的GPU瘦脸分支没有yaw/roll/pitch输入、左右脸对称比门限、侧脸强度开关或末端五官恢复蒙版。原生函数名检索也未发现`objc_stub::yaw/roll/pitch` getter;这是有界负面结果,不能据此宣称整个APP绝无任何姿态相关逻辑。其他脸型项仍有固定画面X方向位移,不能把瘦脸的局部点特性扩张成所有控制都具有相同的转头适应性。
## 同一素材的独立验证
运行独立的[离线检测工具](../../scripts/inspect-face-geometry.m),从用户视频在0、2、4、6、8、10、12、14、16、18、20秒各取一帧。请求使用Vision revision 3 / 76点,图像最长边1280。全部11帧均返回1张脸、76点;4秒和18秒的Vision yaw约为π/2,pitch均不可用。
这证明明显侧脸仍可以获得完整二维点阵,也校验了59–75号点确为轮廓、6/13号为瞳孔、47/48/49/52号为鼻部相关点。yaw为检测器返回值,不能当作实测头部角度。工具没有调用或执行原版滤镜,不是原版宿主像素等价测试;本机Vision版本、检测尺寸与宿主缓存可能使点坐标与原版当帧不同。Vision本身报告yaw,也不能说明原版瘦脸实际使用该属性。
用户素材、截图、导出的帧及完整人脸坐标均只保留在被忽略的本地build目录,没有上传远程仓库。入库的报告仅含汇总计数、区域索引和角度信息。
工具复现:
```sh
xcrun clang -fobjc-arc -Wall -Wextra \
-framework Foundation -framework AVFoundation -framework Vision \
-framework ImageIO -framework CoreGraphics -framework CoreMedia \
scripts/inspect-face-geometry.m -o /tmp/inspect-face-geometry
/tmp/inspect-face-geometry '<用户视频路径>' '<本地输出目录>/geometry.json' 0 2 4 6 8 10 12 14 16 18 20
```
## 当前实现差异与修复落点
`PGFaceProcessor.m:290–305`的瘦脸只用框中心/宽高,按画面水平轴同时处理左右;`warpImage:geometry:settings:extent:`额外读取的双眼仅用于大眼。已有`contour/nose/lips`未参与瘦脸,缺失轮廓时仍会按有效框足额变形。pose信息也没有贯通提取、净化、实时稳定和离线存储。
正值采样向外、视觉向内收的符号本身正确;简单反号无法修复。当前公式在框高35%处的测试位置将脸框两边缘间距缩小约20.2%,作用域还可延伸到框外。这个局部计算是当前公式的解析检查,不代表与原版截图的实测宽度差。
后续修正应让瘦脸直接使用稳定后的轮廓和鼻部控制点,按局部方向/半径位移,并校准尺寸补偿和滑块映射。若另加显式姿态或质量衰减,应标记为本项目设计,不能当作已证实的原版算法。完整点阵必须贯通实时与持久化路径,不能只在检测入口新增字段后被后续步骤丢弃。
现有测试只能证明各几何参数改变像素、范围保持和跟踪恢复一致,尚未验证最大瘦脸外观或侧脸形状。修复验证至少应包括同帧正面/半侧/全侧、向左右转脸、歪头、轮廓缺失、正逆播放、100%瘦脸与多个脸型项叠加,和原版在隔离相同其他参数后的对照。
本轮完成机制分析及独立检测,未宣称已修复安装版,也未将用户的“跟踪没问题”扩张为其他未覆盖的原生验收通过。