Spaces:
Paused
Paused
Create app.py
Browse files
app.py
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
from PIL import Image
|
| 3 |
+
import numpy as np
|
| 4 |
+
|
| 5 |
+
def create_empty_image():
|
| 6 |
+
"""
|
| 7 |
+
Create an empty transparent image (512x512, RGBA mode).
|
| 8 |
+
:return: Empty PIL.Image object.
|
| 9 |
+
"""
|
| 10 |
+
return Image.new("RGBA", (512, 512), (0, 0, 0, 0))
|
| 11 |
+
|
| 12 |
+
def overlay_images(images, alphas, positions):
|
| 13 |
+
"""
|
| 14 |
+
Overlay multiple semi-transparent layers and control their positions.
|
| 15 |
+
:param images: List of uploaded images (PIL.Image objects).
|
| 16 |
+
:param alphas: List of transparency values for each layer (floats between 0 and 1).
|
| 17 |
+
:param positions: List of positions for each layer, format: [(x1, y1), (x2, y2), ...].
|
| 18 |
+
:return: Overlayed image with transparent background, overlayed image with black background, and list of individual layer images.
|
| 19 |
+
"""
|
| 20 |
+
if not images:
|
| 21 |
+
return None, None, []
|
| 22 |
+
|
| 23 |
+
# Create a transparent canvas (1024x1024)
|
| 24 |
+
transparent_canvas = Image.new("RGBA", (1024, 1024), (0, 0, 0, 0))
|
| 25 |
+
# Create a black canvas (1024x1024)
|
| 26 |
+
black_canvas = Image.new("RGBA", (1024, 1024), (0, 0, 0, 255))
|
| 27 |
+
layer_images = [] # Store individual layer images
|
| 28 |
+
|
| 29 |
+
# Overlay each layer
|
| 30 |
+
for i, img in enumerate(images):
|
| 31 |
+
# Ensure the image is a PIL.Image object
|
| 32 |
+
if not isinstance(img, Image.Image):
|
| 33 |
+
img = create_empty_image()
|
| 34 |
+
# Resize the layer to 512x512
|
| 35 |
+
layer = img.convert("RGBA").resize((512, 512))
|
| 36 |
+
# Set transparency
|
| 37 |
+
layer = Image.fromarray(
|
| 38 |
+
(np.array(layer) * np.array([1, 1, 1, alphas[i]])).astype(np.uint8)
|
| 39 |
+
)
|
| 40 |
+
# Get layer position
|
| 41 |
+
x, y = positions[i]
|
| 42 |
+
# Expand from bottom-left to top-right
|
| 43 |
+
x_offset = x # X coordinate starts from 0 and expands to the right
|
| 44 |
+
y_offset = 1024 - y - layer.height # Y coordinate starts from the bottom and expands upward
|
| 45 |
+
# Paste the layer onto the transparent canvas
|
| 46 |
+
transparent_canvas.paste(layer, (x_offset, y_offset), layer)
|
| 47 |
+
# Paste the layer onto the black canvas
|
| 48 |
+
black_canvas.paste(layer, (x_offset, y_offset), layer)
|
| 49 |
+
|
| 50 |
+
# Generate individual layer image
|
| 51 |
+
layer_canvas = Image.new("RGBA", (1024, 1024), (0, 0, 0, 0))
|
| 52 |
+
layer_canvas.paste(layer, (x_offset, y_offset), layer)
|
| 53 |
+
layer_images.append(layer_canvas)
|
| 54 |
+
|
| 55 |
+
# If there are fewer than 4 layers, fill with empty images
|
| 56 |
+
while len(layer_images) < 4:
|
| 57 |
+
layer_images.append(create_empty_image())
|
| 58 |
+
|
| 59 |
+
return transparent_canvas, black_canvas, layer_images
|
| 60 |
+
|
| 61 |
+
def crop_image(image, crop_x_min, crop_x_max, crop_y_min, crop_y_max):
|
| 62 |
+
"""
|
| 63 |
+
Crop an image.
|
| 64 |
+
:param image: Input image (PIL.Image object).
|
| 65 |
+
:param crop_x_min: X-axis crop start point.
|
| 66 |
+
:param crop_x_max: X-axis crop end point.
|
| 67 |
+
:param crop_y_min: Y-axis crop start point.
|
| 68 |
+
:param crop_y_max: Y-axis crop end point.
|
| 69 |
+
:return: Cropped image (PIL.Image object).
|
| 70 |
+
"""
|
| 71 |
+
if image is None:
|
| 72 |
+
return None
|
| 73 |
+
# Ensure the crop range is within the image dimensions
|
| 74 |
+
x_min = max(0, min(crop_x_min, image.width))
|
| 75 |
+
x_max = max(0, min(crop_x_max, image.width))
|
| 76 |
+
y_min = max(0, min(crop_y_min, image.height))
|
| 77 |
+
y_max = max(0, min(crop_y_max, image.height))
|
| 78 |
+
# Crop the image
|
| 79 |
+
return image.crop((x_min, y_min, x_max, y_max))
|
| 80 |
+
|
| 81 |
+
def update_output(image1, image2, image3, image4, alpha1, alpha2, alpha3, alpha4, x1, y1, x2, y2, x3, y3, x4, y4, crop_x_min, crop_x_max, crop_y_min, crop_y_max):
|
| 82 |
+
"""
|
| 83 |
+
Update the output images.
|
| 84 |
+
:param image1, image2, image3, image4: Uploaded images.
|
| 85 |
+
:param alpha1, alpha2, alpha3, alpha4: Transparency values for each layer.
|
| 86 |
+
:param x1, y1, x2, y2, x3, y3, x4, y4: Positions for each layer.
|
| 87 |
+
:param crop_x_min: X-axis crop start point.
|
| 88 |
+
:param crop_x_max: X-axis crop end point.
|
| 89 |
+
:param crop_y_min: Y-axis crop start point.
|
| 90 |
+
:param crop_y_max: Y-axis crop end point.
|
| 91 |
+
:return: Cropped transparent background image, black background image, and individual layer images.
|
| 92 |
+
"""
|
| 93 |
+
# Print logs to check the type and content of each input
|
| 94 |
+
print("image1:", type(image1), image1)
|
| 95 |
+
print("image2:", type(image2), image2)
|
| 96 |
+
print("image3:", type(image3), image3)
|
| 97 |
+
print("image4:", type(image4), image4)
|
| 98 |
+
print("alpha1:", type(alpha1), alpha1)
|
| 99 |
+
print("alpha2:", type(alpha2), alpha2)
|
| 100 |
+
print("alpha3:", type(alpha3), alpha3)
|
| 101 |
+
print("alpha4:", type(alpha4), alpha4)
|
| 102 |
+
print("x1:", type(x1), x1)
|
| 103 |
+
print("y1:", type(y1), y1)
|
| 104 |
+
print("x2:", type(x2), x2)
|
| 105 |
+
print("y2:", type(y2), y2)
|
| 106 |
+
print("x3:", type(x3), x3)
|
| 107 |
+
print("y3:", type(y3), y3)
|
| 108 |
+
print("x4:", type(x4), x4)
|
| 109 |
+
print("y4:", type(y4), y4)
|
| 110 |
+
print("crop_x_min:", type(crop_x_min), crop_x_min)
|
| 111 |
+
print("crop_x_max:", type(crop_x_max), crop_x_max)
|
| 112 |
+
print("crop_y_min:", type(crop_y_min), crop_y_min)
|
| 113 |
+
print("crop_y_max:", type(crop_y_max), crop_y_max)
|
| 114 |
+
|
| 115 |
+
# If an image is None, use an empty image
|
| 116 |
+
images = [
|
| 117 |
+
image1 if image1 is not None else create_empty_image(),
|
| 118 |
+
image2 if image2 is not None else create_empty_image(),
|
| 119 |
+
image3 if image3 is not None else create_empty_image(),
|
| 120 |
+
image4 if image4 is not None else create_empty_image()
|
| 121 |
+
]
|
| 122 |
+
alphas = [alpha1, alpha2, alpha3, alpha4] # Transparency list
|
| 123 |
+
positions = [(x1, y1), (x2, y2), (x3, y3), (x4, y4)] # Position list
|
| 124 |
+
|
| 125 |
+
# Call the overlay function
|
| 126 |
+
transparent_image, black_image, layer_images = overlay_images(images, alphas, positions)
|
| 127 |
+
|
| 128 |
+
# Crop the output images
|
| 129 |
+
transparent_image = crop_image(transparent_image, crop_x_min, crop_x_max, crop_y_min, crop_y_max)
|
| 130 |
+
black_image = crop_image(black_image, crop_x_min, crop_x_max, crop_y_min, crop_y_max)
|
| 131 |
+
layer_images = [crop_image(img, crop_x_min, crop_x_max, crop_y_min, crop_y_max) for img in layer_images]
|
| 132 |
+
|
| 133 |
+
# Return 6 values (cropped transparent background image + black background image + 4 individual layer images)
|
| 134 |
+
return [transparent_image, black_image] + layer_images
|
| 135 |
+
|
| 136 |
+
# Example data
|
| 137 |
+
example_images = [
|
| 138 |
+
Image.open("furina_yellow.webp") if "furina_yellow.webp" else create_empty_image(),
|
| 139 |
+
Image.open("nilou_blue.webp") if "nilou_blue.webp" else create_empty_image(),
|
| 140 |
+
create_empty_image(), # Layer 3 is an empty image
|
| 141 |
+
create_empty_image() # Layer 4 is an empty image
|
| 142 |
+
]
|
| 143 |
+
example_alphas = [1.0, 1.0, 1.0, 1.0] # Example transparency values
|
| 144 |
+
example_positions = [(101, 0), (264, 0), (0, 0), (0, 0)] # Example positions
|
| 145 |
+
example_crop_x = (160, 850) # Example X-axis crop range
|
| 146 |
+
example_crop_y = (360, 1020) # Example Y-axis crop range
|
| 147 |
+
|
| 148 |
+
# Gradio interface
|
| 149 |
+
with gr.Blocks() as demo:
|
| 150 |
+
gr.Markdown("## 🎨 Layer Overlay Application")
|
| 151 |
+
gr.Markdown("Upload multiple semi-transparent layers (PNG format), set transparency and position, and generate the overlayed image.")
|
| 152 |
+
|
| 153 |
+
with gr.Row():
|
| 154 |
+
# Left column: Inputs
|
| 155 |
+
with gr.Column():
|
| 156 |
+
gr.Markdown("### Upload Layers")
|
| 157 |
+
image1 = gr.Image(label="Layer 1", type="pil", image_mode="RGBA")
|
| 158 |
+
image2 = gr.Image(label="Layer 2", type="pil", image_mode="RGBA")
|
| 159 |
+
image3 = gr.Image(label="Layer 3", type="pil", image_mode="RGBA")
|
| 160 |
+
image4 = gr.Image(label="Layer 4", type="pil", image_mode="RGBA")
|
| 161 |
+
|
| 162 |
+
gr.Markdown("### Set Transparency")
|
| 163 |
+
alpha1 = gr.Slider(0, 1, value=1, label="Layer 1 Transparency")
|
| 164 |
+
alpha2 = gr.Slider(0, 1, value=1, label="Layer 2 Transparency")
|
| 165 |
+
alpha3 = gr.Slider(0, 1, value=1, label="Layer 3 Transparency")
|
| 166 |
+
alpha4 = gr.Slider(0, 1, value=1, label="Layer 4 Transparency")
|
| 167 |
+
|
| 168 |
+
gr.Markdown("### Set Positions")
|
| 169 |
+
with gr.Row():
|
| 170 |
+
x1 = gr.Slider(0, 512, value=0, label="Layer 1 X Position")
|
| 171 |
+
y1 = gr.Slider(0, 512, value=0, label="Layer 1 Y Position")
|
| 172 |
+
with gr.Row():
|
| 173 |
+
x2 = gr.Slider(0, 512, value=0, label="Layer 2 X Position")
|
| 174 |
+
y2 = gr.Slider(0, 512, value=0, label="Layer 2 Y Position")
|
| 175 |
+
with gr.Row():
|
| 176 |
+
x3 = gr.Slider(0, 512, value=0, label="Layer 3 X Position")
|
| 177 |
+
y3 = gr.Slider(0, 512, value=0, label="Layer 3 Y Position")
|
| 178 |
+
with gr.Row():
|
| 179 |
+
x4 = gr.Slider(0, 512, value=0, label="Layer 4 X Position")
|
| 180 |
+
y4 = gr.Slider(0, 512, value=0, label="Layer 4 Y Position")
|
| 181 |
+
|
| 182 |
+
gr.Markdown("### Set Crop Range")
|
| 183 |
+
with gr.Row():
|
| 184 |
+
crop_x_min = gr.Slider(0, 1024, value=0, label="X-axis Crop Start")
|
| 185 |
+
crop_x_max = gr.Slider(0, 1024, value=1024, label="X-axis Crop End")
|
| 186 |
+
with gr.Row():
|
| 187 |
+
crop_y_min = gr.Slider(0, 1024, value=0, label="Y-axis Crop Start")
|
| 188 |
+
crop_y_max = gr.Slider(0, 1024, value=1024, label="Y-axis Crop End")
|
| 189 |
+
|
| 190 |
+
run_button = gr.Button("Generate Overlayed Image")
|
| 191 |
+
|
| 192 |
+
# Right column: Outputs
|
| 193 |
+
with gr.Column():
|
| 194 |
+
gr.Markdown("### Overlay Results")
|
| 195 |
+
transparent_output = gr.Image(label="Overlayed Image (Transparent Background)", type="pil")
|
| 196 |
+
black_output = gr.Image(label="Overlayed Image (Black Background)", type="pil")
|
| 197 |
+
gr.Markdown("### Individual Layer Images")
|
| 198 |
+
layer1_image = gr.Image(label="Layer 1 Image", type="pil")
|
| 199 |
+
layer2_image = gr.Image(label="Layer 2 Image", type="pil")
|
| 200 |
+
layer3_image = gr.Image(label="Layer 3 Image", type="pil")
|
| 201 |
+
layer4_image = gr.Image(label="Layer 4 Image", type="pil")
|
| 202 |
+
|
| 203 |
+
# Bind events
|
| 204 |
+
run_button.click(
|
| 205 |
+
update_output,
|
| 206 |
+
inputs=[
|
| 207 |
+
image1, image2, image3, image4, # Images
|
| 208 |
+
alpha1, alpha2, alpha3, alpha4, # Transparency
|
| 209 |
+
x1, y1, x2, y2, x3, y3, x4, y4, # Positions
|
| 210 |
+
crop_x_min, crop_x_max, # X-axis crop range
|
| 211 |
+
crop_y_min, crop_y_max # Y-axis crop range
|
| 212 |
+
],
|
| 213 |
+
outputs=[transparent_output, black_output, layer1_image, layer2_image, layer3_image, layer4_image]
|
| 214 |
+
)
|
| 215 |
+
|
| 216 |
+
# Add examples
|
| 217 |
+
gr.Examples(
|
| 218 |
+
examples=[
|
| 219 |
+
[
|
| 220 |
+
example_images[0], example_images[1], example_images[2], example_images[3], # Images
|
| 221 |
+
example_alphas[0], example_alphas[1], example_alphas[2], example_alphas[3], # Transparency
|
| 222 |
+
example_positions[0][0], example_positions[0][1], # Layer 1 position
|
| 223 |
+
example_positions[1][0], example_positions[1][1], # Layer 2 position
|
| 224 |
+
example_positions[2][0], example_positions[2][1], # Layer 3 position
|
| 225 |
+
example_positions[3][0], example_positions[3][1], # Layer 4 position
|
| 226 |
+
example_crop_x[0], example_crop_x[1], # X-axis crop range
|
| 227 |
+
example_crop_y[0], example_crop_y[1] # Y-axis crop range
|
| 228 |
+
]
|
| 229 |
+
],
|
| 230 |
+
inputs=[
|
| 231 |
+
image1, image2, image3, image4, # Images
|
| 232 |
+
alpha1, alpha2, alpha3, alpha4, # Transparency
|
| 233 |
+
x1, y1, x2, y2, x3, y3, x4, y4, # Positions
|
| 234 |
+
crop_x_min, crop_x_max, # X-axis crop range
|
| 235 |
+
crop_y_min, crop_y_max # Y-axis crop range
|
| 236 |
+
],
|
| 237 |
+
outputs=[transparent_output, black_output, layer1_image, layer2_image, layer3_image, layer4_image],
|
| 238 |
+
fn=update_output,
|
| 239 |
+
cache_examples=True
|
| 240 |
+
)
|
| 241 |
+
|
| 242 |
+
# Launch the application
|
| 243 |
+
if __name__ == "__main__":
|
| 244 |
+
demo.launch(share=True)
|