Skip to content

Instantly share code, notes, and snippets.

@tuzz tuzz/gaussian
Last active May 3, 2020

Embed
What would you like to do?
Generate the weights/offsets for an efficient two-pass Gaussian blur filter of different sizes.
#!/usr/bin/env ruby
# Generate the weights/ offsets for an efficient two-pass Gaussian blur filter of different sizes.
# Based on: http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
#
# Usage: ./gaussian [epsilon]
require "bigdecimal/util"
EPSILON = Float(ARGV[0] || 0.05)
@cache = {}
def row(n)
@cache[n] ||= (n == 0 ? [1] : [1, *row(n - 1).each_cons(2).map(&:sum), 1])
end
last_size = 0
1_000_000.times do |row_number|
next if row_number.odd?
row = row(row_number)
sum = row.sum
peak = row[row.size / 2].to_d / sum
# Remove weights that make negligible difference to the output image.
row = row.reject { |n| n.to_d / sum < peak * EPSILON }
sum = row.sum
# If we've already seen a row of this size, skip those that are more truncated.
next if row.size == last_size
last_size = row.size
midpoint = row.size / 2
next if midpoint.odd?
weights_d = row.map { |n| n.to_d / sum }[midpoint..]
offsets_d = (0..midpoint).map(&:to_d)
break if weights_d[0] == 0 # Precision limit reached
weights_l = weights_d[1..].each_slice(2).map(&:sum)
offsets_l = offsets_d[1..].each_slice(2).map do |t1, t2|
numerator = offsets_d[t1] * weights_d[t1] + offsets_d[t2] * weights_d[t2]
denominator = weights_d[t1] + weights_d[t2]
numerator / denominator
end
weights_l.unshift(weights_d.first)
offsets_l.unshift(0.0)
puts "offsets_l: #{offsets_l.map(&:to_f).inspect}"
puts "weights_l: #{weights_l.map(&:to_f).inspect}"
puts "texture reads per pixel: #{weights_d.size}"
puts "effective kernel size: #{row.size}"
puts "pascal's triangle row: #{row_number}"
puts
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.