Foto’s bewerken voor publicatie

Please keep in mind that this post is about 9 years old.
Technology may have changed in the meantime.

Foto’s die ik publiceer (vooral op mijn andere blog en Flickr) zijn allemaal voorzien van een logo en de naam van mijn site. In mijn vorige leven was ik programmeur, dus je dacht toch niet dat ik dat allemaal handmatig deed…?

Vandaag heb ik wat aanpassingen gemaakt aan het script dat dat voor me doet, en toen dacht ik ‘Laat ik het op mijn site zetten…‘; zo heb ik weer eens een berichtje op deze site, en wie weet heeft een ander er ook nog eens iets aan.

Uiteraard is het weer niet voor Windows, maar voor Unix-achtige systemen.

Eerst even wat achtergrond info. Mijn directory-structuur voor foto’s (en andere media) ziet er als volgt uit:

~
 `- media
    |- audio
    |  |- original
    |  |- publish
    |  `- resize
    |- flickr
    |  |- publish
    |  `- resize
    |- gpx
    |  |- gpx
    |  |- jpg
    |  |- png
    |  `- work
    |- images
    |  |- original
    |  |- publish
    |  `- resize
    |- video
    `- watermerk

Nieuwe foto’s verplaats ik van mijn camera naar de directory ~/media/images/original/, in subdirectories die de datum als naam hebben (20151025). Als ik foto’s wil publiceren op mijn blog en/of Flickr, maak ik een subdirectory met zo’n zelfde naam (20151025) in ~/media/images/resize/ en/of ~/media/flickr/resize/, en kopieer ik de foto’s daarheen. Vervolgens start ik mijn script (dat je in zijn geheel onderaan dit bericht vind):

~/bin/peregrino_media.sh images

of

~/bin/peregrino_media.sh flickr

of

~/bin/peregrino_media.sh images flickr

Het script bewerkt dan alle foto’s voor publicatie, en verplaatst ze naar de respektievelijke subdirectories publish/. De bewerkingen die uitgevoerd worden, zijn:

  • schalen van de foto’s (max. 800×800 pixels voor mijn blog; max. 1000×1000 px voor Flickr)
  • de naam van mijn site op de foto plakken
  • het logo van mijn site op de foto plakken
  • de Exif-tag ImageDescription verwijderen
  • de Exif-tag Artist toevoegen
  • de Exif-tag Copyright toevoegen

Dit is mijn script (als de tekst te breed is voor je scherm, klik dan op het script, en gebruik vervolgens je pijltoetsen om naar rechts/links te scrollen):

#!/usr/bin/env bash

# ~/bin/peregrino_media.sh

############################################################

#                   peregrino_media.sh
#                        door Rob

# rob@ohreally.nl
############################################################
# http://ohreally.nl/

# Bulk edit elPeregrino.nl media for publication.

############################################################
#
# Copyright (c) 2013 Rob la Lau (rob@ohreally.nl)
#
# This work is licensed under the
#   Creative Commons Attribution 4.0 International License
# To view a copy of this license, visit
#   http://creativecommons.org/licenses/by/4.0/
# or send a letter to
#   Creative Commons
#   444 Castro Street, Suite 900
#   Mountain View, California, 94041
#   USA
#
############################################################

############################################################
# Configuration.
#
# All variables may be changed from the environment.

# Artist info.
: ${ARTIST_NAME:='Rob la Lau'}
: ${ARTIST_EMAIL:='rob@elperegrino.nl'}

# Watermarks.
#
# Copyright.
: ${WM_COPYRIGHT='~/media/watermerk/copyright.png'}
: ${WM_COPYRIGHT_DISSOLVE:=15}
: ${WM_COPYRIGHT_GRAVITY:=NorthWest}
: ${WM_COPYRIGHT_GEOMETRY=''}
#
# Logo.
: ${WM_LOGO='~/media/watermerk/klompen.png'}
: ${WM_LOGO_DISSOLVE:=50}
: ${WM_LOGO_GRAVITY:=SouthEast}
: ${WM_LOGO_GEOMETRY='+5+5'}

# Base directory.
: ${BASE='/data'}

# Applications.
: ${CONVERT=`which convert`}
: ${EXIFTOOL=`which exiftool`}
: ${GPX2PNG="`which perl` '~/bin/gpx2png.pl'"}

#
# End of configuration.
############################################################

# We need at least 1 parameter.
[ $# -eq 0 ] && {
    echo "Too few parameters."
    exit
}

# Correct Exif data:
# - remove description
# - add artist and copyright info
correct_exif() {
    local file=${1}
    local year=${2}

    # If we don't receive a correct year, get one from the file itself.
    [ -z "${year}" -o "${year}" = "-" ] && {
        # File creation.
        created=`stat -c '%w' "${file}"`
        [ "${created}" = "-" ] && {
            # Last modification.
            created=`stat -c '%y' "${file}"`
            [ "${created}" = "-" ] && {
                # Last status change.
                created=`stat -c '%z' "${file}"`
                [ "${created}" = "-" ] && {
                    # Last access.
                    created=`stat -c '%x' "${file}"`
                    [ "${created}" = "-" ] && {
                        # Fall back to now.
                        created=`date +'%Y'`
                    }
                }
            }
        }
        year=${created%%-*}
    }

    # Remove and add Exif tags.
    "${EXIFTOOL}" -m -q -overwrite_original -ImageDescription= -Artist="Camera owner, ${ARTIST_NAME}; Photographer, ${ARTIST_NAME}; Image creator, ${ARTIST_NAME}" -Copyright="Copyright, ${ARTIST_NAME} (${ARTIST_EMAIL}), ${year}. All rights reserved." "${file}"
}

# Process image:
# - resize
# - add copyright notice
# - add logo
# - move to publication directory
# - correct Exif data
process_image() {
    local file=${1}
    local size=${2}

    if [ -e "./publish/${file}" ]; then
        # Never process a file twice.
        echo "${file} : exists, skipping"
    else
        # Resize; add copyright and logo.
        "${CONVERT}" "./resize/${file}" -resize ${size} ${WM_COPYRIGHT_ARGS} ${WM_LOGO_ARGS} "./publish/${file}"

        # Correct Exif data.
        copyrightyear=`exiftool -q -CreateDate "./resize/${file}" | awk -F':' '{gsub(/ /,"",\$2)}{print \$2}'`
        correct_exif "./publish/${file}" ${copyrightyear}

        # Delete original.
        rm "./resize/${file}"

        echo "${file} : done"
    fi
}

# Process image directory.
process_image_directory() {
    local path=${1}
    local size=${2}

    # No use continuing without the right tools.
    [ -z "${CONVERT}" -o -z "${EXIFTOOL}" ] && {
        echo "${dir}: Toolset not complete."
        return
    }

    # Construct ImageMagick arguments.
    [ -n "${WM_COPYRIGHT}" ] && {
        WM_COPYRIGHT_ARGS=${WM_COPYRIGHT}
        [ -n "${WM_COPYRIGHT_DISSOLVE}" ] && WM_COPYRIGHT_ARGS+=" -compose dissolve -define compose:args=${WM_COPYRIGHT_DISSOLVE}"
        WM_COPYRIGHT_ARGS+=" -gravity ${WM_COPYRIGHT_GRAVITY}"
        [ -n "${WM_COPYRIGHT_GEOMETRY}" ] && WM_COPYRIGHT_ARGS+=" -geometry ${WM_COPYRIGHT_GEOMETRY}"
        WM_COPYRIGHT_ARGS+=' -composite'
    }
    [ -n "${WM_LOGO}" ] && {
        WM_LOGO_ARGS=${WM_LOGO}
        [ -n "${WM_LOGO_DISSOLVE}" ] && WM_LOGO_ARGS+=" -compose dissolve -define compose:args=${WM_LOGO_DISSOLVE}"
        WM_LOGO_ARGS+=" -gravity ${WM_LOGO_GRAVITY}"
        [ -n "${WM_LOGO_GEOMETRY}" ] && WM_LOGO_ARGS+=" -geometry ${WM_LOGO_GEOMETRY}"
        WM_LOGO_ARGS+=' -composite'
    }

    # Loop through files in 'resize' directory.
    cd "${path}"
    ls -1 ./resize | while read file; do
        if [ -d "./resize/${file}" ]; then
            # Process subdirectories.
            if [ `ls -1 "./resize/${file}" | wc -l` = 0 ]; then
                # Remove empty subdirectories.
                rmdir "./resize/${file}"
            else
                # Create subdirectory for publication.
                [ ! -d "./publish/${file}" ] && mkdir "./publish/${file}"

                # Loop through subdirectory.
                ls -1 "./resize/${file}" | while read f; do
                    # Process each image.
                    process_image "${file}/${f}" ${size}
                done
            fi
        else
            # If it's not a subdirectory, it must be a file; process it.
            process_image "${file}" ${size}
        fi
    done

    # Clean up.
    unset WM_COPYRIGHT_ARGS WM_LOGO_ARGS
}

# Loop through arguments and treat them as directory names.
while (( ${#} )); do
    dir=${1}
    shift
    path="${BASE}/${dir}"
    [ ! -d "${path}" ] && {
        echo "${dir} : Not a directory."
        continue
    }
    case "${dir}" in
        audio)
            ###FIXME
            # No functionality for audio, yet.
            echo "${dir} : Don't know how to process."
            ;;
        flickr)
            # Resize images for publication on Flickr.
            # (Same as blog, but larger.)
            size=1000x1000
            process_image_directory ${path} ${size}
            ;;
        gpx)
            # Create JPEG image from GPX track.

            # No use continuing without the right tools.
            [ -z "${CONVERT}" -o -z "${EXIFTOOL}" -o -z "${GPX2PNG}" ] && {
                echo "${dir}: Toolset not complete."
                continue
            }

            # Arguments for `convert'.
            # (Hardcoded, since this is only used for elPeregrino.nl.)
            [ -n "${WM_COPYRIGHT}" ] && WM_COPYRIGHT_ARGS="'${WM_COPYRIGHT}' -compose dissolve -define compose:args=50 -gravity NorthWest -composite"
            [ -n "${WM_LOGO}" ] && WM_LOGO_ARGS="'${WM_LOGO}' -compose dissolve -define compose:args=30 -gravity NorthEast -geometry +5+5 -composite"

            # Loop through GPX tracks.
            cd "${path}/work"
            ls -1 ../gpx | while read file; do
                png=${file/%.gpx/.png}
                jpg=${file/%.gpx/.jpg}

                if [ -e "../jpg/${jpg}" ]; then
                    # Never process a file twice.
                    echo "${file} : exists, skipping"
                else
                    # Create PNG image from track.
                    ${GPX2PNG} -o "../png/${png}" "../gpx/${file}"

                    # Resize, add copyright and logo, and convert to JPEG.
                    "${CONVERT}" "../png/${png}" -resize 1000x1000 ${WM_COPYRIGHT_ARGS} ${WM_LOGO_ARGS} "../jpg/${jpg}"

                    # Correct Exif data.
                    created=`stat -c '%w' "../gpx/${file}"`
                    [ "${created}" = "-" ] && {
                        created=`stat -c '%y' "../gpx/${file}"`
                    }
                    copyrightyear=${created%%-*}
                    correct_exif "../jpg/${jpg}" ${copyrightyear}

                    echo "${file} : done"
                fi
            done

            # Clean up.
            unset WM_COPYRIGHT_ARGS WM_LOGO_ARGS
            ;;
        images)
            # Resize images for publication on blog.
            # (Same as Flickr, but smaller.)
            size=800x800
            process_image_directory ${path} ${size}
            ;;
        video)
            ###FIXME
            # No functionality for video, yet.
            echo "${dir} : Don't know how to process."
            ;;
        *)
            # Warn and continue for other arguments.
            echo "${dir} : Don't know how to process."
            ;;
    esac
done

`convert‘ is onderdeel van ImageMagick.
`exiftool‘ is de command line applicatie van ExifTool.
In het begin van mijn pelgrimage hield ik GPS-tracks bij. In dit script gebruik ik gpx2png om van de GPX-bestanden PNG-afbeeldingen te generen, die ik vervolgens verder kan bewerken.

De transparantie van de watermerken regel ik in dit script (dissolve); de watermerken zijn dus ‘gewone’ afbeeldingen.

Mocht je je afvragen waarom de configuratievariabelen zo ‘raar’ gedefinieerd worden: dat is om ze vanuit mijn environment te kunnen wijzigen. Zo kan ik mijn script bijvoorbeeld ook gebruiken om een andere website-naam op de foto’s te zetten wanneer ik dat wil:

#!/usr/bin/env bash

# ~/bin/oh_media.sh

############################################################

#                       oh_media.sh
#                        door  Rob

# rob@ohreally.nl
############################################################
# http://ohreally.nl/

# Bulk edit ohreally.nl media for publication.

############################################################
#
# Copyright (c) 2015 Rob la Lau (rob@ohreally.nl)
#
# This work is licensed under the
#   Creative Commons Attribution 4.0 International License
# To view a copy of this license, visit
#   http://creativecommons.org/licenses/by/4.0/
# or send a letter to
#   Creative Commons
#   444 Castro Street, Suite 900
#   Mountain View, California, 94041
#   USA
#
############################################################

############################################################
# Configuration.
#
# All variables may be changed from the environment.

# Artist info.
export ARTIST_NAME=${ARTIST_NAME:-'Rob la Lau'}
export ARTIST_EMAIL=${ARTIST_EMAIL:-'rob@ohreally.nl'}

# Watermarks.
#
# Copyright.
export WM_COPYRIGHT=${WM_COPYRIGHT-'~/media/watermerk/ohreally.nl.png'}
export WM_COPYRIGHT_DISSOLVE=${WM_COPYRIGHT_DISSOLVE:-35}
export WM_COPYRIGHT_GRAVITY=${WM_COPYRIGHT_GRAVITY:-South}
export WM_COPYRIGHT_GEOMETRY=${WM_COPYRIGHT_GEOMETRY-'+0+20'}
#
# Logo.
export WM_LOGO=${WM_LOGO-''}

#
# End of configuration.
############################################################

# Call ./peregrino_media.sh to do the actual editing.
"$(dirname $(realpath $0))/peregrino_media.sh" $@

Dus als ik een foto wil publiceren op dit blog, doe ik

~/bin/oh_media.sh images

Of, als ik zelfs daaraan nog iets wil veranderen:

env ARTIST_EMAIL='rob@roblalau.net' ~/bin/oh_media.sh images

(En dat is de wijziging die ik vandaag aan mijn script gemaakt heb.)