# Photo collage | Shortcuts Library

> Beautiful, dynamic photo collages. Way beyond grid/ horizontal/ vertical!

## What this shortcut does

Watch the video tutorial!
Direct download link
Notes/Limitations
100% native shortcut painstakingly adapted from my Python version, requires no other apps/shortcuts to run
It's faster than it used to be, but you'll still have to wait a bit, up to 1-2 minutes or more, for larger collages (50+ images)
If you want more speed, check out the Python and Pythonista (iOS) versions on my GitHub
that run over 10X faster
On iOS, Photo collage can fail if too much memory is used
If this happens, use the included "Preprocess images" utility which creates downscaled copies of images in a new photo album, then rerun Photo collage with those images
It will also run faster if preprocessing is done first
Can run by sharing images from the Share Sheet (works better with Siri Shortcut than with Pythonista version)
On macOS, sharing images from Photos to a Shortcut doesn't work (thanks, Apple....)
Interface
Run the shortcut from within the Shortcuts app, from a widget, or using the Share Sheet to share photos directly from a photo app (Photos/Google Photos/etc)
Select options for the collage and then select “Continue”
The collage will be generated and previewed, and you then have options to save and/or make a new collage from the same batch of selected images
The shortcut supports using 1 or 2 levels of nesting. The reason to use more than one level of nesting is to get a more dynamic collage, and with greater size difference between the smallest and largest images in the collage.
Features
(feel free to zoom into these images)
Credits
Thanks to @gluebyte for
Join Shortcuts
, necessary for developing this shortcut by allowing for the independent development of subroutines
Thanks to @Avieshek for
 Update Engine
used for update checking
Thanks to
@atnbueno
for the inspiration to use Shortcuts's in-built ability to run JS for optimizing the algorithms used in this shortcut.
How it works
The partition problem
This shortcut works by solving the so-called "partition problem", where, given a set of items with different "weights" (values), and a set number of groups into which they need to be organized, you want to determine the grouping such that each group has the same total (sum of) weights. Let's see how this applies to photo collages.
Row-based collages
Initially, this shortcut started from a Python program I wrote years ago that only produced row-ordered collages like this one.
In this case, the "weights" are the aspect ratios of each image, and the number of groups is the number of rows. The number of rows is determined by the user-specified collage target aspect ratio and the average aspect ratio of the images. The solution to the problem will be a grouping such that each row has the same width.
The partition problem is a known problem in computer science, and I used this Stack Overflow Python implementation to modify (heavily) delimitry's collage maker in order to make the original version of PyPhotoCollage.
After the grouping of images into rows is determined, I still need to ensure everything lines up correctly. So, images in each row are resized to a common height, preserving aspect ratio, and combined into one image per row. Then, the rows are resized to a common width, preserving aspect ratio. Although there are slight differences in height between rows,
I think the results are pretty decent
.
Refactoring to a Shortcut
Turning the Python version into a
native
Shortcut was not easy. The main difficulty lies in the lack of the ability to modify a particular item in a list. In my case, I needed to modify items in two-dimensional lists to make this work, which I achieved by treating dictionaries like lists, where each item's key is its index, had it been a real list. Although it's not at all efficient, this allows one to create multi-dimensional arrays, and to modify individual elements within a multi-dimensional array. For example, the key "i,j" is used to represent the value at row
i
and column
j
.
Another limitation of Shortcuts is the lack of what programmers call "functions". This can be overcome by having a Shortcut use the "Run Shortcut" action to
run itself
, but with a specific input that changes the program flow of the Shortcut. I learned this trick from the amazing JellyCuts iOS app,
which I hope has a long and exciting future
! (Before seeing how JellyCuts did it, I would have been forced to resort to using multiple shortcuts to achieve the same functionality.) But it turns out this method of recursive shortcut calling has considerable overhead since the shortcut is re-parsed on each recursive call. So I've removed all functions in favor of putting the necessary actions in-place. Now the only "function" that remains is the one that allows the shortcut to call itself recursively when producing nested collages.
Where possible, "functions" are implemented in JavaScript, where they run somewhere around 5000X faster than the equivalent Shortcuts "code". For example,
here's the JS version of the linear partition algorithm in shortcut form
. This simplified the shortcut, reducing it by over 200 actions.
Then I showed my Mom
, and it wasn't good enough.
Row-ordered and nested collages
To appease my Mother (and, to be honest, myself), I had to spice things up. First, supporting row-ordered collages was fairly simple. I only needed to rotate the images up front, run the same algorithm, and rotate the resulting collage back the other way.
To break away from row- or column-ordered collage styles, I needed another approach. This was something I had thought about previously, and the solution was tedius. This time around, I found a much simpler method: to recursively call the Photo collage shortcut on smaller batches of images. So, when enough images are provided to the shortcut, it will take a subset of those images and make a collage out of those, and the resulting sub-collage will then be used as if it was a single image in the original collage.
The shortcut supports using 1 or 2 levels of nesting, so that if there are enough images in a given batch, those images too can be broken into yet more batches, resulting in a collage of collages of collages of images. The reason to use more than one level of nesting is to get a more dynamic collage, and with greater size difference between the smallest and largest images in the collage.
The shortcut actually supports an arbitrary number of nesting levels, but to keep the options menu simpler, I limited it to 2. The Python and Pythonista versions allow the user to specify more than 2 levels.
To keep it looking dynamic, the size of the batches and the aspect ratios and row/column orderings of nested collages are determined randomly (though bounded) (see the "Create batches" function). Also, because a dictionary in Shortcuts can only contain text or numbers, I had to base64-encode images that are passed into the shortcut recursively. Then, when the shortcut is called by itself in this way, the base64-encoded images must be decoded, and the user-options modified so that the recursive shortcut calls can proceed without any user interaction.
Additional work was required in order to ensure that things like image ordering are correctly handled despite the style of collage selected (row/column/nested). Then, Shortcuts made it fairly easy to do things like image spacing, and image/collage corner rounding.
(Interestingly, the solutions for row-ordered and nested collages only came to me while working on the shortcut, and so I actually originally implemented these in Shortcuts. Once I had everything working,
it took only one day to add these features to the Python version of the collage tool
. Adding corner-rounding using only the Python
pillow
library was a bit of a pain.)
Debugging and testing
Now, Shortcuts can fail on iOS for any number of reasons that
have nothing to do with whether they're written correctly
. This is because a shortcut will be killed by iOS if it exceeds memory limits, and there are also just tons of bugs in Shortcuts that you encounter along the way. Additionally, you get precisely zero useful information when a shortcut
called by another shortcut
fails for any reason. This makes debugging recursive calls to the shortcut nearly impossible.
Shortcuts lacks proper debugging tools—you cannot step through, one action at a time, or pause to see what values are saved to variables at a given time—so I had to make due. I did this by implementing extensive logging throughout the shortcut (disabled by default), and using an auxiliary shortcut for testing that runs the Photo collage shortcut 864 times in order to test nearly every possible combination of user options and numbers of images. The original testing run would have produced over 12-thousand collages, so I had to scale down the number of combinations of user options tested.
An example of logged information can be found below, along with an example of the output produced by the auxiliary testing shortcut
.
Also, to better understand the structure of nested collages, I added the ability to indicate nesting depth using colored borders on the images at each level of recursion.
Debug logging, colored borders, and the testing routine are not intended for use by regular users, but if you want to try them out, you can set them manually inside the shortcut using the config dictionary at the top.
The auxiliary shortcut used for testing can be found here (download link)
And so you see, this is not your typical photo collage shortcut!
Optimizations
The shortcut now runs 3.5X-43X faster than the original version described here. The biggest optimization was to implement the number crunching using JavaScript, which runs around 5000X faster than the equivalent Shortcuts "code", and which was done for the linear partition algorithm and a few others. The next optimization was to take advantage of parallel image processing by running image operations in batches instead of one image at a time (which I originally did to avoid crashes on iOS; this way you can use it to a lesser extent without risking a crash).
Example log output for a nested collage of 25 images
I've left out some of the repetative parts, indicated by an ellipsis "..."
2024-06-10 10:28:04 Starting photo collage on Mac with dictionary input...
2024-06-10 10:28:10 Depth 0, 50 image(s)
2024-06-10 10:28:10 Depth 0, user options: {"Target aspect ratio":1,"Round image corners":0,"Collage type":"Nested (2)","Collage max width\/height":2000,"Image spacing":0,"Round collage corners":0,"Image order":"Shuffle"}
2024-06-10 10:28:10 Depth 0, converting/resizing/masking/rotating 50 images to PNG or JPEG depending on if corner rounding or image spacing are used (PNG if the following is greater than 0, or JPEG otherwise: 0)
2024-06-10 10:28:10 Depth 0, converting batch 1 of 1
2024-06-10 10:28:16 Depth 0, shuffling order of 50 images
2024-06-10 10:28:18 Depth 0, can make at most 16.6666666666667 batches from 50 images (need more than 3 batches to recurse)
2024-06-10 10:28:19 Depth 0, creating batches from 50 between 3 and 14
2024-06-10 10:28:20 Depth 0, making 11 batches from 50. Batches: {"7":31,"3":[7,8,9,10,11,12,13,14,15],"8":32,"4":16,"11":[44,45,46,47,48,49,50],"9":[33,34,35,36,37,38,39],"5":[17,18,19,20,21,22,23,24,25],"1":1,"6":[26,27,28,29,30],"2":[2,3,4,5,6],"10":[40,41,42,43]}
2024-06-10 10:28:20 Depth 0, preparing batch 1, size 1
2024-06-10 10:28:21 Depth 0, preparing batch 2, size 5
2024-06-10 10:28:21 Depth 0, Processing image operation batch 1 for batch 2
2024-06-10 10:28:21 Depth 0, batch 2, encoding to base64
2024-06-10 10:28:22 Depth 1, 5 image(s)
2024-06-10 10:28:23 Depth 1, user options: {"Target aspect ratio":0.75,"Max recursion depth":2,"Round image corners":"0","Collage type":"Nested (2)","Collage max width\/height":1000,"Image spacing":0,"Round collage corners":"0","Image order":"Shuffle","Recursion depth":1}
2024-06-10 10:28:23 Depth 1, can make at most 1.66666666666667 batches from 5 images (need more than 3 batches to recurse)
2024-06-10 10:28:24 Depth 1, aspect ratio 0.75, 3 row(s) and 1.66666666666667-ish col(s), common height = 333, common width =. Resizing...
2024-06-10 10:28:24 Depth 1, resizing image batch 1 of 1
2024-06-10 10:28:25 Depth 1, running linear partition routine to create 3 row(s)
2024-06-10 10:28:26 Depth 1, combining row 1
2024-06-10 10:28:26 Depth 1, combining row 2
2024-06-10 10:28:26 Depth 1, combining final row (count = 2)
2024-06-10 10:28:27 Depth 1, resizing row 1 of 3 to common width of 694
2024-06-10 10:28:27 Depth 1, resizing row 2 of 3 to common width of 694
2024-06-10 10:28:27 Depth 1, resizing row 3 of 3 to common width of 694
2024-06-10 10:28:27 Depth 1, reversing order of rows...
2024-06-10 10:28:27 Depth 1, combining rows vertically
2024-06-10 10:28:28 Depth 1, finished in 5 seconds
2024-06-10 10:28:28 Depth 0, batch 2 complete
2024-06-10 10:28:28 Depth 0, preparing batch 3, size 9
2024-06-10 10:28:29 Depth 0, Processing image operation batch 1 for batch 3
2024-06-10 10:28:29 Depth 0, batch 3, encoding to base64
...
2024-06-10 10:30:55 Depth 0, batch 8 complete
2024-06-10 10:30:55 Depth 0, preparing batch 9, size 1
2024-06-10 10:30:56 Depth 0, aspect ratio 1, 4 row(s) and 2.25-ish col(s), common height = 500, common width =. Resizing...
2024-06-10 10:30:56 Depth 0, resizing image batch 1 of 1
2024-06-10 10:30:57 Depth 0, running linear partition routine to create 4 row(s)
2024-06-10 10:30:57 Depth 0, combining row 1
2024-06-10 10:30:58 Depth 0, combining row 2
2024-06-10 10:30:58 Depth 0, combining row 3
2024-06-10 10:30:58 Depth 0, combining final row (count = 2)
2024-06-10 10:30:59 Depth 0, resizing row 1 of 4 to common width of 1361
2024-06-10 10:30:59 Depth 0, resizing row 2 of 4 to common width of 1361
2024-06-10 10:30:59 Depth 0, resizing row 3 of 4 to common width of 1361
2024-06-10 10:30:59 Depth 0, resizing row 4 of 4 to common width of 1361
2024-06-10 10:30:59 Depth 0, reversing order of rows...
2024-06-10 10:31:00 Depth 0, combining rows vertically
2024-06-10 10:31:00 Depth 0, finished in 1 minute
Example output of Photo collage testing shortcut
Download the testing shortcut to try it yourself
Here's a sample of the contents of the resulting csv file.
Note that the aspect ratio error is meaningless for the rows with target aspect ratio of 0.001 and 1000.
#
Num images
Collage max width/height
Image spacing
Round image corners
Round collage corners
Target aspect ratio
Collage type
Image order
Wall time
Out width
Out height
Out aspect ratio
Collage size % rel error
Aspect ratio % rel error
1
25
2000
0
0
10
1
Nested (2)
Shuffle
42
2157
1606
1.34
7.9
34.3
2
25
2000
0
0
10
1
Nested (2)
Oldest first
40
2091
1861
1.12
4.6
12.4
3
25
2000
0
0
10
1
Nested (2)
Newest first
48
1370
1186
1.16
-31.5
15.5
4
25
2000
0
0
10
1
Simple by rows
Shuffle
55
2265
1542
1.47
13.3
46.9
5
25
2000
0
0
10
1
Simple by rows
Oldest first
61
2274
1549
1.47
13.7
46.8
6
25
2000
0
0
10
1
Simple by rows
Newest first
23
2204
1504
1.47
10.2
46.5
7
25
2000
0
0
10
1
Simple by columns
Shuffle
17
1801
1632
1.1
-10
10.4
8
25
2000
0
0
10
1
Simple by columns
Oldest first
50
1765
1583
1.11
-11.8
11.5
9
25
2000
0
0
10
1
Simple by columns
Newest first
64
1765
1583
1.11
-11.8
11.5
10
25
2000
0
0
10
0.001
Nested (2)
Shuffle
46
97
2211
0.04
10.6
4287.2
11
25
2000
0
0
10
0.001
Nested (2)
Oldest first
46
97
2211
0.04
10.6
4287.2
12
25
2000
0
0
10
0.001
Nested (2)
Newest first
48
97
2211
0.04
10.6
4287.2
13
25
2000
0
0
10
0.001
Simple by rows
Shuffle
48
97
2211
0.04
10.6
4287.2

## Compatibility

iPhone, iPad, Mac, Apple Watch

## Required apps



[View this page](https://shortcutlibrary.app/shortcuts/photo-collage)

[Download Shortcuts Library on the App Store](https://apps.apple.com/app/id6759494377)
