|
import gradio as gr |
|
from PIL import Image |
|
import numpy as np |
|
|
|
def brightness_to_opacity_overlay(foreground_img, background_img, invert_opacity=False): |
|
if foreground_img is None or background_img is None: |
|
return None |
|
|
|
|
|
bg = background_img.convert("RGBA") |
|
fg = foreground_img.convert("RGB").resize(bg.size) |
|
|
|
|
|
fg_array = np.array(fg) |
|
luminance = np.dot(fg_array[...,:3], [0.299, 0.587, 0.114]).astype(np.uint8) |
|
|
|
|
|
alpha = 255 - luminance if invert_opacity else luminance |
|
|
|
|
|
white_rgb = np.ones_like(fg_array) * 255 |
|
rgba_array = np.dstack((white_rgb, alpha)).astype(np.uint8) |
|
overlay = Image.fromarray(rgba_array, mode="RGBA") |
|
|
|
|
|
bg_arr = np.array(bg).astype(np.float32) / 255.0 |
|
ov_arr = np.array(overlay).astype(np.float32) / 255.0 |
|
|
|
|
|
alpha_bg = bg_arr[..., 3:4] |
|
alpha_ov = ov_arr[..., 3:4] |
|
|
|
|
|
out_alpha = alpha_ov + alpha_bg * (1 - alpha_ov) |
|
out_rgb = ( |
|
ov_arr[..., :3] * alpha_ov + |
|
bg_arr[..., :3] * alpha_bg * (1 - alpha_ov) |
|
) / np.clip(out_alpha, 1e-6, 1) |
|
|
|
|
|
out_image = np.dstack((out_rgb, out_alpha)).clip(0, 1) * 255 |
|
out_image = Image.fromarray(out_image.astype(np.uint8), mode="RGBA") |
|
|
|
return out_image |
|
|
|
|
|
iface = gr.Interface( |
|
fn=brightness_to_opacity_overlay, |
|
inputs=[ |
|
gr.Image(type="pil", label="Mask Source (Brightness to Alpha)"), |
|
gr.Image(type="pil", label="Background Image (with or without transparency)"), |
|
gr.Checkbox(label="Invert Opacity", value=False) |
|
], |
|
outputs=gr.Image(type="pil", label="Final Composite"), |
|
title="Brightness-to-Alpha Overlay Tool (with Transparency Fix)", |
|
description=( |
|
"Uses the brightness of the first image as alpha, resizes to match the second image, " |
|
"and overlays correctly even when the background has transparency." |
|
) |
|
) |
|
|
|
if __name__ == "__main__": |
|
iface.launch() |