In-context 3D-aware Hair Import and Transfer for Images
SIGGRAPH Conference Papers '26
A. Heidari, A. Alimohammadi, W. Michel Pinto Lira, A. Bar-Lev, and A. Mahdavi-Amiri
HairPort transfers a reference hairstyle onto a source face while explicitly handling large pose and scale differences through 3D-aware alignment before image synthesis.
- May 2026: ACM assigned the HairPort DOI: 10.1145/3799902.3811046.
- May 2026: HairPort was accepted to SIGGRAPH Conference Papers '26.
- May 2026: Launched the HairPort project page.
- April 2026: Initial HairPort source code released; packaging and dependency manifests will be finalized soon.
- April 2026: Released the Baldy dataset for paired bald/original image training.
- April 2026: Released the Bald Converter LoRA weights.
Figure 1. HairPort transfers hairstyles across challenging identity, pose, scale, and style differences.
Figure 2. HairPort removes source hair, aligns the reference hairstyle in 3D, and synthesizes the final transfer from source-aligned hair evidence.
Figure 3. Baldy dataset generation and LoRA-based Bald Converter finetuning for in-context bald generation.
Paper Abstract
Transferring hairstyles between images is an important but challenging task in computer graphics, computer vision, and visual effects. It enables users to explore new looks without physically altering their hair, with applications in virtual try-on systems, augmented reality, and entertainment. Most prior works operate best under small pose gaps, and they fall short under large viewpoint and scale differences, where missing hair content must be synthesized rather than transferred.
We propose HairPort, a 3D-aware hairstyle transfer framework that addresses these issues by explicitly separating hair removal from transfer and enforcing geometric consistency before synthesis. We introduce a Bald Converter, which produces realistic bald versions of faces through LoRA-based in-context adaptation of FLUX. To train the Bald Converter, we introduce a new dataset, Baldy, containing 6,400 paired bald and original images across diverse identities and conditions. We also use a 3D-aware Transfer Pipeline that reconstructs and re-renders the reference hairstyle from the target viewpoint before compositing it onto the source image. Being 3D-aware, our method supports large pose and scale discrepancies between the source and target. With these components in place, we employ a conditional flow-matching generator to synthesize the final image conditioned on the bald source, the pose-aligned hair rendering, the original reference image, and a text prompt. Together, our method enables accurate, pose-consistent, and identity-preserving hairstyle transfer, outperforming existing methods both qualitatively and quantitatively.
Official source preview. This repository contains the official SIGGRAPH 2026 implementation snapshot, including the pipeline source, configuration, asset layout, README figures, Bald Converter links, and dataset links.
Packaging is still being finalized: this snapshot does not include
pyproject.toml,setup.py,setup.cfg, a pinned dependency manifest, or installed console-script entry points. For now, run commands from the repository root withpython -m ...and setPYTHONPATHas shown below.
- Python >= 3.10
- CUDA-capable GPU recommended; most stages are GPU-heavy and >=24 GB VRAM is recommended
- Blender >= 4.0 for multi-view rendering in 3D landmark and rendering stages
- Hugging Face access for auto-downloaded model weights
conda create -n hairport python=3.11 -y
conda activate hairportInstall the PyTorch build matching your CUDA version. Example for CUDA 12.4:
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu124git clone https://github.com/deepmancer/HairPort.git
cd HairPort
export PYTHONPATH="$PWD:${PYTHONPATH}"The current full dependency recipe is documented in scripts/install.sh. It is a full bootstrap/reference script rather than a minimal in-place package installer, so review it before running or adapting it. It includes system-level tools, source builds, CUDA-specific packages, and optional project utilities.
# From the parent directory that contains HairPort:
cd ..
bash HairPort/scripts/install.sh
cd HairPortThis clones CodeFormer, MV-Adapter, and SHeaP at the pinned inference revisions in the setup script:
bash scripts/setup_submodules.shHi3DGen is not bundled. Generate shape meshes externally and place them at shape_mesh/<id>/shape_mesh.glb before running the full transfer pipeline.
- Register and download FLAME assets from flame.is.tue.mpg.de.
- Place the required files at:
assets/base_models/flame/parametric_models/generic_model.pkl
assets/base_models/flame/vertex_mappings/FLAME_masks.pkl
assets/landmarks/flame/eyelids.pt
assets/landmarks/flame/mediapipe_landmark_embedding.npz
generic_model.pt is auto-generated from generic_model.pkl during setup and preflight.
Validate external modules and user-supplied assets before beginning GPU inference:
python -m hairport.preflightAfter the validated GPU smoke run, export the exact publication environment lock:
bash scripts/export_environment_lock.shCommit the generated requirements.inference.lock.txt alongside the immutable models.*_revision settings used for reported results.
Most model weights are downloaded automatically on first use.
huggingface-cli loginThis example transfers the reference hairstyle from reference.png onto the source face in source.png.
mkdir -p my_project/image
mkdir -p my_project/matted_image
mkdir -p my_project/matted_image_centered
cp source.png my_project/image/source.png
cp reference.png my_project/image/reference.png
cp source.png my_project/matted_image_centered/source.png
cp reference.png my_project/matted_image_centered/reference.pngThe filename stem becomes the identity ID used throughout the pipeline.
HairPort expects user-provided shape meshes under shape_mesh/. Stage 3 then runs MVAdapter texturing and writes textured_mesh.glb outputs under mvadapter/<shape_provider>/<id>/textured_mesh.glb.
mkdir -p my_project/shape_mesh/source
mkdir -p my_project/shape_mesh/reference
# Required files:
# my_project/shape_mesh/source/shape_mesh.glb
# my_project/shape_mesh/reference/shape_mesh.glbFor MVAdapter texturing, centered mattes are required at matted_image_centered/<id>.png.
Create my_project/pairs.csv:
target_id,source_id,lift_3d,head_diff_angle
reference,source,True,0.98target_id is the identity providing the reference hairstyle. source_id is the identity providing the source face. The lift_3d and head_diff_angle columns are optional; if omitted, they are computed automatically and written to generated pair_decisions.json output. Input pairs.csv is never modified.
python -m hairport.pipeline \
--data_dir my_project \
--shape_provider hi3dgen \
--texture_provider mvadapter \
--bald_version w_segThe full 3D-aware run produces both official conditioning variants:
my_project/view_aligned/shape_hi3dgen__texture_mvadapter/reference_to_source/w_seg/3d_aware/enhanced/transferred_klein/hair_restored.png
my_project/view_aligned/shape_hi3dgen__texture_mvadapter/reference_to_source/w_seg/3d_aware/blended/transferred_klein/hair_restored.png
Depending on GPU memory and model cache state, the full pipeline can take several minutes per transfer pair.
from hairport.pipeline import HairPortPipeline
pipeline = HairPortPipeline(
data_dir="my_project",
shape_provider="hi3dgen",
texture_provider="mvadapter",
bald_version="w_seg",
)
results = pipeline.run()
for r in results:
status = "OK" if r.success else "FAIL"
print(f"[{status}] {r.stage:20s} {r.duration_seconds:.1f}s")| # | Stage | What happens |
|---|---|---|
| 1 | Baldify | Generate a realistic bald source portrait using the Bald Converter. |
| 2 | Caption | Outpaint bald images and generate text descriptions with Qwen Image-Edit. |
| 3 | Shape Mesh | Texture canonical shape_mesh/<id>/shape_mesh.glb inputs with MVAdapter. |
| 4 | Landmark 3D | Estimate 3D facial landmarks from the supported single frontal Blender render. |
| 5 | Align View | Optimize camera alignment from reference hairstyle to source face. |
| 6 | Render View | Render target-hair alignment views from the postprocessed textured mesh. |
| 7 | Enhance View | Refine rendered views with FLUX.2 Klein 9B and CodeFormer. |
| 8 | Blend Hair | Warp and Poisson-blend enhanced hair onto the bald source. |
| 9 | Transfer Hair | Synthesize final outputs for enhanced-view and blended-view conditioning. |
Run a subset of stages when debugging:
# Resume from a stage
python -m hairport.pipeline --data_dir my_project --start render_view
# Run only selected stages
python -m hairport.pipeline --data_dir my_project --only blend_hair transfer_hair
# Skip selected stages
python -m hairport.pipeline --data_dir my_project --skip shape_mesh landmark_3dHairPort expects inputs and writes intermediates under data_dir:
data_dir/
├── image/ # Input portraits
│ ├── source.png
│ └── reference.png
├── matted_image/ # Background-removed images
├── matted_image_centered/ # Centered mattes used for MVAdapter texturing
├── pairs.csv # Transfer pairs
│
├── shape_mesh/ # User-provided input meshes
│ ├── source/shape_mesh.glb
│ └── reference/shape_mesh.glb
├── mvadapter/hi3dgen/ # Generated textured meshes (Stage 3)
│ ├── source/textured_mesh.glb
│ └── reference/textured_mesh.glb
│
├── bald/
│ └── w_seg/
│ ├── image/ # Bald portraits
│ └── image_outpainted/ # Outpainted bald images
├── lmk_3d/
│ └── shape_hi3dgen__texture_mvadapter/
│ └── <identity>/
│ ├── postprocessed_textured_mesh.glb
│ └── landmarks_3d.npy
│
└── view_aligned/
└── shape_hi3dgen__texture_mvadapter/
├── pair_decisions.json # Generated decisions; pairs.csv remains input-only
└── <target>_to_<source>/
└── <bald_version>/
├── alignment/ # Version-scoped rendered/enhanced views
└── 3d_aware/
├── blending/
├── enhanced/transferred_klein/hair_restored.png
└── blended/transferred_klein/hair_restored.png
HairPort uses a centralized OmegaConf configuration. Defaults live in configs/default.yaml.
Configuration covers:
- Global settings:
device,seed - Paths: asset directories and external module locations
- Models: Hugging Face IDs, LoRA weights, checkpoints
- Per-stage parameters: resolution, inference steps, thresholds
- Prompts used across the pipeline
landmark_3d.num_perturbations: 0: the supported inference contract is single frontal viewtransfer_hair.conditioning_sources: [enhanced, blended]: generated final variantscache.policy: validated: artifacts are reused only when their provenance sidecar matches
Generated artifacts carry .provenance.json sidecars recording resolved configuration, inputs, model identifiers/revisions, and seeds. Existing artifacts without matching provenance are regenerated. Validated cache reuse is disabled while the repository checkout has uncommitted changes, since such a state cannot name the producing code exactly.
For publication runs, set the models.*_revision values to immutable Hugging Face snapshot commits; preflight warns when a selected stage uses an unpinned revision. These fields cover FLUX, RealVis/SDXL, ControlNet, MV-Adapter, SAM, BEN2, face parsing, Qwen, and Bald Converter dependencies used by the supported pipeline.
Per-item seeds make sampled decisions reproducible; exact pixel equality additionally depends on deterministic GPU kernels and is recorded in provenance through the active Torch/cuDNN determinism flags.
Stage 3 invokes the pinned external MV-Adapter checkout; for reported runs, set shape_mesh.sdxl_model_id to an immutable local Hugging Face snapshot path so its internal loader cannot resolve a moving repository head.
Override defaults with a custom YAML file:
python -m hairport.pipeline --config configs/my_experiment.yamlOr with dot-list CLI overrides:
python -m hairport.pipeline --set device=cpu seed=123 enhance_view.num_inference_steps=6Programmatic configuration:
from hairport.config import load_config, set_config
cfg = load_config(
"configs/my_experiment.yaml",
overrides=["device=cpu", "baldify.seed=123"],
)
set_config(cfg)Each stage can be run directly from the repository root:
python -m hairport.stages.baldify --data_dir my_project
python -m hairport.stages.caption --data_dir my_project
python -m hairport.stages.shape_mesh --data_dir my_project
python -m hairport.stages.landmark_3d --data_dir my_project
python -m hairport.stages.align_view --data_dir my_project
python -m hairport.stages.render_view --data_dir my_project
python -m hairport.stages.enhance_view --data_dir my_project
python -m hairport.stages.blend_hair --data_dir my_project
python -m hairport.stages.transfer_hair --data_dir my_projectUse the Bald Converter independently when you only need bald portrait generation:
from hairport.bald_konverter import BaldKonverterPipeline
pipeline = BaldKonverterPipeline(mode="auto")
result = pipeline("portrait.jpg")
result.bald_image.save("bald.png")CLI form:
python -m hairport.bald_konverter.cli --input photo.jpg --output bald.png
python -m hairport.bald_konverter.cli --input-dir ./faces/ --output-dir ./bald/
python -m hairport.bald_konverter.cli --input photo.jpg --output bald.png --mode w_segfrom hairport.fit_lmk import estimate_3d_landmarks
results = estimate_3d_landmarks(
mesh_path="head.glb",
cam_loc=[0.0, -1.45, 0.0],
cam_rot=[1.5708, 0.0, 0.0],
output_dir="./landmarks_output",
)HairPort/
├── configs/
│ └── default.yaml # Centralized YAML configuration
├── scripts/
│ ├── install.sh # Full dependency recipe
│ └── setup_submodules.sh # External module setup
├── assets/
│ ├── images/ # README figures
│ ├── base_models/flame/ # FLAME models + vertex mappings
│ └── landmarks/flame/ # FLAME landmark and eyelid assets
├── hairport/
│ ├── pipeline.py # HairPortPipeline orchestrator
│ ├── config.py # OmegaConf config system
│ ├── data.py # Dataset path management
│ ├── stages/ # Pipeline stage modules
│ ├── bald_konverter/ # Bald Converter package
│ ├── fit_lmk/ # 3D landmark estimation
│ ├── core/ # Shared vision and geometry components
│ ├── utility/ # Rendering, warping, outpainting utilities
│ └── postprocessing/ # Hair restoration and mask helpers
├── LICENSE
└── README.md
| Module | Repository | Used for |
|---|---|---|
| CodeFormer | sczhou/CodeFormer | Face super-resolution |
| MV-Adapter | huanngzh/MV-Adapter | Multi-view generation adapter for SDXL |
| SHeaP | deepmancer/SHeaP | FLAME-based head segmentation and orientation |
HairPort builds on a number of excellent open-source projects and research assets. We thank the authors and maintainers of MV-Adapter, Hi3DGen, CodeFormer, SHeaP, FLAME, MediaPipe, Segment Anything, BEN2, Hugging Face Diffusers/Transformers, FLUX, and Qwen for making their work available to the community.
Please refer to the respective projects for their licenses, model terms, and citation requirements.
If you use HairPort in your research, please cite:
Publication details: SIGGRAPH Conference Papers '26, July 19--23, 2026, Los Angeles, CA, USA. DOI: 10.1145/3799902.3811046. ACM ISBN: 979-8-4007-2554-8/2026/07.
@inproceedings{heidari2026hairport,
title = {HairPort: In-context 3D-aware Hair Import and Transfer for Images},
author = {A. Heidari and A. Alimohammadi and W. Michel Pinto Lira and A. Bar-Lev and A. Mahdavi-Amiri},
booktitle = {Special Interest Group on Computer Graphics and Interactive Techniques Conference Conference Papers (SIGGRAPH Conference Papers '26)},
year = {2026},
isbn = {979-8-4007-2554-8/2026/07},
doi = {10.1145/3799902.3811046},
url = {https://doi.org/10.1145/3799902.3811046},
location = {Los Angeles, CA, USA}
}This repository is released under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).
Unless otherwise noted, this license applies to the HairPort source code, documentation, and repository-owned assets. Third-party code, models, datasets, and external assets retain their own upstream licenses, model terms, and citation requirements.
Copyright (c) 2026