ravegenerator / raveblender.py
mirs44's picture
Rename app.py to raveblender.py
4ab63bb verified
raw
history blame
5.33 kB
import gradio as gr
from PIL import Image, ImageOps
import numpy as np
import os
import uuid
import random
from scipy import ndimage
# Ensure there's a directory for outputs
os.makedirs("outputs", exist\_ok=True)
def make\_square(img, size=3000, fill\_color=(0, 0, 0)):
x, y = img.size
scale = size / max(x, y)
new\_size = (int(x \* scale), int(y \* scale))
img = img.resize(new\_size, Image.Resampling.LANCZOS)
new\_img = Image.new("RGB", (size, size), fill\_color)
new\_img.paste(img, ((size - new\_size\[0]) // 2, (size - new\_size\[1]) // 2))
return new\_img
def pixel\_shuffle(img\_array, block\_size=10, shuffle\_strength=0.5):
"""Shuffle pixels in blocks to create generative effect"""
height, width, channels = img\_array.shape
result = np.copy(img\_array)
```
# Create blocks for shuffling
h_blocks = height // block_size
w_blocks = width // block_size
# Create list of block coordinates
blocks = []
for i in range(h_blocks):
for j in range(w_blocks):
blocks.append((i, j))
# Shuffle a percentage of blocks based on strength
num_blocks_to_shuffle = int(len(blocks) * shuffle_strength)
blocks_to_shuffle = random.sample(blocks, num_blocks_to_shuffle)
# Create a shuffled version of these blocks
target_positions = blocks_to_shuffle.copy()
random.shuffle(target_positions)
# Perform the shuffling
for (src_i, src_j), (tgt_i, tgt_j) in zip(blocks_to_shuffle, target_positions):
src_y, src_x = src_i * block_size, src_j * block_size
tgt_y, tgt_x = tgt_i * block_size, tgt_j * block_size
# Swap blocks
temp = np.copy(result[src_y:src_y+block_size, src_x:src_x+block_size])
result[src_y:src_y+block_size, src_x:src_x+block_size] = result[tgt_y:tgt_y+block_size, tgt_x:tgt_x+block_size]
result[tgt_y:tgt_y+block_size, tgt_x:tgt_x+block_size] = temp
return result
```
def flow\_distortion(img\_array, strength=10):
"""Apply flow-based distortion to simulate generative models"""
height, width, channels = img\_array.shape
result = np.zeros\_like(img\_array, dtype=np.float32)
```
# Create random flow fields for x and y directions
flow_x = np.random.normal(0, strength, (height, width))
flow_y = np.random.normal(0, strength, (height, width))
# Smooth the flow fields
flow_x = ndimage.gaussian_filter(flow_x, sigma=30)
flow_y = ndimage.gaussian_filter(flow_y, sigma=30)
# Create meshgrid for coordinate mapping
y_coords, x_coords = np.meshgrid(np.arange(height), np.arange(width), indexing='ij')
# Add flow to coordinates
x_mapped = x_coords + flow_x
y_mapped = y_coords + flow_y
# Clip to ensure we stay within bounds
x_mapped = np.clip(x_mapped, 0, width-1)
y_mapped = np.clip(y_mapped, 0, height-1)
# Sample from the original image using the warped coordinates
for c in range(channels):
result[:, :, c] = ndimage.map_coordinates(img_array[:, :, c], [y_mapped, x_mapped], order=1)
return result
```
def blend\_images\_with\_rearrangement(images, block\_size=20, shuffle\_strength=0.3, flow\_strength=5):
if len(images) < 2:
return "Upload at least two images.", None
```
try:
# Process images
processed = []
for img in images:
try:
processed.append(make_square(Image.open(img)))
except Exception as e:
return f"Error processing image: {str(e)}", None
# Convert images to numpy arrays
img_arrays = [np.array(img).astype(np.float32) for img in processed]
# Create a base canvas
base = np.zeros_like(img_arrays[0])
# Divide the images into a grid and randomly select pixels from different images
height, width, _ = base.shape
for i in range(0, height, block_size):
for j in range(0, width, block_size):
# Get end coordinates for the block
end_i = min(i + block_size, height)
end_j = min(j + block_size, width)
# Randomly select which image to pull this block from
source_img = random.choice(img_arrays)
base[i:end_i, j:end_j] = source_img[i:end_i, j:end_j]
# Apply pixel shuffling to the composite image
base = pixel_shuffle(base, block_size, shuffle_strength)
# Apply flow distortion to further randomize
base = flow_distortion(base, flow_strength)
# Blend with original images to preserve some coherence
for img_array in img_arrays:
base = base * 0.7 + img_array * 0.3 / len(img_arrays)
final = Image.fromarray(np.uint8(np.clip(base, 0, 255)))
# Save to file
output_path = f"outputs/amalgam_{uuid.uuid4().hex[:8]}.png"
final.save(output_path)
return final, output_path
except Exception as e:
return f"Error during blending: {str(e)}", None
```
demo = gr.Interface(
fn=blend\_images\_with\_rearrangement,
inputs=\[
gr.File(file\_types=\["image"], file\_count="multiple", label="Upload 2–5 stills"),
gr.Slider(minimum=5, maximum=100, value=20, step=5, label="Block Size (pixels)"),
gr.Slider(minimum=0.0, maximum=1.0, value=0.3, step=0.05, label="Shuffle Strength"),
gr.Slider(minimum=0, maximum=20, value=5, step=1, label="Flow Distortion")
],
outputs=\[
gr.Image(label="Generated Image"),
gr.File(label="Download Image")
],
title="Amalgamator",
description="Upload up to 5 stills. Outputs a 3000x3000 image with pixel rearrangement to create a truly generative look."
)
demo.launch()