File size: 7,692 Bytes
2c983e6
 
 
 
 
 
 
 
5f84054
2c983e6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# -*- coding: utf-8 -*-
"""godraveapp.ipynb

Automatically generated by Colab.

Original file is located at
    https://colab.research.google.com/drive/1MHm84PnI1EaofvmNUzaIVeLj7kBB3FL1
"""

import gradio as gr
from PIL import Image, ImageOps
import numpy as np
import os
import uuid

# 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))
    # Replace deprecated ANTIALIAS with modern equivalent
    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 blend_images(images):
    if len(images) < 2:
        return "Upload at least two images.", None

    try:
        # Add error handling for image processing
        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

        base = np.array(processed[0]).astype(np.float32)

        for img in processed[1:]:
            base = (base + np.array(img).astype(np.float32)) / 2

        final = Image.fromarray(np.uint8(base))

        # 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,
    inputs=gr.File(file_types=["image"], file_count="multiple", label="Upload 2–5 stills"),
    outputs=[
        gr.Image(label="Blended Image"),
        gr.File(label="Download Image")
    ],
    title="Amalgamator",
    description="Upload up to 5 stills. Outputs a 3000x3000 blended image preserving the aesthetic. Save it as PNG below."
)

demo.launch()

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()