- Installation
- Quick Start
- Command Line Arguments
- Example Commands
- Batch Inference
- Evaluation
- Dataset
- Default Configuration
- Citation
Official implementation of Fill My Mirror.
This repository contains the code for generating consistent reflections in mirrors by combining geometry estimation, projection, and dual-mask diffusion-based inpainting.
The following instructions were tested on Linux with Python 3.10.
Clone the repository with submodules to also download the MoGe and MASt3R dependencies.
git clone --recurse-submodules https://github.com/OfekBasson/Fill-My-Mirror.git
cd Fill-My-MirrorIf you already cloned without submodules, run:
git submodule update --init --recursiveCreate a clean environment to avoid dependency conflicts.
conda create -n fill-my-mirror python=3.10
conda activate fill-my-mirrorpip install -r requirements.txt
pip install -e third_party/MoGe
pip install -e .This project requires Blender for scene rendering (and for evaluation on the Blender dataset).
Run the provided installation script:
bash scripts/install_blender.shThis downloads and extracts Blender to:
external/blender/
Required only if you plan to run evaluation with the Reflection Consistency Score (RCS) masking on real images.
MASt3R is not a pip-installable package — install its dependencies directly:
pip install -r third_party/MASt3R/requirements.txt
pip install -r third_party/MASt3R/dust3r/requirements.txtRequired only if you plan to use a depth-anything/* geometry model (e.g. depth-anything/DA3-LARGE-1.1).
pip install xformers "torch>=2" torchvision
pip install git+https://github.com/ByteDance-Seed/Depth-Anything-3.gitNote: Depth Anything 3 does not yet support multi-mirror scenes. Use MoGe-V2 (
Ruicheng/moge-2-vitl-normal) for images with more than one mirror.
Run the example script:
scripts/run_example.shOr run directly with your own image and mask:
python -m fill_my_mirror --image /path/to/image.png --mask /path/to/mask.pngOr use a sample from the HuggingFace dataset by index:
python -m fill_my_mirror --hf-index 0Option A — local files (--image and --mask must be provided together):
| Argument | Description |
|---|---|
--image /path/to/image.png |
Path to the input image |
--mask /path/to/mask.png |
Path to the binary mask of the mirror region |
Option B — HuggingFace dataset sample:
| Argument | Description |
|---|---|
--hf-index INDEX |
Index of a sample from the dataset (0 to dataset size − 1). Used with the real-images dataset by default, with the Blender dataset when --use-blender-data is set, or with the MirrorBench V2 dataset when --use-mirrorbench-data is set. The sample's prompt is used as the prompt unless --prompt is also given. |
| Argument | Default | Description |
|---|---|---|
--config |
configs/config.yaml |
Path to a YAML configuration file |
--blender_path |
(from config) | Path to the Blender executable. Overrides the config file value. |
--prompt |
(from config or dataset) | Text prompt for the diffusion model. Overrides the config file value and any prompt from the HuggingFace sample. |
--prompt-2 |
— | Optional second text prompt |
--output_path |
(from config) | Path to save the final output image. Overrides the config file value. |
--height |
1024 |
Desired output height |
--width |
1024 |
Desired output width. Automatically adjusted to fit the input aspect ratio if needed. |
--strength |
1.0 |
Inpainting strength |
--num-inference-steps |
30 |
Number of diffusion inference steps |
--guidance-scale |
30.0 |
Guidance scale for the diffusion model |
--num-images-per-prompt |
1 |
Number of images to generate per prompt |
--max-sequence-length |
512 |
Maximum text sequence length |
--seed |
0 |
Random seed for reproducibility |
--use-blender-data |
False |
Load a sample from the Blender HuggingFace dataset. Requires --hf-index. When set, geometry is read directly from the dataset (no MoGe inference). |
--use-mirrorbench-data |
False |
Load a sample from the MirrorBench V2 (SynMirrorV2) dataset. Requires --hf-index. See MirrorBench V2 setup. |
--n |
6.0 |
Power n for the alpha^n interpolation |
--t-prime |
750.0 |
First timestep threshold for mask interpolation |
The file paths below are illustrative — replace them with the actual paths to your image and mask files.
Basic example with local files:
python -m fill_my_mirror \
--image /path/to/image.png \
--mask /path/to/mask.png \
--prompt "A standing mirror reflects a bed with a dotted cover in a cozy bedroom."Example using a HuggingFace dataset sample (no local files needed):
python -m fill_my_mirror --hf-index 5Example with a custom output path:
python -m fill_my_mirror \
--image /path/to/image.png \
--mask /path/to/mask.png \
--output_path outputs/result.pngExample with a custom prompt:
python -m fill_my_mirror \
--image /path/to/image.png \
--mask /path/to/mask.png \
--prompt "Generate a realistic mirror reflection consistent with the scene geometry and lighting."Run the pipeline on every sample in the HuggingFace dataset with scripts/run_batch.py.
Results are saved as {output_dir}/seed_{seed}/{index}.png, ready for use with the batch evaluation command.
python scripts/run_batch.py --dataset real --output-dir outputs/batch_real/Required:
| Argument | Choices | Description |
|---|---|---|
--dataset |
real, blender, mirrorbench_v2 |
Which dataset to use |
--output-dir |
Root output directory. Results saved to {output_dir}/seed_{seed}/{index}.png |
Optional:
| Argument | Default | Description |
|---|---|---|
--config |
configs/config.yaml |
Path to a YAML configuration file |
--start-index |
0 |
First dataset index to process (inclusive) |
--end-index |
(full dataset) | Last dataset index to process (exclusive) |
--skip-existing |
False |
Skip indices whose output file already exists (useful for resuming) |
--blender-path |
(from config) | Path to the Blender executable. Overrides the config file value |
All standard pipeline arguments (--prompt, --strength, --num-inference-steps, --guidance-scale, --seed, --n, --t-prime, etc.) are accepted and forwarded to the pipeline unchanged — see Command Line Arguments for their descriptions and defaults.
Metrics are reported for two regions of the mirror and the full image:
| Metric | Region | Description |
|---|---|---|
clip_similarity | Full image | CLIP ViT-B/32 image–text cosine similarity |
psnr_full_mirror | Full mirror mask | PSNR restricted to mirror pixels |
ssim_full_mirror | SSIM restricted to mirror pixels | |
lpips_full_mirror | LPIPS restricted to mirror pixels | |
psnr_constrained | Constrained pixels only | PSNR restricted to geometrically-determined pixels |
ssim_constrained | SSIM restricted to geometrically-determined pixels | |
lpips_constrained | LPIPS restricted to geometrically-determined pixels |
Evaluate a single locally provided image (no dataset required):
python scripts/evaluate.py local \
--generated /path/to/generated.png \
--gt /path/to/gt_image.png \
--mask /path/to/mask.png \
--save-dir eval/sample_0/ \
--prompt "A standing mirror reflects a bed with a dotted cover in a cozy bedroom."local arguments — required:
| Argument | Description |
|---|---|
--generated |
Path to the generated/inpainted image |
--gt |
Path to the ground-truth image |
--mask |
Path to the binary mirror mask |
--save-dir |
Directory to save the metrics CSV |
local arguments — optional:
| Argument | Default | Description |
|---|---|---|
--prompt |
— | Text prompt for CLIP similarity |
--rcs-dilation |
5 |
Dilation radius for the RCS mask |
--config |
configs/config.yaml |
Path to a YAML configuration file |
Evaluate a batch of results against the real-images HuggingFace dataset:
python scripts/evaluate.py batch \
--results-dir outputs/my_run/ \
--dataset real \
--output-dir outputs/eval/real/Evaluate against the Blender dataset:
python scripts/evaluate.py batch \
--results-dir outputs/blender_run/ \
--dataset blender \
--output-dir outputs/eval/blender/batch arguments — required:
| Argument | Choices | Description |
|---|---|---|
--results-dir |
Directory containing result PNGs named {index}.png |
|
--dataset |
real, blender, mirrorbench_v2 |
Which dataset to load ground truth from |
--output-dir |
Directory to save per-sample and aggregate CSVs |
batch arguments — optional:
| Argument | Default | Description |
|---|---|---|
--prompt |
— | Override CLIP prompt for all samples |
--rcs-dilation |
5 |
Dilation radius for the RCS mask |
--blender-path |
(from config) | Path to the Blender executable (needed for --dataset blender) |
--config |
configs/config.yaml |
Path to a YAML configuration file |
The constrained mask isolates mirror pixels whose appearance is geometrically determined by the visible scene — enabling a more faithful evaluation of reflection correctness than metrics over the full mirror region.
Real images (HF dataset or local) — computed via the Reflection Consistency Score (RCS): MASt3R finds dense correspondences between the scene view and a flipped mirror view; matched mirror pixels are dilated and intersected with the mirror mask.
Blender dataset — derived from ground-truth 3D geometry: the projection pipeline is run with GT point cloud and intrinsics; pixels covered by the projection are the constrained region.
Two datasets are provided:
Contains 50 real-world mirror scenes with the following columns per sample:
| Column | Type | Description |
|---|---|---|
id |
int32 | Sample index |
image |
image | Input image (mirror region is black) |
mask |
image | Binary mask of the mirror region |
gt_image |
image | Ground-truth image (mirror filled) |
prompt |
string | Text description of the scene |
Contains 15 rendered Blender scenes with mirrors, including ground-truth mirror masks, geometry, and manually assigned prompts.
| Column | Type | Description |
|---|---|---|
id |
int32 | Sample index |
image |
image | Rendered scene image |
mask |
image | Ground-truth binary mask of the mirror region |
gt_image |
image | Ground-truth image (mirror filled) |
prompt |
string | Manually assigned text description of the scene |
points |
float32 (800×800×3) | 3D point cloud of the scene geometry |
depth |
float32 (800×800) | Depth map of the scene |
intrinsics |
float32 (3×3) | Camera intrinsics matrix |
A large-scale synthetic benchmark from MirrorVerse (CVPR'25) containing 207K rendered scenes. The HDF5 files are distributed as tar archives on HuggingFace and must be downloaded and extracted before use. The split CSV (test.csv) is downloaded automatically.
Setup:
Run the provided download script to fetch and extract all archives:
python scripts/download_mirrorbench_v2.pyTo download and extract a single batch only:
python scripts/download_mirrorbench_v2.py --batch 1Note: batch indices start at 1 (there is no batch_0.tar); valid values are 1-35.
After extraction the directory should look like:
data/mirrorbench_v2/
hf-objaverse-v4/
000-010/
8014aa16057a495795f7bf8a02a3ebe0/
0.hdf5 1.hdf5 2.hdf5
...
abo_v4/
...
Geometry is derived from the HDF5 depth map and camera intrinsics — no MoGe inference is run.
Run a single sample:
python -m fill_my_mirror --use-mirrorbench-data --hf-index 0Run batch inference:
python scripts/run_batch.py --dataset mirrorbench_v2 --output-dir outputs/batch_mirrorbench/The default configuration is stored in configs/config.yaml:
prompt: "Complete the mirror reflection realistically and consistently with the scene geometry."
default_output_path: "outputs/result.png"
blender_path: "external/blender/blender-4.4.3-linux-x64/blender"
geometry_model_name: "Ruicheng/moge-2-vitl-normal"
inpainting_model_name: "black-forest-labs/FLUX.1-Fill-dev"
mast3r_model_name: "naver/MASt3R_ViTLarge_BaseDecoder_512_catmlpdpt_metric"geometry_model_name selects the geometry estimator used for real images (Blender samples always use ground-truth geometry).
| Model ID | Notes |
|---|---|
Ruicheng/moge-2-vitl-normal |
Default; MoGe-V2 monocular geometry estimator |
depth-anything/DA3-LARGE-1.1 |
Depth Anything 3; any depth-anything/* variant is accepted |
To switch, edit configs/config.yaml:
geometry_model_name: "depth-anything/DA3-LARGE-1.1"Note: Depth Anything 3 does not yet support multi-mirror scenes. Use MoGe-V2 for images with more than one mirror.
inpainting_model_name selects the dual-mask backend.
| Model ID | Backend class | Notes |
|---|---|---|
black-forest-labs/FLUX.1-Fill-dev |
DualMaskInterpolatedFluxFillPipeline |
Default; native inpainting backend built on FluxFillPipeline |
black-forest-labs/FLUX.2-klein-base-9B |
DualMaskFlux2KleinInpaintPipeline |
Reference-guided inpainting; passes the original scene as image and the projected mirror view as image_reference |
To switch, edit configs/config.yaml:
inpainting_model_name: "black-forest-labs/FLUX.1-Fill-dev"Both backends use the same dual-mask noise interpolation parameters n and
t_prime. When an argument is unsupported by the selected backend, Fill My
Mirror logs a warning and silently ignores it.
If you use this code in your research, please cite:
@article{fillmymirror2026,
title = {Fill My Mirror: Geometry-Constrained Mirror Inpainting},
author = {Basson, Ofek and Vainer, Shimon and Hel-Or, Yacov and Fried, Ohad},
journal = {arXiv preprint arXiv:2609.03740},
year = {2026},
}