Han-Wei Kung1 · Tuomas Varanka2 · Nicu Sebe1
1 University of Trento, Italy | 2 University of Oulu, Finland
| Input | LDFA | RiDDLE | Textual Inv. | Ours | ||
|---|---|---|---|---|---|---|
![]() | ![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
| ID anonymized | ✅ | ✅ | ✅ | ✅ | ||
| Subject agnostic | ✅ | ✅ | ❌ | ✅ | ||
| Attr & scene kept | ❌ Poor | ❌ Poor | ✅ Good | ✅ Good | ||
| Attr controllable | ❌ | ❌ | ❌ | ✅ Default | ✅ Aging | ✅ Ethnicity |
Our reverse personalization method anonymizes faces without subject fine-tuning, while preserving the original facial attributes and surrounding scene. It also supports intuitive control over which attributes are retained or modified.
The pipeline consists of four main stages:
┌─────────────────┐
│ Input Image │
└────────┬────────┘
│
▼
┌─────────────────────────────┐
│ Face Detection & Alignment │ ← 1. Face Alignment (SFD detector)
│ (utils/extractor.py) │
└────────┬────────────────────┘
│
▼
┌─────────────────────────────┐
│ Face Embedding Extraction │ ← 2. ArcFace (InsightFace's buffalo_l model)
│ (utils/face_embedding.py) │
└────────┬────────────────────┘
│
▼
┌─────────────────────────────┐
│ DDPM Inversion Process │ ← 3a. Stable Diffusion XL + LEDITS++
│ (sdxl/leditspp/) │
└────────┬────────────────────┘
│
▼
┌─────────────────────────────┐
│ Guided Image Generation │ ← 3b. IP-Adapter-FaceID
│ (with anonymized identity) │
└────────┬────────────────────┘
│
▼
┌─────────────────────────────┐
│ Face Merging │ ← 4. Image composition
│ (utils/merger.py) │
└────────┬────────────────────┘
│
▼
┌─────────────────┐
│ Output Image │
└─────────────────┘
Face Extraction Layer (utils/extractor.py)
Identity Embedding Layer (utils/face_embedding.py)
Diffusion Model Layer (sdxl/leditspp/)
Composition Layer (utils/merger.py)
Clone the repository
git clone https://github.com/hanweikung/reverse-personalization.git
cd reverse-personalization
Create and activate the conda environment
conda env create -f environment.yml
conda activate reverse-personalization
Download models (automatic on first run)
stabilityai/stable-diffusion-xl-base-1.0h94/IP-Adapter-FaceID~/.insightfaceEnsure you have sufficient disk space (~15GB) and internet connectivity.
For images with a single, already aligned face (e.g., from FFHQ or CelebA-HQ datasets):
from anonymize_faces_in_image import anonymize_faces_in_image
def main():
# Path to your input image (already aligned face)
input_image_path = "path/to/aligned_face.jpg"
# Run anonymization (enable_face_detection=False by default)
anonymized_image = anonymize_faces_in_image(
input_image=input_image_path,
)
# Save the result
anonymized_image.save("output_anonymized.png")
# Or display it
# anonymized_image.show()
if __name__ == "__main__":
main()
For images containing unaligned faces or multiple people:
from anonymize_faces_in_image import anonymize_faces_in_image
def main():
# Path to your input image with unaligned faces
input_image_path = "path/to/image_with_people.jpg"
# Run anonymization with face extraction and alignment enabled
anonymized_image = anonymize_faces_in_image(
input_image=input_image_path,
enable_face_detection=True, # Enable face detection and alignment
)
# Save the result
anonymized_image.save("output_anonymized.png")
if __name__ == "__main__":
main()
For fine-tuned control over the anonymization process:
anonymized_image = anonymize_faces_in_image(
input_image="input.jpg",
attribute_prompt="an old man", # Control face attributes
sd_model_path="stabilityai/stable-diffusion-xl-base-1.0",
insightface_model_path="~/.insightface",
# GPU Configuration
device_num=0, # GPU device ID
# Diffusion Model Parameters
skip=0.7, # Skip fraction of diffusion timesteps (0.0-1.0)
num_inversion_steps=100, # Number of DDPM inversion steps
guidance_scale=-10.0, # Negative guidance for anonymization
# Face Processing
enable_face_detection=False, # Enable face extraction/alignment for unaligned images
face_image_size=1024, # Resolution of extracted face region (used when enable_face_detection=True)
det_thresh=0.1, # Face detection confidence threshold
det_size=640, # Face detection model input size
# Identity Control
id_emb_scale=1.0, # Identity embedding scale factor
ip_adapter_scale=1.0, # IP-Adapter influence strength
# Reproducibility
seed=0, # Random seed
)
python main.py
This will process the default image at my_dataset/images/00080.png and save the result as output.png.
| Parameter | Type | Default | Description |
|---|---|---|---|
input_image | str/Path | Required | Path to input image file |
attribute_prompt | str/None | None | Prompt for controlling face attributes (e.g., "a young woman") |
sd_model_path | str | "stabilityai/stable-diffusion-xl-base-1.0" | Hugging Face model ID or local path |
insightface_model_path | str | "~/.insightface" | Path to InsightFace models |
device_num | int | 0 | CUDA device number (GPU ID) |
skip | float | 0.7 | Fraction of diffusion timesteps to skip (0.0-1.0) |
id_emb_scale | float | 1.0 | Identity embedding scaling factor |
guidance_scale | float | -10.0 | CFG scale (negative for anonymization) |
num_inversion_steps | int | 100 | Number of DDPM inversion/generation steps |
enable_face_detection | bool | False | Enable face detection, extraction, and alignment for unaligned images |
face_image_size | int | 1024 | Resolution of extracted face regions (px) - only used when enable_face_detection=True |
det_thresh | float | 0.1 | Face detection confidence threshold (0.0-1.0) |
ip_adapter_scale | float | 1.0 | IP-Adapter conditioning strength |
det_size | int | 640 | Face detection model input size (px) |
seed | int | 0 | Random seed for reproducibility |
enable_face_detection=False (default)enable_face_detection=Trueguidance_scale (more negative)reverse-personalization/
│
├── anonymize_faces_in_image.py # Main anonymization function
├── main.py # Example usage script
├── environment.yml # Conda environment specification
├── LICENSE # GNU AGPL v3 license
├── README.md # This file
│
├── sdxl/ # Stable Diffusion XL components
│ └── leditspp/
│ ├── pipeline_stable_diffusion_xl.py # Custom SDXL pipeline
│ ├── pipeline_output.py # Output data structures
│ └── scheduling_dpmsolver_multistep_inject.py # Custom scheduler
│
├── utils/ # Utility modules
│ ├── extractor.py # Face detection and extraction
│ ├── face_embedding.py # Identity embedding extraction
│ ├── merger.py # Face composition utilities
│ └── sample_vector.py # Vector sampling helpers
│
├── my_dataset/ # Sample dataset directory
│ └── images/ # Input images
│
└── assets/
└── images/
└── teaser/ # Teaser images for README
The system uses face_alignment with the SFD (Single Shot Face Detector) to locate all faces in the input image. For each detected face:
InsightFace's buffalo_l model extracts a 512-dimensional identity embedding vector for each face:
The aligned face image undergoes DDPM inversion:
Using the inverted latents and a modified identity embedding:
The anonymized face is composited back into the original image:
@InProceedings{Kung_2026_WACV,
author = {Kung, Han-Wei and Varanka, Tuomas and Sebe, Nicu},
title = {Reverse Personalization},
booktitle = {Proceedings of the IEEE/CVF Winter Conference on Applications of Computer Vision (WACV)},
month = {March},
year = {2026},
pages = {988-999}
}
Python
100.0%
Han-Wei Kung1 · Tuomas Varanka2 · Nicu Sebe1
1 University of Trento, Italy | 2 University of Oulu, Finland
| Input | LDFA | RiDDLE | Textual Inv. | Ours | ||
|---|---|---|---|---|---|---|
![]() | ![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
| ID anonymized | ✅ | ✅ | ✅ | ✅ | ||
| Subject agnostic | ✅ | ✅ | ❌ | ✅ | ||
| Attr & scene kept | ❌ Poor | ❌ Poor | ✅ Good | ✅ Good | ||
| Attr controllable | ❌ | ❌ | ❌ | ✅ Default | ✅ Aging | ✅ Ethnicity |
Our reverse personalization method anonymizes faces without subject fine-tuning, while preserving the original facial attributes and surrounding scene. It also supports intuitive control over which attributes are retained or modified.
The pipeline consists of four main stages:
┌─────────────────┐
│ Input Image │
└────────┬────────┘
│
▼
┌─────────────────────────────┐
│ Face Detection & Alignment │ ← 1. Face Alignment (SFD detector)
│ (utils/extractor.py) │
└────────┬────────────────────┘
│
▼
┌─────────────────────────────┐
│ Face Embedding Extraction │ ← 2. ArcFace (InsightFace's buffalo_l model)
│ (utils/face_embedding.py) │
└────────┬────────────────────┘
│
▼
┌─────────────────────────────┐
│ DDPM Inversion Process │ ← 3a. Stable Diffusion XL + LEDITS++
│ (sdxl/leditspp/) │
└────────┬────────────────────┘
│
▼
┌─────────────────────────────┐
│ Guided Image Generation │ ← 3b. IP-Adapter-FaceID
│ (with anonymized identity) │
└────────┬────────────────────┘
│
▼
┌─────────────────────────────┐
│ Face Merging │ ← 4. Image composition
│ (utils/merger.py) │
└────────┬────────────────────┘
│
▼
┌─────────────────┐
│ Output Image │
└─────────────────┘
Face Extraction Layer (utils/extractor.py)
Identity Embedding Layer (utils/face_embedding.py)
Diffusion Model Layer (sdxl/leditspp/)
Composition Layer (utils/merger.py)
Clone the repository
git clone https://github.com/hanweikung/reverse-personalization.git
cd reverse-personalization
Create and activate the conda environment
conda env create -f environment.yml
conda activate reverse-personalization
Download models (automatic on first run)
stabilityai/stable-diffusion-xl-base-1.0h94/IP-Adapter-FaceID~/.insightfaceEnsure you have sufficient disk space (~15GB) and internet connectivity.
For images with a single, already aligned face (e.g., from FFHQ or CelebA-HQ datasets):
from anonymize_faces_in_image import anonymize_faces_in_image
def main():
# Path to your input image (already aligned face)
input_image_path = "path/to/aligned_face.jpg"
# Run anonymization (enable_face_detection=False by default)
anonymized_image = anonymize_faces_in_image(
input_image=input_image_path,
)
# Save the result
anonymized_image.save("output_anonymized.png")
# Or display it
# anonymized_image.show()
if __name__ == "__main__":
main()
For images containing unaligned faces or multiple people:
from anonymize_faces_in_image import anonymize_faces_in_image
def main():
# Path to your input image with unaligned faces
input_image_path = "path/to/image_with_people.jpg"
# Run anonymization with face extraction and alignment enabled
anonymized_image = anonymize_faces_in_image(
input_image=input_image_path,
enable_face_detection=True, # Enable face detection and alignment
)
# Save the result
anonymized_image.save("output_anonymized.png")
if __name__ == "__main__":
main()
For fine-tuned control over the anonymization process:
anonymized_image = anonymize_faces_in_image(
input_image="input.jpg",
attribute_prompt="an old man", # Control face attributes
sd_model_path="stabilityai/stable-diffusion-xl-base-1.0",
insightface_model_path="~/.insightface",
# GPU Configuration
device_num=0, # GPU device ID
# Diffusion Model Parameters
skip=0.7, # Skip fraction of diffusion timesteps (0.0-1.0)
num_inversion_steps=100, # Number of DDPM inversion steps
guidance_scale=-10.0, # Negative guidance for anonymization
# Face Processing
enable_face_detection=False, # Enable face extraction/alignment for unaligned images
face_image_size=1024, # Resolution of extracted face region (used when enable_face_detection=True)
det_thresh=0.1, # Face detection confidence threshold
det_size=640, # Face detection model input size
# Identity Control
id_emb_scale=1.0, # Identity embedding scale factor
ip_adapter_scale=1.0, # IP-Adapter influence strength
# Reproducibility
seed=0, # Random seed
)
python main.py
This will process the default image at my_dataset/images/00080.png and save the result as output.png.
| Parameter | Type | Default | Description |
|---|---|---|---|
input_image | str/Path | Required | Path to input image file |
attribute_prompt | str/None | None | Prompt for controlling face attributes (e.g., "a young woman") |
sd_model_path | str | "stabilityai/stable-diffusion-xl-base-1.0" | Hugging Face model ID or local path |
insightface_model_path | str | "~/.insightface" | Path to InsightFace models |
device_num | int | 0 | CUDA device number (GPU ID) |
skip | float | 0.7 | Fraction of diffusion timesteps to skip (0.0-1.0) |
id_emb_scale | float | 1.0 | Identity embedding scaling factor |
guidance_scale | float | -10.0 | CFG scale (negative for anonymization) |
num_inversion_steps | int | 100 | Number of DDPM inversion/generation steps |
enable_face_detection | bool | False | Enable face detection, extraction, and alignment for unaligned images |
face_image_size | int | 1024 | Resolution of extracted face regions (px) - only used when enable_face_detection=True |
det_thresh | float | 0.1 | Face detection confidence threshold (0.0-1.0) |
ip_adapter_scale | float | 1.0 | IP-Adapter conditioning strength |
det_size | int | 640 | Face detection model input size (px) |
seed | int | 0 | Random seed for reproducibility |
enable_face_detection=False (default)enable_face_detection=Trueguidance_scale (more negative)reverse-personalization/
│
├── anonymize_faces_in_image.py # Main anonymization function
├── main.py # Example usage script
├── environment.yml # Conda environment specification
├── LICENSE # GNU AGPL v3 license
├── README.md # This file
│
├── sdxl/ # Stable Diffusion XL components
│ └── leditspp/
│ ├── pipeline_stable_diffusion_xl.py # Custom SDXL pipeline
│ ├── pipeline_output.py # Output data structures
│ └── scheduling_dpmsolver_multistep_inject.py # Custom scheduler
│
├── utils/ # Utility modules
│ ├── extractor.py # Face detection and extraction
│ ├── face_embedding.py # Identity embedding extraction
│ ├── merger.py # Face composition utilities
│ └── sample_vector.py # Vector sampling helpers
│
├── my_dataset/ # Sample dataset directory
│ └── images/ # Input images
│
└── assets/
└── images/
└── teaser/ # Teaser images for README
The system uses face_alignment with the SFD (Single Shot Face Detector) to locate all faces in the input image. For each detected face:
InsightFace's buffalo_l model extracts a 512-dimensional identity embedding vector for each face:
The aligned face image undergoes DDPM inversion:
Using the inverted latents and a modified identity embedding:
The anonymized face is composited back into the original image:
@InProceedings{Kung_2026_WACV,
author = {Kung, Han-Wei and Varanka, Tuomas and Sebe, Nicu},
title = {Reverse Personalization},
booktitle = {Proceedings of the IEEE/CVF Winter Conference on Applications of Computer Vision (WACV)},
month = {March},
year = {2026},
pages = {988-999}
}
Python
100.0%