
Consider the scenario:
- you have multiple images in different sizes
- you need to add text annotations to all the image such that the text is in a consistent x/y position
- you need to the annotations to be the same size when varoius images are displayed to fit
One solution is to scale all images to a common size and then create our annotation with fixed point size. Whilst this works this is a bit of a hack and does not allow you to link through to the same fuill size image.
The better solution is to use a font size (and location placement) that is proportional to the size of the image.Great, but trying to perform the calculations for each image in an image editor is cumbersome, time consuming and annoying. But there is another way with ImageMagick - this works on linux and will just need minor tailoring for your platform
Firstly, we will need to determine:
- the font we want to use, if you want to install a new font from Google fonts
Note that ImageMagick may not see a different name of your font so verify its name.$ cp font*.ttf ~/.local/share/fonts $ fc-cache -f -v $ fc-list ... ~/.local/share/fonts/Oxanium-ExtraBold.ttf: Oxanium,Oxanium ExtraBold:style=ExtraBold,Regular ~/.local/share/fonts/Oxanium-Light.ttf: Oxanium,Oxanium Light:style=Light,Regular ~/.local/share/fonts/Oxanium-Regular.ttf: Oxanium:style=Regular ~/.local/share/fonts/Oxanium-ExtraLight.ttf: Oxanium,Oxanium ExtraLight:style=ExtraLight,Regular ~/.local/share/fonts/Oxanium-SemiBold.ttf: Oxanium,Oxanium SemiBold:style=SemiBold,Regular ~/.local/share/fonts/Oxanium-Bold.ttf: Oxanium:style=Bold ~/.local/share/fonts/Oxanium-Medium.ttf: Oxanium,Oxanium Medium:style=Medium,Regular ...
$ magick -list font ... Font: Oxanium-Bold family: Oxanium glyphs: ~/.local/share/fonts/Oxanium-Bold.ttf Font: Oxanium-ExtraBold family: Oxanium glyphs: ~/.local/share/fonts/Oxanium-ExtraBold.ttf Font: Oxanium-ExtraLight family: Oxanium glyphs: ~/.local/share/fonts/Oxanium-ExtraLight.ttf Font: Oxanium-Light family: Oxanium glyphs: ~/.local/share/fonts/Oxanium-Light.ttf ... - determine the relative position and size of the text, lets say offset 5% from far left and 10% from top
$ for i in in.jpg; do \ magick "$i" \ -fill white -font Oxanium-Regular \ -pointsize "%[fx:w*0.035]" \ -gravity northwest \ -annotate "+%[fx:w*0.05]+%[fx:h*0.10]" \ "$(exiftool -s3 -p '$LensID @ f/$Aperture' "$i")" \ "out-$i" done


And display the same images with browser-scaled to 75% width, we can see that the text is the same relative size and at the position


Above, we are specifying (via
-pointsize "%[fx:w*0.035]") an arbituary text size that works well enough for my usecase. If we want a true proportional text occupancy width (say 50% of width), we have to do something like:$ for i in in.jpg; do \
IM_TEXT="$(exiftool -s3 -p '$LensID @ f/$Aperture' "$i")"
IM_LEN=${#IM_TEXT}
# IM_CROP="-gravity center -crop 2000x800+0+0 +repage"
# IM_STROKE="-stroke black -strokewidth 2"
magick "$i" \
${IM_CROP} \
-fill white ${IM_STROKE} -font Oxanium-Regular \
-pointsize "%[fx:(w*0.5)/(${IM_LEN}*0.6)]" \
-gravity northwest \
-annotate "+%[fx:w*0.05]+%[fx:h*0.10]" \
"${IM_TEXT}\n%wx%h" \
"out-$i"
done
Another useful and time saving trick is that sometimes you need to crop out centre sections of the image ahead of annotating. This can also be easily achieved by using the
-gravity center -crop ... - using ImageMagick's usual command, you may specify this as absolutes (-crop 2000x800+0+0) or as percentages (-crop 50%x+0+0) etc.This works great but it does not take into account the background that can bleed into the text. To solve we can generate a transparent bounding box.
#!/bin/bash
#
failed_files=()
for i in "$@"; do
EXIF_DATA="$(exiftool -s3 -p '$LensID @ f/$Aperture' "$i" 2>/dev/null)"
# EXIF_DATA="$(exiftool -s3 -p '$LensID @ f/$Aperture'$'\n''$CreateDate' "$i" 2>/dev/null)"
# If the EXIF data is empty, log the failure and skip to the next file
if [ -z "$EXIF_DATA" ]; then
failed_files+=("$i")
continue
fi
# params
IM_TEXT_LOCATION="${IM_TEXT_LOCATION:-southwest}"
# IM_CROP="-gravity center -crop 2000x800+0+0 +repage"
IM_BOUND_TEXT_COLOUR="${IM_BOUND_TEXT_COLOUR:-white}"
# IM_STROKE="-stroke black -strokewidth 2"
IM_BOUND_BOX_COLOUR="${IM_BOUND_BOX_COLOUR:-grey20}"
IM_BOUND_BOX_OPACITY="${IM_BOUND_BOX_OPACITY:-0.6}" # opacity of bounded box, 1 - solid, 0 - fully transparent
IM_BOUND_TEXT_Y_OFFSET="${IM_BOUND_TEXT_Y_OFFSET:-0.05}" # % offset from vertical edge
IM_BOUND_TEXT_X_OFFSET="${IM_BOUND_TEXT_X_OFFSET:-0.05}" # % offset from horizontal edge
IM_PADDING_RATIO="${IM_PADDING_RATIO:-0.03}" # % padding ratio of bounded box and text
IM_FONT="${IM_FONT:-Oxanium-Regular}"
DIM=$(magick "$i" ${IM_CROP} -format "%wx%h" info:)
IM_TEXT="${EXIF_DATA}"$'\n'"${DIM}"
IM_LEN=$(echo "$IM_TEXT" | awk '{ if (length($0) > max) max = length($0) } END { print max }')
P_SIZE=$(magick "$i" ${IM_CROP} -format "%[fx:(w*0.5)/($IM_LEN*0.6)]" info:)
X_OFF=$(magick "$i" ${IM_CROP} -format "%[fx:int(w*${IM_BOUND_TEXT_X_OFFSET})]" info:)
Y_OFF=$(magick "$i" ${IM_CROP} -format "%[fx:int(h*${IM_BOUND_TEXT_Y_OFFSET})]" info:)
magick "$i" \
${IM_CROP} \
\( -background none \
-fill ${IM_BOUND_TEXT_COLOUR} ${IM_STROKE} \
-font ${IM_FONT} \
-pointsize "$P_SIZE" \
-gravity west \
label:"$IM_TEXT" \
-trim +repage \
-bordercolor none \
-border "%[fx:int(w*${IM_PADDING_RATIO})]x%[fx:int(w*(${IM_PADDING_RATIO}*0.8))]" \
\( +clone \
-alpha transparent \
-fill "${IM_BOUND_BOX_COLOUR}" \
-draw "roundrectangle 0,0 %[fx:w-1],%[fx:h-1] 12,12" \
-channel A -evaluate multiply ${IM_BOUND_BOX_OPACITY} +channel \
\) \
+swap -compose Over -composite \
\) \
-gravity ${IM_TEXT_LOCATION} \
-geometry "+${X_OFF}+${Y_OFF}" \
-composite \
"out-$i"
done
if [ ${#failed_files[@]} -ne 0 ]; then
echo "The following files failed due to missing EXIF data:"
for failed in "${failed_files[@]}"; do
echo " $failed"
done
fi
The following are 3x images scaled to the same size as a diptych which shows the problem with the original solution on the left with the subsequent images having correct proportional text and bounding boxes: ie different input sizes but when scaled to the same size the generated annotations looking identical in size, position, and padding.
No comments:
Post a Comment