script/worker_analysis in image_optim-0.24.3 vs script/worker_analysis in image_optim-0.25.0
- old
+ new
@@ -131,20 +131,26 @@
end
end
# Delegate to worker with short id
class WorkerVariant < DelegateClass(ImageOptim::Worker)
- attr_reader :name, :id, :cons_id
+ attr_reader :name, :id, :cons_id, :required
def initialize(klass, image_optim, options)
+ @required = options.delete(:required)
+ @run_order = options.delete(:run_order)
allow_consecutive_on = Array(options.delete(:allow_consecutive_on))
@image_optim = image_optim
@name = klass.bin_sym.to_s + options_string(options)
__setobj__(klass.new(image_optim, options))
@id = klass.bin_sym.to_s + options_string(self.options)
@cons_id = [klass, allow_consecutive_on.map{ |key| [key, send(key)] }]
end
+ def run_order
+ @run_order || super
+ end
+
def etag
[
id,
bin_versions,
source_digest,
@@ -153,11 +159,12 @@
private
def bin_versions
@bin_versions ||= used_bins.map do |name|
- @image_optim.resolve_bin!(name).to_s
+ bin = @image_optim.resolve_bin!(name)
+ "#{bin.name} #{bin.version}"
end
end
def source_digest
@digest ||= begin
@@ -187,11 +194,11 @@
success = worker.optimize(src, dst)
time = Process.times.sum - start
dst_size = success ? dst.size : nil
digest = (success ? dst : src).digest
- cache = digest.sub(/../, '\0/') + ".#{src.image_format}"
+ cache = digest.sub(/../, '\0/')
result = new(worker.id, success, time, src.size, dst_size, cache)
if success
path = result.path
unless path.exist?
path.dirname.mkpath
@@ -272,11 +279,13 @@
workers.each(&block)
end
end
def run_workers(src, workers, last_result = nil, &block)
+ required_workers = workers.select(&:required)
with_progress(workers, last_result) do |worker|
+ next if required_workers.any?{ |w| w.run_order < worker.run_order }
worker_result, result_image = run_worker(src, worker)
steps = (last_result ? last_result.steps : []) + [worker_result]
chain_result = ChainResult.new(src.image_format, steps)
chain_result.difference = difference_with(result_image)
@@ -304,23 +313,16 @@
end
def difference_with(other)
run_cache[:difference][other.digest] ||=
Cache.get(:difference, [@path.digest, other.digest].sort, nil) do
- images = [flatten_animation(@path), flatten_animation(other)]
+ images = for_compare(@path, other)
- alpha_presence = images.map do |image|
- Cmd.capture("identify -format '%A' #{image.to_s.shellescape}")
- end
- if alpha_presence.uniq.length == 2
- images.map!{ |image| underlay_noise(image) }
- end
-
nrmse = Cmd.capture(%W[
convert
- #{images[0]} -auto-orient
- #{images[1]} -auto-orient
+ #{images[0].image_format}:#{images[0]} -auto-orient
+ #{images[1].image_format}:#{images[1]} -auto-orient
-metric RMSE
-compare
-format %[distortion]
info:
].shelljoin).to_f
@@ -329,20 +331,37 @@
end
nrmse
end
end
+ def for_compare(*images)
+ images.map!{ |image| flatten_animation(image) }
+
+ alpha_presence = images.map do |image|
+ !!Cmd.capture(%W[
+ identify
+ -format %A
+ #{image.image_format}:#{image.to_s.shellescape}
+ ].shelljoin)[/true/i]
+ end
+ if alpha_presence.uniq.length != 1
+ images.map!{ |image| underlay_noise(image) }
+ end
+
+ images
+ end
+
def flatten_animation(image)
run_cache[:flatten][image.digest] ||= begin
if image.image_format == :gif
flattened = image.temp_path
Cmd.run(*%W[
convert
- #{image}
+ #{image.image_format}:#{image}
-coalesce
-append
- #{flattened}
+ #{image.image_format}:#{flattened}
]) || fail("failed flattening of #{image}")
flattened
else
image
end
@@ -352,16 +371,16 @@
def underlay_noise(image)
run_cache[:noise][image.digest] ||= begin
with_noise = image.temp_path
Cmd.run(*%W[
convert
- #{image}
+ #{image.image_format}:#{image}
+noise Random
- #{image}
+ #{image.image_format}:#{image}
-flatten
-alpha off
- #{with_noise}
+ #{image.image_format}:#{with_noise}
]) || fail("failed underlaying noise to #{image}")
with_noise
end
end
end
@@ -453,39 +472,38 @@
chains.sort_by!{ |chain| [chain.optimized_size, chain.time] }
chains.each(&block)
end
end
- def initialize(option_variants)
- option_variants = HashHelpers.deep_symbolise_keys(option_variants)
+ def initialize(config)
+ config = HashHelpers.deep_symbolise_keys(config)
image_optim = ImageOptim.new
@workers_by_format = Hash.new{ |h, k| h[k] = [] }
ImageOptim::Worker.klasses.each do |klass|
- worker_options_config = option_variants.delete(klass.bin_sym) || {}
- allow_consecutive_on = worker_options_config.delete(:allow_consecutive_on)
- worker_option_variants = case worker_options_config
- when Array
- worker_options_config
- when Hash
- worker_options_config.variants
- else
- fail "Array or Hash expected, got #{worker_options_config}"
+ worker_config = config.delete(klass.bin_sym)
+ next if worker_config == false
+ worker_config ||= {}
+
+ option_variants = worker_config.delete(:variants) || [{}]
+ option_variants = case option_variants
+ when Array then option_variants
+ when Hash then option_variants.variants
+ else fail "Array or Hash expected, got #{option_variants}"
end
- worker_option_variants.each do |options|
- options = HashHelpers.deep_symbolise_keys(options)
- options[:allow_consecutive_on] = allow_consecutive_on
+ option_variants.each do |options|
+ options = HashHelpers.deep_symbolise_keys(options).merge(worker_config)
worker = WorkerVariant.new(klass, image_optim, options)
worker.image_formats.each do |format|
@workers_by_format[format] << worker
end
end
end
log_workers_by_format
- fail "unknown variants: #{option_variants}" unless option_variants.empty?
+ fail "unknown variants: #{config}" unless config.empty?
end
def analyse(paths)
results = collect_results(paths)
@@ -551,39 +569,39 @@
def template_path
FSPath("#{File.dirname(__FILE__)}/template/#{File.basename(__FILE__)}.erb")
end
end
-def option_variants
+abort <<-HELP if ARGV.empty?
+Specify paths for analysis.
+
+Example of `.analysis_variants.yml`:
+ jhead:
+ required: true # don't skip this worker
+ jpegtran: # 3 worker variants
+ variants:
+ - jpegrescan: true
+ - progressive: true
+ - progressive: false
+ optipng: # 6 worker variants by combining options
+ variants:
+ level: [6, 7]
+ interlace: [true, false, nil]
+ gifsicle: # allow variants with different interlace to run consecutively
+ variants:
+ allow_consecutive_on: interlace
+ interlace: [true, false]
+ careful: [true, false]
+ # other workers will be used with default options
+HELP
+
+Analyser.new(begin
path = '.analysis_variants.yml'
case h = YAML.load_file(path)
when Hash then h
when false then {}
else abort "expected a hash in #{path}"
end
rescue Errno::ENOENT => e
warn e
{}
-end
-
-analyser = Analyser.new(option_variants)
-
-if ARGV.empty?
- abort <<-HELP
-Specify paths for analysis.
-
-Example of `.analysis_variants.yml`:
- jpegtran: # 3 worker variants
- - jpegrescan: true
- - progressive: true
- - progressive: false
- optipng: # 6 worker variants by combining options
- level: [6, 7]
- interlace: [true, false, nil]
- gifsicle: # allow variants with different interlace to run consecutively
- allow_consecutive_on: interlace
- interlace: [true, false]
- careful: [true, false]
- # other workers will be used with default options
- HELP
-end
-analyser.analyse(ARGV)
+end).analyse(ARGV)