Add ffhq-uv: FFHQ-UV RGB fitting scripts (native env, like p3dm) + stevie example outputs
Browse files- .gitattributes +3 -0
- ffhq-uv/README.md +77 -0
- ffhq-uv/ffhq_uv_install.sh +151 -0
- ffhq-uv/ffhq_uv_run.sh +101 -0
- outputs/ffhq-uv/stevie/stevie.mtl +5 -0
- outputs/ffhq-uv/stevie/stevie_exp.obj +0 -0
- outputs/ffhq-uv/stevie/stevie_id.obj +0 -0
- outputs/ffhq-uv/stevie/stevie_input_vis.png +3 -0
- outputs/ffhq-uv/stevie/stevie_uv.png +3 -0
- outputs/ffhq-uv/stevie/stevie_vis.png +3 -0
.gitattributes
CHANGED
|
@@ -38,3 +38,6 @@ outputs/scail2/scail2-test_00002_.mp4 filter=lfs diff=lfs merge=lfs -text
|
|
| 38 |
outputs/qwen-edit-v4/mode1-4step_00001_.png filter=lfs diff=lfs merge=lfs -text
|
| 39 |
outputs/qwen-edit-v4/mode2-8step_00001_.png filter=lfs diff=lfs merge=lfs -text
|
| 40 |
outputs/qwen-edit-v4/mode3-quality-noref2_00001_.png filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
outputs/qwen-edit-v4/mode1-4step_00001_.png filter=lfs diff=lfs merge=lfs -text
|
| 39 |
outputs/qwen-edit-v4/mode2-8step_00001_.png filter=lfs diff=lfs merge=lfs -text
|
| 40 |
outputs/qwen-edit-v4/mode3-quality-noref2_00001_.png filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
outputs/ffhq-uv/stevie/stevie_uv.png filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
outputs/ffhq-uv/stevie/stevie_vis.png filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
outputs/ffhq-uv/stevie/stevie_input_vis.png filter=lfs diff=lfs merge=lfs -text
|
ffhq-uv/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ffhq-uv — FFHQ-UV single-photo → 3D face mesh + UV texture
|
| 2 |
+
|
| 3 |
+
One photo in, fitted 3D face mesh (HiFi3D++ topology) plus a 1024×1024 facial
|
| 4 |
+
UV-texture map out, using [FFHQ-UV](https://github.com/csbhr/FFHQ-UV)
|
| 5 |
+
(Bai et al., CVPR 2023) RGB fitting. Runs on the native python of the
|
| 6 |
+
`plx1029/comfyui-qwen` RunPod image (python 3.12, torch 2.8+cu128) —
|
| 7 |
+
**no venv, no conda, torch is not reinstalled**.
|
| 8 |
+
|
| 9 |
+
## Install (once per pod, ~3 min + ~2.5 GB downloads)
|
| 10 |
+
|
| 11 |
+
```bash
|
| 12 |
+
./ffhq_uv_install.sh
|
| 13 |
+
```
|
| 14 |
+
|
| 15 |
+
Everything lands in `$FFHQUV_HOME` (default `/workspace/ffhq-uv`). Re-running
|
| 16 |
+
is safe; completed steps are skipped. Checkpoints and topology assets are
|
| 17 |
+
fetched from the **official Hugging Face mirror** (`csbhr/FFHQ-UV`) — no
|
| 18 |
+
Baidu/OneDrive account needed, and no registration (unlike FLAME/p3dm).
|
| 19 |
+
|
| 20 |
+
## Run (~2 min per photo on H100)
|
| 21 |
+
|
| 22 |
+
```bash
|
| 23 |
+
./ffhq_uv_run.sh photo.jpg # -> /workspace/outputs/ffhq-uv/photo/
|
| 24 |
+
./ffhq_uv_run.sh photo.jpg -o /some/dir --force
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
Outputs: `<name>_uv.png` (1024×1024 UV-texture map — the headline output),
|
| 28 |
+
`<name>_id.obj` (neutral/identity head mesh, HiFi3D++ topology, 20481 verts),
|
| 29 |
+
`<name>_exp.obj` (mesh with the photo's expression), `<name>.mtl` (links the
|
| 30 |
+
meshes to the texture — the obj/mtl/png triplet opens textured in Blender or
|
| 31 |
+
any obj viewer), `<name>_vis.png` (input | skin mask | parsing | landmarks |
|
| 32 |
+
fitted render | uv), `<name>_input_vis.png` (preprocessing check).
|
| 33 |
+
|
| 34 |
+
Plus a `raw/` subfolder with the complete machine-readable fitting results:
|
| 35 |
+
all three stages' meshes/coeffs (`stage*_coeffs.pt`: HiFi3D++ id/exp/tex
|
| 36 |
+
coefficients, pose, lighting, predicted 68 landmarks; stages 2-3 also GAN
|
| 37 |
+
latents `latents_z`/`latents_w`), per-step optimization visuals, fitting log,
|
| 38 |
+
tensorboard events, the aligned 224×224 input tensors (`input_data.pt`,
|
| 39 |
+
`preprocess/<name>_processed.pt`: image, skin mask, parse mask, landmarks,
|
| 40 |
+
arcface warp matrix).
|
| 41 |
+
|
| 42 |
+
Pipeline: MTCNN 5-pt + 68-landmark detection (TF frozen graphs, CPU) → skin
|
| 43 |
+
mask + face parsing → similarity alignment to 224×224 → Deep3D (HiFi3D++
|
| 44 |
+
basis) coefficient init → StyleGAN2-based texture-GAN latent search (spherical
|
| 45 |
+
surface, fixed shape, 100 steps) → joint texture+shape optimization (200
|
| 46 |
+
steps, arcface id loss + VGG/LPIPS + photometric + landmark losses, rendered
|
| 47 |
+
with nvdiffrast CUDA rasterizer).
|
| 48 |
+
|
| 49 |
+
## What the installer does differently (vs. upstream repo)
|
| 50 |
+
|
| 51 |
+
Upstream targets python 3.7 / torch 1.7.1 / TF 1.15 + conda. To run on the
|
| 52 |
+
native py3.12 / torch 2.8 / numpy 2.1 env:
|
| 53 |
+
|
| 54 |
+
- **checkpoints from the official HF mirror** (`csbhr/FFHQ-UV` dataset repo)
|
| 55 |
+
instead of Baidu Netdisk/OneDrive; only the ~2.5 GB the RGB-fitting
|
| 56 |
+
pipeline needs (not the 300+ GB dataset, not the StyleGAN/e4e/DPR editing
|
| 57 |
+
checkpoints used only for dataset creation)
|
| 58 |
+
- **tensorflow-cpu** (modern 2.x) instead of tensorflow-gpu 1.15; the two TF1
|
| 59 |
+
frozen-graph detectors (MTCNN, 68-landmarks) are run through the
|
| 60 |
+
`tf.compat.v1` API — the only source patch needed. CPU on purpose: seconds
|
| 61 |
+
of inference, and no CUDA/cuDNN conflicts with torch
|
| 62 |
+
- **no pytorch3d needed** (RGB fitting only uses nvdiffrast, already present
|
| 63 |
+
from p3dm; the installer installs it if missing); stylegan2 custom CUDA ops
|
| 64 |
+
JIT-compile on first use and fall back to reference implementations if the
|
| 65 |
+
build fails
|
| 66 |
+
- `TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD=1` at runtime (repo predates torch 2.6
|
| 67 |
+
`weights_only` default)
|
| 68 |
+
- pip runs against a constraints file pinning the already-installed
|
| 69 |
+
numpy/torch/opencv/kornia/… so the ComfyUI env is never disturbed; only two
|
| 70 |
+
new packages are actually installed (`dominate`, `tensorflow-cpu`)
|
| 71 |
+
|
| 72 |
+
## Licenses
|
| 73 |
+
|
| 74 |
+
FFHQ-UV code and checkpoints are for **non-commercial research** use (see the
|
| 75 |
+
[upstream repo](https://github.com/csbhr/FFHQ-UV)); checkpoints are downloaded
|
| 76 |
+
from the authors' official Hugging Face mirror at install time and are not
|
| 77 |
+
redistributed here. HiFi3D++ topology © Tencent AI Lab (REALY).
|
ffhq-uv/ffhq_uv_install.sh
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# =============================================================================
|
| 3 |
+
# ffhq_uv_install.sh — one-shot installer for FFHQ-UV RGB fitting
|
| 4 |
+
# (single photo -> 3D face mesh + 1024x1024 UV texture)
|
| 5 |
+
#
|
| 6 |
+
# Installs https://github.com/csbhr/FFHQ-UV (RGB_Fitting pipeline, HiFi3D++
|
| 7 |
+
# topology + GAN texture decoder) into the NATIVE python environment (no venv,
|
| 8 |
+
# no conda, no torch reinstall). Tested on: Ubuntu 24.04, python 3.12,
|
| 9 |
+
# torch 2.8.0+cu128, H100 (RunPod image plx1029/comfyui-qwen).
|
| 10 |
+
#
|
| 11 |
+
# Idempotent: safe to re-run; finished steps are skipped.
|
| 12 |
+
#
|
| 13 |
+
# Requirements:
|
| 14 |
+
# - torch with CUDA already installed (the script will not install torch)
|
| 15 |
+
# - ~2.5 GB disk for checkpoints (downloaded from the official HF mirror
|
| 16 |
+
# csbhr/FFHQ-UV — no Baidu/OneDrive account needed)
|
| 17 |
+
#
|
| 18 |
+
# Usage:
|
| 19 |
+
# ./ffhq_uv_install.sh
|
| 20 |
+
#
|
| 21 |
+
# Layout (override with FFHQUV_HOME):
|
| 22 |
+
# $FFHQUV_HOME/FFHQ-UV code + checkpoints + topo assets
|
| 23 |
+
# $FFHQUV_HOME/staging per-image input staging
|
| 24 |
+
# $FFHQUV_HOME/fitting_output per-image fitting results
|
| 25 |
+
# =============================================================================
|
| 26 |
+
set -euo pipefail
|
| 27 |
+
|
| 28 |
+
FFHQUV_HOME="${FFHQUV_HOME:-/workspace/ffhq-uv}"
|
| 29 |
+
REPO="$FFHQUV_HOME/FFHQ-UV"
|
| 30 |
+
|
| 31 |
+
log() { echo -e "\n\033[1;36m[ffhq-uv-install]\033[0m $*"; }
|
| 32 |
+
|
| 33 |
+
mkdir -p "$FFHQUV_HOME" "$FFHQUV_HOME/staging" "$FFHQUV_HOME/fitting_output"
|
| 34 |
+
cd "$FFHQUV_HOME"
|
| 35 |
+
|
| 36 |
+
# ---------------------------------------------------------------- sanity checks
|
| 37 |
+
log "checking python/torch"
|
| 38 |
+
python3 - <<'EOF'
|
| 39 |
+
import torch
|
| 40 |
+
assert torch.cuda.is_available(), "CUDA torch required"
|
| 41 |
+
print(f" torch {torch.__version__} cuda {torch.version.cuda} ok")
|
| 42 |
+
EOF
|
| 43 |
+
|
| 44 |
+
# ---------------------------------------------------- constraints (protect env)
|
| 45 |
+
# Pin the currently-installed versions of shared heavyweight packages so pip
|
| 46 |
+
# cannot up/downgrade them while installing FFHQ-UV deps (keeps ComfyUI safe).
|
| 47 |
+
log "generating pip constraints from current env"
|
| 48 |
+
python3 - > "$FFHQUV_HOME/constraints.txt" <<'EOF'
|
| 49 |
+
from importlib.metadata import version, PackageNotFoundError
|
| 50 |
+
for p in ["numpy","torch","torchvision","pillow","scipy","scikit-image",
|
| 51 |
+
"opencv-python","kornia","tensorboard","huggingface_hub",
|
| 52 |
+
"transformers","safetensors","requests"]:
|
| 53 |
+
try: print(f"{p}=={version(p)}")
|
| 54 |
+
except PackageNotFoundError: pass
|
| 55 |
+
EOF
|
| 56 |
+
CONS="-c $FFHQUV_HOME/constraints.txt"
|
| 57 |
+
|
| 58 |
+
# ------------------------------------------------------------------ pip deps
|
| 59 |
+
# Almost everything (opencv, kornia, scikit-image, tensorboard, nvdiffrast) is
|
| 60 |
+
# already in the native env. Only two new packages are needed:
|
| 61 |
+
# dominate - tiny, HTML logging used by the fitting logger
|
| 62 |
+
# tensorflow-cpu - runs the two frozen-graph detectors (MTCNN + 68 landmarks)
|
| 63 |
+
# in step1 preprocessing; CPU build on purpose (a few sec of
|
| 64 |
+
# inference, avoids CUDA/cuDNN clashes with torch)
|
| 65 |
+
log "installing python dependencies (native env, constrained)"
|
| 66 |
+
python3 -c "import dominate" 2>/dev/null || pip install -q $CONS dominate
|
| 67 |
+
python3 -c "import tensorflow" 2>/dev/null || pip install -q $CONS tensorflow-cpu
|
| 68 |
+
|
| 69 |
+
# nvdiffrast: normally already present (e.g. from p3dm_install.sh)
|
| 70 |
+
python3 -c "import nvdiffrast" 2>/dev/null || {
|
| 71 |
+
log "installing nvdiffrast"
|
| 72 |
+
pip install -q $CONS ninja
|
| 73 |
+
pip install -q $CONS --no-build-isolation "git+https://github.com/NVlabs/nvdiffrast.git"
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
# ------------------------------------------------------------------ FFHQ-UV
|
| 77 |
+
if [ ! -d "$REPO" ]; then
|
| 78 |
+
log "cloning FFHQ-UV"
|
| 79 |
+
git clone --depth 1 https://github.com/csbhr/FFHQ-UV.git "$REPO"
|
| 80 |
+
fi
|
| 81 |
+
|
| 82 |
+
# ------------------------------------------------------- numpy 2 compat patch
|
| 83 |
+
# numpy 2 moved VisibleDeprecationWarning into np.exceptions, and no longer
|
| 84 |
+
# builds ragged arrays implicitly (t[0]/t[1] are 1-element arrays)
|
| 85 |
+
sed -i 's/category=np\.VisibleDeprecationWarning/category=np.exceptions.VisibleDeprecationWarning/;
|
| 86 |
+
s/trans_params = np\.array(\[w0, h0, s, t\[0\], t\[1\]\])/trans_params = np.array([w0, h0, s, float(t[0]), float(t[1])])/' \
|
| 87 |
+
"$REPO/RGB_Fitting/utils/preprocess_utils.py"
|
| 88 |
+
|
| 89 |
+
# --------------------------------------------------- tf1 -> tf2 compat patches
|
| 90 |
+
# The MTCNN + 68-landmark detectors are TF1 frozen graphs; modern tensorflow
|
| 91 |
+
# runs them through the compat.v1 API with eager execution disabled.
|
| 92 |
+
log "applying tensorflow compat patches"
|
| 93 |
+
for f in "$REPO/RGB_Fitting/third_party/mtcnn/detect_face_with_mtcnn.py" \
|
| 94 |
+
"$REPO/RGB_Fitting/third_party/landmark68/detect_lm68.py"; do
|
| 95 |
+
grep -q "tensorflow.compat.v1" "$f" || \
|
| 96 |
+
sed -i 's/^import tensorflow as tf$/import tensorflow.compat.v1 as tf; tf.disable_eager_execution()/' "$f"
|
| 97 |
+
grep -q "tensorflow.compat.v1" "$f" || { echo "tf compat patch failed: $f"; exit 1; }
|
| 98 |
+
done
|
| 99 |
+
|
| 100 |
+
# ------------------------------------------------- checkpoints + topo assets
|
| 101 |
+
# Official HF mirror of the project (csbhr/FFHQ-UV) hosts all checkpoints and
|
| 102 |
+
# topology assets — only the files the RGB-fitting pipeline needs are fetched.
|
| 103 |
+
log "downloading checkpoints + topo assets (~2.5 GB, official HF mirror)"
|
| 104 |
+
REPO_DIR="$REPO" python3 - <<'EOF'
|
| 105 |
+
import os
|
| 106 |
+
from huggingface_hub import snapshot_download
|
| 107 |
+
snapshot_download(
|
| 108 |
+
repo_id="csbhr/FFHQ-UV", repo_type="dataset",
|
| 109 |
+
local_dir=os.environ["REPO_DIR"],
|
| 110 |
+
allow_patterns=[
|
| 111 |
+
"checkpoints/lm_model/68lm_detector.pb",
|
| 112 |
+
"checkpoints/mtcnn_model/mtcnn_model.pb",
|
| 113 |
+
"checkpoints/parsing_model/79999_iter.pth",
|
| 114 |
+
"checkpoints/resnet_model/resnet18-5c106cde.pth",
|
| 115 |
+
"checkpoints/deep3d_model/epoch_latest.pth",
|
| 116 |
+
"checkpoints/arcface_model/ms1mv3_arcface_r50_fp16_backbone.pth",
|
| 117 |
+
"checkpoints/texgan_model/texgan_ffhq_uv.pth",
|
| 118 |
+
"checkpoints/vgg_model/vgg16.pt",
|
| 119 |
+
"topo_assets/*",
|
| 120 |
+
],
|
| 121 |
+
)
|
| 122 |
+
print(" checkpoints ok")
|
| 123 |
+
EOF
|
| 124 |
+
|
| 125 |
+
# ------------------------------------------------------------------ verify
|
| 126 |
+
log "verifying imports + files"
|
| 127 |
+
REPO_DIR="$REPO" python3 - <<'EOF'
|
| 128 |
+
import os, warnings
|
| 129 |
+
warnings.filterwarnings("ignore")
|
| 130 |
+
os.environ.setdefault("TF_CPP_MIN_LOG_LEVEL", "3")
|
| 131 |
+
import nvdiffrast, kornia, dominate, cv2, skimage
|
| 132 |
+
import tensorflow.compat.v1 as tf
|
| 133 |
+
assert hasattr(tf, "GraphDef") and hasattr(tf, "InteractiveSession")
|
| 134 |
+
repo = os.environ["REPO_DIR"]
|
| 135 |
+
for f in ["checkpoints/lm_model/68lm_detector.pb",
|
| 136 |
+
"checkpoints/mtcnn_model/mtcnn_model.pb",
|
| 137 |
+
"checkpoints/parsing_model/79999_iter.pth",
|
| 138 |
+
"checkpoints/resnet_model/resnet18-5c106cde.pth",
|
| 139 |
+
"checkpoints/deep3d_model/epoch_latest.pth",
|
| 140 |
+
"checkpoints/arcface_model/ms1mv3_arcface_r50_fp16_backbone.pth",
|
| 141 |
+
"checkpoints/texgan_model/texgan_ffhq_uv.pth",
|
| 142 |
+
"checkpoints/vgg_model/vgg16.pt",
|
| 143 |
+
"topo_assets/hifi3dpp_model_info.mat",
|
| 144 |
+
"topo_assets/unwrap_1024_info.mat",
|
| 145 |
+
"topo_assets/similarity_Lm3D_all.mat"]:
|
| 146 |
+
p = os.path.join(repo, f)
|
| 147 |
+
assert os.path.getsize(p) > 0, p
|
| 148 |
+
print(" all good")
|
| 149 |
+
EOF
|
| 150 |
+
|
| 151 |
+
log "install complete. run: ./ffhq_uv_run.sh <photo.jpg>"
|
ffhq-uv/ffhq_uv_run.sh
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# =============================================================================
|
| 3 |
+
# ffhq_uv_run.sh — single photo in -> 3D face mesh + UV texture out (FFHQ-UV)
|
| 4 |
+
#
|
| 5 |
+
# Usage:
|
| 6 |
+
# ./ffhq_uv_run.sh <photo.(jpg|png)> [-o <output_dir>] [--force]
|
| 7 |
+
#
|
| 8 |
+
# -o DIR output directory (default: /workspace/outputs/ffhq-uv/<name>)
|
| 9 |
+
# --force redo preprocessing/fitting even if cached results exist
|
| 10 |
+
#
|
| 11 |
+
# Outputs (in <output_dir>):
|
| 12 |
+
# <name>_uv.png 1024x1024 facial UV-texture map (headline output)
|
| 13 |
+
# <name>_id.obj fitted head mesh, identity/neutral (HiFi3D++ topology)
|
| 14 |
+
# <name>_exp.obj fitted head mesh with expression
|
| 15 |
+
# <name>.mtl material linking the meshes to the UV texture
|
| 16 |
+
# <name>_vis.png input | skin mask | parsing | landmarks | render | uv
|
| 17 |
+
# <name>_input_vis.png preprocessing visualization (step1)
|
| 18 |
+
# raw/ full fitting results (all stages, coeffs, latents,
|
| 19 |
+
# logs, per-step visuals) + preprocessed input data
|
| 20 |
+
#
|
| 21 |
+
# Requires ffhq_uv_install.sh to have been run first.
|
| 22 |
+
# =============================================================================
|
| 23 |
+
set -euo pipefail
|
| 24 |
+
|
| 25 |
+
FFHQUV_HOME="${FFHQUV_HOME:-/workspace/ffhq-uv}"
|
| 26 |
+
REPO="$FFHQUV_HOME/FFHQ-UV"
|
| 27 |
+
TEXGAN="texgan_ffhq_uv.pth"
|
| 28 |
+
|
| 29 |
+
IMG="" ; OUT="" ; FORCE=0
|
| 30 |
+
while [ $# -gt 0 ]; do
|
| 31 |
+
case "$1" in
|
| 32 |
+
-o) OUT="$2"; shift 2;;
|
| 33 |
+
--force) FORCE=1; shift;;
|
| 34 |
+
-h|--help) grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit 0;;
|
| 35 |
+
*) IMG="$1"; shift;;
|
| 36 |
+
esac
|
| 37 |
+
done
|
| 38 |
+
[ -n "$IMG" ] && [ -f "$IMG" ] || { echo "usage: $0 <photo.jpg> [-o outdir] [--force]"; exit 1; }
|
| 39 |
+
[ -d "$REPO" ] || { echo "FFHQ-UV not installed - run ffhq_uv_install.sh first"; exit 1; }
|
| 40 |
+
|
| 41 |
+
# sanitized job name from the file stem (used as folder + file identifier)
|
| 42 |
+
STEM="$(basename "$IMG")"; STEM="${STEM%.*}"
|
| 43 |
+
NAME="$(echo "$STEM" | tr -c 'a-zA-Z0-9' '_' | sed 's/_*$//;s/^_*//' | cut -c1-60)"
|
| 44 |
+
OUT="${OUT:-/workspace/outputs/ffhq-uv/$NAME}"
|
| 45 |
+
|
| 46 |
+
export TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD=1 # repo predates torch>=2.6 weights_only default
|
| 47 |
+
export TF_CPP_MIN_LOG_LEVEL=3 # silence tensorflow banner
|
| 48 |
+
if [ -z "${TORCH_CUDA_ARCH_LIST:-}" ]; then
|
| 49 |
+
export TORCH_CUDA_ARCH_LIST="$(python3 -c "import torch; print('%d.%d+PTX' % torch.cuda.get_device_capability())")"
|
| 50 |
+
fi
|
| 51 |
+
|
| 52 |
+
log() { echo -e "\033[1;35m[ffhq-uv]\033[0m $*"; }
|
| 53 |
+
T0=$(date +%s)
|
| 54 |
+
|
| 55 |
+
# stage the photo in a folder named after the job (step1 processes a folder)
|
| 56 |
+
STAGE="$FFHQUV_HOME/staging/$NAME"
|
| 57 |
+
PROC="$STAGE/processed_data"
|
| 58 |
+
FITOUT="$FFHQUV_HOME/fitting_output/$NAME"
|
| 59 |
+
if [ "$FORCE" = 1 ]; then rm -rf "$STAGE" "$FITOUT"; fi
|
| 60 |
+
mkdir -p "$STAGE"
|
| 61 |
+
cp -f "$IMG" "$STAGE/$NAME.${IMG##*.}"
|
| 62 |
+
|
| 63 |
+
cd "$REPO/RGB_Fitting"
|
| 64 |
+
|
| 65 |
+
log "1/2 preprocessing (MTCNN + 68 landmarks, skin mask, face parsing, alignment)"
|
| 66 |
+
if [ ! -f "$PROC/$NAME.pt" ]; then
|
| 67 |
+
python3 step1_process_data.py --input_dir "$STAGE" --output_dir "$PROC" \
|
| 68 |
+
--checkpoints_dir ../checkpoints --topo_dir ../topo_assets > /dev/null 2>&1 \
|
| 69 |
+
|| { echo "preprocessing failed, rerunning verbosely:"; \
|
| 70 |
+
python3 step1_process_data.py --input_dir "$STAGE" --output_dir "$PROC" \
|
| 71 |
+
--checkpoints_dir ../checkpoints --topo_dir ../topo_assets; exit 1; }
|
| 72 |
+
fi
|
| 73 |
+
[ -f "$PROC/$NAME.pt" ] || { echo "ERROR: no face detected / preprocessing failed for $IMG"; exit 1; }
|
| 74 |
+
|
| 75 |
+
log "2/2 RGB fitting (deep3d init -> texture search -> joint tex+shape optimization)"
|
| 76 |
+
if [ ! -f "$FITOUT/$NAME/stage3_mesh_id.obj" ]; then
|
| 77 |
+
python3 step2_fit_processed_data.py --input_dir "$PROC" --output_dir "$FITOUT" \
|
| 78 |
+
--checkpoints_dir ../checkpoints --topo_dir ../topo_assets \
|
| 79 |
+
--texgan_model_name "$TEXGAN" 2>&1 | grep -E "Stage|stage [0-9]|Fit image|Error|Traceback" || true
|
| 80 |
+
fi
|
| 81 |
+
RES="$FITOUT/$NAME"
|
| 82 |
+
[ -f "$RES/stage3_mesh_id.obj" ] || { echo "ERROR: fitting produced no mesh"; exit 1; }
|
| 83 |
+
|
| 84 |
+
# --------------------------------------------------------------- final outputs
|
| 85 |
+
mkdir -p "$OUT" "$OUT/raw"
|
| 86 |
+
cp "$RES/stage3_uv.png" "$OUT/${NAME}_uv.png"
|
| 87 |
+
# meshes + material, renamed with internal references fixed up
|
| 88 |
+
sed "s/stage3_mesh\.mlt/${NAME}.mtl/" "$RES/stage3_mesh_id.obj" > "$OUT/${NAME}_id.obj"
|
| 89 |
+
sed "s/stage3_mesh\.mlt/${NAME}.mtl/" "$RES/stage3_mesh_exp.obj" > "$OUT/${NAME}_exp.obj"
|
| 90 |
+
sed "s/stage3_uv\.png/${NAME}_uv.png/" "$RES/stage3_mesh.mlt" > "$OUT/${NAME}.mtl"
|
| 91 |
+
cp "$RES/stage3_vis.png" "$OUT/${NAME}_vis.png"
|
| 92 |
+
[ -f "${PROC}_vis/$NAME.png" ] && cp "${PROC}_vis/$NAME.png" "$OUT/${NAME}_input_vis.png"
|
| 93 |
+
|
| 94 |
+
# raw fitting + preprocessing data (all stages, coeffs, GAN latents, logs,
|
| 95 |
+
# per-step visuals, aligned input tensors) for downstream use
|
| 96 |
+
cp -r "$RES/." "$OUT/raw/"
|
| 97 |
+
mkdir -p "$OUT/raw/preprocess"
|
| 98 |
+
cp "$PROC/$NAME.pt" "$OUT/raw/preprocess/${NAME}_processed.pt"
|
| 99 |
+
|
| 100 |
+
log "done in $(( $(date +%s) - T0 ))s -> $OUT"
|
| 101 |
+
ls -la "$OUT"
|
outputs/ffhq-uv/stevie/stevie.mtl
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
newmtl blinn1SG
|
| 2 |
+
Ka 0.200000 0.200000 0.200000
|
| 3 |
+
Kd 1.000000 1.000000 1.000000
|
| 4 |
+
Ks 1.000000 1.000000 1.000000
|
| 5 |
+
map_Kd stevie_uv.png
|
outputs/ffhq-uv/stevie/stevie_exp.obj
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
outputs/ffhq-uv/stevie/stevie_id.obj
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
outputs/ffhq-uv/stevie/stevie_input_vis.png
ADDED
|
Git LFS Details
|
outputs/ffhq-uv/stevie/stevie_uv.png
ADDED
|
Git LFS Details
|
outputs/ffhq-uv/stevie/stevie_vis.png
ADDED
|
Git LFS Details
|