Quick Hacks: A Script to Extract a Single Image/Frame From Video

Long ago, I posted the simple way to get a frame of a video using ffmpeg. I’ve been using that technique for a long time.

It can be a bit unwieldy for iteratively finding a specific frame, as when using a terminal you have to move the cursor to the time specification. So I wrote a very small wrapper script to put the time part at or towards the end:


#!/bin/bash
# f.sh - single frame

USAGE="f.sh infile timecode [outfile]"

if [ "$#" == "0" ]; then
        echo "$USAGE"
        exit 1
fi

if [ -e "$1" ]; then
        video="$1"
else
        echo "file not found: $1"
        exit 1
fi

if [ ! -z "$2" ]; then
        time="$2"
else
        echo "Need timecode!"
        exit 1
fi

# if we have a filename write to that, else imagemagick display

if [ ! -z "$3" ]; then
        echo "ffmpeg -i \"$video\" -ss $time  -vframes 1 -f image2 \"$3\""
        ffmpeg -loglevel quiet -hide_banner -ss $time -i "$video" -vframes 1 -f image2 "$3"
else
        echo "ffmpeg -i \"$video\" -ss $3  -vframes 1 -f image2 - | display"
        ffmpeg -hide_banner -loglevel quiet -ss $time  -i "$video" -vframes 1 -f image2 - | display
fi

Most of that is usage explanation, but broadly it has two modes:

  • display an image (f.sh video time)
  • write an image (f.sh video time image)

It’s more convenient to use it, hit ? and amend the time than to move the cursor into the depth of an ffmpeg command.

Quick Hacks: A script to import photos to month-based directories (like Lightroom)

tl;dr: A bash script written in 15 minutes imports files as expected!

I was clearing photos off an SD card so that I have space to photograph a friend’s event this evening. Back on Windows, I would let Lightroom handle imports. Darktable is my photo management software of choice, but it leaves files where they are during import:

Importing a folder does not mean that darktable copies your images into another folder. It just means that the images are visible in lighttable and thus can be developed.

I had photos ranging from July last year until this month, so I needed to put them in directories from 2017/07 to 2018/02. But looking up metadata, copying and pasting seems like a tedious misuse of my time* so I wrote a little script to do so. It is not robust due to some assumptions (eg that the ‘year’ directory already exists) but it got the job done.

#!/bin/bash
# importcanon.sh - import from (mounted) sd card to directories based on date

CARD_BASEDIR="/tmp/canon"
PHOTO_PATH="DCIM/100CANON/"

TARGET_BASEDIR="/home/robert/mounts/storage/photos"

function copy_file_to_dir() {
    if [ ! -d "$2" ]; then
        echo "$2 does not exist!"
        mkdir "$2"
    fi
    cp "$1" "$2"
}

function determine_import_year_month() {
    #echo "exiftool -d "%Y-%m-%d" -S -s -DateTimeOriginal $1"
    yearmonth=$(exiftool -d "%Y/%m/" -S -s -DateTimeOriginal "$1")
    echo $yearmonth
}

printf "%s%sn" "$CARD_BASEDIR" "$PHOTO_PATH"

i=0
find "$CARD_BASEDIR/$PHOTO_PATH" -type f | while read file
do
    ym=$(determine_import_year_month "$file")
    copy_file_to_dir "$file" "$TARGET_BASEDIR/$ym"
    if let "$i %10 == 0"; then
        echo "Processed file $i ($file)"
    fi
    let i++

done

This uses exiftool to extract the year and month (in the form YYYY/MM), and that is used to give a target to cp.

The enclosing function has a check to see if the directory exists ([ ! -d "$2" ]) before copying. Using rsync would have achieved the effect of auto-creating a directory if needed, but that i) involves another tool ii) probably slows things down slightly due to invocation time iii) writing it this way let me remind myself of how to check for directory existence.

I still occasionally glance at how to iterate over files in bash, even though there are other ways of doing so!

There is also a little use of modulo in there to print some status output.

Not pretty, glamorous or robust but it got the job done!


*: Golden rule: leave computers to do things that they are good at

Why Won’t My GIMP Python Plug-in Show Up Under Filters?

tl;dr: Did you put it in ~/.gimp-2.8/plug-ins and set the executable bit ?

It’s been a while since I developed a script to automate tasks in GIMP. I figured I would do one for the repetitive tasks for creating a custom YouTube Thumbnail (more on that later perhaps). But my script wasn’t showing up in the Filters menu.

I had found the preference for setting the directory: Edit ? Preferences ? Folders ? Plug-Ins (not that GIMP treats python as plug-ins, not scripts); with the default user folder being ~/.gimp-2.8/plug-ins. But the plug-in dind’t show up.

Restart GIMP. Still nothing.

Ask on IRC. Double check the documentation (always a good idea). Aha!

Scheme and Python plug-ins are readable text files. C-language and Python plug-in files must have permissions set to allow execution.

chmod +x myscript.py later, and it registered!

Hope this saves someone the twenty or thirty minutes it took me to find this out!