本项目精简自 https://github.com/InterDigitalInc/FeatureStyleEncoder 原项目代码逻辑比较复杂,不便于我们理解论文
本项目对原始 FeatureStyleEncoder 代码库做了大幅精简,只保留两个核心推理功能:
| 功能 | 入口 | 说明 |
|---|---|---|
| 人脸重建 (inversion) | python infer.py reconstruct |
输入一张人脸图像,编码后重建 |
| 人脸编辑 (editing) | python infer.py edit |
输入图像 + 编辑方向,输出编辑后图像 |
FeatureStyleEncoder-main/
├── arcface_iresnet.py # ArcFace IResNet-50 backbone
├── fs_encoder.py # FS Encoder (特征风格编码器)
├── infer.py # 推理入口 (reconstruct + edit)
|── stylegan2/
│ ├── model.py # StyleGAN2 Generator
│ └── op/ # CUDA 算子 (upfirdn2d, fused_act)
├── edit_boundary/
│ ├── Eyeglasses_boundary.npy # 戴眼镜方向
│ ├── Heavy_Makeup_boundary.npy # 浓妆方向
│ └── Smiling_boundary.npy # 微笑方向
├── pretrained_models/ # 预训练权重(需自行下载)
│ ├── backbone.pth # ArcFace backbone
│ ├── 143_enc.pth # FS Encoder 权重
│ └── psp_ffhq_encode.pt # StyleGAN2 / PSP 权重
└── ARCHITECTURE.md # 本文档
文件:fs_encoder.py
输入:256×256 人脸图像 (1, 3, 256, 256)
│
┌────▼────┐
│ conv │ IResNet-50 前3层 (conv1 + bn1 + prelu)
└────┬────┘
│
┌────▼────┐
│ block_1 │ IResNet layer1: (1, 64, 128, 128)
└────┬────┘ → avg_pool(3×3) → feature_1 (1, 64, 3, 3)
│
┌────▼────┐
│ block_2 │ IResNet layer2: (1, 128, 64, 64)
└────┬────┘ → avg_pool(3×3) → feature_2 (1, 128, 3, 3)
│
┌────▼────┐
│ block_3 │ IResNet layer3: (1, 256, 32, 32)
└────┬────┘ → content_layer → fea (1, 512, H, W) ← 空间内容特征
│ → avg_pool(3×3) → feature_3 (1, 256, 3, 3)
│
┌────▼────┐
│ block_4 │ IResNet layer4: (1, 512, 16, 16)
└────┬────┘ → avg_pool(3×3) → feature_4 (1, 512, 3, 3)
│
concat([feature_1, feature_2, feature_3, feature_4])
reshape → (1, 960*9)
│
18× Linear(960*9, 512)
│
stack → w_delta (1, 18, 512) ← W+ 潜码增量
输出:w_delta (1, 18, 512), fea (1, 512, H, W)
关键点:
stride=(2,2)控制content_layer最后一层卷积的步长- 最终 W+ 潜码 =
w_delta + dlatent_avg(加上 StyleGAN 的平均潜码)
文件:arcface_iresnet.py
标准 IResNet-50(InsightFace 版本),用作 fs_encoder_v2 的特征提取骨干网络。
层结构:[3, 4, 14, 3](IBasicBlock)
文件:pixel2style2pixel/models/stylegan2/model.py
输入:W+ 潜码 (1, 18, 512) + 可选的 features_in 列表
解码器共 18 层 StyledConv(对应 4×4 到 1024×1024)
层索引 5 的空间分辨率:32×32(对应编码器 content feature 的分辨率)
features_in = [None, None, None, None, None, fea, None, ..., None]
0 1 2 3 4 5 6 17
feature_scale=1.0 时,第 5 层输出 = fea(完全替换)
输入图像 (任意尺寸)
│
▼
Resize → 1024×1024, Normalize[-1,1] img (1,3,1024,1024)
│
▼
Downscale ×2 (bilinear) img_256 (1,3,256,256)
│
▼
fs_encoder_v2(img_256)
├─── w_delta (1,18,512)
└─── fea (1,512,H,W)
│
▼
w = w_delta + dlatent_avg W+ 潜码 (1,18,512)
│
▼
features_in = [None]*5 + [fea] + [None]*12
│
▼
StyleGAN2 Generator([w], features_in=features_in, feature_scale=1.0)
│
▼
x_recon (1,3,1024,1024) → Clip[0,1] → 保存
输入图像
│
├─── [同重建流程] ───► w (1,18,512), fea (1,512,H,W)
│
▼
boundary = np.load("xxx_boundary.npy") shape: (1, 9216)=(1, 18*512)
w_edit = (w.reshape(1,-1) + strength * boundary).reshape(1,18,512)
│
├──► StyleGAN2([w], return_features=True) → feats_orig
└──► StyleGAN2([w_edit], return_features=True) → feats_edit
│
▼
delta = feats_edit[5] - feats_orig[5] 层5的特征差
fea_new = fea + delta 补偿编码器内容特征
│
▼
features_in = [None]*5 + [fea_new] + [None]*12
│
▼
StyleGAN2([w_edit], features_in=features_in, feature_scale=1.0)
│
▼
x_edit → Clip[0,1] → 保存
为什么需要特征补偿 (delta)?
编码器输出的 fea 是从原始 W 潜码对应的图像空间提取的内容特征。
当我们将 W 潜码移动到 w_edit 后,StyleGAN 解码器在第 5 层会生成不同的中间特征。
直接注入原始 fea 会造成 W 空间与特征空间的不一致。
通过加上 delta = feats_edit[5] - feats_orig[5],将内容特征迁移到编辑后的特征空间,
从而在保持身份信息的同时实现属性编辑。
python infer.py reconstruct \
--input test/00020.jpg \
--output output/recon.jpg \
--arcface_path pretrained_models/backbone.pth \
--enc_path pretrained_models/143_enc.pth \
--stylegan_path pixel2style2pixel/pretrained_models/psp_ffhq_encode.pt# 微笑
python infer.py edit \
--input test/00020.jpg \
--boundary boundaries_ours/Smiling_boundary.npy \
--output output/edit_smiling.jpg \
--strength 5.0
# 戴眼镜
python infer.py edit \
--input test/00020.jpg \
--boundary boundaries_ours/Eyeglasses_boundary.npy \
--output output/edit_glasses.jpg \
--strength 5.0
# 浓妆(反向编辑用负数 strength)
python infer.py edit \
--input test/00020.jpg \
--boundary boundaries_ours/Heavy_Makeup_boundary.npy \
--output output/edit_makeup.jpg \
--strength -5.0torch >= 1.8
torchvision
numpy
Pillow