Photo collage
Tim Wilson, January 2024
Creates a proper photo collage from 2 or more images. Adapted from my Python (and Pythonista) script at https://github.com/twilsonco/PyPhotoCollage
<===== BEGIN CONFIGURATION =====>
The following options control how nested collages work.
Set the maximum number of nested collages. This option overrides those that follow.
Define min and max number of images used for nested collages. Only takes effect if the number of total images is ≥ 3 times the min size. If too low a min is used, there will be no nesting. If too low a max is used, it will limit the nesting depth (i.e. how many nested collages can be nested inside other nested collages).
*The max value is multiplied by the max recursion level to allow for deeper nesting.
Define min and max aspect ratio bounds for sub-collages, which are chosen at random.
<===== END CONFIGURATION =====>
Set debug printing to True (1) to log detailed shortcut activity to iCloud/Shortcuts/PhotoCollage/log.txt
Debug option to put colored outlines on each image to indicate which level of recursion was used to process it
Functions used when calling shortcut recursively, taking a dictionary as input
Get user options
Get options from the user. First define default options
Prepare menu prompt. Include in prompt for iOS users to use the “prepare images” tool if they have problems.
Splitting by em-space “ “
Splitting by em-space “ “
Initialize 2d array
Initialize a 2d array to the specified value.
Arguments (name, type, required/optional, notes):
fname: string, required, must be “Initialize 2d array”
nrows: integer, required, must be > 0
ncols: integer, required, must be > 0
initval: optional, defaults to 0
Set 2d array element
Set the value of the element at (i, j) in the provided 2d array to the specified value.
Arguments (name, type, required/optional, notes):
fname: string, required, must be “Set 2d array element”
array: 2d array (dictionary) made with “Initialize 2d array”, required
i: integer, required, must be 0 > irow ≥ nrows
j: integer, required, must be 0 > icol ≥ ncols
val: any, required
Get index of list item
Return the 1-based index of the element in a list matching the specified value, or -1 if not found.
Arguments (name: type, required/optional, notes):
fname: string, required, must be “Get index of list item”
list: list, required
val: any, required
Get shuffled order
Return a list of integers in shuffled order
Arguments (name, type, required/optional, notes):
fname: string, required, must be “Get shuffled order”
n: integer number defining the list size, required
Create batches
Take list of items and create batches with specified min/max sizes. Leftover items that don’t fit into a batch are added as individual batches. Returns a List of lists (batches).
Arguments (name, type, required/optional, notes):
fname: string, required, must be “Create batches”
n: integer number of items to be put into batches, required
sizemin: minimum batch size, required
sizemax: maximum batch size, required
Batches are created, but it’s a problem that all the single-item batches are at the end of the list of batches. So shuffle the order of batches and then go in and restore the order of the batch items, such the first batch has items 1, 2, 3, etc. and the second has 4, 5, 6, etc.
Linear partition problem
Implements the python function in the below comment.
Arguments (name: type, required/optional, notes):
fname: string, required, must be “Linear partition”
seq: list of image aspect ratios, required
nrows: integer number of rows in the collage, required
n = len(seq)
table = [[0] * k for x in range(n)]
solution = [[0] * (k-1) for x in range(n-1)]
for i in range(n):
table[i][0] = seq[i] + (table[i-1][0] if i else 0)
for j in range(k):
table[0][j] = seq[0]
for i in range(1, n):
for j in range(1, k):
table[i][j], solution[i-1][j-1] = min(
((max(table[x][j-1], table[i][0]-table[x][0]), x) for x in range(i)),
key=itemgetter(0))
((max(table[x][j-1], table[i][0]-table[x][0]), x) for x in range(i))
Photo collage recursive call for nested collage
Create a photo collage from the images in the provided dictionary. Return the collage as an image.
Arguments (name, type, required/optional, notes):
fname: string, required, must be “Photo collage”
options: options dict as returned from the “Get options” function, required
images: list of base64 encoded images, required
Fresh run of shortcut, so prepare images and get user options
Get input images
Cap number of images that can be used
Get user options
Preprocess images to smaller size/format and quit shortcut so that it can be run fresh with a lower memory footprint.
Set DoRotate based on collage type and recursion depth
Update max batch size based on number of input images
Image conversion/resizing/masking and ordering (below) only needs to be applied for initial call, not on recursive calls. However, rotation is also done for recursive calls.
Now resize if longest edge is greater than collage size (bug is preventing evaluating the expression “max(Width,Height)”)
Now mask and rotate. The user-specified corner rounding applies based on the percentage of longest image edge
Apply user-selected ordering, one of “Input order”, “Shuffle”, “Oldest first”, “Newest first”
For nested collages, perform batching and recursive call. Only do this if not beyond the max recursion limit and there are enough images in the current call to allow for it. Doing it here so that resulting sub-collages are treated for resizing as normal.
Conditionally create batches and prepare options dictionary for recursive call. Corner rounding will be set to zero, image spaceing is doubled (since nested collages will be smaller), aspect ratio will be randomly chosen between the specified min and max values (see top of shortcut), collage width and height are reduced
Also don’t recurse if the collage aspect ratio is too wide or too tall such that a vertical or horizontal strip should be used instead
List of color patches in base64 in the following order: 1 cyan, 2 orange, 3 red, 4 green, 5 purple, 6 magenta, 7 yellow
Overlay colored border on images of next betch
Color image based on recursion depth
Compute number of rows in collage
Resize images to common width or height depending on number of rows, and apply per-image corner rounding if used
Multiple images per row, so resize to common height
Single column collage, so resize to common width
Reverse order of rows to bring them back into order
For recursive calls and calls using an input dictionary, return output image here
“Show result” has a pretty low pixel height limit for displaying images on iOS, but can fall back on quick look
Check for updates, only once per run (recursive calls have exited already)
【Auto-Update Routine】
Checks For Latest Shortcut Version
↳ https://routinehub.co/shortcut/16590/
【Auto-Update Routine Ends Here】
Support → https://www.reddit.com/r/shortcuts/comments/18j4f9p/
【⚙️】
Convert Semantic Number to Decimals
↳ https://routinehub.co/shortcut/16589/