LumiPic β€” Single-Image SDR to HDR LoRA

Converts standard dynamic range (SDR) images to high dynamic range (HDR) EXR files β€” float-valued, with range well beyond what an 8-bit SDR output can carry.

Based on LumiVid (paper) β€” the Lightricks research that introduced LogC3-encoded diffusion for HDR generation. LumiPic is the same technique adapted to single-image diffusion transformers; the technique is base-model agnostic. Two trained LoRA families are published here, on different bases.

Examples

Same 20 HDR outputs, viewed at two extreme exposure offsets β€” highlights still hold structure at EV+6, shadows still hold information at EV-6.

Exposure +6 (highlights pulled down):

EV+6

Exposure -6 (shadows pushed up):

EV-6

Weights

Qwen-Image-Edit-2511 (mature)

5+ training iterations, 563 MB per LoRA. Best quality, larger base (~54 GB).

  • v5b_step2000.safetensors β€” Qwen default. Most robust overall; best on stylized/AI-generated SDR inputs.
  • v9_step1500.safetensors β€” alternative. LumiVid-aligned augs (joint HDR+SDR EV shifts, luminance blur p=1.0). Slightly better on natural photos.
  • hdrdit_v1_QE2511.safetensors β€” original v1 release.

FLUX.2-klein-base-4B (alpha)

Single training iteration, 88 MB per LoRA. Apache 2.0 base, 5Γ— smaller than Qwen, fastest end-to-end.

  • klein4b_alpha_step1750.safetensors β€” klein-4B default. Faithful HDR look; well-balanced.
  • klein4b_alpha_step1000.safetensors β€” alternative. Aggressive HDR (higher p99) but tends to blow out bright highlights.

FLUX.2-klein-base-9B (alpha)

Single training iteration, 158 MB per LoRA. Larger klein variant β€” more capacity, more nuanced HDR. Base model is gated on HF (request access at the base model page before first run).

  • klein9b_alpha_step2000.safetensors β€” klein-9B default. Stable, well-behaved (mean p99 9.1, no scenes saturated in our 20-image benchmark).
  • klein9b_alpha_step1250.safetensors β€” most "good" scenes (13/20 in 5-50 p99 range, 0 saturated). Practically the best by per-scene quality count.
  • klein9b_alpha_step1000.safetensors β€” most aggressive (mean p99 20.6, but 4 scenes saturated). Pre-overfit peak HDR range.
  • klein9b_alpha_step{250,500,750,1500,1750}.safetensors β€” intermediate snapshots, available for experimentation.

The training overfit curve was steep on klein-9B (mean p99: 1000=20.6 β†’ 1250=11.1 β†’ 1500=9.3 β†’ 1750=9.6 β†’ 2000=9.1). Step 2000 is the safest default; step 1250 is the practical sweet spot.

Usage

Qwen-Image-Edit-2511:

from diffusers import QwenImageEditPipeline
import torch
from PIL import Image

pipe = QwenImageEditPipeline.from_pretrained("Qwen/Qwen-Image-Edit-2511", torch_dtype=torch.bfloat16)
pipe.load_lora_weights("oumoumad/LumiPic", weight_name="v5b_step2000.safetensors")
pipe = pipe.to("cuda")

image = Image.open("photo.jpg").convert("RGB")
output = pipe(prompt="Convert this image to HDR", image=image,
              num_inference_steps=40, guidance_scale=3.0, output_type="pt")
# Decode LogC3 β†’ linear HDR (see logc3.py in GitHub repo)

FLUX.2-klein-base-4B / 9B (requires bleeding-edge diffusers: pip install "git+https://github.com/huggingface/diffusers.git"):

from diffusers import Flux2KleinPipeline
import torch
from PIL import Image

# 4B (Apache 2.0, ungated)
pipe = Flux2KleinPipeline.from_pretrained("black-forest-labs/FLUX.2-klein-base-4B", torch_dtype=torch.bfloat16)
pipe.load_lora_weights("oumoumad/LumiPic", weight_name="klein4b_alpha_step1750.safetensors")

# 9B (gated β€” accept license on HF first)
# pipe = Flux2KleinPipeline.from_pretrained("black-forest-labs/FLUX.2-klein-base-9B", torch_dtype=torch.bfloat16)
# pipe.load_lora_weights("oumoumad/LumiPic", weight_name="klein9b_alpha_step2000.safetensors")

pipe.enable_model_cpu_offload()  # or pipe.to("cuda") if you have 32GB+

image = Image.open("photo.jpg").convert("RGB")
output = pipe(prompt="Convert this image to HDR", image=image,
              num_inference_steps=25, guidance_scale=3.0, output_type="pt")

See the GitHub repo for complete inference code with EXR output (inference.py for Qwen, inference_klein.py for klein).

Quick Inference

git clone https://github.com/oumad/LumiPic.git && cd LumiPic
pip install -r requirements.txt
python inference.py --image photo.jpg          # Qwen path (production)
python inference_klein.py --image photo.jpg    # klein path (alpha)

The base model and LoRA weights download automatically on first run.

ComfyUI

Three ready-to-use workflows:

Both use the Gear Β· LogC3 Decode + Save EXR node from ComfyUI_Gear for the LogC3 decode + EXR write. Drop the JSON onto your canvas, place the matching LoRA file in ComfyUI/models/loras/{qwen,flux}/hdr/, install ComfyUI_Gear, queue with prompt "Convert this image to HDR".

Training

  • Technique: LoRA (rank 32) on the DiT transformer, trained to output ARRI LogC3-encoded HDR
  • Dataset: ~260–606 diverse HDR pairs (Poly Haven HDRIs, RED/ARRI footage, CG renders, Blender scenes), augmented with exposure shifts, luminance blur, contrast, JPEG, white-balance jitter
  • Hyperparams: rank 32, alpha 32, bf16, AdamW lr 1e-4, flowmatch scheduler
  • Steps: 2000 (Qwen sweet spot ~1500; klein-4B sweet spot ~1500–1750, peak HDR at 1000)
  • Trained with Ostris AI-Toolkit. Qwen LoRAs use a fork with float32 .npy targets (npy-float32-targets branch); klein LoRAs use upstream + a 4-line VAE-on-GPU patch to flux2_model.py.
Downloads last month
1,172
Inference Providers NEW

Model tree for oumoumad/LumiPic

Adapter
(93)
this model

Paper for oumoumad/LumiPic