Last active
September 22, 2020 11:05
-
-
Save liweiyap/035d6fd3745f3d737e22e452a4b40dad to your computer and use it in GitHub Desktop.
Script to vectorise images on Linux OS using Potrace. Use cases include scanned pages. Reduces image file size π.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# exit immediately when: | |
# - a command fails | |
# - undeclared variables are used | |
set -o errexit -o nounset | |
# error codes | |
OS_ERR=1 | |
GETOPT_FAIL=2 | |
WRONG_ARGS=3 | |
PROG_ERR=4 | |
if [[ "$OSTYPE" != "linux-gnu"* ]]; then | |
echo 'ERROR: Script to be run only on Linux' | |
exit $OS_ERR | |
fi | |
usage() | |
{ | |
echo "Usage: $0 [-b|--blacklevel <black_level>][-t|--turdsize <turd_size>][-r|--rotation <angle>][<inputFileName>] | |
Args: | |
black_level: no. b/w 0 and 1. | |
turd_size: no. higher than 0. | |
angle: no. b/w 0 and 360 degrees. | |
inputFileName: if not provided, then the script is run for everything in current directory | |
Flags: | |
--blacklevel: if not provided, default value of black_level is 0.5. | |
--turdsize: if not provided, default value of turd_size is 2. | |
--rotation: if not provided, default value of angle is 0." | |
} | |
# make sure getopt is working | |
! getopt --test > /dev/null | |
if [[ ${PIPESTATUS[0]} -ne 4 ]]; then | |
echo 'ERROR: `getopt --test` failed in this environment.' | |
exit $GETOPT_FAIL | |
fi | |
# parse args | |
OPTIONS=b:t:r: | |
LONGOPTS=blacklevel:,turdsize:,rotate: | |
! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@") | |
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then | |
echo 'ERROR: Wrong arguments' | |
usage | |
exit $WRONG_ARGS | |
fi | |
# read the output from getopt this way to handle the quoting right: | |
eval set -- "$PARSED" | |
black_level=0.5 | |
turd_size=2 | |
rotation=0 | |
while true; do | |
case "$1" in | |
-b|--blacklevel) | |
black_level="$2" | |
shift 2 | |
;; | |
-t|--turdsize) | |
turd_size="$2" | |
shift 2 | |
;; | |
-r|--rotate) | |
rotation="$2" | |
shift 2 | |
;; | |
--) | |
shift | |
break | |
;; | |
*) | |
echo "Programming error" | |
exit $PROG_ERR | |
;; | |
esac | |
done | |
isIntegerGreaterThanZero() | |
{ | |
regex='^[0-9]+$'; | |
if [[ $1 =~ $regex ]]; then | |
true | |
else | |
false | |
fi | |
} | |
isNumberBetweenZeroAndOne() | |
{ | |
if isIntegerGreaterThanZero $1 && ( (( $1 == 0)) || (( $1 == 1)) ); then | |
true | |
return | |
fi | |
regex='^[0-9]+\.[0-9]*$'; | |
if [[ $1 =~ $regex ]]; then | |
true | |
else | |
false | |
fi | |
} | |
# check validity of args | |
if ! isIntegerGreaterThanZero $turd_size ; then | |
echo 'ERROR: parameter for <-t|--turdsize> must be positive integer' | |
usage | |
exit $WRONG_ARGS | |
fi | |
if ! isIntegerGreaterThanZero $rotation ; then | |
echo 'ERROR: parameter for <-r|--rotation> must be positive integer' | |
usage | |
exit $WRONG_ARGS | |
fi | |
if (( rotation > 360 )); then | |
echo 'ERROR: parameter for <-r|--rotation> must be in degrees b/w 0 and 360' | |
usage | |
exit $WRONG_ARGS | |
fi | |
if ! isNumberBetweenZeroAndOne $black_level ; then | |
echo 'ERROR: parameter for <-b|--black_level> must be a number b/w 0 and 1' | |
usage | |
exit $WRONG_ARGS | |
fi | |
# handle non-option arguments | |
if [[ $# -eq 0 ]]; then | |
FILES_TO_VECTORISE=* | |
else | |
FILES_TO_VECTORISE="$@" | |
fi | |
# install programs if uninstalled | |
if ! loc="$(type -p "potrace")" || [[ -z $loc ]]; then | |
sudo apt-get update | |
sudo apt-get install potrace | |
fi | |
if ! command -v pdftoppm -png $FILES_TO_VECTORISE &> /dev/null; then | |
sudo apt-get update | |
sudo apt-get install poppler-utils | |
fi | |
something_to_vectorise=false | |
# finally, vectorise | |
for file in $FILES_TO_VECTORISE; do | |
if [ ${file#*.} == "pdf" ]; then | |
pdftoppm -png ${file} "${file%%.*}" | |
mv "${file%%.*}-1.png" "${file%%.*}.png" | |
file="${file%%.*}.png" | |
fi | |
if [ ${file#*.} != "jpg" ] && [ ${file#*.} != "png" ] ; then | |
continue | |
fi | |
# convert to tmp file usable by potrace | |
convert ${file} "${file%%.*}.ppm" | |
# now, vectorise | |
potrace --svg --rotate $rotation --turdsize $turd_size --blacklevel $black_level "${file%%.*}.ppm" | |
# remove tmp file | |
rm "${file%%.*}.ppm" | |
something_to_vectorise=true | |
# check if filesize of svg is smaller than that of img | |
non_vectorised_filesize=$(stat -c%s "$file") | |
vectorised_filesize=$(stat -c%s "${file%%.*}.svg") | |
if (( vectorised_filesize > non_vectorised_filesize )); then | |
echo "WARNING: Vectorised file size ($vectorised_filesize bytes) is greater than pre-vectorised file size ($non_vectorised_filesize bytes) for $file" | |
fi | |
done | |
if [ "$something_to_vectorise" = false ] ; then | |
echo 'No files (.jpg, .png, .pdf) to vectorise.' | |
fi | |
echo 'End of script' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment