3

How to create a collation of images in the format:

image_name_1 [IMAGE#1]
image_name_2 [IMAGE#2]
image_name_3 [IMAGE#3]
...

Using Imagemagick I can say something like:

montage -label '%f' -mode concatenate -tile 1x foo*.png out.png

But this adds the file name below the image + it does not account for name width.

Where the file name is printed (as in conventional cartesian y-axis) is of no concern as far as it is to the left of the correct image.

Which tool is of no concern as long as it is available for 32-bit Linux.

2

I'm no ImageMagick expert, so there is probably a better way to do this, but you can do this in 2 steps, first adding the text to the left of each image into an intermediate file, then doing the montage.

for file in foo*.png
do  convert "$file" -splice 100x0 -gravity west -annotate +10+0 '%f' /tmp/"$(basename "$file")"
done
montage -mode concatenate -tile 1x /tmp/*.png out.png

You need to adjust the splice value 100 to be wide enough for your widest filename label.


An interesting alternative that uses a single command is

convert \
 $(for file in foo*.png
   do echo '(' label:"$(basename "$file")" -gravity center "$file" +append ')'
   done) -gravity west -append out.png

where you use +append to join the label and image together horizontally, then -append to join the results vertically. It is not exactly what you need, but could be a starting point for further experimentation.

  • Thanks. This fit well. I have played around with ways to calculate width / height etc. but it is rather messy. I'll add a post anyway, (in case some one want to hack on it some more.) This is however the cleaner way. Only addition could perhaps be the "find width of text" routine. Have a nice day. – user129107 May 15 '16 at 2:37
  • 1
    @user129107 I've added a simpler alternative that is not quite what you want but might be another starting point. – meuh May 15 '16 at 9:29
  • Nice. I'll look closer at this. Your first solution fits the task, (at least for me), in any case. (I.e. I do not need a complete automation or similar.) – user129107 May 15 '16 at 19:01
1

If anyone want to hack on this some more, here are a messy though perhaps some hints or helps that catch interest.

The procedure requests max height and width from file names to generate labels that fits. One feature missing is the vertical centering of the text (which, TBH, looks better.)

To get width + height for a text (as integer) one way is to do:

convert -debug annotate  xc: -font Arial -pointsize 24 \
-annotate 0 'Test' null: 2>&1 | \
awk -vRS='[;.]' -F': ' '/width|height/{print $2}'

but doing this for each file-name in a loop adds a major increase in time / resource when having moderately many files +.

In the script below I therefore generate two temporary files with all file names. One to get max height, and another to get max width.

The procedure would break if file names has new-line.

#!/bin/bash

# Default Options
font=Arial
font_size=12
pad=20

usage()
{
    cat <<EOF_USAGE
Usage: $0 [OPTION] <images>

  -f | --font       <font>
  -z | --font_size  <N>
  -h | --help       [f|font]
EOF_USAGE
    exit 1
}

# Check for user-options
while [[ "$1" ]]
do
    case "$1" in
    --font|-f)font=$2; shift;;
    --help|-h)
        if [[ "$2" = "f" || "$2" = "font" ]]
        then
            convert -list font
            printf "\nCMD: convert -list font\n"
        fi
        usage
        exit 1
        ;;
    --font_size|-z)font_size=$2; shift;;
    --pad|-p)pad=$2; shift;;
    --)shift; break;;
    *)break;;
    esac
    shift
done

# Shallow Next option check (is it a file at all?)
! [[ -f "$1" ]] && printf "Not a file: %s\n" "$1" && exit 2

printf "Processing %d files ...\n" "$#"

txt_w=0
txt_h=0
tmp_dir=$(mktemp -d)

printf "tmp dir : %s\n" "$tmp_dir"

images=("$@")

printf "Calculating max width / height ..."

# Find max width from file names using font + font-size
txt_w=$(convert \
    -debug annotate  xc: \
    -font "$font" \
    -pointsize "$font_size" \
    -annotate 0 "\"$(printf "%s\n" "${images[@]}")\"" null: 2>&1 | \
    sed -n 's/.*Metrics:.* width: \([^.;]*\)[;.].*/\1/p' | sort -n | tail -n1)

# Find max height from file names using font + font-size
txt_h=$(convert \
    -debug annotate  xc: \
    -font "$font" \
    -pointsize "$font_size" \
    -annotate 0 "\"$(printf "%s" "${images[@]}")\"" null: 2>&1 | \
    sed -n 's/.*Metrics:.* height: \([^.;]*\)[;.].*/\1/p')

printf "\r\033[KWidth   : %d\n" "$txt_w"
printf "Height  : %d\n" "$txt_h"

# Add padding pixels
(( txt_w+=pad*2 ))

# Create the labeled images
for img in "${images[@]}"
do
    printf "\r\033[KCreating label for \`%s'" "$img"
    convert "$img" \
        -splice ${txt_w}x$txt_h \
        -gravity west \
        -font "$font" \
        -pointsize $font_size \
        -annotate +$pad+0 '%f' \
        "$tmp_dir"/"$(basename "$img")"
done

printf "\r\033[KMontage ...\n"

# Combine / Collate / Montage
montage -mode concatenate -tile 1x $(printf "$tmp_dir/%s " "${images[@]}") out.png

printf "Done!\n"

# Clean up
rm -r "$tmp_dir"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.