optimize_images.sh 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. #!/bin/bash
  2. # Borrowed from https://gist.github.com/lgiraudel/6065155
  3. # Inplace mode added by Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
  4. PROGNAME=${0##*/}
  5. INPUT=''
  6. QUIET='0'
  7. NOSTATS='0'
  8. INPLACE='0' # (1=Images are replaced, 0=New images are stored into $OUTPUT)
  9. max_input_size=0
  10. max_output_size=0
  11. usage()
  12. {
  13. cat <<EO
  14. Usage: $PROGNAME [options]
  15. Script to optimize JPG and PNG images in a directory.
  16. Options:
  17. EO
  18. cat <<EO | column -s\& -t
  19. -h, --help & shows this help
  20. -q, --quiet & disables output
  21. -i, --input [dir] & specify input directory (current directory by default)
  22. -o, --output [dir] & specify output directory ("output" by default)
  23. -ns, --no-stats & no stats at the end
  24. -p, --inplace & optimizes files inplace
  25. EO
  26. }
  27. # $1: input image
  28. # $2: output image
  29. optimize_image()
  30. {
  31. input_file_size=$(stat -c%s "$1")
  32. max_input_size=$(expr $max_input_size + $input_file_size)
  33. if [ "${1##*.}" = "png" ]; then
  34. #optipng -o1 -clobber -quiet $1 -out $2.firstpass
  35. optipng -o1 -quiet $1 -out $2.firstpass
  36. pngcrush -q -rem alla -reduce $2.firstpass $2 >/dev/null
  37. rm -fr $2.firstpass
  38. fi
  39. if [ "${1##*.}" = "jpg" -o "${1##*.}" = "jpeg" ]; then
  40. jpegtran -copy none -progressive $1 > $2
  41. fi
  42. output_file_size=$(stat -c%s "$2")
  43. max_output_size=$(expr $max_output_size + $output_file_size)
  44. }
  45. get_max_file_length()
  46. {
  47. local maxlength=0
  48. IMAGES=$(find $INPUT -regextype posix-extended -regex '.*\.(jpg|jpeg|png)' | grep -v $OUTPUT)
  49. for CURRENT_IMAGE in $IMAGES; do
  50. filename=$(basename "$CURRENT_IMAGE")
  51. if [[ ${#filename} -gt $maxlength ]]; then
  52. maxlength=${#filename}
  53. fi
  54. done
  55. echo "$maxlength"
  56. }
  57. main()
  58. {
  59. test=`type pngcrush >/dev/null 2>&1`
  60. result=$?
  61. if [ "x$result" == "x1" ]; then
  62. echo "Tool pngcrush not found" && exit
  63. fi
  64. test=`type optipng >/dev/null 2>&1`
  65. result=$?
  66. if [ "x$result" == "x1" ]; then
  67. echo "Tool optipng not found" && exit
  68. fi
  69. test=`type jpegtran >/dev/null 2>&1`
  70. result=$?
  71. if [ "x$result" == "x1" ]; then
  72. echo "Tool jpegtran not found" && exit
  73. fi
  74. # If $INPUT is empty, then we use current directory
  75. if [[ "$INPUT" == "" ]]; then
  76. INPUT=$(pwd)
  77. fi
  78. # If $OUTPUT is empty, then we use the directory "output" in the current directory
  79. if [[ "$OUTPUT" == "" ]]; then
  80. OUTPUT=$(pwd)/output
  81. fi
  82. # If inplace, we use /tmp for output
  83. if [[ "$INPLACE" == "1" ]]; then
  84. OUTPUT='/tmp/optimize'
  85. fi
  86. echo "Mode is $INPLACE (1=Images are replaced, 0=New images are stored into $OUTPUT)"
  87. # We create the output directory
  88. mkdir -p $OUTPUT
  89. # To avoid some troubles with filename with spaces, we store the current IFS (Internal File Separator)...
  90. SAVEIFS=$IFS
  91. # ...and we set a new one
  92. IFS=$(echo -en "\n\b")
  93. max_filelength=`get_max_file_length`
  94. pad=$(printf '%0.1s' "."{1..600})
  95. sDone=' [ DONE ]'
  96. linelength=$(expr $max_filelength + ${#sDone} + 5)
  97. # Search of all jpg/jpeg/png in $INPUT
  98. # We remove images from $OUTPUT if $OUTPUT is a subdirectory of $INPUT
  99. echo "Scan $INPUT to find images"
  100. IMAGES=$(find $INPUT -regextype posix-extended -regex '.*\.(jpg|jpeg|png)' | grep -v $OUTPUT)
  101. if [ "$QUIET" == "0" ]; then
  102. echo --- Optimizing $INPUT ---
  103. echo
  104. fi
  105. for CURRENT_IMAGE in $IMAGES; do
  106. echo "Process $CURRENT_IMAGE"
  107. filename=$(basename $CURRENT_IMAGE)
  108. if [ "$QUIET" == "0" ]; then
  109. printf '%s ' "$filename"
  110. printf '%*.*s' 0 $((linelength - ${#filename} - ${#sDone} )) "$pad"
  111. fi
  112. optimize_image $CURRENT_IMAGE $OUTPUT/$filename
  113. # Replace file
  114. if [[ "$INPLACE" == "1" ]]; then
  115. mv $OUTPUT/$filename $CURRENT_IMAGE
  116. fi
  117. if [ "$QUIET" == "0" ]; then
  118. printf '%s\n' "$sDone"
  119. fi
  120. done
  121. # Cleanup
  122. if [[ "$INPLACE" == "1" ]]; then
  123. rm -rf $OUTPUT
  124. fi
  125. # we restore the saved IFS
  126. IFS=$SAVEIFS
  127. if [ "$NOSTATS" == "0" -a "$QUIET" == "0" ]; then
  128. echo
  129. echo "Input: " $(human_readable_filesize $max_input_size)
  130. echo "Output: " $(human_readable_filesize $max_output_size)
  131. space_saved=$(expr $max_input_size - $max_output_size)
  132. echo "Space save: " $(human_readable_filesize $space_saved)
  133. fi
  134. }
  135. human_readable_filesize()
  136. {
  137. echo -n $1 | awk 'function human(x) {
  138. s=" b Kb Mb Gb Tb"
  139. while (x>=1024 && length(s)>1)
  140. {x/=1024; s=substr(s,4)}
  141. s=substr(s,1,4)
  142. xf=(s==" b ")?"%5d ":"%.2f"
  143. return sprintf( xf"%s", x, s)
  144. }
  145. {gsub(/^[0-9]+/, human($1)); print}'
  146. }
  147. SHORTOPTS="h,i:,o:,q,s,p"
  148. LONGOPTS="help,input:,output:,quiet,no-stats,inplace"
  149. ARGS=$(getopt -s bash --options $SHORTOPTS --longoptions $LONGOPTS --name $PROGNAME -- "$@")
  150. # Syntax
  151. if [ "x$1" != "xlist" -a "x$1" != "xfix" ]
  152. then
  153. echo "Usage: optimize_images.sh (list|fix) -i dirtoscan"
  154. exit
  155. fi
  156. eval set -- "$ARGS"
  157. while true; do
  158. case $1 in
  159. -h|--help)
  160. usage
  161. exit 0
  162. ;;
  163. -i|--input)
  164. shift
  165. INPUT=$1
  166. ;;
  167. -o|--output)
  168. shift
  169. OUTPUT=$1
  170. ;;
  171. -q|--quiet)
  172. QUIET='1'
  173. ;;
  174. -s|--no-stats)
  175. NOSTATS='1'
  176. ;;
  177. -p|--inplace)
  178. INPLACE='1'
  179. ;;
  180. --)
  181. shift
  182. break
  183. ;;
  184. *)
  185. shift
  186. break
  187. ;;
  188. esac
  189. shift
  190. done
  191. # To convert
  192. if [ "x$1" = "xlist" ]
  193. then
  194. INPLACE=0
  195. fi
  196. main