Skip to content

Conversation

@elismasilva
Copy link
Contributor

@elismasilva elismasilva commented Apr 1, 2025

What does this PR do?

This PR implements a pipeline for (CVPR 2025) FaithDiff: Unleashing Diffusion Priors for Faithful Image Super-resolution
[Project Page]   [Paper]

I think this can complement #9740, with a step 1 where it is possible to restore low quality images, in addition to it also allowing a great upscale. For more details see the project links above.

Thanks to @JyChen9811 for his amazing work! Questions about the paper can be directed to him directly.

Example Usage

This example upscale and restores a low-quality image. The input image has a resolution of 512x512 and will be upscaled at a scale of 2x, to a final resolution of 1024x1024. It is possible to upscale to a larger scale, but it is recommended that the input image be at least 1024x1024 in these cases. To upscale this image by 4x, for example, it would be recommended to re-input the result into a new 2x processing, thus performing progressive scaling.

Local Test

import random
import numpy as np
import torch
from diffusers import AutoencoderKL
from pipeline_faithdiff_stable_diffusion_xl import FaithDiffStableDiffusionXLPipeline
from huggingface_hub import hf_hub_download
from diffusers.utils import load_image
from PIL import Image

device = "cuda"
dtype = torch.float16
MAX_SEED = np.iinfo(np.int32).max

# Download weights for additional unet layers
model_file = hf_hub_download(
    "jychen9811/FaithDiff",
    filename="FaithDiff.bin", local_dir="./proc_data/faithdiff", local_dir_use_symlinks=False
)

# Initialize the models and pipeline
vae = AutoencoderKL.from_pretrained("madebyollin/sdxl-vae-fp16-fix", torch_dtype=dtype)

model_id = "SG161222/RealVisXL_V4.0"
pipe = FaithDiffStableDiffusionXLPipeline.from_pretrained(
    model_id,
    torch_dtype=dtype,
    vae=vae,
    unet=None, #<- Do not load with original model.    
    use_safetensors=True,
    variant="fp16",
).to(device)

# Here we need use pipeline internal unet model
pipe.unet = pipe.unet_model.from_pretrained(model_id, subfolder="unet", variant="fp16", use_safetensors=True)

# Load aditional layers to the model
pipe.unet.load_additional_layers(weight_path="proc_data/faithdiff/FaithDiff.bin", dtype=dtype)

# Enable vae tiling
pipe.set_encoder_tile_settings()
pipe.enable_vae_tiling()

# Optimization
pipe.enable_model_cpu_offload()

#input params
prompt = "The image features a woman in her 55s with blonde hair and a white shirt, smiling at the camera. She appears to be in a good mood and is wearing a white scarf around her neck. "
upscale = 2 # scale here
start_point = "lr" # or "noise"
latent_tiled_overlap = 0.5
latent_tiled_size = 1024

# Load image
lq_image = load_image("https://huggingface.co/datasets/DEVAIEXP/assets/resolve/main/woman.png")
original_height = lq_image.height
original_width = lq_image.width
print(f"Current resolution: H:{original_height} x W:{original_width}")

width = original_width * int(upscale)
height = original_height * int(upscale)
print(f"Final resolution: H:{height} x W:{width}")

# Restoration
image = lq_image.resize((width, height), Image.LANCZOS)
input_image, width_init, height_init, width_now, height_now = pipe.check_image_size(image)

generator = torch.Generator(device=device).manual_seed(random.randint(0, MAX_SEED))
gen_image = pipe(lr_img=input_image, 
                 prompt = prompt,                  
                 num_inference_steps=20, 
                 guidance_scale=5, 
                 generator=generator, 
                 start_point=start_point, 
                 height = height_now, 
                 width=width_now, 
                 overlap=latent_tiled_overlap, 
                 target_size=(latent_tiled_size, latent_tiled_size)
                ).images[0]

cropped_image = gen_image.crop((0, 0, width_init, height_init))
cropped_image.save("data/result.png")

Local testing after published

import random
import numpy as np
import torch
from diffusers import DiffusionPipeline, AutoencoderKL
from huggingface_hub import hf_hub_download
from diffusers.utils import load_image
from PIL import Image

device = "cuda"
dtype = torch.float16
MAX_SEED = np.iinfo(np.int32).max

# Download weights for additional unet layers
model_file = hf_hub_download(
    "jychen9811/FaithDiff",
    filename="FaithDiff.bin", local_dir="./proc_data/faithdiff", local_dir_use_symlinks=False
)

# Initialize the models and pipeline
vae = AutoencoderKL.from_pretrained("madebyollin/sdxl-vae-fp16-fix", torch_dtype=dtype)

model_id = "SG161222/RealVisXL_V4.0"
pipe = DiffusionPipeline.from_pretrained(
    model_id,
    torch_dtype=dtype,
    vae=vae,
    unet=None, #<- Do not load with original model.
    custom_pipeline="pipeline_faithdiff_stable_diffusion_xl",    
    use_safetensors=True,
    variant="fp16",
).to(device)

# Here we need use pipeline internal unet model
pipe.unet = pipe.unet_model.from_pretrained(model_id, subfolder="unet", variant="fp16", use_safetensors=True)

# Load aditional layers to the model
pipe.unet.load_additional_layers(weight_path="proc_data/faithdiff/FaithDiff.bin", dtype=dtype)

# Enable vae tiling
pipe.set_encoder_tile_settings()
pipe.enable_vae_tiling()

# Optimization
pipe.enable_model_cpu_offload()

#input params
prompt = "The image features a woman in her 55s with blonde hair and a white shirt, smiling at the camera. She appears to be in a good mood and is wearing a white scarf around her neck. "
upscale = 2 # scale here
start_point = "lr" # or "noise"
latent_tiled_overlap = 0.5
latent_tiled_size = 1024

# Load image
lq_image = load_image("https://huggingface.co/datasets/DEVAIEXP/assets/resolve/main/woman.png")
original_height = lq_image.height
original_width = lq_image.width
print(f"Current resolution: H:{original_height} x W:{original_width}")

width = original_width * int(upscale)
height = original_height * int(upscale)
print(f"Final resolution: H:{height} x W:{width}")

# Restoration
image = lq_image.resize((width, height), Image.LANCZOS)
input_image, width_init, height_init, width_now, height_now = pipe.check_image_size(image)

generator = torch.Generator(device=device).manual_seed(random.randint(0, MAX_SEED))
gen_image = pipe(lr_img=input_image, 
                 prompt = prompt,                  
                 num_inference_steps=20, 
                 guidance_scale=5, 
                 generator=generator, 
                 start_point=start_point, 
                 height = height_now, 
                 width=width_now, 
                 overlap=latent_tiled_overlap, 
                 target_size=(latent_tiled_size, latent_tiled_size)
                ).images[0]

cropped_image = gen_image.crop((0, 0, width_init, height_init))
cropped_image.save("data/result.png")

Before submitting

Result

Who can review?

@asomoza @sayakpaul @yiyixuxu

Copy link
Collaborator

@yiyixuxu yiyixuxu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool, thank you!

@HuggingFaceDocBuilderDev

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

@yiyixuxu yiyixuxu merged commit c4646a3 into huggingface:main Apr 2, 2025
7 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants