Recently, I’ve tried to make a simple collage with ChatGPT. Surprisingly, it wasn’t able to help me, so I ended up using an online collage maker from Pixlr. It is very cool on its own, but I started to think about making my own app. And here it is.
Let’s start with source images, I found some across the internet:
Links
Photo by Abet Llacer: https://www.pexels.com/photo/cute-dog-wearing-a-reindeer-headband-14720760/
Photo by Anna-Louise: https://www.pexels.com/photo/674723/
Photo by Studio Naae: https://www.pexels.com/photo/studio-shoot-of-silver-and-golden-christmas-baubles-against-white-background-19507868/
Photo by William Warby: https://www.pexels.com/photo/19561041/
Photo by Em Hopper: https://www.pexels.com/photo/14769816/
https://pixabay.com/illustrations/christmas-bauble-christmas-1079926/
https://pixabay.com/vectors/christmas-pattern-design-wallpaper-6809681/
https://pixabay.com/illustrations/red-christmas-tree-christmas-2892235/
https://pixabay.com/illustrations/christmas-tree-lights-stars-glowing-2928142/
https://pixabay.com/photos/snowman-town-urban-321034/
Photo by Monika Grabkowska on Unsplash
Photo by Annie Spratt on Unsplash
Photo by Brooke Lark on Unsplash
Photo by Nathan Anderson on Unsplash
Photo by Element5 Digital on Unsplash
As you can see here, I’ve selected quite a few images in different orientations for different layouts—square, vertical (portrait), and horizontal (album).
Let’s start with squares. the most logical (and the most straightforward one) way is to make a grid out of images: X by X for square canvas, n+X by X for horizontal, and X by n+X for vertical.
<span>canvas_width</span><span>,</span> <span>canvas_height</span> <span>=</span> <span>canvas_size</span><span>images_num</span> <span>=</span> <span>len</span><span>(</span><span>images</span><span>)</span><span># Create the canvas </span> <span>collage</span> <span>=</span> <span>Image</span><span>.</span><span>new</span><span>(</span><span>"</span><span>RGB</span><span>"</span><span>,</span> <span>canvas_size</span><span>,</span> <span>bg_color</span><span>)</span><span>if</span> <span>canvas_width</span> <span>==</span> <span>canvas_height</span><span>:</span><span># Square canvas: Determine grid dimensions for a square layout </span> <span>grid_size</span> <span>=</span> <span>math</span><span>.</span><span>ceil</span><span>(</span><span>math</span><span>.</span><span>sqrt</span><span>(</span><span>images_num</span><span>))</span><span>cell_size</span> <span>=</span> <span>(</span><span>canvas_width</span> <span>-</span> <span>(</span><span>grid_size</span> <span>+</span> <span>1</span><span>)</span> <span>*</span> <span>padding</span><span>)</span> <span>//</span> <span>grid_size</span><span>for</span> <span>idx</span><span>,</span> <span>img_path</span> <span>in</span> <span>enumerate</span><span>(</span><span>images</span><span>):</span><span>img</span> <span>=</span> <span>Image</span><span>.</span><span>open</span><span>(</span><span>img_path</span><span>)</span><span>img</span> <span>=</span> <span>ImageOps</span><span>.</span><span>fit</span><span>(</span><span>img</span><span>,</span> <span>(</span><span>cell_size</span><span>,</span> <span>cell_size</span><span>),</span> <span>method</span><span>=</span><span>Image</span><span>.</span><span>Resampling</span><span>.</span><span>LANCZOS</span><span>)</span><span># Calculate position in the grid </span> <span>x</span> <span>=</span> <span>(</span><span>idx</span> <span>%</span> <span>grid_size</span><span>)</span> <span>*</span> <span>(</span><span>cell_size</span> <span>+</span> <span>padding</span><span>)</span> <span>+</span> <span>padding</span><span>y</span> <span>=</span> <span>(</span><span>idx</span> <span>//</span> <span>grid_size</span><span>)</span> <span>*</span> <span>(</span><span>cell_size</span> <span>+</span> <span>padding</span><span>)</span> <span>+</span> <span>padding</span><span>collage</span><span>.</span><span>paste</span><span>(</span><span>img</span><span>,</span> <span>(</span><span>x</span><span>,</span> <span>y</span><span>))</span><span>else</span><span>:</span><span># Non-square canvas: Determine grid dimensions for the closest layout </span> <span>cols</span> <span>=</span> <span>math</span><span>.</span><span>ceil</span><span>(</span><span>math</span><span>.</span><span>sqrt</span><span>(</span><span>images_num</span><span>))</span><span>rows</span> <span>=</span> <span>math</span><span>.</span><span>ceil</span><span>(</span><span>images_num</span> <span>/</span> <span>cols</span><span>)</span><span>offset_x</span><span>,</span> <span>offset_y</span> <span>=</span> <span>0</span><span>,</span> <span>0</span><span># Adjust grid dimensions and offsets based on canvas proportions </span> <span>if</span> <span>canvas_width</span> <span>></span> <span>canvas_height</span><span>:</span><span>if</span> <span>cols</span> <span><</span> <span>rows</span><span>:</span><span>cols</span><span>,</span> <span>rows</span> <span>=</span> <span>rows</span><span>,</span> <span>cols</span><span>if</span> <span>centered</span><span>:</span><span>offset_x</span> <span>=</span> <span>(</span><span>canvas_width</span> <span>-</span> <span>(</span><span>rows</span> <span>*</span> <span>(</span><span>canvas_height</span> <span>//</span> <span>rows</span><span>)))</span> <span>//</span> <span>2</span><span>elif</span> <span>canvas_width</span> <span><</span> <span>canvas_height</span><span>:</span><span>if</span> <span>cols</span> <span>></span> <span>rows</span><span>:</span><span>cols</span><span>,</span> <span>rows</span> <span>=</span> <span>rows</span><span>,</span> <span>cols</span><span>if</span> <span>centered</span><span>:</span><span>offset_y</span> <span>=</span> <span>(</span><span>canvas_height</span> <span>-</span> <span>(</span><span>cols</span> <span>*</span> <span>(</span><span>canvas_width</span> <span>//</span> <span>cols</span><span>)))</span> <span>//</span> <span>2</span><span># Calculate cell dimensions </span> <span>cell_width</span> <span>=</span> <span>(</span><span>canvas_width</span> <span>-</span> <span>(</span><span>cols</span> <span>+</span> <span>1</span><span>)</span> <span>*</span> <span>padding</span><span>)</span> <span>//</span> <span>cols</span><span>cell_height</span> <span>=</span> <span>(</span><span>canvas_height</span> <span>-</span> <span>(</span><span>rows</span> <span>+</span> <span>1</span><span>)</span> <span>*</span> <span>padding</span><span>)</span> <span>//</span> <span>rows</span><span># Use the smaller dimension if centering is enabled </span> <span>if</span> <span>centered</span><span>:</span><span>cell_width</span> <span>=</span> <span>cell_height</span> <span>=</span> <span>min</span><span>(</span><span>cell_width</span><span>,</span> <span>cell_height</span><span>)</span><span>for</span> <span>idx</span><span>,</span> <span>img_path</span> <span>in</span> <span>enumerate</span><span>(</span><span>images</span><span>):</span><span>img</span> <span>=</span> <span>Image</span><span>.</span><span>open</span><span>(</span><span>img_path</span><span>)</span><span>img</span> <span>=</span> <span>ImageOps</span><span>.</span><span>fit</span><span>(</span><span>img</span><span>,</span> <span>(</span><span>cell_width</span><span>,</span> <span>cell_height</span><span>),</span> <span>method</span><span>=</span><span>Image</span><span>.</span><span>Resampling</span><span>.</span><span>LANCZOS</span><span>)</span><span># Calculate position in the grid </span> <span>x</span> <span>=</span> <span>(</span><span>idx</span> <span>%</span> <span>cols</span><span>)</span> <span>*</span> <span>(</span><span>cell_width</span> <span>+</span> <span>padding</span><span>)</span> <span>+</span> <span>padding</span> <span>+</span> <span>offset_x</span><span>y</span> <span>=</span> <span>(</span><span>idx</span> <span>//</span> <span>cols</span><span>)</span> <span>*</span> <span>(</span><span>cell_height</span> <span>+</span> <span>padding</span><span>)</span> <span>+</span> <span>padding</span> <span>+</span> <span>offset_y</span><span>collage</span><span>.</span><span>paste</span><span>(</span><span>img</span><span>,</span> <span>(</span><span>x</span><span>,</span> <span>y</span><span>))</span><span>canvas_width</span><span>,</span> <span>canvas_height</span> <span>=</span> <span>canvas_size</span> <span>images_num</span> <span>=</span> <span>len</span><span>(</span><span>images</span><span>)</span> <span># Create the canvas </span> <span>collage</span> <span>=</span> <span>Image</span><span>.</span><span>new</span><span>(</span><span>"</span><span>RGB</span><span>"</span><span>,</span> <span>canvas_size</span><span>,</span> <span>bg_color</span><span>)</span> <span>if</span> <span>canvas_width</span> <span>==</span> <span>canvas_height</span><span>:</span> <span># Square canvas: Determine grid dimensions for a square layout </span> <span>grid_size</span> <span>=</span> <span>math</span><span>.</span><span>ceil</span><span>(</span><span>math</span><span>.</span><span>sqrt</span><span>(</span><span>images_num</span><span>))</span> <span>cell_size</span> <span>=</span> <span>(</span><span>canvas_width</span> <span>-</span> <span>(</span><span>grid_size</span> <span>+</span> <span>1</span><span>)</span> <span>*</span> <span>padding</span><span>)</span> <span>//</span> <span>grid_size</span> <span>for</span> <span>idx</span><span>,</span> <span>img_path</span> <span>in</span> <span>enumerate</span><span>(</span><span>images</span><span>):</span> <span>img</span> <span>=</span> <span>Image</span><span>.</span><span>open</span><span>(</span><span>img_path</span><span>)</span> <span>img</span> <span>=</span> <span>ImageOps</span><span>.</span><span>fit</span><span>(</span><span>img</span><span>,</span> <span>(</span><span>cell_size</span><span>,</span> <span>cell_size</span><span>),</span> <span>method</span><span>=</span><span>Image</span><span>.</span><span>Resampling</span><span>.</span><span>LANCZOS</span><span>)</span> <span># Calculate position in the grid </span> <span>x</span> <span>=</span> <span>(</span><span>idx</span> <span>%</span> <span>grid_size</span><span>)</span> <span>*</span> <span>(</span><span>cell_size</span> <span>+</span> <span>padding</span><span>)</span> <span>+</span> <span>padding</span> <span>y</span> <span>=</span> <span>(</span><span>idx</span> <span>//</span> <span>grid_size</span><span>)</span> <span>*</span> <span>(</span><span>cell_size</span> <span>+</span> <span>padding</span><span>)</span> <span>+</span> <span>padding</span> <span>collage</span><span>.</span><span>paste</span><span>(</span><span>img</span><span>,</span> <span>(</span><span>x</span><span>,</span> <span>y</span><span>))</span> <span>else</span><span>:</span> <span># Non-square canvas: Determine grid dimensions for the closest layout </span> <span>cols</span> <span>=</span> <span>math</span><span>.</span><span>ceil</span><span>(</span><span>math</span><span>.</span><span>sqrt</span><span>(</span><span>images_num</span><span>))</span> <span>rows</span> <span>=</span> <span>math</span><span>.</span><span>ceil</span><span>(</span><span>images_num</span> <span>/</span> <span>cols</span><span>)</span> <span>offset_x</span><span>,</span> <span>offset_y</span> <span>=</span> <span>0</span><span>,</span> <span>0</span> <span># Adjust grid dimensions and offsets based on canvas proportions </span> <span>if</span> <span>canvas_width</span> <span>></span> <span>canvas_height</span><span>:</span> <span>if</span> <span>cols</span> <span><</span> <span>rows</span><span>:</span> <span>cols</span><span>,</span> <span>rows</span> <span>=</span> <span>rows</span><span>,</span> <span>cols</span> <span>if</span> <span>centered</span><span>:</span> <span>offset_x</span> <span>=</span> <span>(</span><span>canvas_width</span> <span>-</span> <span>(</span><span>rows</span> <span>*</span> <span>(</span><span>canvas_height</span> <span>//</span> <span>rows</span><span>)))</span> <span>//</span> <span>2</span> <span>elif</span> <span>canvas_width</span> <span><</span> <span>canvas_height</span><span>:</span> <span>if</span> <span>cols</span> <span>></span> <span>rows</span><span>:</span> <span>cols</span><span>,</span> <span>rows</span> <span>=</span> <span>rows</span><span>,</span> <span>cols</span> <span>if</span> <span>centered</span><span>:</span> <span>offset_y</span> <span>=</span> <span>(</span><span>canvas_height</span> <span>-</span> <span>(</span><span>cols</span> <span>*</span> <span>(</span><span>canvas_width</span> <span>//</span> <span>cols</span><span>)))</span> <span>//</span> <span>2</span> <span># Calculate cell dimensions </span> <span>cell_width</span> <span>=</span> <span>(</span><span>canvas_width</span> <span>-</span> <span>(</span><span>cols</span> <span>+</span> <span>1</span><span>)</span> <span>*</span> <span>padding</span><span>)</span> <span>//</span> <span>cols</span> <span>cell_height</span> <span>=</span> <span>(</span><span>canvas_height</span> <span>-</span> <span>(</span><span>rows</span> <span>+</span> <span>1</span><span>)</span> <span>*</span> <span>padding</span><span>)</span> <span>//</span> <span>rows</span> <span># Use the smaller dimension if centering is enabled </span> <span>if</span> <span>centered</span><span>:</span> <span>cell_width</span> <span>=</span> <span>cell_height</span> <span>=</span> <span>min</span><span>(</span><span>cell_width</span><span>,</span> <span>cell_height</span><span>)</span> <span>for</span> <span>idx</span><span>,</span> <span>img_path</span> <span>in</span> <span>enumerate</span><span>(</span><span>images</span><span>):</span> <span>img</span> <span>=</span> <span>Image</span><span>.</span><span>open</span><span>(</span><span>img_path</span><span>)</span> <span>img</span> <span>=</span> <span>ImageOps</span><span>.</span><span>fit</span><span>(</span><span>img</span><span>,</span> <span>(</span><span>cell_width</span><span>,</span> <span>cell_height</span><span>),</span> <span>method</span><span>=</span><span>Image</span><span>.</span><span>Resampling</span><span>.</span><span>LANCZOS</span><span>)</span> <span># Calculate position in the grid </span> <span>x</span> <span>=</span> <span>(</span><span>idx</span> <span>%</span> <span>cols</span><span>)</span> <span>*</span> <span>(</span><span>cell_width</span> <span>+</span> <span>padding</span><span>)</span> <span>+</span> <span>padding</span> <span>+</span> <span>offset_x</span> <span>y</span> <span>=</span> <span>(</span><span>idx</span> <span>//</span> <span>cols</span><span>)</span> <span>*</span> <span>(</span><span>cell_height</span> <span>+</span> <span>padding</span><span>)</span> <span>+</span> <span>padding</span> <span>+</span> <span>offset_y</span> <span>collage</span><span>.</span><span>paste</span><span>(</span><span>img</span><span>,</span> <span>(</span><span>x</span><span>,</span> <span>y</span><span>))</span>canvas_width, canvas_height = canvas_size images_num = len(images) # Create the canvas collage = Image.new("RGB", canvas_size, bg_color) if canvas_width == canvas_height: # Square canvas: Determine grid dimensions for a square layout grid_size = math.ceil(math.sqrt(images_num)) cell_size = (canvas_width - (grid_size + 1) * padding) // grid_size for idx, img_path in enumerate(images): img = Image.open(img_path) img = ImageOps.fit(img, (cell_size, cell_size), method=Image.Resampling.LANCZOS) # Calculate position in the grid x = (idx % grid_size) * (cell_size + padding) + padding y = (idx // grid_size) * (cell_size + padding) + padding collage.paste(img, (x, y)) else: # Non-square canvas: Determine grid dimensions for the closest layout cols = math.ceil(math.sqrt(images_num)) rows = math.ceil(images_num / cols) offset_x, offset_y = 0, 0 # Adjust grid dimensions and offsets based on canvas proportions if canvas_width > canvas_height: if cols < rows: cols, rows = rows, cols if centered: offset_x = (canvas_width - (rows * (canvas_height // rows))) // 2 elif canvas_width < canvas_height: if cols > rows: cols, rows = rows, cols if centered: offset_y = (canvas_height - (cols * (canvas_width // cols))) // 2 # Calculate cell dimensions cell_width = (canvas_width - (cols + 1) * padding) // cols cell_height = (canvas_height - (rows + 1) * padding) // rows # Use the smaller dimension if centering is enabled if centered: cell_width = cell_height = min(cell_width, cell_height) for idx, img_path in enumerate(images): img = Image.open(img_path) img = ImageOps.fit(img, (cell_width, cell_height), method=Image.Resampling.LANCZOS) # Calculate position in the grid x = (idx % cols) * (cell_width + padding) + padding + offset_x y = (idx // cols) * (cell_height + padding) + padding + offset_y collage.paste(img, (x, y))
Enter fullscreen mode Exit fullscreen mode
Square grid collage
Working with rectangles is even simpler, now we only need to deal with a single row or column:
<span>canvas_width</span><span>,</span> <span>canvas_height</span> <span>=</span> <span>canvas_size</span><span>images_num</span> <span>=</span> <span>len</span><span>(</span><span>images</span><span>)</span><span># Create the canvas </span> <span>collage</span> <span>=</span> <span>Image</span><span>.</span><span>new</span><span>(</span><span>"</span><span>RGB</span><span>"</span><span>,</span> <span>canvas_size</span><span>,</span> <span>bg_color</span><span>)</span><span># Determine block size based on orientation </span> <span>if</span> <span>orientation</span> <span>==</span> <span>"</span><span>horizontal</span><span>"</span><span>:</span><span>block_height</span> <span>=</span> <span>(</span><span>canvas_height</span> <span>-</span> <span>(</span><span>images_num</span> <span>+</span> <span>1</span><span>)</span> <span>*</span> <span>padding</span><span>)</span> <span>//</span> <span>images_num</span><span>block_width</span> <span>=</span> <span>canvas_width</span><span>elif</span> <span>orientation</span> <span>==</span> <span>"</span><span>vertical</span><span>"</span><span>:</span><span>block_width</span> <span>=</span> <span>(</span><span>canvas_width</span> <span>-</span> <span>(</span><span>images_num</span> <span>+</span> <span>1</span><span>)</span> <span>*</span> <span>padding</span><span>)</span> <span>//</span> <span>images_num</span><span>block_height</span> <span>=</span> <span>canvas_height</span><span>else</span><span>:</span><span>raise</span> <span>ValueError</span><span>(</span><span>"</span><span>Orientation must be </span><span>'</span><span>horizontal</span><span>'</span><span> or </span><span>'</span><span>vertical</span><span>'</span><span>.</span><span>"</span><span>)</span><span># Centering offsets </span> <span>offset_x</span><span>,</span> <span>offset_y</span> <span>=</span> <span>0</span><span>,</span> <span>0</span><span>if</span> <span>centered</span><span>:</span><span>if</span> <span>orientation</span> <span>==</span> <span>"</span><span>horizontal</span><span>"</span> <span>and</span> <span>images_num</span> <span>*</span> <span>(</span><span>block_height</span> <span>+</span> <span>padding</span><span>)</span> <span><</span> <span>canvas_height</span><span>:</span><span>offset_y</span> <span>=</span> <span>(</span><span>canvas_height</span> <span>-</span> <span>(</span><span>images_num</span> <span>*</span> <span>(</span><span>block_height</span> <span>+</span> <span>padding</span><span>)))</span> <span>//</span> <span>2</span><span>if</span> <span>orientation</span> <span>==</span> <span>"</span><span>vertical</span><span>"</span> <span>and</span> <span>images_num</span> <span>*</span> <span>(</span><span>block_width</span> <span>+</span> <span>padding</span><span>)</span> <span><</span> <span>canvas_width</span><span>:</span><span>offset_x</span> <span>=</span> <span>(</span><span>canvas_width</span> <span>-</span> <span>(</span><span>images_num</span> <span>*</span> <span>(</span><span>block_width</span> <span>+</span> <span>padding</span><span>)))</span> <span>//</span> <span>2</span><span># Resize and paste images </span> <span>for</span> <span>idx</span><span>,</span> <span>img_path</span> <span>in</span> <span>enumerate</span><span>(</span><span>images</span><span>):</span><span>img</span> <span>=</span> <span>Image</span><span>.</span><span>open</span><span>(</span><span>img_path</span><span>)</span><span>img</span> <span>=</span> <span>ImageOps</span><span>.</span><span>fit</span><span>(</span><span>img</span><span>,</span> <span>(</span><span>block_width</span><span>,</span> <span>block_height</span><span>),</span> <span>method</span><span>=</span><span>Image</span><span>.</span><span>Resampling</span><span>.</span><span>LANCZOS</span><span>)</span><span># Calculate position </span> <span>x</span><span>,</span> <span>y</span> <span>=</span> <span>0</span><span>,</span> <span>0</span><span>if</span> <span>orientation</span> <span>==</span> <span>"</span><span>horizontal</span><span>"</span><span>:</span><span>y</span> <span>=</span> <span>idx</span> <span>*</span> <span>(</span><span>block_height</span> <span>+</span> <span>padding</span><span>)</span> <span>+</span> <span>padding</span> <span>+</span> <span>offset_y</span><span>elif</span> <span>orientation</span> <span>==</span> <span>"</span><span>vertical</span><span>"</span><span>:</span><span>x</span> <span>=</span> <span>idx</span> <span>*</span> <span>(</span><span>block_width</span> <span>+</span> <span>padding</span><span>)</span> <span>+</span> <span>padding</span> <span>+</span> <span>offset_x</span><span>collage</span><span>.</span><span>paste</span><span>(</span><span>img</span><span>,</span> <span>(</span><span>x</span><span>,</span> <span>y</span><span>))</span><span>canvas_width</span><span>,</span> <span>canvas_height</span> <span>=</span> <span>canvas_size</span> <span>images_num</span> <span>=</span> <span>len</span><span>(</span><span>images</span><span>)</span> <span># Create the canvas </span> <span>collage</span> <span>=</span> <span>Image</span><span>.</span><span>new</span><span>(</span><span>"</span><span>RGB</span><span>"</span><span>,</span> <span>canvas_size</span><span>,</span> <span>bg_color</span><span>)</span> <span># Determine block size based on orientation </span> <span>if</span> <span>orientation</span> <span>==</span> <span>"</span><span>horizontal</span><span>"</span><span>:</span> <span>block_height</span> <span>=</span> <span>(</span><span>canvas_height</span> <span>-</span> <span>(</span><span>images_num</span> <span>+</span> <span>1</span><span>)</span> <span>*</span> <span>padding</span><span>)</span> <span>//</span> <span>images_num</span> <span>block_width</span> <span>=</span> <span>canvas_width</span> <span>elif</span> <span>orientation</span> <span>==</span> <span>"</span><span>vertical</span><span>"</span><span>:</span> <span>block_width</span> <span>=</span> <span>(</span><span>canvas_width</span> <span>-</span> <span>(</span><span>images_num</span> <span>+</span> <span>1</span><span>)</span> <span>*</span> <span>padding</span><span>)</span> <span>//</span> <span>images_num</span> <span>block_height</span> <span>=</span> <span>canvas_height</span> <span>else</span><span>:</span> <span>raise</span> <span>ValueError</span><span>(</span><span>"</span><span>Orientation must be </span><span>'</span><span>horizontal</span><span>'</span><span> or </span><span>'</span><span>vertical</span><span>'</span><span>.</span><span>"</span><span>)</span> <span># Centering offsets </span> <span>offset_x</span><span>,</span> <span>offset_y</span> <span>=</span> <span>0</span><span>,</span> <span>0</span> <span>if</span> <span>centered</span><span>:</span> <span>if</span> <span>orientation</span> <span>==</span> <span>"</span><span>horizontal</span><span>"</span> <span>and</span> <span>images_num</span> <span>*</span> <span>(</span><span>block_height</span> <span>+</span> <span>padding</span><span>)</span> <span><</span> <span>canvas_height</span><span>:</span> <span>offset_y</span> <span>=</span> <span>(</span><span>canvas_height</span> <span>-</span> <span>(</span><span>images_num</span> <span>*</span> <span>(</span><span>block_height</span> <span>+</span> <span>padding</span><span>)))</span> <span>//</span> <span>2</span> <span>if</span> <span>orientation</span> <span>==</span> <span>"</span><span>vertical</span><span>"</span> <span>and</span> <span>images_num</span> <span>*</span> <span>(</span><span>block_width</span> <span>+</span> <span>padding</span><span>)</span> <span><</span> <span>canvas_width</span><span>:</span> <span>offset_x</span> <span>=</span> <span>(</span><span>canvas_width</span> <span>-</span> <span>(</span><span>images_num</span> <span>*</span> <span>(</span><span>block_width</span> <span>+</span> <span>padding</span><span>)))</span> <span>//</span> <span>2</span> <span># Resize and paste images </span> <span>for</span> <span>idx</span><span>,</span> <span>img_path</span> <span>in</span> <span>enumerate</span><span>(</span><span>images</span><span>):</span> <span>img</span> <span>=</span> <span>Image</span><span>.</span><span>open</span><span>(</span><span>img_path</span><span>)</span> <span>img</span> <span>=</span> <span>ImageOps</span><span>.</span><span>fit</span><span>(</span><span>img</span><span>,</span> <span>(</span><span>block_width</span><span>,</span> <span>block_height</span><span>),</span> <span>method</span><span>=</span><span>Image</span><span>.</span><span>Resampling</span><span>.</span><span>LANCZOS</span><span>)</span> <span># Calculate position </span> <span>x</span><span>,</span> <span>y</span> <span>=</span> <span>0</span><span>,</span> <span>0</span> <span>if</span> <span>orientation</span> <span>==</span> <span>"</span><span>horizontal</span><span>"</span><span>:</span> <span>y</span> <span>=</span> <span>idx</span> <span>*</span> <span>(</span><span>block_height</span> <span>+</span> <span>padding</span><span>)</span> <span>+</span> <span>padding</span> <span>+</span> <span>offset_y</span> <span>elif</span> <span>orientation</span> <span>==</span> <span>"</span><span>vertical</span><span>"</span><span>:</span> <span>x</span> <span>=</span> <span>idx</span> <span>*</span> <span>(</span><span>block_width</span> <span>+</span> <span>padding</span><span>)</span> <span>+</span> <span>padding</span> <span>+</span> <span>offset_x</span> <span>collage</span><span>.</span><span>paste</span><span>(</span><span>img</span><span>,</span> <span>(</span><span>x</span><span>,</span> <span>y</span><span>))</span>canvas_width, canvas_height = canvas_size images_num = len(images) # Create the canvas collage = Image.new("RGB", canvas_size, bg_color) # Determine block size based on orientation if orientation == "horizontal": block_height = (canvas_height - (images_num + 1) * padding) // images_num block_width = canvas_width elif orientation == "vertical": block_width = (canvas_width - (images_num + 1) * padding) // images_num block_height = canvas_height else: raise ValueError("Orientation must be 'horizontal' or 'vertical'.") # Centering offsets offset_x, offset_y = 0, 0 if centered: if orientation == "horizontal" and images_num * (block_height + padding) < canvas_height: offset_y = (canvas_height - (images_num * (block_height + padding))) // 2 if orientation == "vertical" and images_num * (block_width + padding) < canvas_width: offset_x = (canvas_width - (images_num * (block_width + padding))) // 2 # Resize and paste images for idx, img_path in enumerate(images): img = Image.open(img_path) img = ImageOps.fit(img, (block_width, block_height), method=Image.Resampling.LANCZOS) # Calculate position x, y = 0, 0 if orientation == "horizontal": y = idx * (block_height + padding) + padding + offset_y elif orientation == "vertical": x = idx * (block_width + padding) + padding + offset_x collage.paste(img, (x, y))
Enter fullscreen mode Exit fullscreen mode
Strip collage
Stack collage
But the mixed input – that’s the place where fun begins! Strictly speaking, the problem of packing is a serious mathematical task, no jokes, look in Wiki.
Packing problems are a class of optimization problems in mathematics that involve attempting to pack objects together into containers. The goal is to either pack a single container as densely as possible or pack all objects using as few containers as possible. Many of these problems can be related to real-life packaging, storage and transportation issues. Each packing problem has a dual covering problem, which asks how many of the same objects are required to completely cover every region of the container, where objects are allowed to overlap.
Anyway, trying to implement all of them, could be tiresome, let’s add some creativity! One of the most interesting ways to put images on collage is using golden ratio.
And how can we implement the golden ratio in collage? It’s actually not that complicated: we divide free space by 1 + 5 2 \frac{1 + \sqrt{5}}{2} 21+5 and reverse direction from the previous (eg. first top, second bottom, etc).
Golden ratio animated
<span>canvas_width</span><span>,</span> <span>canvas_height</span> <span>=</span> <span>canvas_size</span><span># Initialize the working area for placing images </span> <span>working_area</span> <span>=</span> <span>{</span><span>"</span><span>x</span><span>"</span><span>:</span> <span>0</span><span>,</span><span>"</span><span>y</span><span>"</span><span>:</span> <span>0</span><span>,</span><span>"</span><span>width</span><span>"</span><span>:</span> <span>canvas_width</span><span>,</span><span>"</span><span>height</span><span>"</span><span>:</span> <span>canvas_height</span><span>}</span><span># Initialize the canvas (background image or color) </span> <span>collage</span> <span>=</span> <span>None</span><span>if</span> <span>bg_image</span><span>:</span><span>collage</span> <span>=</span> <span>Image</span><span>.</span><span>open</span><span>(</span><span>bg_image</span><span>)</span><span>if</span> <span>bg_color</span><span>:</span><span>collage</span> <span>=</span> <span>Image</span><span>.</span><span>new</span><span>(</span><span>"</span><span>RGB</span><span>"</span><span>,</span> <span>canvas_size</span><span>,</span> <span>bg_color</span><span>)</span><span># Determine the initial layout orders </span> <span>horizontal_order</span> <span>=</span> <span>"</span><span>right-to-left</span><span>"</span><span>vertical_order</span> <span>=</span> <span>"</span><span>bottom-to-top</span><span>"</span><span># Determine the initial layout orders </span> <span>if</span> <span>randomization</span><span>:</span><span>horizontal_order</span> <span>=</span> <span>random</span><span>.</span><span>choice</span><span>([</span><span>"</span><span>right-to-left</span><span>"</span><span>,</span> <span>"</span><span>left-to-right</span><span>"</span><span>])</span><span>vertical_order</span> <span>=</span> <span>random</span><span>.</span><span>choice</span><span>([</span><span>"</span><span>bottom-to-top</span><span>"</span><span>,</span> <span>"</span><span>top-to-bottom</span><span>"</span><span>])</span><span>random</span><span>.</span><span>shuffle</span><span>(</span><span>images</span><span>)</span><span>x</span><span>,</span> <span>y</span> <span>=</span> <span>0</span><span>,</span> <span>0</span> <span># Starting position </span> <span>for</span> <span>idx</span><span>,</span> <span>img_path</span> <span>in</span> <span>enumerate</span><span>(</span><span>images</span><span>):</span><span>img</span> <span>=</span> <span>Image</span><span>.</span><span>open</span><span>(</span><span>img_path</span><span>)</span><span># Decide whether to split horizontally or vertically based on working area dimensions </span> <span>if</span> <span>working_area</span><span>[</span><span>"</span><span>width</span><span>"</span><span>]</span> <span>></span> <span>working_area</span><span>[</span><span>"</span><span>height</span><span>"</span><span>]:</span> <span># Horizontal split </span> <span>img</span> <span>=</span> <span>ImageOps</span><span>.</span><span>fit</span><span>(</span><span>img</span><span>,</span> <span>(</span><span>int</span><span>(</span><span>working_area</span><span>[</span><span>"</span><span>width</span><span>"</span><span>]</span> <span>/</span> <span>GOLDEN_RATIO</span><span>),</span> <span>working_area</span><span>[</span><span>"</span><span>height</span><span>"</span><span>]),</span> <span>method</span><span>=</span><span>Image</span><span>.</span><span>Resampling</span><span>.</span><span>LANCZOS</span><span>)</span><span># Adjust position and update working area </span> <span>if</span> <span>horizontal_order</span> <span>==</span> <span>"</span><span>right-to-left</span><span>"</span><span>:</span><span>horizontal_order</span> <span>=</span> <span>"</span><span>left-to-right</span><span>"</span><span>working_area</span><span>[</span><span>"</span><span>x</span><span>"</span><span>]</span> <span>+=</span> <span>img</span><span>.</span><span>width</span> <span>+</span> <span>padding</span><span>else</span><span>:</span><span>x</span> <span>=</span> <span>x</span> <span>+</span> <span>working_area</span><span>[</span><span>"</span><span>width</span><span>"</span><span>]</span> <span>-</span> <span>img</span><span>.</span><span>width</span><span>horizontal_order</span> <span>=</span> <span>"</span><span>right-to-left</span><span>"</span><span>working_area</span><span>[</span><span>"</span><span>width</span><span>"</span><span>]</span> <span>-=</span> <span>img</span><span>.</span><span>width</span> <span>+</span> <span>padding</span><span>else</span><span>:</span> <span># Vertical split </span> <span>img</span> <span>=</span> <span>ImageOps</span><span>.</span><span>fit</span><span>(</span><span>img</span><span>,</span> <span>(</span><span>working_area</span><span>[</span><span>"</span><span>width</span><span>"</span><span>],</span> <span>int</span><span>(</span><span>working_area</span><span>[</span><span>"</span><span>height</span><span>"</span><span>]</span> <span>/</span> <span>GOLDEN_RATIO</span><span>)),</span> <span>method</span><span>=</span><span>Image</span><span>.</span><span>Resampling</span><span>.</span><span>LANCZOS</span><span>)</span><span># Adjust position and update working area </span> <span>if</span> <span>vertical_order</span> <span>==</span> <span>"</span><span>bottom-to-top</span><span>"</span><span>:</span><span>working_area</span><span>[</span><span>"</span><span>y</span><span>"</span><span>]</span> <span>+=</span> <span>img</span><span>.</span><span>height</span> <span>+</span> <span>padding</span><span>vertical_order</span> <span>=</span> <span>"</span><span>top-to-bottom</span><span>"</span><span>else</span><span>:</span><span>y</span> <span>=</span> <span>y</span> <span>+</span> <span>working_area</span><span>[</span><span>"</span><span>height</span><span>"</span><span>]</span> <span>-</span> <span>img</span><span>.</span><span>height</span><span>vertical_order</span> <span>=</span> <span>"</span><span>bottom-to-top</span><span>"</span><span>working_area</span><span>[</span><span>"</span><span>height</span><span>"</span><span>]</span> <span>-=</span> <span>img</span><span>.</span><span>height</span> <span>+</span> <span>padding</span><span># Paste the image onto the canvas </span> <span>collage</span><span>.</span><span>paste</span><span>(</span><span>img</span><span>,</span> <span>(</span><span>x</span><span>,</span> <span>y</span><span>))</span><span>x</span><span>,</span> <span>y</span> <span>=</span> <span>working_area</span><span>[</span><span>"</span><span>x</span><span>"</span><span>],</span> <span>working_area</span><span>[</span><span>"</span><span>y</span><span>"</span><span>]</span> <span># Update position for the next image </span><span># Stop if the working area becomes too small </span> <span>if</span> <span>working_area</span><span>[</span><span>"</span><span>width</span><span>"</span><span>]</span> <span><=</span> <span>0</span> <span>or</span> <span>working_area</span><span>[</span><span>"</span><span>height</span><span>"</span><span>]</span> <span><=</span> <span>0</span><span>:</span><span>print</span><span>(</span><span>"</span><span>Working area exhausted. Stopping collage creation.</span><span>"</span><span>)</span><span>break</span><span>canvas_width</span><span>,</span> <span>canvas_height</span> <span>=</span> <span>canvas_size</span> <span># Initialize the working area for placing images </span> <span>working_area</span> <span>=</span> <span>{</span> <span>"</span><span>x</span><span>"</span><span>:</span> <span>0</span><span>,</span> <span>"</span><span>y</span><span>"</span><span>:</span> <span>0</span><span>,</span> <span>"</span><span>width</span><span>"</span><span>:</span> <span>canvas_width</span><span>,</span> <span>"</span><span>height</span><span>"</span><span>:</span> <span>canvas_height</span> <span>}</span> <span># Initialize the canvas (background image or color) </span> <span>collage</span> <span>=</span> <span>None</span> <span>if</span> <span>bg_image</span><span>:</span> <span>collage</span> <span>=</span> <span>Image</span><span>.</span><span>open</span><span>(</span><span>bg_image</span><span>)</span> <span>if</span> <span>bg_color</span><span>:</span> <span>collage</span> <span>=</span> <span>Image</span><span>.</span><span>new</span><span>(</span><span>"</span><span>RGB</span><span>"</span><span>,</span> <span>canvas_size</span><span>,</span> <span>bg_color</span><span>)</span> <span># Determine the initial layout orders </span> <span>horizontal_order</span> <span>=</span> <span>"</span><span>right-to-left</span><span>"</span> <span>vertical_order</span> <span>=</span> <span>"</span><span>bottom-to-top</span><span>"</span> <span># Determine the initial layout orders </span> <span>if</span> <span>randomization</span><span>:</span> <span>horizontal_order</span> <span>=</span> <span>random</span><span>.</span><span>choice</span><span>([</span><span>"</span><span>right-to-left</span><span>"</span><span>,</span> <span>"</span><span>left-to-right</span><span>"</span><span>])</span> <span>vertical_order</span> <span>=</span> <span>random</span><span>.</span><span>choice</span><span>([</span><span>"</span><span>bottom-to-top</span><span>"</span><span>,</span> <span>"</span><span>top-to-bottom</span><span>"</span><span>])</span> <span>random</span><span>.</span><span>shuffle</span><span>(</span><span>images</span><span>)</span> <span>x</span><span>,</span> <span>y</span> <span>=</span> <span>0</span><span>,</span> <span>0</span> <span># Starting position </span> <span>for</span> <span>idx</span><span>,</span> <span>img_path</span> <span>in</span> <span>enumerate</span><span>(</span><span>images</span><span>):</span> <span>img</span> <span>=</span> <span>Image</span><span>.</span><span>open</span><span>(</span><span>img_path</span><span>)</span> <span># Decide whether to split horizontally or vertically based on working area dimensions </span> <span>if</span> <span>working_area</span><span>[</span><span>"</span><span>width</span><span>"</span><span>]</span> <span>></span> <span>working_area</span><span>[</span><span>"</span><span>height</span><span>"</span><span>]:</span> <span># Horizontal split </span> <span>img</span> <span>=</span> <span>ImageOps</span><span>.</span><span>fit</span><span>(</span><span>img</span><span>,</span> <span>(</span><span>int</span><span>(</span><span>working_area</span><span>[</span><span>"</span><span>width</span><span>"</span><span>]</span> <span>/</span> <span>GOLDEN_RATIO</span><span>),</span> <span>working_area</span><span>[</span><span>"</span><span>height</span><span>"</span><span>]),</span> <span>method</span><span>=</span><span>Image</span><span>.</span><span>Resampling</span><span>.</span><span>LANCZOS</span><span>)</span> <span># Adjust position and update working area </span> <span>if</span> <span>horizontal_order</span> <span>==</span> <span>"</span><span>right-to-left</span><span>"</span><span>:</span> <span>horizontal_order</span> <span>=</span> <span>"</span><span>left-to-right</span><span>"</span> <span>working_area</span><span>[</span><span>"</span><span>x</span><span>"</span><span>]</span> <span>+=</span> <span>img</span><span>.</span><span>width</span> <span>+</span> <span>padding</span> <span>else</span><span>:</span> <span>x</span> <span>=</span> <span>x</span> <span>+</span> <span>working_area</span><span>[</span><span>"</span><span>width</span><span>"</span><span>]</span> <span>-</span> <span>img</span><span>.</span><span>width</span> <span>horizontal_order</span> <span>=</span> <span>"</span><span>right-to-left</span><span>"</span> <span>working_area</span><span>[</span><span>"</span><span>width</span><span>"</span><span>]</span> <span>-=</span> <span>img</span><span>.</span><span>width</span> <span>+</span> <span>padding</span> <span>else</span><span>:</span> <span># Vertical split </span> <span>img</span> <span>=</span> <span>ImageOps</span><span>.</span><span>fit</span><span>(</span><span>img</span><span>,</span> <span>(</span><span>working_area</span><span>[</span><span>"</span><span>width</span><span>"</span><span>],</span> <span>int</span><span>(</span><span>working_area</span><span>[</span><span>"</span><span>height</span><span>"</span><span>]</span> <span>/</span> <span>GOLDEN_RATIO</span><span>)),</span> <span>method</span><span>=</span><span>Image</span><span>.</span><span>Resampling</span><span>.</span><span>LANCZOS</span><span>)</span> <span># Adjust position and update working area </span> <span>if</span> <span>vertical_order</span> <span>==</span> <span>"</span><span>bottom-to-top</span><span>"</span><span>:</span> <span>working_area</span><span>[</span><span>"</span><span>y</span><span>"</span><span>]</span> <span>+=</span> <span>img</span><span>.</span><span>height</span> <span>+</span> <span>padding</span> <span>vertical_order</span> <span>=</span> <span>"</span><span>top-to-bottom</span><span>"</span> <span>else</span><span>:</span> <span>y</span> <span>=</span> <span>y</span> <span>+</span> <span>working_area</span><span>[</span><span>"</span><span>height</span><span>"</span><span>]</span> <span>-</span> <span>img</span><span>.</span><span>height</span> <span>vertical_order</span> <span>=</span> <span>"</span><span>bottom-to-top</span><span>"</span> <span>working_area</span><span>[</span><span>"</span><span>height</span><span>"</span><span>]</span> <span>-=</span> <span>img</span><span>.</span><span>height</span> <span>+</span> <span>padding</span> <span># Paste the image onto the canvas </span> <span>collage</span><span>.</span><span>paste</span><span>(</span><span>img</span><span>,</span> <span>(</span><span>x</span><span>,</span> <span>y</span><span>))</span> <span>x</span><span>,</span> <span>y</span> <span>=</span> <span>working_area</span><span>[</span><span>"</span><span>x</span><span>"</span><span>],</span> <span>working_area</span><span>[</span><span>"</span><span>y</span><span>"</span><span>]</span> <span># Update position for the next image </span> <span># Stop if the working area becomes too small </span> <span>if</span> <span>working_area</span><span>[</span><span>"</span><span>width</span><span>"</span><span>]</span> <span><=</span> <span>0</span> <span>or</span> <span>working_area</span><span>[</span><span>"</span><span>height</span><span>"</span><span>]</span> <span><=</span> <span>0</span><span>:</span> <span>print</span><span>(</span><span>"</span><span>Working area exhausted. Stopping collage creation.</span><span>"</span><span>)</span> <span>break</span>canvas_width, canvas_height = canvas_size # Initialize the working area for placing images working_area = { "x": 0, "y": 0, "width": canvas_width, "height": canvas_height } # Initialize the canvas (background image or color) collage = None if bg_image: collage = Image.open(bg_image) if bg_color: collage = Image.new("RGB", canvas_size, bg_color) # Determine the initial layout orders horizontal_order = "right-to-left" vertical_order = "bottom-to-top" # Determine the initial layout orders if randomization: horizontal_order = random.choice(["right-to-left", "left-to-right"]) vertical_order = random.choice(["bottom-to-top", "top-to-bottom"]) random.shuffle(images) x, y = 0, 0 # Starting position for idx, img_path in enumerate(images): img = Image.open(img_path) # Decide whether to split horizontally or vertically based on working area dimensions if working_area["width"] > working_area["height"]: # Horizontal split img = ImageOps.fit(img, (int(working_area["width"] / GOLDEN_RATIO), working_area["height"]), method=Image.Resampling.LANCZOS) # Adjust position and update working area if horizontal_order == "right-to-left": horizontal_order = "left-to-right" working_area["x"] += img.width + padding else: x = x + working_area["width"] - img.width horizontal_order = "right-to-left" working_area["width"] -= img.width + padding else: # Vertical split img = ImageOps.fit(img, (working_area["width"], int(working_area["height"] / GOLDEN_RATIO)), method=Image.Resampling.LANCZOS) # Adjust position and update working area if vertical_order == "bottom-to-top": working_area["y"] += img.height + padding vertical_order = "top-to-bottom" else: y = y + working_area["height"] - img.height vertical_order = "bottom-to-top" working_area["height"] -= img.height + padding # Paste the image onto the canvas collage.paste(img, (x, y)) x, y = working_area["x"], working_area["y"] # Update position for the next image # Stop if the working area becomes too small if working_area["width"] <= 0 or working_area["height"] <= 0: print("Working area exhausted. Stopping collage creation.") break
Enter fullscreen mode Exit fullscreen mode
Golden ratio collage
Huh, so we finally have the script. Now we need GUI.
I have never worked with Streamlit before, so this is a nice opportunity to learn something new. While you can argue that Streamlit is not a “real” GUI (since we can safely assume it’s a web server with an HTML template engine), it’s a very useful instrument, especially handy when you don’t need something complex.
Anyway, let’s proceed.
First of all, constants.
<span>GOLDEN_RATIO</span> <span>=</span> <span>(</span><span>1</span> <span>+</span> <span>math</span><span>.</span><span>sqrt</span><span>(</span><span>5</span><span>))</span> <span>/</span> <span>2</span> <span># Define the golden ratio </span><span>MIN_IMAGES</span> <span>=</span> <span>4</span><span>MAX_IMAGES</span> <span>=</span> <span>20</span><span>MIN_PADDING</span> <span>=</span> <span>0</span><span>MAX_PADDING</span> <span>=</span> <span>50</span><span># Provided data for social media sizes </span><span>SOCIAL_MEDIA_IMAGE_SIZES</span> <span>=</span> <span>{</span><span>"</span><span>Instagram Feed Square</span><span>"</span><span>:</span> <span>(</span><span>1080</span><span>,</span> <span>1080</span><span>),</span><span>"</span><span>Instagram Feed Portrait</span><span>"</span><span>:</span> <span>(</span><span>1080</span><span>,</span> <span>1350</span><span>),</span><span>"</span><span>Instagram Feed Landscape</span><span>"</span><span>:</span> <span>(</span><span>1080</span><span>,</span> <span>608</span><span>),</span><span>"</span><span>Instagram Stories/Reels</span><span>"</span><span>:</span> <span>(</span><span>1080</span><span>,</span> <span>1920</span><span>),</span><span>"</span><span>Facebook Feed Post</span><span>"</span><span>:</span> <span>(</span><span>1200</span><span>,</span> <span>630</span><span>),</span><span>"</span><span>Facebook Stories</span><span>"</span><span>:</span> <span>(</span><span>1080</span><span>,</span> <span>1920</span><span>),</span><span>"</span><span>Facebook Event Cover</span><span>"</span><span>:</span> <span>(</span><span>1920</span><span>,</span> <span>1005</span><span>),</span><span>"</span><span>YouTube Thumbnail</span><span>"</span><span>:</span> <span>(</span><span>1280</span><span>,</span> <span>720</span><span>),</span><span>"</span><span>YouTube Channel Art</span><span>"</span><span>:</span> <span>(</span><span>2560</span><span>,</span> <span>1440</span><span>),</span><span>"</span><span>X Single Image</span><span>"</span><span>:</span> <span>(</span><span>1200</span><span>,</span> <span>675</span><span>),</span><span>"</span><span>X Multi-Image</span><span>"</span><span>:</span> <span>(</span><span>1200</span><span>,</span> <span>600</span><span>),</span><span>"</span><span>LinkedIn Feed Post</span><span>"</span><span>:</span> <span>(</span><span>1200</span><span>,</span> <span>627</span><span>),</span><span>"</span><span>LinkedIn Company Cover</span><span>"</span><span>:</span> <span>(</span><span>1128</span><span>,</span> <span>191</span><span>),</span><span>}</span><span># Icons for each platform </span><span>SOCIAL_MEDIA_ICONS</span> <span>=</span> <span>{</span><span>"</span><span>Instagram</span><span>"</span><span>:</span> <span>"</span><span>https://upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Instagram_icon.png/240px-Instagram_icon.png</span><span>"</span><span>,</span><span>"</span><span>Facebook</span><span>"</span><span>:</span> <span>"</span><span>https://upload.wikimedia.org/wikipedia/commons/5/51/Facebook_f_logo_%282019%29.svg</span><span>"</span><span>,</span><span>"</span><span>YouTube</span><span>"</span><span>:</span> <span>"</span><span>https://upload.wikimedia.org/wikipedia/commons/0/09/YouTube_full-color_icon_%282017%29.svg</span><span>"</span><span>,</span><span>"</span><span>X</span><span>"</span><span>:</span> <span>"</span><span>https://upload.wikimedia.org/wikipedia/commons/thumb/2/28/X_icon_black.svg/240px-X_icon_black.svg.png</span><span>"</span><span>,</span><span>"</span><span>LinkedIn</span><span>"</span><span>:</span> <span>"</span><span>https://upload.wikimedia.org/wikipedia/commons/c/ca/LinkedIn_logo_initials.png</span><span>"</span><span>,</span><span>}</span><span>GOLDEN_RATIO</span> <span>=</span> <span>(</span><span>1</span> <span>+</span> <span>math</span><span>.</span><span>sqrt</span><span>(</span><span>5</span><span>))</span> <span>/</span> <span>2</span> <span># Define the golden ratio </span> <span>MIN_IMAGES</span> <span>=</span> <span>4</span> <span>MAX_IMAGES</span> <span>=</span> <span>20</span> <span>MIN_PADDING</span> <span>=</span> <span>0</span> <span>MAX_PADDING</span> <span>=</span> <span>50</span> <span># Provided data for social media sizes </span><span>SOCIAL_MEDIA_IMAGE_SIZES</span> <span>=</span> <span>{</span> <span>"</span><span>Instagram Feed Square</span><span>"</span><span>:</span> <span>(</span><span>1080</span><span>,</span> <span>1080</span><span>),</span> <span>"</span><span>Instagram Feed Portrait</span><span>"</span><span>:</span> <span>(</span><span>1080</span><span>,</span> <span>1350</span><span>),</span> <span>"</span><span>Instagram Feed Landscape</span><span>"</span><span>:</span> <span>(</span><span>1080</span><span>,</span> <span>608</span><span>),</span> <span>"</span><span>Instagram Stories/Reels</span><span>"</span><span>:</span> <span>(</span><span>1080</span><span>,</span> <span>1920</span><span>),</span> <span>"</span><span>Facebook Feed Post</span><span>"</span><span>:</span> <span>(</span><span>1200</span><span>,</span> <span>630</span><span>),</span> <span>"</span><span>Facebook Stories</span><span>"</span><span>:</span> <span>(</span><span>1080</span><span>,</span> <span>1920</span><span>),</span> <span>"</span><span>Facebook Event Cover</span><span>"</span><span>:</span> <span>(</span><span>1920</span><span>,</span> <span>1005</span><span>),</span> <span>"</span><span>YouTube Thumbnail</span><span>"</span><span>:</span> <span>(</span><span>1280</span><span>,</span> <span>720</span><span>),</span> <span>"</span><span>YouTube Channel Art</span><span>"</span><span>:</span> <span>(</span><span>2560</span><span>,</span> <span>1440</span><span>),</span> <span>"</span><span>X Single Image</span><span>"</span><span>:</span> <span>(</span><span>1200</span><span>,</span> <span>675</span><span>),</span> <span>"</span><span>X Multi-Image</span><span>"</span><span>:</span> <span>(</span><span>1200</span><span>,</span> <span>600</span><span>),</span> <span>"</span><span>LinkedIn Feed Post</span><span>"</span><span>:</span> <span>(</span><span>1200</span><span>,</span> <span>627</span><span>),</span> <span>"</span><span>LinkedIn Company Cover</span><span>"</span><span>:</span> <span>(</span><span>1128</span><span>,</span> <span>191</span><span>),</span> <span>}</span> <span># Icons for each platform </span><span>SOCIAL_MEDIA_ICONS</span> <span>=</span> <span>{</span> <span>"</span><span>Instagram</span><span>"</span><span>:</span> <span>"</span><span>https://upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Instagram_icon.png/240px-Instagram_icon.png</span><span>"</span><span>,</span> <span>"</span><span>Facebook</span><span>"</span><span>:</span> <span>"</span><span>https://upload.wikimedia.org/wikipedia/commons/5/51/Facebook_f_logo_%282019%29.svg</span><span>"</span><span>,</span> <span>"</span><span>YouTube</span><span>"</span><span>:</span> <span>"</span><span>https://upload.wikimedia.org/wikipedia/commons/0/09/YouTube_full-color_icon_%282017%29.svg</span><span>"</span><span>,</span> <span>"</span><span>X</span><span>"</span><span>:</span> <span>"</span><span>https://upload.wikimedia.org/wikipedia/commons/thumb/2/28/X_icon_black.svg/240px-X_icon_black.svg.png</span><span>"</span><span>,</span> <span>"</span><span>LinkedIn</span><span>"</span><span>:</span> <span>"</span><span>https://upload.wikimedia.org/wikipedia/commons/c/ca/LinkedIn_logo_initials.png</span><span>"</span><span>,</span> <span>}</span>GOLDEN_RATIO = (1 + math.sqrt(5)) / 2 # Define the golden ratio MIN_IMAGES = 4 MAX_IMAGES = 20 MIN_PADDING = 0 MAX_PADDING = 50 # Provided data for social media sizes SOCIAL_MEDIA_IMAGE_SIZES = { "Instagram Feed Square": (1080, 1080), "Instagram Feed Portrait": (1080, 1350), "Instagram Feed Landscape": (1080, 608), "Instagram Stories/Reels": (1080, 1920), "Facebook Feed Post": (1200, 630), "Facebook Stories": (1080, 1920), "Facebook Event Cover": (1920, 1005), "YouTube Thumbnail": (1280, 720), "YouTube Channel Art": (2560, 1440), "X Single Image": (1200, 675), "X Multi-Image": (1200, 600), "LinkedIn Feed Post": (1200, 627), "LinkedIn Company Cover": (1128, 191), } # Icons for each platform SOCIAL_MEDIA_ICONS = { "Instagram": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Instagram_icon.png/240px-Instagram_icon.png", "Facebook": "https://upload.wikimedia.org/wikipedia/commons/5/51/Facebook_f_logo_%282019%29.svg", "YouTube": "https://upload.wikimedia.org/wikipedia/commons/0/09/YouTube_full-color_icon_%282017%29.svg", "X": "https://upload.wikimedia.org/wikipedia/commons/thumb/2/28/X_icon_black.svg/240px-X_icon_black.svg.png", "LinkedIn": "https://upload.wikimedia.org/wikipedia/commons/c/ca/LinkedIn_logo_initials.png", }
Enter fullscreen mode Exit fullscreen mode
As you can see here, it’s self-explanatory. I’ve created several pre-defined media templates for a better user expirience.
Next, the page config. For the background, I’ve used Hero Pattern
<span>st</span><span>.</span><span>set_page_config</span><span>(</span><span>page_title</span><span>=</span><span>'</span><span>Photo Collage Maker</span><span>'</span><span>,</span><span>page_icon</span><span>=</span><span>"</span><span>🧊</span><span>"</span><span>,</span><span>layout</span><span>=</span><span>"</span><span>centered</span><span>"</span><span>,</span><span>)</span><span>st</span><span>.</span><span>title</span><span>(</span><span>"</span><span>Photo Collage Maker</span><span>"</span><span>)</span><span>st</span><span>.</span><span>markdown</span><span>(</span><span>f</span><span>"""</span><span> <style> .stApp {{ background-color: #fefae0; background-image: url(</span><span>"</span><span>data:image/svg+xml,%3Csvg xmlns=</span><span>'</span><span>http://www.w3.org/2000/svg</span><span>'</span><span> width=</span><span>'</span><span>152</span><span>'</span><span> height=</span><span>'</span><span>152</span><span>'</span><span> viewBox=</span><span>'</span><span>0 0 152 152</span><span>'</span><span>%3E%3Cg fill-rule=</span><span>'</span><span>evenodd</span><span>'</span><span>%3E%3Cg id=</span><span>'</span><span>temple</span><span>'</span><span> fill=</span><span>'</span><span>%23283618</span><span>'</span><span> fill-opacity=</span><span>'</span><span>0.1</span><span>'</span><span>%3E%3Cpath d=</span><span>'</span><span>M152 150v2H0v-2h28v-8H8v-20H0v-2h8V80h42v20h20v42H30v8h90v-8H80v-42h20V80h42v40h8V30h-8v40h-42V50H80V8h40V0h2v8h20v20h8V0h2v150zm-2 0v-28h-8v20h-20v8h28zM82 30v18h18V30H82zm20 18h20v20h18V30h-20V10H82v18h20v20zm0 2v18h18V50h-18zm20-22h18V10h-18v18zm-54 92v-18H50v18h18zm-20-18H28V82H10v38h20v20h38v-18H48v-20zm0-2V82H30v18h18zm-20 22H10v18h18v-18zm54 0v18h38v-20h20V82h-18v20h-20v20H82zm18-20H82v18h18v-18zm2-2h18V82h-18v18zm20 40v-18h18v18h-18zM30 0h-2v8H8v20H0v2h8v40h42V50h20V8H30V0zm20 48h18V30H50v18zm18-20H48v20H28v20H10V30h20V10h38v18zM30 50h18v18H30V50zm-2-40H10v18h18V10z</span><span>'</span><span>/%3E%3C/g%3E%3C/g%3E%3C/svg%3E</span><span>"</span><span>); }} </style> </span><span>"""</span><span>,</span><span>unsafe_allow_html</span><span>=</span><span>True</span><span>)</span><span>st</span><span>.</span><span>set_page_config</span><span>(</span> <span>page_title</span><span>=</span><span>'</span><span>Photo Collage Maker</span><span>'</span><span>,</span> <span>page_icon</span><span>=</span><span>"</span><span>🧊</span><span>"</span><span>,</span> <span>layout</span><span>=</span><span>"</span><span>centered</span><span>"</span><span>,</span> <span>)</span> <span>st</span><span>.</span><span>title</span><span>(</span><span>"</span><span>Photo Collage Maker</span><span>"</span><span>)</span> <span>st</span><span>.</span><span>markdown</span><span>(</span> <span>f</span><span>"""</span><span> <style> .stApp {{ background-color: #fefae0; background-image: url(</span><span>"</span><span>data:image/svg+xml,%3Csvg xmlns=</span><span>'</span><span>http://www.w3.org/2000/svg</span><span>'</span><span> width=</span><span>'</span><span>152</span><span>'</span><span> height=</span><span>'</span><span>152</span><span>'</span><span> viewBox=</span><span>'</span><span>0 0 152 152</span><span>'</span><span>%3E%3Cg fill-rule=</span><span>'</span><span>evenodd</span><span>'</span><span>%3E%3Cg id=</span><span>'</span><span>temple</span><span>'</span><span> fill=</span><span>'</span><span>%23283618</span><span>'</span><span> fill-opacity=</span><span>'</span><span>0.1</span><span>'</span><span>%3E%3Cpath d=</span><span>'</span><span>M152 150v2H0v-2h28v-8H8v-20H0v-2h8V80h42v20h20v42H30v8h90v-8H80v-42h20V80h42v40h8V30h-8v40h-42V50H80V8h40V0h2v8h20v20h8V0h2v150zm-2 0v-28h-8v20h-20v8h28zM82 30v18h18V30H82zm20 18h20v20h18V30h-20V10H82v18h20v20zm0 2v18h18V50h-18zm20-22h18V10h-18v18zm-54 92v-18H50v18h18zm-20-18H28V82H10v38h20v20h38v-18H48v-20zm0-2V82H30v18h18zm-20 22H10v18h18v-18zm54 0v18h38v-20h20V82h-18v20h-20v20H82zm18-20H82v18h18v-18zm2-2h18V82h-18v18zm20 40v-18h18v18h-18zM30 0h-2v8H8v20H0v2h8v40h42V50h20V8H30V0zm20 48h18V30H50v18zm18-20H48v20H28v20H10V30h20V10h38v18zM30 50h18v18H30V50zm-2-40H10v18h18V10z</span><span>'</span><span>/%3E%3C/g%3E%3C/g%3E%3C/svg%3E</span><span>"</span><span>); }} </style> </span><span>"""</span><span>,</span> <span>unsafe_allow_html</span><span>=</span><span>True</span> <span>)</span>st.set_page_config( page_title='Photo Collage Maker', page_icon="🧊", layout="centered", ) st.title("Photo Collage Maker") st.markdown( f""" <style> .stApp {{ background-color: #fefae0; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='152' height='152' viewBox='0 0 152 152'%3E%3Cg fill-rule='evenodd'%3E%3Cg id='temple' fill='%23283618' fill-opacity='0.1'%3E%3Cpath d='M152 150v2H0v-2h28v-8H8v-20H0v-2h8V80h42v20h20v42H30v8h90v-8H80v-42h20V80h42v40h8V30h-8v40h-42V50H80V8h40V0h2v8h20v20h8V0h2v150zm-2 0v-28h-8v20h-20v8h28zM82 30v18h18V30H82zm20 18h20v20h18V30h-20V10H82v18h20v20zm0 2v18h18V50h-18zm20-22h18V10h-18v18zm-54 92v-18H50v18h18zm-20-18H28V82H10v38h20v20h38v-18H48v-20zm0-2V82H30v18h18zm-20 22H10v18h18v-18zm54 0v18h38v-20h20V82h-18v20h-20v20H82zm18-20H82v18h18v-18zm2-2h18V82h-18v18zm20 40v-18h18v18h-18zM30 0h-2v8H8v20H0v2h8v40h42V50h20V8H30V0zm20 48h18V30H50v18zm18-20H48v20H28v20H10V30h20V10h38v18zM30 50h18v18H30V50zm-2-40H10v18h18V10z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); }} </style> """, unsafe_allow_html=True )
Enter fullscreen mode Exit fullscreen mode
After that, state and reducers ().
The simple and most obvious way
<span># pre-define session state variables </span> <span>if</span> <span>'</span><span>images</span><span>'</span> <span>not</span> <span>in</span> <span>st</span><span>.</span><span>session_state</span><span>:</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>images</span> <span>=</span> <span>None</span><span>if</span> <span>'</span><span>platform</span><span>'</span> <span>not</span> <span>in</span> <span>st</span><span>.</span><span>session_state</span><span>:</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>platform</span> <span>=</span> <span>None</span><span>if</span> <span>'</span><span>background</span><span>'</span> <span>not</span> <span>in</span> <span>st</span><span>.</span><span>session_state</span><span>:</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>background</span> <span>=</span> <span>None</span><span>if</span> <span>'</span><span>layout</span><span>'</span> <span>not</span> <span>in</span> <span>st</span><span>.</span><span>session_state</span><span>:</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>layout</span> <span>=</span> <span>None</span><span>if</span> <span>'</span><span>randomization</span><span>'</span> <span>not</span> <span>in</span> <span>st</span><span>.</span><span>session_state</span><span>:</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>randomization</span> <span>=</span> <span>False</span><span>if</span> <span>'</span><span>centering</span><span>'</span> <span>not</span> <span>in</span> <span>st</span><span>.</span><span>session_state</span><span>:</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>centering</span> <span>=</span> <span>False</span><span>if</span> <span>'</span><span>padding</span><span>'</span> <span>not</span> <span>in</span> <span>st</span><span>.</span><span>session_state</span><span>:</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>padding</span> <span>=</span> <span>MIN_PADDING</span><span>if</span> <span>'</span><span>collage</span><span>'</span> <span>not</span> <span>in</span> <span>st</span><span>.</span><span>session_state</span><span>:</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>collage</span> <span>=</span> <span>None</span><span># pre-define session state variables </span> <span>if</span> <span>'</span><span>images</span><span>'</span> <span>not</span> <span>in</span> <span>st</span><span>.</span><span>session_state</span><span>:</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>images</span> <span>=</span> <span>None</span> <span>if</span> <span>'</span><span>platform</span><span>'</span> <span>not</span> <span>in</span> <span>st</span><span>.</span><span>session_state</span><span>:</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>platform</span> <span>=</span> <span>None</span> <span>if</span> <span>'</span><span>background</span><span>'</span> <span>not</span> <span>in</span> <span>st</span><span>.</span><span>session_state</span><span>:</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>background</span> <span>=</span> <span>None</span> <span>if</span> <span>'</span><span>layout</span><span>'</span> <span>not</span> <span>in</span> <span>st</span><span>.</span><span>session_state</span><span>:</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>layout</span> <span>=</span> <span>None</span> <span>if</span> <span>'</span><span>randomization</span><span>'</span> <span>not</span> <span>in</span> <span>st</span><span>.</span><span>session_state</span><span>:</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>randomization</span> <span>=</span> <span>False</span> <span>if</span> <span>'</span><span>centering</span><span>'</span> <span>not</span> <span>in</span> <span>st</span><span>.</span><span>session_state</span><span>:</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>centering</span> <span>=</span> <span>False</span> <span>if</span> <span>'</span><span>padding</span><span>'</span> <span>not</span> <span>in</span> <span>st</span><span>.</span><span>session_state</span><span>:</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>padding</span> <span>=</span> <span>MIN_PADDING</span> <span>if</span> <span>'</span><span>collage</span><span>'</span> <span>not</span> <span>in</span> <span>st</span><span>.</span><span>session_state</span><span>:</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>collage</span> <span>=</span> <span>None</span># pre-define session state variables if 'images' not in st.session_state: st.session_state.images = None if 'platform' not in st.session_state: st.session_state.platform = None if 'background' not in st.session_state: st.session_state.background = None if 'layout' not in st.session_state: st.session_state.layout = None if 'randomization' not in st.session_state: st.session_state.randomization = False if 'centering' not in st.session_state: st.session_state.centering = False if 'padding' not in st.session_state: st.session_state.padding = MIN_PADDING if 'collage' not in st.session_state: st.session_state.collage = None
Enter fullscreen mode Exit fullscreen mode
or a more elegant one
<span># Define default session state values </span><span>default_values</span> <span>=</span> <span>{</span><span>"</span><span>images</span><span>"</span><span>:</span> <span>None</span><span>,</span><span>"</span><span>platform</span><span>"</span><span>:</span> <span>None</span><span>,</span><span>"</span><span>background</span><span>"</span><span>:</span> <span>None</span><span>,</span><span>"</span><span>layout</span><span>"</span><span>:</span> <span>None</span><span>,</span><span>"</span><span>randomization</span><span>"</span><span>:</span> <span>False</span><span>,</span><span>"</span><span>centering</span><span>"</span><span>:</span> <span>False</span><span>,</span><span>"</span><span>padding</span><span>"</span><span>:</span> <span>MIN_PADDING</span><span>,</span><span>"</span><span>collage</span><span>"</span><span>:</span> <span>None</span><span>,</span><span>}</span><span># Initialize session state </span><span>for</span> <span>key</span><span>,</span> <span>value</span> <span>in</span> <span>default_values</span><span>.</span><span>items</span><span>():</span><span>if</span> <span>key</span> <span>not</span> <span>in</span> <span>st</span><span>.</span><span>session_state</span><span>:</span><span>st</span><span>.</span><span>session_state</span><span>[</span><span>key</span><span>]</span> <span>=</span> <span>value</span><span># Define default session state values </span><span>default_values</span> <span>=</span> <span>{</span> <span>"</span><span>images</span><span>"</span><span>:</span> <span>None</span><span>,</span> <span>"</span><span>platform</span><span>"</span><span>:</span> <span>None</span><span>,</span> <span>"</span><span>background</span><span>"</span><span>:</span> <span>None</span><span>,</span> <span>"</span><span>layout</span><span>"</span><span>:</span> <span>None</span><span>,</span> <span>"</span><span>randomization</span><span>"</span><span>:</span> <span>False</span><span>,</span> <span>"</span><span>centering</span><span>"</span><span>:</span> <span>False</span><span>,</span> <span>"</span><span>padding</span><span>"</span><span>:</span> <span>MIN_PADDING</span><span>,</span> <span>"</span><span>collage</span><span>"</span><span>:</span> <span>None</span><span>,</span> <span>}</span> <span># Initialize session state </span><span>for</span> <span>key</span><span>,</span> <span>value</span> <span>in</span> <span>default_values</span><span>.</span><span>items</span><span>():</span> <span>if</span> <span>key</span> <span>not</span> <span>in</span> <span>st</span><span>.</span><span>session_state</span><span>:</span> <span>st</span><span>.</span><span>session_state</span><span>[</span><span>key</span><span>]</span> <span>=</span> <span>value</span># Define default session state values default_values = { "images": None, "platform": None, "background": None, "layout": None, "randomization": False, "centering": False, "padding": MIN_PADDING, "collage": None, } # Initialize session state for key, value in default_values.items(): if key not in st.session_state: st.session_state[key] = value
Enter fullscreen mode Exit fullscreen mode
and state management
<span>def</span> <span>set_active_platform</span><span>(</span><span>active_platform</span><span>):</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>platform</span> <span>=</span> <span>active_platform</span><span>def</span> <span>set_active_images</span><span>(</span><span>active_images</span><span>):</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>images</span> <span>=</span> <span>active_images</span><span>def</span> <span>set_active_background</span><span>(</span><span>category</span><span>,</span> <span>active_background</span><span>):</span><span>if</span> <span>category</span> <span>is</span> <span>None</span> <span>and</span> <span>active_background</span> <span>is</span> <span>None</span><span>:</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>background</span> <span>=</span> <span>None</span><span>else</span><span>:</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>background</span> <span>=</span> <span>{</span><span>"</span><span>category</span><span>"</span><span>:</span> <span>category</span><span>,</span><span>"</span><span>value</span><span>"</span><span>:</span> <span>active_background</span><span>}</span><span>def</span> <span>set_active_layout</span><span>(</span><span>active_layout</span><span>):</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>layout</span> <span>=</span> <span>active_layout</span><span>def</span> <span>handle_create_collage_button_click</span><span>():</span><span># Initialize the canvas (background image or color) </span> <span>new_collage</span> <span>=</span> <span>None</span><span>if</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>background</span><span>[</span><span>"</span><span>category</span><span>"</span><span>]</span> <span>==</span> <span>"</span><span>image</span><span>"</span><span>:</span><span>new_collage</span> <span>=</span> <span>Image</span><span>.</span><span>open</span><span>(</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>background</span><span>[</span><span>"</span><span>value</span><span>"</span><span>])</span><span>else</span><span>:</span><span>new_collage</span> <span>=</span> <span>Image</span><span>.</span><span>new</span><span>(</span><span>"</span><span>RGB</span><span>"</span><span>,</span> <span>SOCIAL_MEDIA_IMAGE_SIZES</span><span>[</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>platform</span><span>],</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>background</span><span>[</span><span>"</span><span>value</span><span>"</span><span>])</span><span>match</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>layout</span><span>:</span><span>case</span> <span>"</span><span>golden_ratio</span><span>"</span><span>:</span><span>new_collage</span> <span>=</span> <span>golden_ratio_collage</span><span>(</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>images</span><span>,</span> <span>new_collage</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>padding</span><span>,</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>randomization</span><span>)</span><span>case</span> <span>"</span><span>grid</span><span>"</span><span>:</span><span>new_collage</span> <span>=</span> <span>grid_collage</span><span>(</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>images</span><span>,</span> <span>new_collage</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>padding</span><span>,</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>randomization</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>centering</span><span>)</span><span>case</span> <span>"</span><span>strip</span><span>"</span><span>:</span><span>new_collage</span> <span>=</span> <span>lane_collage</span><span>(</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>images</span><span>,</span> <span>new_collage</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>padding</span><span>,</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>randomization</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>centering</span><span>,</span><span>orientation</span><span>=</span><span>"</span><span>vertical</span><span>"</span><span>)</span><span>case</span> <span>"</span><span>stack</span><span>"</span><span>:</span><span>new_collage</span> <span>=</span> <span>lane_collage</span><span>(</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>images</span><span>,</span> <span>new_collage</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>padding</span><span>,</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>randomization</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>centering</span><span>,</span><span>orientation</span><span>=</span><span>"</span><span>horizontal</span><span>"</span><span>)</span><span>case</span> <span>"</span><span>auto</span><span>"</span><span>:</span><span>new_collage</span> <span>=</span> <span>auto_layout</span><span>(</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>images</span><span>,</span> <span>new_collage</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>padding</span><span>,</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>randomization</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>centering</span><span>)</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>collage</span> <span>=</span> <span>new_collage</span><span>def</span> <span>set_active_platform</span><span>(</span><span>active_platform</span><span>):</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>platform</span> <span>=</span> <span>active_platform</span> <span>def</span> <span>set_active_images</span><span>(</span><span>active_images</span><span>):</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>images</span> <span>=</span> <span>active_images</span> <span>def</span> <span>set_active_background</span><span>(</span><span>category</span><span>,</span> <span>active_background</span><span>):</span> <span>if</span> <span>category</span> <span>is</span> <span>None</span> <span>and</span> <span>active_background</span> <span>is</span> <span>None</span><span>:</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>background</span> <span>=</span> <span>None</span> <span>else</span><span>:</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>background</span> <span>=</span> <span>{</span> <span>"</span><span>category</span><span>"</span><span>:</span> <span>category</span><span>,</span> <span>"</span><span>value</span><span>"</span><span>:</span> <span>active_background</span> <span>}</span> <span>def</span> <span>set_active_layout</span><span>(</span><span>active_layout</span><span>):</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>layout</span> <span>=</span> <span>active_layout</span> <span>def</span> <span>handle_create_collage_button_click</span><span>():</span> <span># Initialize the canvas (background image or color) </span> <span>new_collage</span> <span>=</span> <span>None</span> <span>if</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>background</span><span>[</span><span>"</span><span>category</span><span>"</span><span>]</span> <span>==</span> <span>"</span><span>image</span><span>"</span><span>:</span> <span>new_collage</span> <span>=</span> <span>Image</span><span>.</span><span>open</span><span>(</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>background</span><span>[</span><span>"</span><span>value</span><span>"</span><span>])</span> <span>else</span><span>:</span> <span>new_collage</span> <span>=</span> <span>Image</span><span>.</span><span>new</span><span>(</span><span>"</span><span>RGB</span><span>"</span><span>,</span> <span>SOCIAL_MEDIA_IMAGE_SIZES</span><span>[</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>platform</span><span>],</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>background</span><span>[</span><span>"</span><span>value</span><span>"</span><span>])</span> <span>match</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>layout</span><span>:</span> <span>case</span> <span>"</span><span>golden_ratio</span><span>"</span><span>:</span> <span>new_collage</span> <span>=</span> <span>golden_ratio_collage</span><span>(</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>images</span><span>,</span> <span>new_collage</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>padding</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>randomization</span><span>)</span> <span>case</span> <span>"</span><span>grid</span><span>"</span><span>:</span> <span>new_collage</span> <span>=</span> <span>grid_collage</span><span>(</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>images</span><span>,</span> <span>new_collage</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>padding</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>randomization</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>centering</span><span>)</span> <span>case</span> <span>"</span><span>strip</span><span>"</span><span>:</span> <span>new_collage</span> <span>=</span> <span>lane_collage</span><span>(</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>images</span><span>,</span> <span>new_collage</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>padding</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>randomization</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>centering</span><span>,</span> <span>orientation</span><span>=</span><span>"</span><span>vertical</span><span>"</span><span>)</span> <span>case</span> <span>"</span><span>stack</span><span>"</span><span>:</span> <span>new_collage</span> <span>=</span> <span>lane_collage</span><span>(</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>images</span><span>,</span> <span>new_collage</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>padding</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>randomization</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>centering</span><span>,</span> <span>orientation</span><span>=</span><span>"</span><span>horizontal</span><span>"</span><span>)</span> <span>case</span> <span>"</span><span>auto</span><span>"</span><span>:</span> <span>new_collage</span> <span>=</span> <span>auto_layout</span><span>(</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>images</span><span>,</span> <span>new_collage</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>padding</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>randomization</span><span>,</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>centering</span><span>)</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>collage</span> <span>=</span> <span>new_collage</span>def set_active_platform(active_platform): st.session_state.platform = active_platform def set_active_images(active_images): st.session_state.images = active_images def set_active_background(category, active_background): if category is None and active_background is None: st.session_state.background = None else: st.session_state.background = { "category": category, "value": active_background } def set_active_layout(active_layout): st.session_state.layout = active_layout def handle_create_collage_button_click(): # Initialize the canvas (background image or color) new_collage = None if st.session_state.background["category"] == "image": new_collage = Image.open(st.session_state.background["value"]) else: new_collage = Image.new("RGB", SOCIAL_MEDIA_IMAGE_SIZES[st.session_state.platform], st.session_state.background["value"]) match st.session_state.layout: case "golden_ratio": new_collage = golden_ratio_collage(st.session_state.images, new_collage, st.session_state.padding, st.session_state.randomization) case "grid": new_collage = grid_collage(st.session_state.images, new_collage, st.session_state.padding, st.session_state.randomization, st.session_state.centering) case "strip": new_collage = lane_collage(st.session_state.images, new_collage, st.session_state.padding, st.session_state.randomization, st.session_state.centering, orientation="vertical") case "stack": new_collage = lane_collage(st.session_state.images, new_collage, st.session_state.padding, st.session_state.randomization, st.session_state.centering, orientation="horizontal") case "auto": new_collage = auto_layout(st.session_state.images, new_collage, st.session_state.padding, st.session_state.randomization, st.session_state.centering) st.session_state.collage = new_collage
Enter fullscreen mode Exit fullscreen mode
I prefer to keep it simple, but you can always modify it to be a more centralized state-reducer thing, though.
I’ve also added the auto layout, so when a user is in doubt, we can do our best automatically.
<span>canvas_width</span><span>,</span> <span>canvas_height</span> <span>=</span> <span>collage</span><span>.</span><span>size</span><span>images_num</span> <span>=</span> <span>len</span><span>(</span><span>images</span><span>)</span><span>image_objects_list</span> <span>=</span> <span>[]</span><span>squares</span> <span>=</span> <span>0</span><span>horizontal_rectangles</span> <span>=</span> <span>0</span><span>vertical_rectangles</span> <span>=</span> <span>0</span><span>total_area</span> <span>=</span> <span>0</span><span>aspect_ratio_sum_width</span> <span>=</span> <span>aspect_ratio_sum_height</span> <span>=</span> <span>0</span><span>for</span> <span>idx</span><span>,</span> <span>img_path</span> <span>in</span> <span>enumerate</span><span>(</span><span>images</span><span>):</span><span>img</span> <span>=</span> <span>Image</span><span>.</span><span>open</span><span>(</span><span>img_path</span><span>)</span><span>image_objects_list</span><span>.</span><span>append</span><span>({</span><span>"</span><span>image</span><span>"</span><span>:</span> <span>img</span><span>})</span><span>total_area</span> <span>+=</span> <span>img</span><span>.</span><span>width</span> <span>*</span> <span>img</span><span>.</span><span>height</span><span>if</span> <span>img</span><span>.</span><span>height</span> <span>==</span> <span>img</span><span>.</span><span>width</span><span>:</span><span>squares</span> <span>+=</span> <span>1</span><span>aspect_ratio_sum_width</span> <span>+=</span> <span>1</span><span>aspect_ratio_sum_height</span> <span>+=</span> <span>1</span><span>elif</span> <span>img</span><span>.</span><span>height</span> <span>></span> <span>img</span><span>.</span><span>width</span><span>:</span><span>vertical_rectangles</span> <span>+=</span> <span>1</span><span>aspect_ratio_sum_height</span> <span>+=</span> <span>img</span><span>.</span><span>height</span> <span>/</span> <span>img</span><span>.</span><span>width</span><span>aspect_ratio_sum_width</span> <span>+=</span> <span>1</span><span>else</span><span>:</span><span>horizontal_rectangles</span> <span>+=</span> <span>1</span><span>aspect_ratio_sum_width</span> <span>+=</span> <span>img</span><span>.</span><span>width</span> <span>/</span> <span>img</span><span>.</span><span>height</span><span>aspect_ratio_sum_height</span> <span>+=</span> <span>1</span><span>if</span> <span>squares</span> <span>==</span> <span>images_num</span><span>:</span><span>return</span> <span>grid_collage</span><span>(</span><span>images</span><span>,</span> <span>collage</span><span>,</span> <span>padding</span><span>,</span> <span>randomization</span><span>,</span> <span>centered</span><span>)</span><span>elif</span> <span>horizontal_rectangles</span> <span>==</span> <span>images_num</span><span>:</span><span>return</span> <span>lane_collage</span><span>(</span><span>images</span><span>,</span> <span>collage</span><span>,</span> <span>padding</span><span>,</span> <span>randomization</span><span>,</span> <span>centered</span><span>,</span> <span>orientation</span><span>=</span><span>"</span><span>horizontal</span><span>"</span><span>)</span><span>elif</span> <span>vertical_rectangles</span> <span>==</span> <span>images_num</span><span>:</span><span>return</span> <span>lane_collage</span><span>(</span><span>images</span><span>,</span> <span>collage</span><span>,</span> <span>padding</span><span>,</span> <span>randomization</span><span>,</span> <span>centered</span><span>,</span> <span>orientation</span><span>=</span><span>"</span><span>vertical</span><span>"</span><span>)</span><span>else</span><span>:</span><span>canvas_area</span> <span>=</span> <span>canvas_width</span> <span>*</span> <span>canvas_height</span><span>scaling_factor</span> <span>=</span> <span>math</span><span>.</span><span>sqrt</span><span>(</span><span>canvas_area</span> <span>/</span> <span>total_area</span><span>)</span><span>rows</span> <span>=</span> <span>0</span><span>cols</span> <span>=</span> <span>0</span><span>aspect_sum_diff</span> <span>=</span> <span>abs</span><span>(</span><span>aspect_ratio_sum_width</span> <span>-</span> <span>aspect_ratio_sum_height</span><span>)</span><span>return</span> <span>golden_ratio_collage</span><span>(</span><span>images</span><span>,</span> <span>collage</span><span>,</span> <span>padding</span><span>,</span> <span>randomization</span><span>)</span><span>canvas_width</span><span>,</span> <span>canvas_height</span> <span>=</span> <span>collage</span><span>.</span><span>size</span> <span>images_num</span> <span>=</span> <span>len</span><span>(</span><span>images</span><span>)</span> <span>image_objects_list</span> <span>=</span> <span>[]</span> <span>squares</span> <span>=</span> <span>0</span> <span>horizontal_rectangles</span> <span>=</span> <span>0</span> <span>vertical_rectangles</span> <span>=</span> <span>0</span> <span>total_area</span> <span>=</span> <span>0</span> <span>aspect_ratio_sum_width</span> <span>=</span> <span>aspect_ratio_sum_height</span> <span>=</span> <span>0</span> <span>for</span> <span>idx</span><span>,</span> <span>img_path</span> <span>in</span> <span>enumerate</span><span>(</span><span>images</span><span>):</span> <span>img</span> <span>=</span> <span>Image</span><span>.</span><span>open</span><span>(</span><span>img_path</span><span>)</span> <span>image_objects_list</span><span>.</span><span>append</span><span>({</span> <span>"</span><span>image</span><span>"</span><span>:</span> <span>img</span> <span>})</span> <span>total_area</span> <span>+=</span> <span>img</span><span>.</span><span>width</span> <span>*</span> <span>img</span><span>.</span><span>height</span> <span>if</span> <span>img</span><span>.</span><span>height</span> <span>==</span> <span>img</span><span>.</span><span>width</span><span>:</span> <span>squares</span> <span>+=</span> <span>1</span> <span>aspect_ratio_sum_width</span> <span>+=</span> <span>1</span> <span>aspect_ratio_sum_height</span> <span>+=</span> <span>1</span> <span>elif</span> <span>img</span><span>.</span><span>height</span> <span>></span> <span>img</span><span>.</span><span>width</span><span>:</span> <span>vertical_rectangles</span> <span>+=</span> <span>1</span> <span>aspect_ratio_sum_height</span> <span>+=</span> <span>img</span><span>.</span><span>height</span> <span>/</span> <span>img</span><span>.</span><span>width</span> <span>aspect_ratio_sum_width</span> <span>+=</span> <span>1</span> <span>else</span><span>:</span> <span>horizontal_rectangles</span> <span>+=</span> <span>1</span> <span>aspect_ratio_sum_width</span> <span>+=</span> <span>img</span><span>.</span><span>width</span> <span>/</span> <span>img</span><span>.</span><span>height</span> <span>aspect_ratio_sum_height</span> <span>+=</span> <span>1</span> <span>if</span> <span>squares</span> <span>==</span> <span>images_num</span><span>:</span> <span>return</span> <span>grid_collage</span><span>(</span><span>images</span><span>,</span> <span>collage</span><span>,</span> <span>padding</span><span>,</span> <span>randomization</span><span>,</span> <span>centered</span><span>)</span> <span>elif</span> <span>horizontal_rectangles</span> <span>==</span> <span>images_num</span><span>:</span> <span>return</span> <span>lane_collage</span><span>(</span><span>images</span><span>,</span> <span>collage</span><span>,</span> <span>padding</span><span>,</span> <span>randomization</span><span>,</span> <span>centered</span><span>,</span> <span>orientation</span><span>=</span><span>"</span><span>horizontal</span><span>"</span><span>)</span> <span>elif</span> <span>vertical_rectangles</span> <span>==</span> <span>images_num</span><span>:</span> <span>return</span> <span>lane_collage</span><span>(</span><span>images</span><span>,</span> <span>collage</span><span>,</span> <span>padding</span><span>,</span> <span>randomization</span><span>,</span> <span>centered</span><span>,</span> <span>orientation</span><span>=</span><span>"</span><span>vertical</span><span>"</span><span>)</span> <span>else</span><span>:</span> <span>canvas_area</span> <span>=</span> <span>canvas_width</span> <span>*</span> <span>canvas_height</span> <span>scaling_factor</span> <span>=</span> <span>math</span><span>.</span><span>sqrt</span><span>(</span><span>canvas_area</span> <span>/</span> <span>total_area</span><span>)</span> <span>rows</span> <span>=</span> <span>0</span> <span>cols</span> <span>=</span> <span>0</span> <span>aspect_sum_diff</span> <span>=</span> <span>abs</span><span>(</span><span>aspect_ratio_sum_width</span> <span>-</span> <span>aspect_ratio_sum_height</span><span>)</span> <span>return</span> <span>golden_ratio_collage</span><span>(</span><span>images</span><span>,</span> <span>collage</span><span>,</span> <span>padding</span><span>,</span> <span>randomization</span><span>)</span>canvas_width, canvas_height = collage.size images_num = len(images) image_objects_list = [] squares = 0 horizontal_rectangles = 0 vertical_rectangles = 0 total_area = 0 aspect_ratio_sum_width = aspect_ratio_sum_height = 0 for idx, img_path in enumerate(images): img = Image.open(img_path) image_objects_list.append({ "image": img }) total_area += img.width * img.height if img.height == img.width: squares += 1 aspect_ratio_sum_width += 1 aspect_ratio_sum_height += 1 elif img.height > img.width: vertical_rectangles += 1 aspect_ratio_sum_height += img.height / img.width aspect_ratio_sum_width += 1 else: horizontal_rectangles += 1 aspect_ratio_sum_width += img.width / img.height aspect_ratio_sum_height += 1 if squares == images_num: return grid_collage(images, collage, padding, randomization, centered) elif horizontal_rectangles == images_num: return lane_collage(images, collage, padding, randomization, centered, orientation="horizontal") elif vertical_rectangles == images_num: return lane_collage(images, collage, padding, randomization, centered, orientation="vertical") else: canvas_area = canvas_width * canvas_height scaling_factor = math.sqrt(canvas_area / total_area) rows = 0 cols = 0 aspect_sum_diff = abs(aspect_ratio_sum_width - aspect_ratio_sum_height) return golden_ratio_collage(images, collage, padding, randomization)
Enter fullscreen mode Exit fullscreen mode
And the final part – the rest of the structure, plus randomization and centralization for the reusability
<span>st</span><span>.</span><span>header</span><span>(</span><span>"</span><span>Step 1. Upload images</span><span>"</span><span>)</span><span>uploaded_files</span> <span>=</span> <span>st</span><span>.</span><span>file_uploader</span><span>(</span><span>"</span><span>Choose images</span><span>"</span><span>,</span> <span>type</span><span>=</span><span>[</span><span>"</span><span>jpg</span><span>"</span><span>,</span> <span>"</span><span>jpeg</span><span>"</span><span>,</span> <span>"</span><span>png</span><span>"</span><span>],</span> <span>accept_multiple_files</span><span>=</span><span>True</span><span>)</span><span>if</span> <span>uploaded_files</span><span>:</span><span>images</span> <span>=</span> <span>[</span><span>Image</span><span>.</span><span>open</span><span>(</span><span>file</span><span>)</span> <span>for</span> <span>file</span> <span>in</span> <span>uploaded_files</span><span>]</span><span>st</span><span>.</span><span>write</span><span>(</span><span>f</span><span>"</span><span>Selected </span><span>{</span><span>len</span><span>(</span><span>images</span><span>)</span><span>}</span><span> images</span><span>"</span><span>)</span><span>if</span> <span>len</span><span>(</span><span>images</span><span>)</span> <span><</span> <span>MIN_IMAGES</span><span>:</span><span>st</span><span>.</span><span>warning</span><span>(</span><span>f</span><span>"</span><span>Please select at least </span><span>{</span><span>MIN_IMAGES</span><span>}</span><span> images for a collage</span><span>"</span><span>)</span><span>elif</span> <span>len</span><span>(</span><span>images</span><span>)</span> <span>></span> <span>MAX_IMAGES</span><span>:</span><span>st</span><span>.</span><span>warning</span><span>(</span><span>f</span><span>"</span><span>Please select at most </span><span>{</span><span>MAX_IMAGES</span><span>}</span><span> images for a collage</span><span>"</span><span>)</span><span>else</span><span>:</span><span>st</span><span>.</span><span>success</span><span>(</span><span>"</span><span>Images uploaded successfully!</span><span>"</span><span>)</span><span>show_images_toggle</span> <span>=</span> <span>st</span><span>.</span><span>toggle</span><span>(</span><span>"</span><span>Show images</span><span>"</span><span>)</span><span>if</span> <span>show_images_toggle</span><span>:</span><span>st</span><span>.</span><span>image</span><span>(</span><span>images</span><span>,</span> <span>use_container_width</span><span>=</span><span>True</span><span>)</span><span>set_active_images</span><span>(</span><span>uploaded_files</span><span>)</span><span>if</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>images</span> <span>is</span> <span>not</span> <span>None</span><span>:</span><span>st</span><span>.</span><span>header</span><span>(</span><span>"</span><span>Step 2. Customize size</span><span>"</span><span>)</span><span>st</span><span>.</span><span>subheader</span><span>(</span><span>"</span><span>Select appropriate media size</span><span>"</span><span>)</span><span>if</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>platform</span> <span>is</span> <span>None</span><span>:</span><span># Render buttons dynamically </span> <span>for</span> <span>platform</span><span>,</span> <span>size</span> <span>in</span> <span>SOCIAL_MEDIA_IMAGE_SIZES</span><span>.</span><span>items</span><span>():</span><span># Extract the base name (e.g., "Instagram" from "Instagram Feed Square") </span> <span>platform_base</span> <span>=</span> <span>platform</span><span>.</span><span>split</span><span>()[</span><span>0</span><span>]</span><span>icon_url</span> <span>=</span> <span>SOCIAL_MEDIA_ICONS</span><span>.</span><span>get</span><span>(</span><span>platform_base</span><span>,</span> <span>None</span><span>)</span><span># Display the button with an icon and label </span> <span>col1</span><span>,</span> <span>col2</span> <span>=</span> <span>st</span><span>.</span><span>columns</span><span>([</span><span>0.2</span><span>,</span> <span>0.8</span><span>])</span> <span># For icon and label alignment </span> <span>with</span> <span>col1</span><span>:</span><span>if</span> <span>icon_url</span><span>:</span><span>st</span><span>.</span><span>image</span><span>(</span><span>icon_url</span><span>,</span> <span>width</span><span>=</span><span>30</span><span>)</span><span>else</span><span>:</span><span>st</span><span>.</span><span>write</span><span>(</span><span>""</span><span>)</span> <span># Placeholder for missing icons </span> <span>with</span> <span>col2</span><span>:</span><span>st</span><span>.</span><span>button</span><span>(</span><span>label</span><span>=</span><span>f</span><span>"</span><span>{</span><span>platform</span><span>}</span><span>: </span><span>{</span><span>size</span><span>[</span><span>0</span><span>]</span><span>}</span><span>x</span><span>{</span><span>size</span><span>[</span><span>1</span><span>]</span><span>}</span><span>"</span><span>,</span> <span>key</span><span>=</span><span>platform</span><span>,</span> <span>help</span><span>=</span><span>platform</span><span>,</span><span>use_container_width</span><span>=</span><span>True</span><span>,</span> <span>args</span><span>=</span><span>(</span><span>platform</span><span>,),</span> <span>on_click</span><span>=</span><span>set_active_platform</span><span>)</span><span>else</span><span>:</span><span># Display active platform info </span> <span>platform_base</span> <span>=</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>platform</span><span>.</span><span>split</span><span>()[</span><span>0</span><span>]</span><span>icon_url</span> <span>=</span> <span>SOCIAL_MEDIA_ICONS</span><span>.</span><span>get</span><span>(</span><span>platform_base</span><span>,</span> <span>None</span><span>)</span><span># Display the button with an icon and label </span> <span>col1</span><span>,</span> <span>col2</span> <span>=</span> <span>st</span><span>.</span><span>columns</span><span>([</span><span>0.2</span><span>,</span> <span>0.8</span><span>])</span> <span># For icon and label alignment </span> <span>with</span> <span>col1</span><span>:</span><span>if</span> <span>icon_url</span><span>:</span><span>st</span><span>.</span><span>image</span><span>(</span><span>icon_url</span><span>,</span> <span>width</span><span>=</span><span>30</span><span>)</span><span>else</span><span>:</span><span>st</span><span>.</span><span>write</span><span>(</span><span>""</span><span>)</span> <span># Placeholder for missing icons </span> <span>with</span> <span>col2</span><span>:</span><span>platform</span> <span>=</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>platform</span><span>st</span><span>.</span><span>write</span><span>(</span><span>f</span><span>"</span><span>Selected: </span><span>{</span><span>platform</span><span>}</span><span> </span><span>{</span><span>SOCIAL_MEDIA_IMAGE_SIZES</span><span>[</span><span>platform</span><span>][</span><span>0</span><span>]</span><span>}</span><span>x</span><span>{</span><span>SOCIAL_MEDIA_IMAGE_SIZES</span><span>[</span><span>platform</span><span>][</span><span>1</span><span>]</span><span>}</span><span>"</span><span>)</span><span>st</span><span>.</span><span>button</span><span>(</span><span>'</span><span>Re-select size</span><span>'</span><span>,</span> <span>args</span><span>=</span><span>(</span><span>None</span><span>,),</span> <span>on_click</span><span>=</span><span>set_active_platform</span><span>,</span> <span>help</span><span>=</span><span>"</span><span>Re-select size</span><span>"</span><span>,</span><span>use_container_width</span><span>=</span><span>True</span><span>,</span> <span>icon</span><span>=</span><span>"</span><span>:material/photo_size_select_actual:</span><span>"</span><span>)</span><span>st</span><span>.</span><span>header</span><span>(</span><span>"</span><span>Step 3. Customize background</span><span>"</span><span>)</span><span>st</span><span>.</span><span>subheader</span><span>(</span><span>"</span><span>Choose color or image</span><span>"</span><span>)</span><span>if</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>background</span> <span>is</span> <span>None</span><span>:</span><span>with</span> <span>st</span><span>.</span><span>container</span><span>(</span><span>border</span><span>=</span><span>True</span><span>):</span><span>column1</span><span>,</span> <span>column2</span> <span>=</span> <span>st</span><span>.</span><span>columns</span><span>([</span><span>1</span><span>,</span> <span>1</span><span>])</span><span>with</span> <span>column1</span><span>:</span><span>color_image_radio</span> <span>=</span> <span>st</span><span>.</span><span>radio</span><span>(</span><span>"</span><span>Background color or image</span><span>"</span><span>,</span><span>[</span><span>"</span><span>color</span><span>"</span><span>,</span> <span>"</span><span>image</span><span>"</span><span>],</span><span>captions</span><span>=</span><span>[</span><span>"</span><span>Solid color</span><span>"</span><span>,</span><span>"</span><span>Image</span><span>"</span><span>,</span><span>],</span><span>)</span><span>with</span> <span>column2</span><span>:</span><span>if</span> <span>color_image_radio</span> <span>==</span> <span>"</span><span>color</span><span>"</span><span>:</span><span>bg_color</span> <span>=</span> <span>st</span><span>.</span><span>color_picker</span><span>(</span><span>"</span><span>Pick A Color</span><span>"</span><span>,</span> <span>None</span><span>)</span><span>if</span> <span>bg_color</span><span>:</span><span>st</span><span>.</span><span>write</span><span>(</span><span>f</span><span>"</span><span>Selected color: </span><span>{</span><span>bg_color</span><span>}</span><span>"</span><span>)</span><span>st</span><span>.</span><span>button</span><span>(</span><span>"</span><span>Set background color</span><span>"</span><span>,</span> <span>on_click</span><span>=</span><span>set_active_background</span><span>,</span> <span>args</span><span>=</span><span>(</span><span>"</span><span>color</span><span>"</span><span>,</span> <span>bg_color</span><span>),</span><span>use_container_width</span><span>=</span><span>True</span><span>,</span> <span>icon</span><span>=</span><span>"</span><span>:material/water_drop:</span><span>"</span><span>)</span><span>else</span><span>:</span><span>bg_file</span> <span>=</span> <span>st</span><span>.</span><span>file_uploader</span><span>(</span><span>"</span><span>Choose image for background</span><span>"</span><span>,</span> <span>type</span><span>=</span><span>[</span><span>"</span><span>jpg</span><span>"</span><span>,</span> <span>"</span><span>jpeg</span><span>"</span><span>,</span> <span>"</span><span>png</span><span>"</span><span>],</span><span>accept_multiple_files</span><span>=</span><span>False</span><span>)</span><span>if</span> <span>bg_file</span><span>:</span><span>bg_img</span> <span>=</span> <span>Image</span><span>.</span><span>open</span><span>(</span><span>bg_file</span><span>)</span><span>st</span><span>.</span><span>image</span><span>(</span><span>bg_img</span><span>,</span> <span>caption</span><span>=</span><span>"</span><span>Selected image</span><span>"</span><span>,</span> <span>use_container_width</span><span>=</span><span>True</span><span>)</span><span>st</span><span>.</span><span>button</span><span>(</span><span>"</span><span>Set background image</span><span>"</span><span>,</span> <span>on_click</span><span>=</span><span>set_active_background</span><span>,</span> <span>args</span><span>=</span><span>(</span><span>"</span><span>image</span><span>"</span><span>,</span> <span>bg_file</span><span>),</span><span>use_container_width</span><span>=</span><span>True</span><span>,</span> <span>icon</span><span>=</span><span>"</span><span>:material/image:</span><span>"</span><span>)</span><span>else</span><span>:</span><span>bg_category</span> <span>=</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>background</span><span>[</span><span>"</span><span>category</span><span>"</span><span>]</span><span>bg_value</span> <span>=</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>background</span><span>[</span><span>"</span><span>value</span><span>"</span><span>]</span><span>if</span> <span>bg_category</span> <span>==</span> <span>"</span><span>color</span><span>"</span><span>:</span><span>st</span><span>.</span><span>write</span><span>(</span><span>f</span><span>"</span><span>Selected color: </span><span>{</span><span>bg_value</span><span>}</span><span>"</span><span>)</span><span>else</span><span>:</span><span>st</span><span>.</span><span>image</span><span>(</span><span>bg_value</span><span>,</span> <span>caption</span><span>=</span><span>"</span><span>Selected image</span><span>"</span><span>,</span> <span>use_container_width</span><span>=</span><span>True</span><span>)</span><span>st</span><span>.</span><span>button</span><span>(</span><span>"</span><span>Re-select background</span><span>"</span><span>,</span> <span>use_container_width</span><span>=</span><span>True</span><span>,</span> <span>on_click</span><span>=</span><span>set_active_background</span><span>,</span><span>args</span><span>=</span><span>(</span><span>None</span><span>,</span> <span>None</span><span>),</span> <span>icon</span><span>=</span><span>"</span><span>:material/format_paint:</span><span>"</span><span>,</span> <span>help</span><span>=</span><span>"</span><span>Re-select background</span><span>"</span><span>)</span><span>st</span><span>.</span><span>header</span><span>(</span><span>"</span><span>Step 4. Customize arrangement of images</span><span>"</span><span>)</span><span>st</span><span>.</span><span>subheader</span><span>(</span><span>"</span><span>Choose layout</span><span>"</span><span>)</span><span>if</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>layout</span> <span>is</span> <span>None</span><span>:</span><span>layout_options</span> <span>=</span> <span>[</span><span>"</span><span>grid</span><span>"</span><span>,</span> <span>"</span><span>strip</span><span>"</span><span>,</span> <span>"</span><span>stack</span><span>"</span><span>,</span> <span>"</span><span>golden_ratio</span><span>"</span><span>,</span> <span>"</span><span>auto</span><span>"</span><span>]</span><span>layout_icons</span> <span>=</span> <span>[</span><span>"</span><span>grid_on</span><span>"</span><span>,</span> <span>"</span><span>table_rows</span><span>"</span><span>,</span> <span>"</span><span>view_column</span><span>"</span><span>,</span> <span>"</span><span>grid_goldenratio</span><span>"</span><span>,</span> <span>"</span><span>auto_awesome</span><span>"</span><span>]</span><span>with</span> <span>st</span><span>.</span><span>container</span><span>(</span><span>border</span><span>=</span><span>True</span><span>):</span><span>columns</span> <span>=</span> <span>st</span><span>.</span><span>columns</span><span>(</span><span>len</span><span>(</span><span>layout_options</span><span>))</span> <span># Create dynamic columns </span><span>for</span> <span>col</span><span>,</span> <span>layout</span><span>,</span> <span>icon</span> <span>in</span> <span>zip</span><span>(</span><span>columns</span><span>,</span> <span>layout_options</span><span>,</span> <span>layout_icons</span><span>):</span><span>with</span> <span>col</span><span>:</span><span>st</span><span>.</span><span>button</span><span>(</span><span>layout</span><span>.</span><span>replace</span><span>(</span><span>"</span><span>_</span><span>"</span><span>,</span> <span>"</span><span> </span><span>"</span><span>),</span> <span>use_container_width</span><span>=</span><span>True</span><span>,</span><span>on_click</span><span>=</span><span>set_active_layout</span><span>,</span> <span>args</span><span>=</span><span>(</span><span>layout</span><span>,),</span> <span>icon</span><span>=</span><span>f</span><span>"</span><span>:material/</span><span>{</span><span>icon</span><span>}</span><span>:</span><span>"</span><span>)</span><span>else</span><span>:</span><span>st</span><span>.</span><span>write</span><span>(</span><span>f</span><span>"</span><span>Selected layout: </span><span>{</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>layout</span><span>}</span><span>"</span><span>)</span><span>st</span><span>.</span><span>button</span><span>(</span><span>"</span><span>Re-select layout</span><span>"</span><span>,</span> <span>use_container_width</span><span>=</span><span>True</span><span>,</span> <span>on_click</span><span>=</span><span>set_active_layout</span><span>,</span><span>args</span><span>=</span><span>(</span><span>None</span><span>,),</span> <span>icon</span><span>=</span><span>"</span><span>:material/grid_off:</span><span>"</span><span>,</span> <span>help</span><span>=</span><span>"</span><span>Re-select layout</span><span>"</span><span>)</span><span>st</span><span>.</span><span>header</span><span>(</span><span>"</span><span>Step 5. Final arrangement</span><span>"</span><span>)</span><span>st</span><span>.</span><span>subheader</span><span>(</span><span>"</span><span>Set up padding, centering, and randomization</span><span>"</span><span>)</span><span>with</span> <span>st</span><span>.</span><span>container</span><span>(</span><span>border</span><span>=</span><span>True</span><span>):</span><span>column1</span><span>,</span> <span>column2</span> <span>=</span> <span>st</span><span>.</span><span>columns</span><span>([</span><span>1</span><span>,</span> <span>1</span><span>])</span><span>with</span> <span>column1</span><span>:</span><span>st</span><span>.</span><span>number_input</span><span>(</span><span>"</span><span>Padding</span><span>"</span><span>,</span> <span>min_value</span><span>=</span><span>MIN_PADDING</span><span>,</span> <span>max_value</span><span>=</span><span>MAX_PADDING</span><span>,</span> <span>key</span><span>=</span><span>'</span><span>padding</span><span>'</span><span>)</span><span>with</span> <span>column2</span><span>:</span><span>st</span><span>.</span><span>checkbox</span><span>(</span><span>"</span><span>Randomize image order</span><span>"</span><span>,</span> <span>key</span><span>=</span><span>'</span><span>randomization</span><span>'</span><span>)</span><span>st</span><span>.</span><span>checkbox</span><span>(</span><span>"</span><span>Center images (if possible)</span><span>"</span><span>,</span> <span>key</span><span>=</span><span>"</span><span>centering</span><span>"</span><span>)</span><span>st</span><span>.</span><span>button</span><span>(</span><span>"</span><span>Create collage</span><span>"</span><span>,</span> <span>use_container_width</span><span>=</span><span>True</span><span>,</span><span>on_click</span><span>=</span><span>handle_create_collage_button_click</span><span>,</span> <span>icon</span><span>=</span><span>"</span><span>:material/auto_awesome_mosaic:</span><span>"</span><span>)</span><span>if</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>collage</span> <span>is</span> <span>not</span> <span>None</span><span>:</span><span>st</span><span>.</span><span>header</span><span>(</span><span>"</span><span>Step 6. Collage preview</span><span>"</span><span>)</span><span>st</span><span>.</span><span>image</span><span>(</span><span>np</span><span>.</span><span>array</span><span>(</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>collage</span><span>),</span> <span>caption</span><span>=</span><span>"</span><span>Collage Preview</span><span>"</span><span>,</span><span>use_container_width</span><span>=</span><span>True</span><span>)</span><span># Convert the image to binary format </span> <span>image_bytes</span> <span>=</span> <span>io</span><span>.</span><span>BytesIO</span><span>()</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>collage</span><span>.</span><span>save</span><span>(</span><span>image_bytes</span><span>,</span> <span>format</span><span>=</span><span>"</span><span>PNG</span><span>"</span><span>)</span> <span># Save as PNG or change to "JPEG" </span> <span>image_bytes</span><span>.</span><span>seek</span><span>(</span><span>0</span><span>)</span> <span># Move cursor to the beginning </span><span># Create a download button </span> <span>st</span><span>.</span><span>download_button</span><span>(</span><span>label</span><span>=</span><span>"</span><span>Download Image</span><span>"</span><span>,</span><span>data</span><span>=</span><span>image_bytes</span><span>,</span><span>file_name</span><span>=</span><span>f</span><span>"</span><span>{</span><span>str</span><span>(</span><span>uuid</span><span>.</span><span>uuid4</span><span>())</span><span>}</span><span>-</span><span>{</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>layout</span><span>}</span><span>-collage.png</span><span>"</span><span>,</span><span>mime</span><span>=</span><span>"</span><span>image/png</span><span>"</span><span>,</span><span>use_container_width</span><span>=</span><span>True</span><span>,</span><span>)</span><span>st</span><span>.</span><span>header</span><span>(</span><span>"</span><span>Step 1. Upload images</span><span>"</span><span>)</span> <span>uploaded_files</span> <span>=</span> <span>st</span><span>.</span><span>file_uploader</span><span>(</span><span>"</span><span>Choose images</span><span>"</span><span>,</span> <span>type</span><span>=</span><span>[</span><span>"</span><span>jpg</span><span>"</span><span>,</span> <span>"</span><span>jpeg</span><span>"</span><span>,</span> <span>"</span><span>png</span><span>"</span><span>],</span> <span>accept_multiple_files</span><span>=</span><span>True</span><span>)</span> <span>if</span> <span>uploaded_files</span><span>:</span> <span>images</span> <span>=</span> <span>[</span><span>Image</span><span>.</span><span>open</span><span>(</span><span>file</span><span>)</span> <span>for</span> <span>file</span> <span>in</span> <span>uploaded_files</span><span>]</span> <span>st</span><span>.</span><span>write</span><span>(</span><span>f</span><span>"</span><span>Selected </span><span>{</span><span>len</span><span>(</span><span>images</span><span>)</span><span>}</span><span> images</span><span>"</span><span>)</span> <span>if</span> <span>len</span><span>(</span><span>images</span><span>)</span> <span><</span> <span>MIN_IMAGES</span><span>:</span> <span>st</span><span>.</span><span>warning</span><span>(</span><span>f</span><span>"</span><span>Please select at least </span><span>{</span><span>MIN_IMAGES</span><span>}</span><span> images for a collage</span><span>"</span><span>)</span> <span>elif</span> <span>len</span><span>(</span><span>images</span><span>)</span> <span>></span> <span>MAX_IMAGES</span><span>:</span> <span>st</span><span>.</span><span>warning</span><span>(</span><span>f</span><span>"</span><span>Please select at most </span><span>{</span><span>MAX_IMAGES</span><span>}</span><span> images for a collage</span><span>"</span><span>)</span> <span>else</span><span>:</span> <span>st</span><span>.</span><span>success</span><span>(</span><span>"</span><span>Images uploaded successfully!</span><span>"</span><span>)</span> <span>show_images_toggle</span> <span>=</span> <span>st</span><span>.</span><span>toggle</span><span>(</span><span>"</span><span>Show images</span><span>"</span><span>)</span> <span>if</span> <span>show_images_toggle</span><span>:</span> <span>st</span><span>.</span><span>image</span><span>(</span><span>images</span><span>,</span> <span>use_container_width</span><span>=</span><span>True</span><span>)</span> <span>set_active_images</span><span>(</span><span>uploaded_files</span><span>)</span> <span>if</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>images</span> <span>is</span> <span>not</span> <span>None</span><span>:</span> <span>st</span><span>.</span><span>header</span><span>(</span><span>"</span><span>Step 2. Customize size</span><span>"</span><span>)</span> <span>st</span><span>.</span><span>subheader</span><span>(</span><span>"</span><span>Select appropriate media size</span><span>"</span><span>)</span> <span>if</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>platform</span> <span>is</span> <span>None</span><span>:</span> <span># Render buttons dynamically </span> <span>for</span> <span>platform</span><span>,</span> <span>size</span> <span>in</span> <span>SOCIAL_MEDIA_IMAGE_SIZES</span><span>.</span><span>items</span><span>():</span> <span># Extract the base name (e.g., "Instagram" from "Instagram Feed Square") </span> <span>platform_base</span> <span>=</span> <span>platform</span><span>.</span><span>split</span><span>()[</span><span>0</span><span>]</span> <span>icon_url</span> <span>=</span> <span>SOCIAL_MEDIA_ICONS</span><span>.</span><span>get</span><span>(</span><span>platform_base</span><span>,</span> <span>None</span><span>)</span> <span># Display the button with an icon and label </span> <span>col1</span><span>,</span> <span>col2</span> <span>=</span> <span>st</span><span>.</span><span>columns</span><span>([</span><span>0.2</span><span>,</span> <span>0.8</span><span>])</span> <span># For icon and label alignment </span> <span>with</span> <span>col1</span><span>:</span> <span>if</span> <span>icon_url</span><span>:</span> <span>st</span><span>.</span><span>image</span><span>(</span><span>icon_url</span><span>,</span> <span>width</span><span>=</span><span>30</span><span>)</span> <span>else</span><span>:</span> <span>st</span><span>.</span><span>write</span><span>(</span><span>""</span><span>)</span> <span># Placeholder for missing icons </span> <span>with</span> <span>col2</span><span>:</span> <span>st</span><span>.</span><span>button</span><span>(</span><span>label</span><span>=</span><span>f</span><span>"</span><span>{</span><span>platform</span><span>}</span><span>: </span><span>{</span><span>size</span><span>[</span><span>0</span><span>]</span><span>}</span><span>x</span><span>{</span><span>size</span><span>[</span><span>1</span><span>]</span><span>}</span><span>"</span><span>,</span> <span>key</span><span>=</span><span>platform</span><span>,</span> <span>help</span><span>=</span><span>platform</span><span>,</span> <span>use_container_width</span><span>=</span><span>True</span><span>,</span> <span>args</span><span>=</span><span>(</span><span>platform</span><span>,),</span> <span>on_click</span><span>=</span><span>set_active_platform</span><span>)</span> <span>else</span><span>:</span> <span># Display active platform info </span> <span>platform_base</span> <span>=</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>platform</span><span>.</span><span>split</span><span>()[</span><span>0</span><span>]</span> <span>icon_url</span> <span>=</span> <span>SOCIAL_MEDIA_ICONS</span><span>.</span><span>get</span><span>(</span><span>platform_base</span><span>,</span> <span>None</span><span>)</span> <span># Display the button with an icon and label </span> <span>col1</span><span>,</span> <span>col2</span> <span>=</span> <span>st</span><span>.</span><span>columns</span><span>([</span><span>0.2</span><span>,</span> <span>0.8</span><span>])</span> <span># For icon and label alignment </span> <span>with</span> <span>col1</span><span>:</span> <span>if</span> <span>icon_url</span><span>:</span> <span>st</span><span>.</span><span>image</span><span>(</span><span>icon_url</span><span>,</span> <span>width</span><span>=</span><span>30</span><span>)</span> <span>else</span><span>:</span> <span>st</span><span>.</span><span>write</span><span>(</span><span>""</span><span>)</span> <span># Placeholder for missing icons </span> <span>with</span> <span>col2</span><span>:</span> <span>platform</span> <span>=</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>platform</span> <span>st</span><span>.</span><span>write</span><span>(</span> <span>f</span><span>"</span><span>Selected: </span><span>{</span><span>platform</span><span>}</span><span> </span><span>{</span><span>SOCIAL_MEDIA_IMAGE_SIZES</span><span>[</span><span>platform</span><span>][</span><span>0</span><span>]</span><span>}</span><span>x</span><span>{</span><span>SOCIAL_MEDIA_IMAGE_SIZES</span><span>[</span><span>platform</span><span>][</span><span>1</span><span>]</span><span>}</span><span>"</span><span>)</span> <span>st</span><span>.</span><span>button</span><span>(</span><span>'</span><span>Re-select size</span><span>'</span><span>,</span> <span>args</span><span>=</span><span>(</span><span>None</span><span>,),</span> <span>on_click</span><span>=</span><span>set_active_platform</span><span>,</span> <span>help</span><span>=</span><span>"</span><span>Re-select size</span><span>"</span><span>,</span> <span>use_container_width</span><span>=</span><span>True</span><span>,</span> <span>icon</span><span>=</span><span>"</span><span>:material/photo_size_select_actual:</span><span>"</span><span>)</span> <span>st</span><span>.</span><span>header</span><span>(</span><span>"</span><span>Step 3. Customize background</span><span>"</span><span>)</span> <span>st</span><span>.</span><span>subheader</span><span>(</span><span>"</span><span>Choose color or image</span><span>"</span><span>)</span> <span>if</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>background</span> <span>is</span> <span>None</span><span>:</span> <span>with</span> <span>st</span><span>.</span><span>container</span><span>(</span><span>border</span><span>=</span><span>True</span><span>):</span> <span>column1</span><span>,</span> <span>column2</span> <span>=</span> <span>st</span><span>.</span><span>columns</span><span>([</span><span>1</span><span>,</span> <span>1</span><span>])</span> <span>with</span> <span>column1</span><span>:</span> <span>color_image_radio</span> <span>=</span> <span>st</span><span>.</span><span>radio</span><span>(</span> <span>"</span><span>Background color or image</span><span>"</span><span>,</span> <span>[</span><span>"</span><span>color</span><span>"</span><span>,</span> <span>"</span><span>image</span><span>"</span><span>],</span> <span>captions</span><span>=</span><span>[</span> <span>"</span><span>Solid color</span><span>"</span><span>,</span> <span>"</span><span>Image</span><span>"</span><span>,</span> <span>],</span> <span>)</span> <span>with</span> <span>column2</span><span>:</span> <span>if</span> <span>color_image_radio</span> <span>==</span> <span>"</span><span>color</span><span>"</span><span>:</span> <span>bg_color</span> <span>=</span> <span>st</span><span>.</span><span>color_picker</span><span>(</span><span>"</span><span>Pick A Color</span><span>"</span><span>,</span> <span>None</span><span>)</span> <span>if</span> <span>bg_color</span><span>:</span> <span>st</span><span>.</span><span>write</span><span>(</span><span>f</span><span>"</span><span>Selected color: </span><span>{</span><span>bg_color</span><span>}</span><span>"</span><span>)</span> <span>st</span><span>.</span><span>button</span><span>(</span><span>"</span><span>Set background color</span><span>"</span><span>,</span> <span>on_click</span><span>=</span><span>set_active_background</span><span>,</span> <span>args</span><span>=</span><span>(</span><span>"</span><span>color</span><span>"</span><span>,</span> <span>bg_color</span><span>),</span> <span>use_container_width</span><span>=</span><span>True</span><span>,</span> <span>icon</span><span>=</span><span>"</span><span>:material/water_drop:</span><span>"</span><span>)</span> <span>else</span><span>:</span> <span>bg_file</span> <span>=</span> <span>st</span><span>.</span><span>file_uploader</span><span>(</span><span>"</span><span>Choose image for background</span><span>"</span><span>,</span> <span>type</span><span>=</span><span>[</span><span>"</span><span>jpg</span><span>"</span><span>,</span> <span>"</span><span>jpeg</span><span>"</span><span>,</span> <span>"</span><span>png</span><span>"</span><span>],</span> <span>accept_multiple_files</span><span>=</span><span>False</span><span>)</span> <span>if</span> <span>bg_file</span><span>:</span> <span>bg_img</span> <span>=</span> <span>Image</span><span>.</span><span>open</span><span>(</span><span>bg_file</span><span>)</span> <span>st</span><span>.</span><span>image</span><span>(</span><span>bg_img</span><span>,</span> <span>caption</span><span>=</span><span>"</span><span>Selected image</span><span>"</span><span>,</span> <span>use_container_width</span><span>=</span><span>True</span><span>)</span> <span>st</span><span>.</span><span>button</span><span>(</span><span>"</span><span>Set background image</span><span>"</span><span>,</span> <span>on_click</span><span>=</span><span>set_active_background</span><span>,</span> <span>args</span><span>=</span><span>(</span><span>"</span><span>image</span><span>"</span><span>,</span> <span>bg_file</span><span>),</span> <span>use_container_width</span><span>=</span><span>True</span><span>,</span> <span>icon</span><span>=</span><span>"</span><span>:material/image:</span><span>"</span><span>)</span> <span>else</span><span>:</span> <span>bg_category</span> <span>=</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>background</span><span>[</span><span>"</span><span>category</span><span>"</span><span>]</span> <span>bg_value</span> <span>=</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>background</span><span>[</span><span>"</span><span>value</span><span>"</span><span>]</span> <span>if</span> <span>bg_category</span> <span>==</span> <span>"</span><span>color</span><span>"</span><span>:</span> <span>st</span><span>.</span><span>write</span><span>(</span><span>f</span><span>"</span><span>Selected color: </span><span>{</span><span>bg_value</span><span>}</span><span>"</span><span>)</span> <span>else</span><span>:</span> <span>st</span><span>.</span><span>image</span><span>(</span><span>bg_value</span><span>,</span> <span>caption</span><span>=</span><span>"</span><span>Selected image</span><span>"</span><span>,</span> <span>use_container_width</span><span>=</span><span>True</span><span>)</span> <span>st</span><span>.</span><span>button</span><span>(</span><span>"</span><span>Re-select background</span><span>"</span><span>,</span> <span>use_container_width</span><span>=</span><span>True</span><span>,</span> <span>on_click</span><span>=</span><span>set_active_background</span><span>,</span> <span>args</span><span>=</span><span>(</span><span>None</span><span>,</span> <span>None</span><span>),</span> <span>icon</span><span>=</span><span>"</span><span>:material/format_paint:</span><span>"</span><span>,</span> <span>help</span><span>=</span><span>"</span><span>Re-select background</span><span>"</span><span>)</span> <span>st</span><span>.</span><span>header</span><span>(</span><span>"</span><span>Step 4. Customize arrangement of images</span><span>"</span><span>)</span> <span>st</span><span>.</span><span>subheader</span><span>(</span><span>"</span><span>Choose layout</span><span>"</span><span>)</span> <span>if</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>layout</span> <span>is</span> <span>None</span><span>:</span> <span>layout_options</span> <span>=</span> <span>[</span><span>"</span><span>grid</span><span>"</span><span>,</span> <span>"</span><span>strip</span><span>"</span><span>,</span> <span>"</span><span>stack</span><span>"</span><span>,</span> <span>"</span><span>golden_ratio</span><span>"</span><span>,</span> <span>"</span><span>auto</span><span>"</span><span>]</span> <span>layout_icons</span> <span>=</span> <span>[</span><span>"</span><span>grid_on</span><span>"</span><span>,</span> <span>"</span><span>table_rows</span><span>"</span><span>,</span> <span>"</span><span>view_column</span><span>"</span><span>,</span> <span>"</span><span>grid_goldenratio</span><span>"</span><span>,</span> <span>"</span><span>auto_awesome</span><span>"</span><span>]</span> <span>with</span> <span>st</span><span>.</span><span>container</span><span>(</span><span>border</span><span>=</span><span>True</span><span>):</span> <span>columns</span> <span>=</span> <span>st</span><span>.</span><span>columns</span><span>(</span><span>len</span><span>(</span><span>layout_options</span><span>))</span> <span># Create dynamic columns </span> <span>for</span> <span>col</span><span>,</span> <span>layout</span><span>,</span> <span>icon</span> <span>in</span> <span>zip</span><span>(</span><span>columns</span><span>,</span> <span>layout_options</span><span>,</span> <span>layout_icons</span><span>):</span> <span>with</span> <span>col</span><span>:</span> <span>st</span><span>.</span><span>button</span><span>(</span><span>layout</span><span>.</span><span>replace</span><span>(</span><span>"</span><span>_</span><span>"</span><span>,</span> <span>"</span><span> </span><span>"</span><span>),</span> <span>use_container_width</span><span>=</span><span>True</span><span>,</span> <span>on_click</span><span>=</span><span>set_active_layout</span><span>,</span> <span>args</span><span>=</span><span>(</span><span>layout</span><span>,),</span> <span>icon</span><span>=</span><span>f</span><span>"</span><span>:material/</span><span>{</span><span>icon</span><span>}</span><span>:</span><span>"</span><span>)</span> <span>else</span><span>:</span> <span>st</span><span>.</span><span>write</span><span>(</span><span>f</span><span>"</span><span>Selected layout: </span><span>{</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>layout</span><span>}</span><span>"</span><span>)</span> <span>st</span><span>.</span><span>button</span><span>(</span><span>"</span><span>Re-select layout</span><span>"</span><span>,</span> <span>use_container_width</span><span>=</span><span>True</span><span>,</span> <span>on_click</span><span>=</span><span>set_active_layout</span><span>,</span> <span>args</span><span>=</span><span>(</span><span>None</span><span>,),</span> <span>icon</span><span>=</span><span>"</span><span>:material/grid_off:</span><span>"</span><span>,</span> <span>help</span><span>=</span><span>"</span><span>Re-select layout</span><span>"</span><span>)</span> <span>st</span><span>.</span><span>header</span><span>(</span><span>"</span><span>Step 5. Final arrangement</span><span>"</span><span>)</span> <span>st</span><span>.</span><span>subheader</span><span>(</span><span>"</span><span>Set up padding, centering, and randomization</span><span>"</span><span>)</span> <span>with</span> <span>st</span><span>.</span><span>container</span><span>(</span><span>border</span><span>=</span><span>True</span><span>):</span> <span>column1</span><span>,</span> <span>column2</span> <span>=</span> <span>st</span><span>.</span><span>columns</span><span>([</span><span>1</span><span>,</span> <span>1</span><span>])</span> <span>with</span> <span>column1</span><span>:</span> <span>st</span><span>.</span><span>number_input</span><span>(</span><span>"</span><span>Padding</span><span>"</span><span>,</span> <span>min_value</span><span>=</span><span>MIN_PADDING</span><span>,</span> <span>max_value</span><span>=</span><span>MAX_PADDING</span><span>,</span> <span>key</span><span>=</span><span>'</span><span>padding</span><span>'</span><span>)</span> <span>with</span> <span>column2</span><span>:</span> <span>st</span><span>.</span><span>checkbox</span><span>(</span><span>"</span><span>Randomize image order</span><span>"</span><span>,</span> <span>key</span><span>=</span><span>'</span><span>randomization</span><span>'</span><span>)</span> <span>st</span><span>.</span><span>checkbox</span><span>(</span><span>"</span><span>Center images (if possible)</span><span>"</span><span>,</span> <span>key</span><span>=</span><span>"</span><span>centering</span><span>"</span><span>)</span> <span>st</span><span>.</span><span>button</span><span>(</span><span>"</span><span>Create collage</span><span>"</span><span>,</span> <span>use_container_width</span><span>=</span><span>True</span><span>,</span> <span>on_click</span><span>=</span><span>handle_create_collage_button_click</span><span>,</span> <span>icon</span><span>=</span><span>"</span><span>:material/auto_awesome_mosaic:</span><span>"</span><span>)</span> <span>if</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>collage</span> <span>is</span> <span>not</span> <span>None</span><span>:</span> <span>st</span><span>.</span><span>header</span><span>(</span><span>"</span><span>Step 6. Collage preview</span><span>"</span><span>)</span> <span>st</span><span>.</span><span>image</span><span>(</span><span>np</span><span>.</span><span>array</span><span>(</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>collage</span><span>),</span> <span>caption</span><span>=</span><span>"</span><span>Collage Preview</span><span>"</span><span>,</span> <span>use_container_width</span><span>=</span><span>True</span><span>)</span> <span># Convert the image to binary format </span> <span>image_bytes</span> <span>=</span> <span>io</span><span>.</span><span>BytesIO</span><span>()</span> <span>st</span><span>.</span><span>session_state</span><span>.</span><span>collage</span><span>.</span><span>save</span><span>(</span><span>image_bytes</span><span>,</span> <span>format</span><span>=</span><span>"</span><span>PNG</span><span>"</span><span>)</span> <span># Save as PNG or change to "JPEG" </span> <span>image_bytes</span><span>.</span><span>seek</span><span>(</span><span>0</span><span>)</span> <span># Move cursor to the beginning </span> <span># Create a download button </span> <span>st</span><span>.</span><span>download_button</span><span>(</span> <span>label</span><span>=</span><span>"</span><span>Download Image</span><span>"</span><span>,</span> <span>data</span><span>=</span><span>image_bytes</span><span>,</span> <span>file_name</span><span>=</span><span>f</span><span>"</span><span>{</span><span>str</span><span>(</span><span>uuid</span><span>.</span><span>uuid4</span><span>())</span><span>}</span><span>-</span><span>{</span><span>st</span><span>.</span><span>session_state</span><span>.</span><span>layout</span><span>}</span><span>-collage.png</span><span>"</span><span>,</span> <span>mime</span><span>=</span><span>"</span><span>image/png</span><span>"</span><span>,</span> <span>use_container_width</span><span>=</span><span>True</span><span>,</span> <span>)</span>st.header("Step 1. Upload images") uploaded_files = st.file_uploader("Choose images", type=["jpg", "jpeg", "png"], accept_multiple_files=True) if uploaded_files: images = [Image.open(file) for file in uploaded_files] st.write(f"Selected {len(images)} images") if len(images) < MIN_IMAGES: st.warning(f"Please select at least {MIN_IMAGES} images for a collage") elif len(images) > MAX_IMAGES: st.warning(f"Please select at most {MAX_IMAGES} images for a collage") else: st.success("Images uploaded successfully!") show_images_toggle = st.toggle("Show images") if show_images_toggle: st.image(images, use_container_width=True) set_active_images(uploaded_files) if st.session_state.images is not None: st.header("Step 2. Customize size") st.subheader("Select appropriate media size") if st.session_state.platform is None: # Render buttons dynamically for platform, size in SOCIAL_MEDIA_IMAGE_SIZES.items(): # Extract the base name (e.g., "Instagram" from "Instagram Feed Square") platform_base = platform.split()[0] icon_url = SOCIAL_MEDIA_ICONS.get(platform_base, None) # Display the button with an icon and label col1, col2 = st.columns([0.2, 0.8]) # For icon and label alignment with col1: if icon_url: st.image(icon_url, width=30) else: st.write("") # Placeholder for missing icons with col2: st.button(label=f"{platform}: {size[0]}x{size[1]}", key=platform, help=platform, use_container_width=True, args=(platform,), on_click=set_active_platform) else: # Display active platform info platform_base = st.session_state.platform.split()[0] icon_url = SOCIAL_MEDIA_ICONS.get(platform_base, None) # Display the button with an icon and label col1, col2 = st.columns([0.2, 0.8]) # For icon and label alignment with col1: if icon_url: st.image(icon_url, width=30) else: st.write("") # Placeholder for missing icons with col2: platform = st.session_state.platform st.write( f"Selected: {platform} {SOCIAL_MEDIA_IMAGE_SIZES[platform][0]}x{SOCIAL_MEDIA_IMAGE_SIZES[platform][1]}") st.button('Re-select size', args=(None,), on_click=set_active_platform, help="Re-select size", use_container_width=True, icon=":material/photo_size_select_actual:") st.header("Step 3. Customize background") st.subheader("Choose color or image") if st.session_state.background is None: with st.container(border=True): column1, column2 = st.columns([1, 1]) with column1: color_image_radio = st.radio( "Background color or image", ["color", "image"], captions=[ "Solid color", "Image", ], ) with column2: if color_image_radio == "color": bg_color = st.color_picker("Pick A Color", None) if bg_color: st.write(f"Selected color: {bg_color}") st.button("Set background color", on_click=set_active_background, args=("color", bg_color), use_container_width=True, icon=":material/water_drop:") else: bg_file = st.file_uploader("Choose image for background", type=["jpg", "jpeg", "png"], accept_multiple_files=False) if bg_file: bg_img = Image.open(bg_file) st.image(bg_img, caption="Selected image", use_container_width=True) st.button("Set background image", on_click=set_active_background, args=("image", bg_file), use_container_width=True, icon=":material/image:") else: bg_category = st.session_state.background["category"] bg_value = st.session_state.background["value"] if bg_category == "color": st.write(f"Selected color: {bg_value}") else: st.image(bg_value, caption="Selected image", use_container_width=True) st.button("Re-select background", use_container_width=True, on_click=set_active_background, args=(None, None), icon=":material/format_paint:", help="Re-select background") st.header("Step 4. Customize arrangement of images") st.subheader("Choose layout") if st.session_state.layout is None: layout_options = ["grid", "strip", "stack", "golden_ratio", "auto"] layout_icons = ["grid_on", "table_rows", "view_column", "grid_goldenratio", "auto_awesome"] with st.container(border=True): columns = st.columns(len(layout_options)) # Create dynamic columns for col, layout, icon in zip(columns, layout_options, layout_icons): with col: st.button(layout.replace("_", " "), use_container_width=True, on_click=set_active_layout, args=(layout,), icon=f":material/{icon}:") else: st.write(f"Selected layout: {st.session_state.layout}") st.button("Re-select layout", use_container_width=True, on_click=set_active_layout, args=(None,), icon=":material/grid_off:", help="Re-select layout") st.header("Step 5. Final arrangement") st.subheader("Set up padding, centering, and randomization") with st.container(border=True): column1, column2 = st.columns([1, 1]) with column1: st.number_input("Padding", min_value=MIN_PADDING, max_value=MAX_PADDING, key='padding') with column2: st.checkbox("Randomize image order", key='randomization') st.checkbox("Center images (if possible)", key="centering") st.button("Create collage", use_container_width=True, on_click=handle_create_collage_button_click, icon=":material/auto_awesome_mosaic:") if st.session_state.collage is not None: st.header("Step 6. Collage preview") st.image(np.array(st.session_state.collage), caption="Collage Preview", use_container_width=True) # Convert the image to binary format image_bytes = io.BytesIO() st.session_state.collage.save(image_bytes, format="PNG") # Save as PNG or change to "JPEG" image_bytes.seek(0) # Move cursor to the beginning # Create a download button st.download_button( label="Download Image", data=image_bytes, file_name=f"{str(uuid.uuid4())}-{st.session_state.layout}-collage.png", mime="image/png", use_container_width=True, )
Enter fullscreen mode Exit fullscreen mode
Sheesh, that was a long one. Anyway, there is still enough room for updates and improvement, adding more variants of the layout, and movable images on collage, etc.
Few of my thoughts:
- PIL is a very good library for working with images, however, it has limitations (no SVG support)
- Streamlit is fast and useful for small or not very complex (in UI/UX sense) projects, but the very moment you need something more complex, its constraint will start to drag you down – use JS instead.
The full (and optimized) code can be seen at my GitHub repo.
The deployed version can be accessed here.
Video of me using it
Thanks for your attention! Feel free to write your thoughts below.
暂无评论内容