Skip to content

Instantly share code, notes, and snippets.

@jbgutierrez
Last active August 29, 2015 14:13
Show Gist options
  • Save jbgutierrez/010109de33e12ceb4f04 to your computer and use it in GitHub Desktop.
Save jbgutierrez/010109de33e12ceb4f04 to your computer and use it in GitHub Desktop.
Hodgepodge of useful scripts to analize different compression tools
#!/usr/bin/env ruby
# coding: UTF-8
require 'bundler/setup'
require "sinatra/base"
require "sinatra/reloader"
require "haml"
require_relative "image"
PWD = File.dirname(__FILE__)
class Server < Sinatra::Base
set :sessions, false
set :bind, '0.0.0.0'
set :public_folder, PWD
set :views, PWD
register Sinatra::Reloader
get '/' do
locals = { count: Image.count }
haml :index, locals: locals
end
get '/sample' do
content_type :json
Image.sample.to_json
end
post '/choose' do
Image.choose params[:file], request.ip
content_type :json
{}.to_json
end
run!
end
# A sample Gemfile
source "https://rubygems.org"
gem "sinatra"
gem "mongoid"
gem "bson_ext"
gem 'sinatra-reloader'
gem 'haml'
#!/usr/bin/env gnuplot
set terminal png size 900, 300
set grid
set key invert reverse Left outside
set xlabel 'DSSIM'
set title "Size vs DSSIM (1920 width)"
set ylabel 'Size (kBs)'
set output "dssim-size-1920.png"
plot [:][:] "datas/imagemagick_1920.dat" using 3:2 title "imagemagick" with lines, "datas/jpegoptim+rescan_1920.dat" using 3:2 title "jpegoptim+rescan" with lines, "datas/mozjpeg_1920.dat" using 3:2 title "mozjpeg" with lines
set xlabel 'Quality'
set xrange [60:90] reverse
set xtics 3
set title "Average DSSIM (1920 width)"
set ylabel 'DSSIM'
set output "quality-dssim-1920.png"
plot [:][:] "datas/imagemagick_1920.dat" using 1:2 title "imagemagick" with lines, "datas/jpegoptim+rescan_1920.dat" using 1:2 title "jpegoptim+rescan" with lines, "datas/mozjpeg_1920.dat" using 1:2 title "mozjpeg" with lines
set title "Average size (1920 width)"
set ylabel 'Size (kBs)'
set output "quality-size-1920.png"
plot [:][:] "datas/imagemagick_1920.dat" using 1:3 title "imagemagick" with lines, "datas/jpegoptim+rescan_1920.dat" using 1:3 title "jpegoptim+rescan" with lines, "datas/mozjpeg_1920.dat" using 1:3 title "mozjpeg" with lines
#!/usr/bin/env ruby
# coding: UTF-8
class Array
def sum
inject(0.0) { |result, el| result + el }
end
def mean
sum / size
end
end
content = File.read 'dssim.log'
widths = {}
content.split("\n").each do |line|
line.gsub!(/resized\/|.jpg/, '')
size, dssim, w, q, tool, _ = *(line.split(/__|_q|_w|_| /).reverse)
samples = widths[w] ||= {}
key = tool + q
if sample = samples[key]
sample[:dssim] << dssim.to_f
sample[:size] << size.to_f
else
samples[key] = {
tool: tool,
q: q,
dssim: [ dssim.to_f ],
size: [ size.to_f ]
}
end
end
widths.each do |w, samples|
samples.each do |key, sample|
line = [
sample[:q],
sample[:dssim].mean,
(sample[:size].mean / 1024.0).to_i
].join ' '
file = 'datas/' + sample[:tool] + '_' + w + '.dat'
`echo "#{line}" >> #{file}`
end
end
require 'bundler/setup'
require "mongoid"
require 'pathname'
Mongoid.load! "mongoid.yml", :production
Mongoid.logger = Logger.new $stdout
class Image
include Mongoid::Document
field :tool
field :file
field :base
field :ip
field :quality, type: BigDecimal
field :width, type: Integer
field :size, type: Integer
field :approved, type: Boolean
def self.load
pathname = Pathname.new "imgs/resized"
pathname.each_child.each do |file|
base, tool, q, w = *file.to_s.split(/__|_q|_w|\./)
size = file.stat.size
attributes = { tool: tool, quality: q, width: w, size: size, file: file.to_s, base: base }
Image.where(attributes).create!
end
end
def self.sample
criteria = where(approved: nil, tool: 'mozjpeg')
skip = Random.rand criteria.count
criteria.skip(skip).pluck(:file).first
end
def self.choose file, ip=''
image = where(file: file).first
images = where(:base => image.base, :tool => 'mozjpeg', :_id.ne => image._id)
images.update_all(approved: false, ip: ip)
image.update(approved: true, ip: ip)
end
end
if __FILE__ == $0
# Image.load
image = Image.sample
puts image
# Image.choose image
end
!!!
%html.no-js{:lang => "es"}
%head
%meta{:charset => "utf-8"}/
%meta{:content => "IE=edge", "http-equiv" => "X-UA-Compatible"}/
%title
%meta{:content => "", :name => "description"}/
%meta{:content => "width=device-width, initial-scale=1", :name => "viewport"}/
%link{:href => "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css", :rel => "stylesheet"}/
%link{:href => "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css", :rel => "stylesheet"}/
%link{:href => "main.css", :rel => "stylesheet"}/
%body
.container-fluid
%h1.page-header Cata a ciegas
.lead.alert.alert-warning.alert-dismissible.fade.in{:role => "alert"}
%button.close{"aria-label" => "Close", "data-dismiss" => "alert", :type => "button"}
%span{"aria-hidden" => "true"} ×
%p
La página irá enseñando imágenes grabadas con distintas
calidades. Se trata de pulsar encima de aquella que se
vea con suficiente calidad.
%p
Si valen las dos pulsar "Cualquier"
%ul#info
%li
Tiempo:
%strong#timer
%li
Calidad:
%strong#quality
#frames
.frame
%img#left
.frame
%img#right
.text-center
%button#both.btn.btn-default.btn-lg{:type => "button", 'data-target' => 'both'} Cualquiera
%script{:src => "https://code.jquery.com/jquery-2.1.3.js"}
/ Latest compiled and minified JavaScript
%script{:src => "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"}
%script{:src => "main.js"}
#!/usr/bin/env bash
log='dssim.log'
for width in 1920; do
declare -i quality=70
while [ $quality -lt 91 ]; do
resize.sh . -q $quality -w $width -r -t mozjpeg
resize.sh . -q $quality -w $width -r -t jpegoptim
resize.sh . -q $quality -w $width -r -t imagemagick
(( quality=quality + 10 ))
done
# calculate stats
for file in *.jpg; do
base=${file/.jpg/}
echo "converting $base.jpg"
raw_base64=`convert $base.jpg -resize $width png:- | base64`
for path in resized/$base*$width*.jpg; do
echo "computing dssim for $path"
dssim=`convert $path png:- | dssim <(echo "$raw_base64" | base64 --decode) /dev/stdin | awk '{print $1}'`
size=`stat -f%z $path`
echo "$path $dssim $size" >> $log
done
done
done
$(document).ready ->
$.ajaxSetup type: "POST"
$(".frame").scroll ->
$frame = $ this
$frame.
siblings().
scrollTop($frame.scrollTop()).
scrollLeft($frame.scrollLeft())
$(".frame img,#both").click ->
$this = $ this
target = $this.data 'target'
ImageCtrl.target target
ImageCtrl =
$left: $ '#left'
$right: $ '#right'
$timer: $ '#timer'
$quality: $ '#quality'
updateTimer: ->
ImageCtrl.$timer.text ImageCtrl.secondsLeft
if not ImageCtrl.secondsLeft--
clearTimeout ImageCtrl.timer
$('#both').addClass 'in'
# ImageCtrl.choose()
resetTimer: ->
clearTimeout @timer
@secondsLeft = Math.floor (@image.highest - @image.lowest) / 3
@secondsLeft = Math.max @secondsLeft, 5
@timer = setInterval @updateTimer, 1000
sample: ->
@$left.attr 'src', ''
@$right.attr 'src', ''
Image.sample (@image) => @draw()
choose: ->
@image.choose =>
ImageCtrl.sample()
@image = null
draw: ->
frames = [@$left, @$right]
frame = if Math.random() > 0.5 then frames.pop() else frames.shift()
frame.data 'target', 'lowest'
frame.attr 'src', @image.lowestSrc()
frame = frames.pop()
frame.data 'target', 'highest'
frame.attr 'src', @image.highestSrc()
@resetTimer()
@$quality.text @image.lowest
target: (target) ->
return unless @image
$('#both').removeClass 'in'
if @image.lowest is @image.highest or target isnt 'highest'
@choose()
else
@image.target 'highest'
@draw()
class Image
constructor: (@file) ->
@reset()
reset: ->
@lowest = 60
@highest = 90
@sample: (done) ->
$.get 'sample', (file) ->
done new Image file
choose: (done) ->
$.post 'choose', {file: @lowestSrc()}, done
target: (target) ->
if target is 'lowest'
@highest -= 2
else
@lowest += 2
lowestSrc: -> @file.replace /q\d+/, "q#{@lowest}"
highestSrc: -> @file.replace /q\d+/, "q#{@highest}"
ImageCtrl.sample()
@import 'compass/utilities';
@import "compass/css3";
#frames {
margin-bottom: 1em;
height: 700px;
.frame {
width: 50%;
height: 700px;
float: right;
text-align: left;
overflow-y: auto;
right: 0;
cursor: pointer;
}
}
#info {
@include inline-list();
li {
padding: 2em;
}
}
#info {
margin: 0 auto;
text-align: center;
display: block;
}
// keyframes mixin
@mixin keyframes($name) {
@-webkit-keyframes #{$name} {
@content;
}
@-moz-keyframes #{$name} {
@content;
}
@-ms-keyframes #{$name} {
@content;
}
@keyframes #{$name} {
@content;
}
}
// keyframes mixin
@mixin animation($content) {
-webkit-animation: $content;
-moz-animation: $content;
-ms-animation: $content;
animation: $content;
}
#both.in {
font-size: 3em;
@include transition-property(font-size);
@include transition-duration(2s);
@include transition-timing-function(ease-in);
@include keyframes(bounce) {
0% { background:red; }
25%, 75% { background:white; }
50% { background:white; }
100% { background:red; }
}
@include animation(bounce 1s infinite);
}
production:
sessions:
default:
hosts:
- localhost:27017
database: advanced-compression-techniques
#!/usr/bin/env bash
# This script lets you resize images in batches with different compression tools
usage(){
cat <<END
usage: resize FILE_OR_FOLDER [options]
The options are as follows:
-w widths (default: 2560 1920 1024 560)
-q quality (default: 75)
-a adjust Only files within the adjusted date will be resized. See 'man date' val[ymwdHMS] (default 1m)
-t tool Compression tool: jpegoptim, mozjpeg or imagemagick (default: imagemagick)
-r Losslessly shrink JPEG file with JPEGrescan (default: 0)
END
exit 1
}
type "convert" > /dev/null 2>&1 || { echo "Please install imagemagick first" >&2; exit 1; }
[[ $# -ge 1 ]] || { echo -e "Incorrect number of parameters\n" >&2; usage; }
files=$1
[[ -e $files ]] || { echo -e "resize: $files: No such file or directory\n" >&2; usage; }
shift
adjust=""
dir="resized"
quality=75
widths="2560 1920 1024 560"
tool='imagemagick'
rescan=0
while getopts ":w:q:a:t:r" opt; do
case $opt in
a) adjust=$OPTARG ;;
q) quality=$OPTARG ;;
r) rescan=1 ;;
t) tool=$OPTARG ;;
w) widths=$OPTARG ;;
--) break ;;
:) echo -e "Option -${OPTARG} is missing an argument\n" >&2; usage ;;
\?) echo -e "Unknown option: -${OPTARG}\n" >&2; usage ;;
esac
done
if [[ -d $files ]]; then
cd $files
if [[ -n $adjust ]]; then
timestamp=`date -j -v-$adjust +"%Y/%m/%d %H:%M"`
files=`find . -newermt "$timestamp" -d 1 -name '*.jpg'`
else
files=`find . -d 1 -name '*.jpg'`
fi
fi
mkdir -p $dir || { echo "Could not create $dir." >&2; exit 1; }
for file in $files; do
for width in $widths; do
resized_file="${file/.jpg/}__${tool}_q${quality}_w${width}.jpg"
echo "generating $resized_file"
case $tool in
"jpegoptim") `convert -resize $width $file jpg:- | jpegoptim -q -p -f --max=$quality --strip-all --stdout --stdin > $dir/$resized_file` ;;
"mozjpeg") `convert -resize $width $file png:- | cjpeg -quality $quality -optimize -dct float -outfile $dir/$resized_file` ;;
"imagemagick") `convert -quality $quality -resize $width $file $dir/$resized_file` ;;
esac
if (( $rescan )); then
optimized_file="${file/.jpg/}_${tool}+rescan_q${quality}_w${width}.jpg"
echo "generating $optimized_file"
`jpegrescan -q -s -i $dir/$resized_file $dir/$optimized_file`
fi
done
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment