From 498d3ed8f1713ef2777ed196d39df3fa9001e36c Mon Sep 17 00:00:00 2001 From: Levon Gevorgyan Date: Sat, 9 Nov 2024 16:55:43 -0600 Subject: [PATCH] update readme and add gnuplot --- README.md | 34 ++++++------- scripts/estimate_fg.sh | 105 ++++++++++++++++++++++++++++++++--------- scripts/recc_encode.sh | 18 +++---- 3 files changed, 111 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 3637dd4..2732825 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,17 @@ The installation of the encoding script creates a symlink to this repo's `./scri Read the specifics in the actual file : `./scripts/recc_encode.sh` ```bash -encode -i input_file [-p] [-c] [-s] [-v] [-g NUM] [output_file_name] [-I] [-U] - -p print the command instead of executing it - -c use cropdetect - -s use same container as input, default is mkv - -g set film grain for encode - -v Print relevant version info +encode -i input_file [options] + [-p] print the command instead of executing it + [-c] use cropdetect + [-s] use same container as input, default is mkv + [-v] Print relevant version info + [-g NUM] set film grain for encode - output_file_name if not set, will create at /home/lgevorgyan/ + [output_file] if not set, will create at $HOME/ - -I Install this as /usr/local/bin/encode - -U Uninstall this from /usr/local/bin/encode + [-I] Install this as /usr/local/bin/encode + [-U] Uninstall this from /usr/local/bin/encode ``` Example usage: - `encode -i input.mkv output.mkv` standard usage @@ -43,13 +43,15 @@ Example usage: ## Estimate film grain script The installation of the script creates a symlink to this repo's `./scripts/estimate_fg.sh` so do NOT remove this repo or the functionality of `estimate-film-grain` will FAIL. The `estimate-film-grain` script is a way to estimate the ideal film grain of a video by encoding it at different film grain values and observing at what point does a higher film grain value result in diminishing returns. ```bash -estimate_fg.sh -i input_file [-o output_file] [-l NUM] [-s NUM] [-h NUM] [-I] [-U] - -o file to output results to - -l low value to use as minimum film-grain - -s step value to use increment from low to high film-grain - -h high value to use as maximum film-grain - -I Install this as /usr/local/bin/estimate-film-grain - -U Uninstall this from /usr/local/bin/estimate-film-grain +estimate-film-grain -i input_file [options] + [-o output_file] file to output results to + [-l NUM] low value to use as minimum film-grain + [-s NUM] step value to use increment from low to high film-grain + [-h NUM] high value to use as maximum film-grain + [-p] plot bitrates using gnuplot + + [-I] Install this as /usr/local/bin/estimate-film-grain + [-U] Uninstall this from /usr/local/bin/estimate-film-grain ``` ## Benchmark script diff --git a/scripts/estimate_fg.sh b/scripts/estimate_fg.sh index 908ca52..3f06713 100755 --- a/scripts/estimate_fg.sh +++ b/scripts/estimate_fg.sh @@ -1,13 +1,14 @@ #!/bin/bash usage() { - echo "estimate_fg.sh -i input_file [-o output_file] [-l NUM] [-s NUM] [-h NUM] [-I] [-U]" - echo -e "\t-o file to output results to" - echo -e "\t-l low value to use as minimum film-grain" - echo -e "\t-s step value to use increment from low to high film-grain" - echo -e "\t-h high value to use as maximum film-grain" - echo -e "\t-I Install this as /usr/local/bin/estimate-film-grain" - echo -e "\t-U Uninstall this from /usr/local/bin/estimate-film-grain" + echo "$(basename "$0") -i input_file [options]" + echo -e "\t[-o output_file] file to output results to" + echo -e "\t[-l NUM] low value to use as minimum film-grain" + echo -e "\t[-s NUM] step value to use increment from low to high film-grain" + echo -e "\t[-h NUM] high value to use as maximum film-grain" + echo -e "\t[-p] plot bitrates using gnuplot" + echo -e "\n\t[-I] Install this as /usr/local/bin/estimate-film-grain" + echo -e "\t[-U] Uninstall this from /usr/local/bin/estimate-film-grain" return 0 } @@ -22,7 +23,7 @@ check_not_negative_optarg() { echoerr() { echo -e "$@" 1>&2; } -OPTS='o:l:s:h:i:IU' +OPTS='po:l:s:h:i:IU' NUM_OPTS="${#OPTS}" # only using -I or -U MIN_OPT=1 @@ -57,6 +58,9 @@ while getopts "$OPTS" flag; do o) OUTPUT_FILE="${OPTARG}" ;; + p) + PLOT='true' + ;; l) check_not_negative_optarg "${OPTARG}" LOW_GRAIN="${OPTARG}" @@ -79,8 +83,8 @@ done # set default values test ! -n "$LOW_GRAIN" && LOW_GRAIN=0 -test ! -n "$STEP_GRAIN" && STEP_GRAIN=5 -test ! -n "$HIGH_GRAIN" && HIGH_GRAIN=30 +test ! -n "$STEP_GRAIN" && STEP_GRAIN=2 +test ! -n "$HIGH_GRAIN" && HIGH_GRAIN=20 echo "Estimating film grain for $INPUT" echo -e "\tTesting grain from $LOW_GRAIN-$HIGH_GRAIN with $STEP_GRAIN step increments" && sleep 2 @@ -99,7 +103,7 @@ get_avg_bitrate() { check_bitrate_bounds() { TEST_BITRATE="$1" TARGET_BITRATE="$2" - TARGET_DELTA="$(echo "$TARGET_BITRATE * .60" | bc)" + TARGET_DELTA="$(echo "$TARGET_BITRATE * .70" | bc)" DIFF_BITRATE=$((TEST_BITRATE - TARGET_BITRATE)) DIFF_BITRATE="$(echo ${DIFF_BITRATE#-})" echoerr "TEST_BITRATE:\t$TEST_BITRATE" @@ -119,10 +123,12 @@ SEGMENT_TIME=4 MAX_SEGMENTS=6 TOTAL_SECONDS="$(get_duration "$INPUT")" INPUT_BITRATE="$(get_avg_bitrate "$INPUT")" -SEGMENT_DIR='/tmp/fg_segments' +CLEAN_INP_NAME="$(echo "$INPUT" | tr ' ' '.' | tr -d '{}[]+')" +SEGMENT_DIR="/tmp/${CLEAN_INP_NAME}/fg_segments" SEGMENTS_LIST="$SEGMENT_DIR/segments_list.txt" OUTPUT_CONCAT="$SEGMENT_DIR/concatenated.mkv" -GRAIN_LOG="grain_log.txt" +OPTS_HASH="$(echo "$@" | sha256sum | tr -d ' ' | cut -d'-' -f1)" +GRAIN_LOG="$SEGMENT_DIR/grain_log-${OPTS_HASH}.txt" segment_video() { # set number of segments and start times @@ -151,7 +157,7 @@ segment_video() { return 0 fi START_TIME="${START_TIMES[$INDEX]}" - OUTPUT_SEGMENT="$SEGMENT_DIR/segment_$INDEX.mkv" + OUTPUT_SEGMENT="$SEGMENT_DIR/segment_${INDEX}.mkv" echo "START_TIME: $START_TIME" ffmpeg -ss "$START_TIME" -i "$INPUT" \ -hide_banner -loglevel error -t "$SEGMENT_TIME" \ @@ -179,16 +185,16 @@ get_output_bitrate() { } encode_segments() { - cd "$SEGMENT_DIR" || exit - mkdir ./encoded || exit + mkdir -p "$SEGMENT_DIR/encoded" echo > "$GRAIN_LOG" - for VIDEO in $(ls segment*.mkv) + for VIDEO in $(ls "$SEGMENT_DIR"/segment_*.mkv) do - echo "$VIDEO" >> "$GRAIN_LOG" - for GRAIN in $(seq $LOW_GRAIN $STEP_GRAIN $HIGH_GRAIN) + echo "file: $VIDEO" >> "$GRAIN_LOG" + for GRAIN in $(seq "$LOW_GRAIN" "$STEP_GRAIN" "$HIGH_GRAIN") do - OUTPUT_VIDEO="encoded/encoded_$VIDEO" - encode -i "$VIDEO" -g $GRAIN "$OUTPUT_VIDEO" + BASE_VID="$(basename "$VIDEO")" + OUTPUT_VIDEO="$SEGMENT_DIR/encoded/encoded_${BASE_VID}" + encode -i "$VIDEO" -g "$GRAIN" "$OUTPUT_VIDEO" BITRATE="$(get_output_bitrate "$OUTPUT_VIDEO")" echo -e "\tgrain: $GRAIN, bitrate: $BITRATE" >> "$GRAIN_LOG" done @@ -200,6 +206,63 @@ encode_segments() { } +plot() { + mapfile -t FILES< <( grep "file:" "$GRAIN_LOG" | cut -d':' -f2 | tr -d ' ' | sort | uniq ) + mapfile -t GRAINS< <( grep "grain:" "$GRAIN_LOG" | cut -d':' -f2 | cut -d',' -f1 | tr -d ' ' | sort -Vu) + declare -a BITRATE_SUMS=() + + for FILE in "${FILES[@]}" + do + # get grains for each file + LINE_FILE="$(grep -n "$FILE" "$GRAIN_LOG" | cut -d':' -f1 )" + START_GRAIN_LINE="$(echo "$LINE_FILE + 1" | bc)" + END_GRAIN_LINE="$(echo "$LINE_FILE + ${#GRAINS[@]}" | bc)" + GRAINS_FOR_FILE="$(sed -n "$START_GRAIN_LINE, $END_GRAIN_LINE p" "$GRAIN_LOG")" + # set baseline bitrate value + BASELINE_BITRATE="$(echo "$GRAINS_FOR_FILE" | tr -d ' ' | grep "grain:${GRAINS[0]}" | cut -d':' -f3)" + # get sum of bitrate percentages + for GRAIN in "${GRAINS[@]}" + do + COMPARE_BITRATE="$(echo "$GRAINS_FOR_FILE" | tr -d ' ' | grep "grain:$GRAIN" | cut -d':' -f3)" + BITRATE_PERCENTAGE="$(echo "$COMPARE_BITRATE / $BASELINE_BITRATE" | bc -l)" + # fix NULL BITRATE_SUM for first comparison + test -n "${BITRATE_SUMS[$GRAIN]}" || BITRATE_SUMS["$GRAIN"]=0 + BITRATE_SUMS["$GRAIN"]="$(echo "$BITRATE_PERCENTAGE + ${BITRATE_SUMS[$GRAIN]}" | bc -l)" + done + done + + # clear plot file + PLOT="$SEGMENT_DIR/plot.dat" + echo -n > "$PLOT" + + # set average bitrates per grain + for GRAIN in "${GRAINS[@]}" + do + AVG_BITRATE="$(echo "${BITRATE_SUMS[$GRAIN]} / ${#FILES[@]}" | bc -l)" + echo -e "$GRAIN\t$AVG_BITRATE" >> "$PLOT" + done + + # set terminal size + TERMINAL="$(tty)" + COLUMNS=$(stty -a <"$TERMINAL" | grep -Po '(?<=columns )\d+') + ROWS=$(stty -a <"$TERMINAL" | grep -Po '(?<=rows )\d+') + + # plot data + gnuplot -p -e " \ + set terminal dumb size $COLUMNS, $ROWS; \ + set autoscale; \ + set style line 1 \ + linecolor rgb '#0060ad' \ + linetype 1 linewidth 2 \ + pointtype 7 pointsize 1.5; \ + plot '$PLOT' with linespoints linestyle 1 + " +} + +test "$PLOT" == 'true' && test -f "$GRAIN_LOG" && \ + { plot ; exit $? ; } get_avg_bitrate "$INPUT" segment_video encode_segments +test "$PLOT" == 'true' && test -f "$GRAIN_LOG" && \ + { plot ; } \ No newline at end of file diff --git a/scripts/recc_encode.sh b/scripts/recc_encode.sh index c485413..361b687 100755 --- a/scripts/recc_encode.sh +++ b/scripts/recc_encode.sh @@ -9,15 +9,15 @@ SCRIPT_DIR="$(dirname "$SCRIPT_PATH")" BUILDER_DIR="$(dirname "$SCRIPT_DIR")" usage() { - echo "encode -i input_file [-p] [-c] [-s] [-v] [-g NUM] [output_file_name] [-I] [-U] " - echo -e "\t-p print the command instead of executing it" - echo -e "\t-c use cropdetect" - echo -e "\t-s use same container as input, default is mkv" - echo -e "\t-g set film grain for encode" - echo -e "\t-v Print relevant version info" - echo -e "\n\toutput_file_name if not set, will create at $HOME/\n" - echo -e "\t-I Install this as /usr/local/bin/encode" - echo -e "\t-U Uninstall this from /usr/local/bin/encode" + echo "$(basename "$0") -i input_file [options] " + echo -e "\t[-p] print the command instead of executing it" + echo -e "\t[-c] use cropdetect" + echo -e "\t[-s] use same container as input, default is mkv" + echo -e "\t[-v] Print relevant version info" + echo -e "\t[-g NUM] set film grain for encode" + echo -e "\n\t[output_file] if not set, will create at $HOME/" + echo -e "\n\t[-I] Install this as /usr/local/bin/encode" + echo -e "\t[-U] Uninstall this from /usr/local/bin/encode" return 0 }