diff --git a/01-rename.sh b/01-rename.sh deleted file mode 100755 index 625db2a..0000000 --- a/01-rename.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash - -## -# Main Program - -dir=$(dirname "$(readlink -f "$0")") -origdir="$dir/orig" -indir="$dir/in" - -if ! cd "$dir" ; then - echo "ERROR: Could not change to directory \"$dir\"; aborted." >&2 - exit 1 -elif ! test -d "$origdir" ; then - echo "ERROR: Directory \"$origdir\" not found; aborted." >&2 - exit 1 -elif ! mkdir -p "$indir" ; then - echo "ERROR: Could not create directory \"$indir\"; aborted." >&2 - exit 1 -elif ! rm -f -- "$indir/"*.jpg ; then - echo "ERROR: Could not remove files in \"$indir\"; aborted." >&2 - exit 1 -else - i=1 - rv=0 - - while read -r jpg ; do - renamed=$(printf "%02d" "$i").jpg - - if cp "$jpg" "$indir/$renamed" ; then - echo "INFO: Copied \"$jpg\" to \"$renamed\"." >&2 - else - echo "WARNING: Could not copy \"$jpg\" to \"$renamed\"." >&2 - rv=1 - fi - - i=$((i+1)) - done < <(ls --reverse --sort time -- "$origdir/"*.jpg) - - exit "$rv" -fi - diff --git a/02-convert.sh b/02-convert.sh deleted file mode 100755 index a4506ed..0000000 --- a/02-convert.sh +++ /dev/null @@ -1,193 +0,0 @@ -#!/bin/bash - -## -# Configuration - -# Before morphology analysis, reduce image dimensions by this factor -simplify=2 - -# Morphology to apply to reduce content to main area -morphology="Open:6" -morphology_shape="Disk" - -# Number of colors images are quantized to for component detection -colors=4 - -# Neighbors to evaluate when detecting connected components -connected_components=8 - -# Area treshold for connected components detection -area_treshold=1000 - -# Sigmoidal contrast adjustment used when trimming -sigmoidal_contrast_trim="30,30%" - -# Sigmoidal contrast adjustment visible in result images -sigmoidal_contrast="45,50%" - -# Number of colors result images are quantized to -posterize=6 - -# Keep intermediate images after processing (note that they will be removed in -# any case at the next run of this script) -keep_tmpfiles=true - -## -# Main Program - -dir="$(dirname "$(readlink -f "$0")")" -tmpdir="$dir/tmp" -indir="$dir/in" -comdir="$dir/com" -outdir="$dir/out" -resize_percent=$((100/simplify)) - -if ! cd "$dir" ; then - echo "ERROR: Could not change to directory \"$dir\"; aborted." >&2 - exit 1 -elif ! mkdir -p "$comdir" "$outdir" "$tmpdir" ; then - echo "ERROR: Could not create working directories; aborted." >&2 - exit 1 -elif ! rm -f -- "$comdir/"*.* ; then - echo "ERROR: Could not delete existing component files; aborted." >&2 - exit 1 -elif ! rm -f -- "$tmpdir/"*.* ; then - echo "ERROR: Could not delete existing tempfiles; aborted." >&2 - exit 1 -elif ! rm -f -- "$outdir/"*.* ; then - echo "ERROR: Could not delete existing results; aborted." >&2 - exit 1 -else - rv=0 - - for jpg in "$indir/"*.jpg ; do - base=$(basename "$jpg" .jpg) - tmp1="$tmpdir/$base.tmp1.png" - tmp2="$tmpdir/$base.tmp2.png" - com="$tmpdir/$base.com.png" - trimmed="$tmpdir/$base.trimmed.png" - inf="$tmpdir/$base.txt" - out="$outdir/$base.png" - - if ! convert \ - "$jpg" \ - -colorspace RGB \ - -sigmoidal-contrast "$sigmoidal_contrast_trim" \ - -resize "$resize_percent%" \ - -colors "$colors" \ - -morphology "$morphology" "$morphology_shape" \ - "$tmp1" - then - echo "ERROR: Could not create test image \"$tmp1\"; skipped." >&2 - rv=1 - continue - fi - - draw=() - v=255 - - while read -r primitive ; do - draw+=("-fill" "rgb($v,$v,$v)" "-draw" "$primitive") - v=$((v-2)) - done < <( - convert \ - "$tmp1" \ - -define connected-components:verbose=true \ - -define connected-components:exclude-header=true \ - -define connected-components:sort-order=increasing \ - -define connected-components:mean-color=true \ - -define connected-components:area-threshold="$area_treshold" \ - -virtual-pixel None \ - -connected-components "$connected_components" \ - -auto-level \ - "$com" | \ - perl -e ' - while(<>){ - chomp; - next unless /(\d+)x(\d+)\+(\d+)\+(\d+)/; - printf - STDERR - "DEBUG: component: $_\n"; - printf - "rectangle %d,%d,%d,%d\n", - $1, $2, $1+$3, $2+$4; - } - - ' | \ - sort | \ - uniq - ) - - printf "DEBUG: connected components in \"%s\":" "$tmp1" - for d in "${draw[@]}" ; do - printf " \"%s\"" "$d" - done - printf "\n" - - if ! convert \ - "$tmp1" \ - -fill black -colorize 100 \ - "${draw[@]}" \ - "$tmp2" - then - echo "ERROR: Could not convert \"$tmp1\" to \"$tmp2\"; skipped." >&2 - rv=1 - continue - elif ! convert \ - "$tmp2" \ - -trim info: \ - > "$inf" - then - echo "ERROR: Could not determine trim info from \"$tmp2\"; skipped." >&2 - rv=1 - continue - fi - - if "$keep_tmpfiles" ; then - if ! convert \ - "$tmp2" \ - -trim \ - "$trimmed" - then - echo "ERROR: Could not test trim \"$tmp2\"; skipped." >&2 - rv=1 - continue - fi - fi - - # upscale crop area from smaller temporary picture sizes - crop=$(simplify=$simplify perl -MEnv -e ' - while(<>) { - chomp(); - s/(\d+)/$1*${simplify}/eg; - /(\d+)x(\d+) \d+x\d+\+(\d+)\+(\d+)/ && do { - print $1."x".$2."+".$3."+".$4; - } - } - ' < "$inf") - - if ! "$keep_tmpfiles" ; then - if ! rm -f -- "$tmp1" "$tmp2" "$trimmed" "$inf" ; then - echo "WARNING: Could not remove temporary files in \"$dir/tmp\"." >&2 - rv=1 - fi - fi - - echo "Processing $base: crop=$crop ..." - - if ! convert \ - "$jpg" \ - -crop "$crop" \ - -sigmoidal-contrast "$sigmoidal_contrast" \ - +dither -posterize "$posterize" \ - "$out" - then - echo "ERROR: Could not process \"$jpg\"; skipped." >&2 - rv=1 - continue - fi - done - - exit "$rv" -fi - diff --git a/03-pdf.sh b/03-pdf.sh deleted file mode 100755 index 057a37f..0000000 --- a/03-pdf.sh +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/bash -# shellcheck disable=SC2046 - -## -# Configuration - -# Paper size of all pages -paper="a4paper" - -# Offset in page where image should be placed -offset="1cm 3cm" - -# Factor to scale image by before placing in page -scale=0.75 - -# Filename of complete result PDF -result="pdf/result.pdf" - -## -# Main Program - -dir="$(dirname "$(readlink -f "$0")")" - -if ! cd "$dir" ; then - echo "ERROR: Could not change to directory \"$dir\"; aborted." >&2 - exit 1 -elif ! mkdir -p pdf tmp ; then - echo "ERROR: Could not create \"$dir/{pdf,tmp}\"; aborted." >&2 - exit 1 -elif ! rm -f -- "pdf/"*.* ; then - echo "ERROR: Could not delete existing results in \"$dir/pdf\"; aborted." >&2 - exit 1 -else - rv=0 - - for png in "out/"*.png ; do - echo "INFO: Processing \"$png\" ..." >&2 - - read -r width height < <( - identify -verbose "$png" | \ - perl -ne '/Geometry: (\d+)x(\d+)/ && print "$1 $2"' - ) - - if [[ -z $width ]] || [[ -z $height ]] ; then - echo "ERROR: Could not identify width or height of \"$png\"; skipped." >&2 - rv=1 - continue - elif [[ $width -gt $height ]] ; then - backdrop=template/a4paper/landscape.pdf - jam_landscape=--landscape - else - backdrop=template/a4paper/portrait.pdf - jam_landscape=--no-landscape - fi - - base=$(basename "$png" .png) - pdf=tmp/$base.pdf - jammed=tmp/$base-pdfjam.pdf - backdropped=tmp/$base.backdropped.pdf - - if ! convert "$png" "$pdf" ; then - echo "ERROR: Could not convert \"$png\" to PDF; skipped." >&2 - rv=1 - continue - elif ! pdfjam \ - --quiet \ - --outfile tmp \ - --paper "$paper" \ - "$jam_landscape" \ - --scale "$scale" \ - --offset "$offset" \ - "$pdf" - then - echo "ERROR: Could not align \"$pdf\"; skipped." >&2 - rv=1 - continue - elif ! pdftk \ - "$jammed" \ - stamp "$backdrop" \ - output "$backdropped" - then - echo "ERROR: Could not watermark \"$pdf\"; skipped." >&2 - rv=1 - continue - fi - done - - if ! pdftk $(ls -- "tmp/"*.backdropped.pdf) cat output "$result" ; then - echo "ERROR: Could not concatenate \"$result\"." >&2 - rv=1 - elif ! rm -f -- "tmp/"*.pdf ; then - echo "ERROR: Could not remove tempfiles in \"$dir/tmp\"." >&2 - rv=1 - fi - - exit "$rv" -fi - diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..4f1a6ea --- /dev/null +++ b/run.sh @@ -0,0 +1,345 @@ +#!/bin/bash + +## +# Configuration + +# Before morphology analysis, reduce image dimensions by this factor +simplify=2 + +# Morphology to apply to reduce content to main area +morphology="Open:6" +morphology_shape="Disk" + +# Number of colors images are quantized to for component detection +colors=4 + +# Neighbors to evaluate when detecting connected components +connected_components=8 + +# Area treshold for connected components detection +area_treshold=1000 + +# Sigmoidal contrast adjustment used when trimming +sigmoidal_contrast_trim="30,30%" + +# Sigmoidal contrast adjustment visible in result images +sigmoidal_contrast="45,50%" + +# Number of colors result images are quantized to +posterize=6 + +# Keep intermediate images after processing (note that they will be removed in +# any case at the next run of this script) +keep_tmpfiles=true + +# Paper size of all pages +paper="a4paper" + +# Offset in page where image should be placed +offset="1cm 3cm" + +# Factor to scale image by before placing in page +scale=0.75 + +# Filename of complete result PDF +result="pdf/result.pdf" + +## +# Functions + +log() { + level=$1 + msg=$2 + + printf "%s: %s\n" "$level" "$msg" >&2 +} + +error() { + log ERROR "$1" + + exit "${2:-1}" +} + +warning() { + log WARNING "$1" +} + +info() { + log INFO "$1" +} + +## +# Arguments + +dir=$1 + +if [[ -z $dir ]] ; then + error "Usage: $0 DIR" +fi + +origdir=$(dirname "$(readlink -f "$dir")") + +if [[ ! -d $origdir ]] ; then + error "Directory not found: $dir ($origdir)" +fi + +## +# Main Program + +job_id=$(printf "%s" "$origdir" | sha256sum | cut -d" " -f1) + +if [[ -n $TMP ]] ; then + tmp_base=$TMP/photos2pdf/$job_id +elif [[ -n $XDG_RUNTIME_DIR ]] ; then + tmp_base=$XDG_RUNTIME_DIR/photos2pdf/$job_id +else + tmp_base=/tmp/photos2pdf/$job_id +fi + +if ! mkdir -p "$tmp_base" ; then + error "Could not create temporary workign directory $tmp_base; aborted" +fi + +indir="$tmp_base/in" +tmpdir="$tmp_base/tmp" +outdir="$tmp_base/out" +pdfdir="$tmp_base/pdf" + +if ! test -d "$origdir" ; then + error "Directory \"$origdir\" not found; aborted." +elif ! mkdir -p "$indir" "$outdir" "$pdfdir" "$tmpdir" ; then + error "Could not create working directories; aborted." +elif ! rm -f -- "$indir/"*.* ; then + error "Could not delete existing input files; aborted." +elif ! rm -f -- "$tmpdir/"*.* ; then + error "Could not delete existing tempfiles; aborted." +elif ! rm -f -- "$outdir/"*.* ; then + error "Could not delete existing results; aborted." +elif ! rm -f -- "$pdfdir/"*.* ; then + error "Could not delete existing PDFs; aborted." +fi + +resize_percent=$((100/simplify)) + +i=1 +rv=0 + +while read -r jpg ; do + renamed=$(printf "%02d" "$i").jpg + + if cp "$jpg" "$indir/$renamed" ; then + info "Copied \"$jpg\" to \"$renamed\"." + else + warning "Could not copy \"$jpg\" to \"$renamed\"." + rv=1 + fi + + i=$((i+1)) +done < <(ls --reverse --sort time -- "$origdir/"*.jpg) + +if [[ $rv -ne 0 ]] ; then + error "One or more errors during input file naming; aborted." "$rv" +fi + +for jpg in "$indir/"*.jpg ; do + base=$(basename "$jpg" .jpg) + tmp1="$tmpdir/$base.tmp1.png" + tmp2="$tmpdir/$base.tmp2.png" + com="$tmpdir/$base.com.png" + trimmed="$tmpdir/$base.trimmed.png" + inf="$tmpdir/$base.txt" + out="$outdir/$base.png" + + if ! convert \ + "$jpg" \ + -colorspace RGB \ + -sigmoidal-contrast "$sigmoidal_contrast_trim" \ + -resize "$resize_percent%" \ + -colors "$colors" \ + -morphology "$morphology" "$morphology_shape" \ + "$tmp1" + then + warning "Could not create test image \"$tmp1\"; skipped." + rv=1 + continue + fi + + draw=() + v=255 + + while read -r primitive ; do + draw+=("-fill" "rgb($v,$v,$v)" "-draw" "$primitive") + v=$((v-2)) + done < <( + convert \ + "$tmp1" \ + -define connected-components:verbose=true \ + -define connected-components:exclude-header=true \ + -define connected-components:sort-order=increasing \ + -define connected-components:mean-color=true \ + -define connected-components:area-threshold="$area_treshold" \ + -virtual-pixel None \ + -connected-components "$connected_components" \ + -auto-level \ + "$com" | \ + perl -e ' + while(<>){ + chomp; + next unless /(\d+)x(\d+)\+(\d+)\+(\d+)/; + printf + STDERR + "DEBUG: component: $_\n"; + printf + "rectangle %d,%d,%d,%d\n", + $1, $2, $1+$3, $2+$4; + } + + ' | \ + sort | \ + uniq + ) + + printf "DEBUG: connected components in \"%s\":" "$tmp1" + for d in "${draw[@]}" ; do + printf " \"%s\"" "$d" + done + printf "\n" + + if ! convert \ + "$tmp1" \ + -fill black -colorize 100 \ + "${draw[@]}" \ + "$tmp2" + then + warning "Could not convert \"$tmp1\" to \"$tmp2\"; skipped." + rv=1 + continue + elif ! convert \ + "$tmp2" \ + -trim info: \ + > "$inf" + then + warning "Could not determine trim info from \"$tmp2\"; skipped." + rv=1 + continue + fi + + if "$keep_tmpfiles" ; then + if ! convert \ + "$tmp2" \ + -trim \ + "$trimmed" + then + warning "Could not test trim \"$tmp2\"; skipped." + rv=1 + continue + fi + fi + + # upscale crop area from smaller temporary picture sizes + crop=$(simplify=$simplify perl -MEnv -e ' + while(<>) { + chomp(); + s/(\d+)/$1*${simplify}/eg; + /(\d+)x(\d+) \d+x\d+\+(\d+)\+(\d+)/ && do { + print $1."x".$2."+".$3."+".$4; + } + } + ' < "$inf") + + if ! "$keep_tmpfiles" ; then + if ! rm -f -- "$tmp1" "$tmp2" "$trimmed" "$inf" ; then + warning "Could not remove temporary files in \"$dir/tmp\"." + rv=1 + fi + fi + + info "Processing $base: crop=$crop ..." + + if ! convert \ + "$jpg" \ + -crop "$crop" \ + -sigmoidal-contrast "$sigmoidal_contrast" \ + +dither -posterize "$posterize" \ + "$out" + then + warning "Could not process \"$jpg\"; skipped." + rv=1 + continue + fi +done + +if [[ $rv -ne 0 ]] ; then + error "One or more errors during image conversion; aborted." "$rv" +fi + +for png in "out/"*.png ; do + info "Processing \"$png\" ..." + + read -r width height < <( + identify -verbose "$png" | \ + perl -ne '/Geometry: (\d+)x(\d+)/ && print "$1 $2"' + ) + + if [[ -z $width ]] || [[ -z $height ]] ; then + warning "Could not identify width or height of \"$png\"; skipped." + rv=1 + continue + elif [[ $width -gt $height ]] ; then + backdrop=template/a4paper/landscape.pdf + jam_landscape=--landscape + else + backdrop=template/a4paper/portrait.pdf + jam_landscape=--no-landscape + fi + + base=$(basename "$png" .png) + pdf=tmp/$base.pdf + jammed=tmp/$base-pdfjam.pdf + backdropped=tmp/$base.backdropped.pdf + + if ! convert "$png" "$pdf" ; then + warning "Could not convert \"$png\" to PDF; skipped." + rv=1 + continue + elif ! pdfjam \ + --quiet \ + --outfile tmp \ + --paper "$paper" \ + "$jam_landscape" \ + --scale "$scale" \ + --offset "$offset" \ + "$pdf" + then + warning "Could not align \"$pdf\"; skipped." + rv=1 + continue + elif ! pdftk \ + "$jammed" \ + stamp "$backdrop" \ + output "$backdropped" + then + warning "Could not watermark \"$pdf\"; skipped." + rv=1 + continue + fi +done + +input=() + +while read -r pdf ; do + input+=("$pdf") +done < <(ls -- "$tmpdir/"*.backdropped.pdf) + +if ! pdftk "${input[@]}" cat output "$result" ; then + warning "Could not concatenate \"$result\"." + rv=1 +elif ! rm -f -- "tmp/"*.pdf ; then + warning "Could not remove tempfiles in \"$dir/tmp\"." + rv=1 +fi + +if [[ $rv -ne 0 ]] ; then + error "One or more errors during PDF generation; aborted." "$rv" +fi +