Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have an IP cam which uploads JPEG pictures to an FTP folder on my Arch Linux box whenever it detects movement in the room it is looking in. It uploads a JPEG every second until all motion activity stops.

It names the JPEG files in the following way: enter image description here

Dissected, it means:

name-of-camera(inside)_1_YEAR-MONTH-DATE-TIME-SECONDS_imagenumber.jpg

I want a script that can make a 1 frame-per-second video from them (easy with ffmpeg I know), BUT, it must be clever enough to only make the video from the images that are within 2 seconds of each other, then delete those jpegs that it used. I say "2 seconds of each other" in-case of network latency where it misses one frame.

Any future images that are within 2 seconds of being taken, should become its own video. So it should basically be able to make videos from each 'event' of motion the camera saw.

I know programs like zoneminder and motion can do this, but I want to design a script instead. Any ideas much appreciated.

share|improve this question
    
what does the 'imagenumber' mean? Is each image in a batch numbered sequentially, or does every single image get a unique imagenumber? –  evilsoup Jun 8 '13 at 17:25
1  
What happens in corner cases such as daylight saving time? If the files have correct timestamps (stat -c %Y file) you should base your script on those rather than relying on the filename(s). –  frostschutz Jun 8 '13 at 20:08
    
Every image gets a unique 'imagenumber'. The camera doesn't know about any batch of images. It just adds the next number to the next image after reading the filesystem. The camera just throws the image down when it sees motion. The files have correct timestamps. I just don't know how to script it. I think inotifywait can do it, just don't know how I'll make it all work. –  nLinked Jun 9 '13 at 17:40
    
will you be open to a java console app? means it runs on the console like a script but needs java to be installed on your server where the jpegs are processed –  tgkprog Jun 10 '13 at 10:03
add comment

1 Answer

up vote 2 down vote accepted

You could generate time stamp from the date and check for span between each file. One issue, as already mentioned in comments, are daylight savings – assuming the date/times are locale specific.

Using stat instead of filenames as base could help on this. But, that gain depends on how the files are uploaded (if timestamps are preserved etc.)

As a starting point (this became much longer then intended) you could try something like this:

#!/bin/bash

declare -a fa_tmp=()    # Array holding tmp files with jpg collections.
declare dd=""           # Date extracted form file name.
declare -i ts=0         # Time stamp from date.
declare -i pre=0        # Previous time stamp.
declare -i lim=2        # Limit in seconds triggering new collection.
fmt_base='+%F-%H_%M_%S' # Format for date to generate video file name.

# Perhaps better using date from file-name:
# export TZ=UTC
# stat --printf=%Y $f

# Loop all jpg files
for f in *.jpg; do
    # Extract date, optionally use mktime() with gawk.
    # This assumes XX_XX_DATETIME_XXX... by split on underscore.
    dd=$(printf "$f" | tr '_' ' ' | awk '{
    printf("%d-%02d-%02d %02d:%02d:%02d",
        substr($3,  1, 4),
        substr($3,  5, 2),
        substr($3,  7, 2),
        substr($3,  9, 2),
        substr($3, 11, 2),
        substr($3, 13, 2))
    }')

    # Create time stamp from date.
    ts=$(date +%s -d "$dd")

    # If duration is greater then lim, create new tmp file.
    if ((ts - pre > lim)); then
        f_tmp="$(mktemp)"
        fa_tmp+=("$f_tmp")
        # First line in tmp file is first time stamp.
        printf "%s\n" "$ts" >> "$f_tmp"
    fi

    # Add filename to current tmp file.
    printf "%s\n" "$f" >> "$f_tmp"

    # Previous is current.
    pre="$ts"
done

declare -i i=1
# Loop tmp files.
for f_tmp in "${fa_tmp[@]}"; do
    printf "PROCESSING: %s\n---------------------------\n" "$f_tmp"
    base=""
    i=1

    # Rename files.
    while read -r img; do
        # First line is time stamp and is used as base for name.
        if [[ "$base" == "" ]]; then
            base=$(date "$fmt_base" -d "@$img")
            continue
        fi
        # New image name.
        iname=$(printf "%s-%04d.jpg" "$base" "$i")
        echo "mv '$img' => '$iname'"
        mv "$img" "$iname"
        ((++i))
    done <"$f_tmp"

    # Generate video.
    if ffmpeg -f image2 \
        -framerate 3 \
        -pattern_type sequence \
        -start_number 1 \
        -i "$base-%04d.jpg" \
        -vcodec mpeg4 \
        -r 6 \
        "$base.mp4"; then

        # Iff success, move jpg's to backup folder.
        mkdir "$base"
        mv $base-*.jpg "$base"
    else
        printf "FAILED:\n" >&2
        ls $base-*.jpg >&2
    fi

    # Remove tmp file.
    rm "$f_tmp"
done
share|improve this answer
    
WOW! This is amazing, I would never have been able to come up with a solution like this! I can't wait to try it out. Thank you so much! I'll post back with an update! –  nLinked Jun 11 '13 at 21:21
    
@nLinked: Hope it works as intended. The mp4 generation with ffmpeg was only a quick mash-up that probably can be better. You would perhaps also add some more error checking on mv etc. Copy a bunch of files to a test folder before going live ;). Also note the mv instead of rm on processed pictures. You might want to manually proof the resulting video before deleting them. –  Sukminder Jun 11 '13 at 21:46
    
Thanks again, Sukminder, and for taking the time to make it for me. It works brilliantly. I'm still playing around with settings and fine-tuning. It has been a real help! –  nLinked Jun 15 '13 at 18:56
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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