New command 'arrange', a better version of 'montage'

Hello there,

These last days, I’ve been working on the implementation of a new command (named arrange) in the G’MIC stdlib, which is intended to be a replacement of the (quite old) command montage.

Context

At the time (2014), the command montage was a request from @patdavid to be able to easily arrange multiple images into a single one, while choosing a possible detailed layout for the arrangement. That was actually a pretty cool idea. This has led in particular to the G’MIC-Qt filter Arrays & Tiles / Montage, which is known to be still used by some people (@Miguel_Pineau did a video on this filter not so long ago).

Problem is: Command montage was actually limited to a few number of images only, because it was implemented in a recursive way, which means stack overlow was occuring when the number of images was too high (technically it’s more correct to say that it was occuring when the transformation tree become too deep but let’s move on! :stuck_out_tongue:).

So I decided to re-implement the idea behind montage from scratch and today I got a first draft that seems to work as expected. So I want to share it here, eventually to get some feedback on it. If you have G’MIC 4.0+, then $ gmic update will let you download the latest stdlib with the command arrange included.

How it works?

Here is the official documentation for this command:

$ gmic h arrange

  arrange:
      "layout",_scaling_factor,_output_layers={ 0:Single | 1:Multiple },"_processing_command"

    Arrange selected images according to a specified layout, described as a string.
     
    'layout' describes how images are combined together, using the following symbols (case-insensitive):
     * A number: Refers to one of the selected images, by its index.
     * 'H' : Puts what follows side by side, horizontally (left / right).
     * 'V' : Puts what follows one above the other, vertically (top / bottom).
     * 'B' : Same as 'H' or 'V', but automatically picks whichever gives the most balanced (square-looking) result.
     * 'X' : Mirrors what follows horizontally (left-right flip).
     * 'Y' : Mirrors what follows vertically (top-bottom flip).
     * 'R' : Rotates what follows by 90 degrees clockwise.
     * 'L' : Rotates what follows by 90 degrees counter-clockwise.
    Any other character (e.g. ';' or ':') can be used as a separator between two numbers to avoid ambiguity (e.g. to distinguish images '0' and '1' from a single image 
    '01').
     
    Images get combined together two by two as the layout is read. For instance:
     * '"H0:1"' puts images 0 and 1 side by side.
     * '"V0:1"' stacks images 0 and 1 on top of each other.
     * '"VH0:1H2:3"' first forms two horizontal pairs (0,1) and (2,3), then stacks
      those two pairs vertically, resulting in a 2x2 grid.
     * '"H0R1"' puts image 0 next to image 1 rotated 90 degrees clockwise.
     
    Four predefined shortcuts are also available for 'layout', to quickly arrange all selected images at once without having to spell out a full layout string:
     * '"H"' : Concatenates all selected images horizontally, in order.
     * '"V"' : Concatenates all selected images vertically, in order.
     * '"AH"' : Automatically arranges all images into a grid, with more columns than rows.
     * '"AV"' : Does the same as 'AH', but more rows than columns.
     
    'scaling_factor' controls how images of different sizes get resized when combined together:
    '0' shrinks to the smallest size involved, '1' grows to the largest, and intermediate values interpolate between both. Values outside range '[0,1]' are also valid.
     
    'output_layers' sets whether the result is a single, flattened image ('Single'), or a set of individual images, each one already resized and positioned exactly as it 
    would be in the final composition ('Multiple').
     
    'processing_command' is an optional G'MIC command (or pipeline of commands) applied to each image individually, just after it has been resized and right before it 
    gets placed into the composition (e.g. to add a colored border, apply a filter, etc.).
     

    Default values: 'scaling_factor=0.5', 'output_layers=0' and 'processing_command=""'.

    Example:
      [#1] sample colorful,eagle,cat,dog +arrange "VH0:1H2:3"
      [#2] sample colorful,eagle,cat,dog,flower,rose arrange AH
      [#3] sample colorful,eagle,duck +arrange VH0R1:2,,,"sepia shrink xy,20 frame xy,8,255 frame xy,12,0"

So, just like montage, the idea is to let you specify the montage layout as a simple string that combines image indices and operators. Some examples are worth thousand words, so:

  • $ gmic sample rose,duck,flower arrange H0:2 assembles images 0 and 2 horizontally (V could be also used for vertical stacking)

As you noticed, the duck image has not been used in the montage: you can ignore some of the input images if you want, they just are removed as the used ones once the montage is done. The super useful thing: images are automatically resized to be arranged the best they can (in the example above, images are automatically resized so their heights fit.

  • gmic sample rose,duck,flower arrange H0:V1:2 combines horizontal and vertical arrangement. Here the layout code should be read as "H(0,V(1,2))" (you can use this one as well and get the same result, but be sure you quote it for your shell!).

  • H and V arranges two images together, but there are a few operators that act only on a single image : R and L (rotation of angle +/- 90°), as well as X and Y (image mirroring along axes X and Y). So,
$ gmic sample duck,monkey arrange HVH0:R0:HXY0:L0:1

  • There are also a few predefined “meta”-layout codes, for stacking images horizontally (H), vertically (V) or as (possibly non-regular) grids (AH and AV).
$ gmic sample rose,flower,tulips,cat,dog,duck,portrait1,portrait5,portrait8 arrange AH

Differences with command montage:

This new command arrange has a clever implementation that uses more “modern” G’MIC features, particularly the ones from the math evaluator. So it explicitely constructs a tree where each node represents a geometric operator or an image, without requiring any recursive call. A direct consequence is: it works with thousands of images if you want, and it is still reasonnably fast :slight_smile:

$ gmic repeat 1024 sample ?,"{v(64,256)}" done to_rgb tic arrange , toc
(...)
[gmic]./ Initialize timer.
[gmic]./ Arrange images [0,1,2,(...),1021,1022,1023] accordingly to layout 'AH', with scaling factor 0.5.
[gmic]./ Elapsed time: 0.608 s.

Apart from that, the syntax of the command and the layout string is pretty close to what montage already proposed, so we don’t lose features (except that we lost the possibility of having the centering mode for the arrangement, but I believe this was not that much used).

Still WIP!

I’ve tested the command a bit and already got a few problems : Sometimes, when the specified layout code is invalid, the part that constructs the transformation tree just hangs forever. That’s something I must fix in the future.

As I said, I’m also interested by your feedback and eventually feature requests related to this command. Thanks by advance for your help !

4 Likes

A funny one-liner that uses arrange :joy::

$ gmic repeat 10 { sp portrait\$\> } r 50%,55%,1,3,0 foreach { arrange VH0:X0HY0:XY0 } arrange AH

Dealing with frames

When using arrange, you can specify an additional "processing_command" parameter that lets you modify your images, after they’ve been resized in their final resolution.
This is very convenient if you want to add frames around your image, but with a frame that has the same size for all images.

Indeed, if you add a frame before invoking arrange, you may get a different scaling factor for each image, resulting in frames not having the same size, as illustred by this example:

$ gmic sample flower,rose,duck frame xy,16,255 frame xy,16,0 arrange HV0:1:2 

(this is ugly right ? :confused: ).

Now, if you specify a custom command as the argument of arrange instead:

$ gmic sample flower,rose,duck arrange HV0:1:2,,,\"resize '{[w,h]-48},1,100%,2' frame xy,16,255 frame xy,8,0\"

(I had to backslash some stuffs to make it work in my bash shell, but of course, all these decorators are not required when used from a G’MIC script).

And now we get: :heart_eyes:

1 Like

That was too simple to implement, so I’ve finally added it (second argument is not align_or_scale_factor and when negative, the montage switch to ‘centering’ mode rather than ‘scaling’).

Curious, since this is better version of montage, couldn’t this entirely be just montage? arrange feels like a name for images ordering command.

@Reptorian Sounds reasonable.

A related challenge is how people whose mother tongue is not English (or French) would perceive or understand the new name. If it is semantically closer to a different meaning, then it could confuse the user.


Overall, a successor to montage is warranted and welcome. It was one of the commands that I originally had the most questions about and prompted me to learn how to write my own. So that is evidence enough that it could be better from way back when.

Just re-using the name “montage” would be good if the “arrange” function does the same thing.
But while it’s a work in progress, with possibly different results, a new name is better to avoid surprising users (“my scripts don’t work anymore!”).

Also, if that command “arrange” is also in “Arrays & Tiles”, the name isn’t that bad: it arranges tiles in an array.

1 Like

I thought a new command name, arrange would be better for the following reasons:

  1. G’MIC commands generally use a verb as the command name to indicate an action.
  2. Even though arrange now implements everything that montage used to do, the meanings of their respective arguments are not exactly the same.
  3. Renaming arrange to montage could indeed break existing scripts that already use montage with its current form. Given that I just released version 4.0 of G’MIC, which claim to focus on stability and robustness, that probably wouldn’t be a good idea.
  4. My idea is therefore to set a new name for this new command, make it used in the G’MIC stdlib, and then put the implementation of montage in gmic-community (so that it won’t appear anymore in the reference documentation, but will still work for older scripts).

And until this new command is fully completed and documented, we can certainly consider other possible names. If you have any interesting suggestions, I’m all ears!

New funny one-liner based on arrange:

$ gmic 256,256,1,1,'255*(x+y<w)' repeat 6 { arg0 '{$>%2}',HV,VH arrange '${}'0:0:0,0 }

1 Like

Update:

Fixed a few bugs, so now the layout code is detected as incorrect rather than entering an infinite loop.
I’ve also added a few additional codes that can be used for the "layout" argument:

#@cli : - '?' : A random image index.
#@cli : - 'X' : Mirrors what follows horizontally (left-right flip).
#@cli : - 'Y' : Mirrors what follows vertically (top-bottom flip).
#@cli : - 'R' : Rotates what follows by 90 degrees clockwise.
#@cli : - 'L' : Rotates what follows by 90 degrees counter-clockwise.
#@cli : - 'H' : Puts what follows side by side, horizontally (left / right).
#@cli : - 'V' : Puts what follows one above the other, vertically (top / bottom).
#@cli : - 'B' : Same as 'H' or 'V', but automatically picks whichever gives the most balanced (square-looking) result.
#@cli : - 'Q' : Rotates what follows by a random multiple of 90 degrees.
#@cli : - 'F' : Flips what follows randomly, along the x-axis or the y-axis.
#@cli : - 'M' : Merges what follows randomly, either horizontally or vertically.

Also fixed multi-layer output.

At this point, I think I’m done with this command. I’m pretty happy with it I must say.
It’s really versatile and I’ll probably use it a lot in the future for quick generation of figures.
I’ve updated the stdlib on the G’MIC website, so do not hesitate to $ gmic update and give some feedback!

I’ve recoded the G’MIC filter Arrays & Tiles / Montage to make it use the newest arrange command, and it seems to work quite well.
If you have the occasion to test it and tell me what you think about it, that would be great!

I see a possible photo-mosaic option in the near future. Even so, I know it’s a matter of resources and time. :slight_smile:

This Kubuntu 24.04 / Gimp 3.2.4 / gmic 4.0.4 pre

The montage works as expected. I did try out a very verbose “5 x4 custom” and that worked fine
V(HH(V(H(0,1),H(2,3),V(H(4,5),H(6,7)) ,V(8,9),HH(V(H(10,11),H(12,13),V(H(14,15),H(16,17)) ,V(18,19))

Interesting using custom code and the commands with no parameters.

1 Like

Another funny oneliner using arrange:

$ gmic '(255)' repeat 6 { +f. 0 arrange VVHH0:0:0HH0:1:0HH0:0:0 }

1 Like

Another one:

$ gmic 1024,1024,1,1,'y-x>=0' repeat 8 { +f. 0 arrange VH0:1H0:0 rs ,50%,1 } f. '255*i(x+y/2-w/2,y)'