Making Maps, Version 2.0: Imagemagick Montage

Well I did it. I looked into using Imagemagick for the full process and came away with a process that makes a clearer, smaller image, so I’m very satisfied.

The goal is to take a set of images and stitch them together like a quilt. Here are the steps I took to do that.

Step 1: Make blank tiles.

The Legend of Zelda map is a grid, and then same grid every time but the way I’ve been making my maps is with blank white spaces for those areas that Link did not travel to that day. The whole script I wrote below is a very dance way of calling this one line imagemagick command repeatedly.

convert -size <span class="pl-vo">${WIDTH}</span>x<span class="pl-vo">${HEIGHT}</span> xc:<span class="pl-vo">${COLOR}</span> <span class="pl-vo">${full name}

There are many fancier and faster ways to create a blank image, or canvas, but this one is good enough for my purposes. I could make all the tiles so I can manually add the ones that I need to the folder with the pre trimmed images.

https://gist.github.com/carlynorama/189e0dc4ebc8624d82a5

Step 2: Prep the image tiles

This step was fairly easy and based on the batch image processing used in Making Maps, Verions 1.5 except instead of adding a border, I need to simply splice on a bit of black at the bottom so when tiles don’t have anyone below them they don’t look so weird.

#!/bin/bash
#Leaves the stats area intact and a 2px black
#border on the other edges. Saves file to a
#sub-direcotry
if [ ! -d ./PROCESSED ]; then mkdir ./PROCESSED;
fi
# processes all pngs
if ls | grep png; then for p in *.png;
do
echo "Processing $p"
convert \( $p -gravity south -crop 1204×862+0+0 +repage \) \
-background black -splice 0x2 \
./PROCESSED/${p/.png}_stats.png
done
fi

Step 3: Learn how to stitch together images

This was super easy. There is a command called montage. that all you need to do is feed a folder of images to and it will stitch them together row by row. That row by row action is why for right now my file names start with the Y coordinate. The basic template for what I’m doing is:

montage *.png -mode concatenate -tile WIDTHxHEIGHT OUTPUTNAME

Testing of montage command, Imagemagick

Testing of montage command, Imagemagick

It can be very funny when it goes wrong. Typical mistakes?

  • Switching width and height (shown here before I wrote creating just a subset map)
  • Having the wrong number of png files for the width vs height being asked for (too few or too many of the blank tiles)
  • Having non-relevant png files in the folder being processed, for example a previously generated map.

Full Map of where I explored (outside) on Day 60.

Full Map of where I explored (outside) on Day 60.

Doing these tests involved me dragging the appropriate white tiles from my “blank_tiles” folder into the folder with the prepped images in it and manually running the montage command. The result is a file that is much clearer, but smaller in file size. This could have been a good place to stop, but I still wanted EASIER. No manual work from me other than the file sizes.

Step 4: Auto create a subset map

This step was pulling it all together. Using inputs on the shell script to indicate what subset of the map I was going to be building, looking at file names to auto create the missing blank tiles with white images, adding a bit of trim and border at the end so the map is upload ready.

For now the trim and border step means that for now I have had to create 2 scripts on my machine. One for when I want trim and one for when there will be a tile in the top left. When there is a tile in the top left the corner is black, not white. The trim function uses the color of the top left corner to determine what to trim. Trimming black eats away at the top of the image. Using Imagemagick to look at the corner color and behave accordingly doesn’t seem like it would be too hard, but I was pretty satisfied with the afternoon’s work.

#!/bin/bash
#creates a montage of images from a selection of
#properly named files. The top left is 0, 0. The bottom
#right is presumed to be 7, 15. A subset of the map
#can be built with the correct inputs to the script.
#REQUIRES IMAGEMAGICK
#—————————— INPUTS —————————–#
#the first parameter passed to the shell script
#will be the bigger of the two y coordinates
Y1=$1
#the second parameter passed to the shell script
#will be the smaller of the two y coordinates
Y2=$2
#the third parameter passed to the shell script
#will be the bigger of the two X coordinates
X1=$3
#the fourth parameter passed to the shell script
#will be the smaller of the two x coordinates
X2=$4
#—————————— CONFIG —————————–#
#what size should the tiles be?
WIDTH=1204
HEIGHT=862
#what is the size of the splice to the bottom?
BB=2
#how thick should the final border be around the whole image?
bordersize=100
#what color should the final border be?
COLOR=white
#In the file name, how much zeropadding is needed?
zpad=2
#What is the name of the sub directory to save the files?
MYDIR="prepped_tiles"
#What is the file type of the images being saved?
EXT="png"
#What should the defaults be if sub coordinates are passed
#during the script call?
if [[ -z $Y1 ]]; then Y1=0 ; fi
if [[ -z $Y2 ]]; then Y2=7 ; fi
if [[ -z $X1 ]]; then X1=0 ; fi
if [[ -z $X2 ]]; then X2=15 ; fi
#—————————— SETUP ——————————#
#Correct width and height to be inclusive.
#(0 to 7 is 8 tiles but 7-0=7)
let map_width=$X2$X1+1
let map_height=$Y2$Y1+1
#If the destination folder doesn't exist, make it.
if [ ! -d ./${MYDIR} ]; then mkdir ./${MYDIR}; fi
#Set datetime for map file name used below. Setting
#it up here means the file name will reflect when the
#script was run not when the file is created.
vardate=$(date +%d\-%m\-%Y\_%H.%M.%S)
#———- PROCESS ALL THE PNGS IN THE CURRENT DIRECTORY ———-#
# processes all pngs, assumes they are all named correctly
if ls | grep png; then for p in *.png; do
echo "Processing $p"
convert \( $p -gravity south -crop ${WIDTH}x${HEIGHT}+0+0 +repage \) \
-background black -splice 0x${BB} \
./${MYDIR}/${p/.png}_stats.png
done
fi
#——— CREATE WHITE BLANK SPACES FOR MISSING TILES ONLY ——–#
#for each row
for i in $(seq $Y1 $Y2); do
#do each column
for j in $(seq $X1 $X2); do
#what is the file prefix we will be looking for?
printf -v fileprefix "%0*dy%0*dx" $zpad $i $zpad $j
if [ ! -e ./${MYDIR}/${fileprefix}* ] ; then
echo "Making white tile for $fileprefix"
fullname=./${MYDIR}/${fileprefix}_${COLOR}.${EXT};
convert -size ${WIDTH}x${HEIGHT} xc:${COLOR} ${fullname}
fi
done
done
#————————– BUILD THE MAP ————————–#
cd ./${MYDIR}
echo "Building Map"
montage *.png -mode concatenate -tile \
${map_width}x${map_height} ../map_${vardate}.png
#————————- TRIM AND BORDER ————————-#
cd ../
echo "Cleaning up map"
#will need to leave off trim when top corner has a tile.
#look for white? trim only if no variables set?
convert \( map*.png -fuzz 1% -trim \) \
-bordercolor ${COLOR} -border ${bordersize}x${bordersize} +repage \
map_${vardate}_ready.png

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s