lib/tools/lens_disto.rb
class Tracksperanto::Tool::LensDisto < Tracksperanto::Tool::Base
include Tracksperanto::UVCoordinates
parameter :k, :cast => :float, :desc => "Quartic distortion coefficient", :default => 0
parameter :kcube, :cast => :float, :desc => "Cubic distortion coefficient", :default => 0
parameter :remove, :cast => :bool, :desc => "Remove distortion instead of adding it"
STEPS = 256
def self.action_description
"Apply or remove lens distortion with the Syntheyes algorithm"
end
def start_export(w, h)
@width, @height = w, h
@aspect = @width.to_f / @height
generate_lut
super
end
def export_point(frame, float_x, float_y, float_residual)
x, y = remove ? undisto(float_x, float_y) : disto(float_x, float_y)
super(frame, x, y, float_residual)
end
private
class Vector2 < Struct.new(:x, :y)
end
class RF < Struct.new(:r, :f)
def inspect
'(%0.3f %0.3f)' % [r, f]
end
def m
r * f
end
end
# We apply disto using a lookup table of y = f(r)
def generate_lut
# Generate the lookup table
@lut = [RF.new(0.0, 1.0)]
max_r = @aspect + 1
increment = max_r / STEPS
r = 0
STEPS.times do | mult |
r += increment
@lut.push(RF.new(r, distort_radius(r)))
end
end
def with_uv(x, y)
vec = Vector2.new(convert_to_uv(x, @width), convert_to_uv(y, @height))
yield(vec)
[convert_from_uv(vec.x, @width), convert_from_uv(vec.y, @height)]
end
# Radius is equal to aspect at the rightmost extremity
def distort_radius(r)
1 + (r*r*(k + kcube * r))
end
def disto(x, y)
with_uv(x, y) do | pt |
# Find the good tuples to interpolate on
f = disto_interpolated(get_radius(pt))
pt.x = pt.x * f
pt.y = pt.y * f
end
end
def get_radius(pt)
# Get the radius of the point
x = pt.x * @aspect
r = Math.sqrt(x.abs**2 + pt.y.abs**2)
end
def undisto(x, y)
with_uv(x, y) do | pt |
# Find the good tuples to interpolate on
f = undisto_interpolated(get_radius(pt))
pt.x = pt.x / f
pt.y = pt.y / f
end
end
def disto_interpolated(r)
left , right = nil, nil
@lut.each_with_index do | rf, i |
if rf.r > r
right = rf
left = @lut[i -1]
return lerp(r, left.r, right.r, left.f, right.f)
end
end
end
def undisto_interpolated(r)
left , right = nil, nil
@lut.each_with_index do | xf, i |
if xf.m > r
right = xf
left = @lut[i -1]
return lerp(r, left.m, right.m, left.f, right.f)
end
end
end
def lerp(x, left_x, right_x, left_y, right_y)
dx = right_x - left_x
dy = right_y - left_y
t = (x - left_x) / dx
left_y + (dy * t)
end
end