Skip to content

Commit

Permalink
fix: key frame extraction (DEV-1513) (#2300)
Browse files Browse the repository at this point in the history
Fixes bugs which prevent the key frame extraction and matrix file creation of the two files:
https://github.com/dasch-swiss/4124-xmlperformance-testdata/blob/main/bitstreams/0.1.mp4
https://github.com/dasch-swiss/4124-xmlperformance-testdata/blob/main/bitstreams/100.mp4

Both files suffer from two different bugs:

Fixes processing of very short moving images (0.1.mp4)
The file is too short for any frame to be extracted with the previous implementation which assumed at least five keyframes to be extracted. Change the script to start extracting keyframes at time unit 0, i.e. the beginning of the file and use the first extract frame as the start of the matrix instead of a later possibly non existing one.

Fix rounding error in calculation of number of matrix files to created (100.mp4)
For some reason the calculation of the number of matrix files was rounded from 0.8... to 2 where the next bigger integer should be 1

fixes issue DEV-1513

Additional changes:
* Add support for debugging output to export-moving-image-frames.sh
  Introduce $DEBUG for the export script as a way to turn on debug out
* Mount scripts folder into sipi for local dev purposed
* Declare function with function keyword
* Formatting - use spaces instead of tabs akin to scala source code
* Fix mimetype check being skipped/failing
	Failed with
	+ file -b --mime-type ./1.mp4 -ne video/mp4
	Usage: file [-bcCdEhikLlNnprsSvzZ0] [--apple] [--extension] [--mime-encoding]
		    [--mime-type] [-e <testname>] [-F <separator>]  [-f <namefile>]
		    [-m <magicfiles>] [-P <parameter=value>] <file> ...
	       file -C [-m <magicfiles>]
	       file [--help]
* Use double quotes in order to prevent globbing and word splitting
* Remove unused code: function collect, unused num variable,unused framestart variable
* Fix warning for read without -r https://github.com/koalaman/shellcheck/wiki/SC2162
* Simplify calculation and rename number_of_frame_files
  • Loading branch information
seakayone committed Nov 24, 2022
1 parent 93179d5 commit 729f071
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 101 deletions.
1 change: 1 addition & 0 deletions docker-compose.yml
Expand Up @@ -33,6 +33,7 @@ services:
- ${LOCAL_HOME}/sipi/config:/sipi/config:delegated
- ${LOCAL_HOME}/sipi/images:/sipi/images:delegated
- ${LOCAL_HOME}/sipi/server:/sipi/server:delegated
- ${LOCAL_HOME}/sipi/scripts:/sipi/scripts:delegated
networks:
- knora-net
environment:
Expand Down
182 changes: 81 additions & 101 deletions sipi/scripts/export-moving-image-frames.sh
Expand Up @@ -7,98 +7,75 @@
# - max. 36 frames per matrix
# - one matrix file corresponds to 6 minutes of film

set -e

dir=$(pwd)
sep='---------------------------------'
if [ "${DEBUG:false}" == true ]; then
set -ex
else
set -e
fi

die() {
echo >&2 "$@"
exit 1
function printSeparator() {
echo "---------------------------------"
}

usage() {
echo "usage: <command> -i [infile]>"
echo ${sep}
echo "-i = Inpute file"
echo ${sep}
function die() {
echo >&2 "$@"
exit 1
}

collect() {
film=''
cnt=1
#for b in `ls`; do
for file in *.$1; do
[ -f ${file} ]
name=$2

for c in ${cnt}; do
if [ "$film" == '' ] || [ "$film" == 'VIDEO_TS.VOB' ]; then
film=${file}
else
case $3 in
true) film="$film|$file" ;;
esac
fi
done
cnt=$(expr ${cnt} + 1)
done
needle="|"
num=$(grep -o "$needle" <<<"$film" | wc -l)
num=$(expr ${num} + 1)
function usage() {
echo "usage: <command> -i [infile]>"
printSeparator
echo "-i = Inpute file"
printSeparator
}

# default variables
# current directory
dir=$(pwd)
# name of the input file for which the keyframes should be extracted
infile=''

# alternative to get some information from the source file:
# ffprobe -v quiet -print_format json -show_format -show_streams ~/sourceFile.mov

while getopts ':i:' OPTION; do
case ${OPTION} in
i) infile=$OPTARG ;;
*) echo 'Unknown Parameter' ;;
esac
case ${OPTION} in
i) infile=$OPTARG ;;
*) echo 'Unknown Parameter' ;;
esac
done

if [ $# -eq 0 ]; then
usage
die ''
usage
die ''
fi

# check the arguments: if they exist
num='^[0-9]+$'

# infile must be given and has to be an mp4
# infile must be given and has to be an mp4 by checking if mime-type is video/mp4
if [ -z "${infile}" ]; then
# must be given
usage
die "ERROR: The Input File (-i) is missing"
# must be given
usage
die "ERROR: The Input File (-i) is missing"
fi

# check if mime-type is video/mp4
if file -b --mime-type "${infile}" -ne "video/mp4"; then
die "ERROR: The Input File (-i) is not mp4"
if [ "$(file -b --mime-type "${infile}")" != "video/mp4" ]; then
die "ERROR: The Input File (-i) is not mp4"
fi

# get the right output path, the file and the filename (without extension)
dir=$(dirname "${infile}")
file=$(basename "${infile}")
name="${file%.*}"

cd $dir
cd "${dir}"

# --
# store the frames and matrix files in specific folder;
# folder has same unique name as the uploaded file
mkdir -p $name
cd $name
mkdir -p "${name}"
cd "${name}"

# --
# read frame rate from input file
framerate=$(ffprobe -v 0 -of csv=p=0 -select_streams v:0 -show_entries stream=r_frame_rate ../"${file}")
frame_rate=$(ffprobe -v 0 -of csv=p=0 -select_streams v:0 -show_entries stream=r_frame_rate ../"${file}")

IFS="/" read -a array <<<"$framerate"
IFS="/" read -ra array <<<"$frame_rate"
numerator="${array[0]}"
denumerator="${array[1]}"

Expand All @@ -110,35 +87,35 @@ aspect=$(ffprobe -v error -select_streams v:0 -show_entries stream=display_aspec

# if aspect ratio does not exist in video file metadata
if [ ! -z {$aspect} ]; then
# get aspect ratio from video dimension (width and height)
aspectW=$(ffprobe -v error -select_streams v:0 -show_entries stream=width -of csv=s=x:p=0 ../"${file}")
aspectH=$(ffprobe -v error -select_streams v:0 -show_entries stream=height -of csv=s=x:p=0 ../"${file}")
else
IFS=':' read -a array <<<"$aspect"
aspectW="${array[0]}"
aspectH="${array[1]}"
# get aspect ratio from video dimension (width and height)
aspectW=$(ffprobe -v error -select_streams v:0 -show_entries stream=width -of csv=s=x:p=0 ../"${file}")
aspectH=$(ffprobe -v error -select_streams v:0 -show_entries stream=height -of csv=s=x:p=0 ../"${file}")
else
IFS=':' read -a array <<<"$aspect"
aspectW="${array[0]}"
aspectH="${array[1]}"
fi

# --
# framesize
# calculate frame width, height and size
framewidth='256'
frameheight=$(($framewidth * $aspectH / $aspectW))
framesize=${framewidth}'x'${frameheight}

echo ${sep}
# --
printSeparator
echo 'Start with frame export'
# check the outpath for the frames;
# if exists, delete it and create new
if [ -d "frames" ]; then
rm -rf ./frames
rm -rf ./frames
fi
mkdir -p 'frames'

framestart=$(echo ${start} + 1 | bc)
ffmpeg -i ../${file} -an -ss 1 -f image2 -s ${framesize} -vf framestep=${fps} frames/${name}'_f_%d.jpg' 2>&1
ffmpeg -i ../"${file}" -an -ss 0 -f image2 -s ${framesize} -vf framestep="${fps}" frames/"${name}"'_f_%d.jpg' 2>&1

echo 'Done'
echo ${sep}
printSeparator

# --
# create the matrix files
Expand All @@ -147,15 +124,14 @@ echo 'Start with creating matrix file'
# change directory frames
cd frames
# Get the number of files: one image = one second of movie
# numfiles is equivalent to the movie duration (in seconds)
numfiles=(*)
numfiles=${#numfiles[@]}
# number_of_frame_files is equivalent to the movie duration (in seconds)
readonly number_of_frame_files=$(find . -type f | wc -l)

image="${name}_f_1.jpg"

# check if file exists
if [[ ! -f "${image}" ]]; then
die "File not found: ${image}"
die "File not found: ${image}"
fi

# grab the identify string, make sure it succeeded
Expand All @@ -179,38 +155,42 @@ frame_height=$(echo "scale=0; ${matrix_height}/6" | bc)
frame_size="${frame_width}x${frame_height}"
# get every 10th image; start with the image number 5;
# how many matrixes will it produce?
calcmatrix=$(echo "scale=4; (${numfiles}/10)/36" | bc)
calcmatrix=$(echo "scale=4; (${number_of_frame_files}/10)/36" | bc)
# Round the exact fraction to the to the next highest integer
# for the number of matrix files to create 'nummatrix'
readonly interim=$(echo "${calcmatrix}+1"|bc)
readonly nummatrix=${interim%.*}

echo '#Matrix (calculated) '${calcmatrix}
nummatrix=$(echo ${calcmatrix} | awk '{printf("%.0f\n",$1 + 0.75)}')
echo '#Matrix (rounded) '${nummatrix}

t=0
while [ ${t} -lt ${nummatrix} ]; do
echo 'Matrix nr '${t}
firstframe=$(echo "scale=0; ${t}*360+5" | bc)
MATRIX=$(find *_${firstframe}.jpg)' '

c=1
while [ ${c} -lt 36 ]; do
sec=$(echo "scale=0; ${firstframe}+(${c}*10)" | bc)
if [ $sec -lt $numfiles ]; then
img="${name}_f_${sec}.jpg"
if [ -f ${img} ]; then
MATRIX+=${img}' '
fi
fi

let c=c+1
done

# here we make the montage of every matrix file
# montage -size 320x180 DB_4_5.jpg DB_4_15.jpg DB_4_25.jpg DB_4_35.jpg -geometry +0+0 montage1.jpg
# $(echo "montage -size ${matrix_size} ${MATRIX} -tile 6x6 -geometry +0+0 -resize ${frame_size} ../matrix/${name}'_m_'${t}'.jpg'")
montage -size ${matrix_size} ${MATRIX} -tile 6x6 -geometry +0+0 -resize ${frame_size} ../${name}'_m_'${t}'.jpg' 2>&1

let t=t+1
echo 'Matrix nr '${t}
firstframe=$(echo "scale=0; ${t}*360+1" | bc)
MATRIX=$(find *_${firstframe}.jpg)' '

c=1
while [ ${c} -lt 36 ]; do
sec=$(echo "scale=0; ${firstframe}+(${c}*10)" | bc)
if [ $sec -lt $number_of_frame_files ]; then
img="${name}_f_${sec}.jpg"
if [ -f ${img} ]; then
MATRIX+=${img}' '
fi
fi

let c=c+1
done

# here we make the montage of every matrix file
# montage -size 320x180 DB_4_5.jpg DB_4_15.jpg DB_4_25.jpg DB_4_35.jpg -geometry +0+0 montage1.jpg
# $(echo "montage -size ${matrix_size} ${MATRIX} -tile 6x6 -geometry +0+0 -resize ${frame_size} ../matrix/${name}'_m_'${t}'.jpg'")
montage -size ${matrix_size} ${MATRIX} -tile 6x6 -geometry +0+0 -resize ${frame_size} ../${name}'_m_'${t}'.jpg' 2>&1

let t=t+1

done

echo 'Done'
echo ${sep}
printSeparator

0 comments on commit 729f071

Please sign in to comment.