RMagickで画像を魚眼風に変換してみた。

普通の画像を魚眼レンズで撮った画像みたいに変換してみた。
画像の中心点を基準にして
点xの変換前の距離をd
変換後の距離をd'
焦点距離をf
魚眼レンズの半径をrとすると、座標の変換式は以下。
d' = (r*d) / (f^2 + d^2)^(1/2)
この変換式を使って処理する。

require 'rubygems'
require "RMagick"

include Magick

def fisheyeconvert(image, filename, f, r)

black = Magick::Pixel.new(0, 0, 0)

img = ImageList.new(image)
generate_img = Image.new(img.columns, img.rows)

center_x = img.columns/2
center_y = img.rows/2

for y in 0...img.rows
  for x in 0...img.columns

    dx = x - center_x
    dy = y - center_y
    d = Math.sqrt(dx*dx + dy*dy)
    cx = (r*dx / (Math.sqrt(f*f + d*d)) + center_x).round
    cy = (r*dy / (Math.sqrt(f*f + d*d)) + center_y).round

    src = img.pixel_color(x, y)

    color = Magick::Pixel.new(src.red, src.green, src.blue)
    generate_img.pixel_color(cx, cy, color)
  end
end

generate_img.write filename
end

image = ARGV[0]
filename = ARGV[1]
f = ARGV[2]
r = ARGV[3]
fisheyeconvert(image, filename, f.to_i, r.to_i)

さて、今回の実験画像はこちら。
菊丸印のステップが踏めます。デスメタルおじさんです。

ruby ./fisheyeconvert.rb deathmetal_fisheye.jpg 230 400


なんか白い!変換した際に、対応しなかった点には何も割り振られてない様子。
なので超適当に補間処理を追加。
対応しなかった点には周りの平均値を割り当てる。

require 'rubygems'
require "RMagick"

include Magick

def fisheyeconvert(image, filename, f, r)

black = Magick::Pixel.new(0, 0, 0)

img = ImageList.new(image)
generate_img = Image.new(img.columns, img.rows)
interpolation_img = Image.new(img.columns, img.rows)

center_x = img.columns/2
center_y = img.rows/2

for y in 0...img.rows
  for x in 0...img.columns

    dx = x - center_x
    dy = y - center_y
    d = Math.sqrt(dx*dx + dy*dy)
    cx = (r*dx / (Math.sqrt(f*f + d*d)) + center_x).round
    cy = (r*dy / (Math.sqrt(f*f + d*d)) + center_y).round

    src = img.pixel_color(x, y)

    color = Magick::Pixel.new(src.red, src.green, src.blue)
    generate_img.pixel_color(cx, cy, color)
    interpolation_img.pixel_color(cx, cy, black)
  end
end

for y in 0...generate_img.rows
  for x in 0...generate_img.columns
    src = interpolation_img.pixel_color(x, y)

    if src.red == 255
      r_array = Array.new
      g_array = Array.new
      b_array = Array.new

      for py in y-2...y+2
        for px in x-2...x+2
          check_img = interpolation_img.pixel_color(px, py)
          if check_img.red == 0
            pixel = generate_img.pixel_color(px, py)
            r_array << pixel.red
            g_array << pixel.green
            b_array << pixel.blue
          end
        end 
      end

      if r_array.size != 0
        mr = r_array.inject(0){|r,i| r+=i }/r_array.size
        mg = g_array.inject(0){|r,i| r+=i }/g_array.size
        mb = b_array.inject(0){|r,i| r+=i }/b_array.size
        interpolation_color = Magick::Pixel.new(mr, mg, mb)
        generate_img.pixel_color(x, y, interpolation_color)
      end

    end
  end
end

generate_img.write filename
interpolation_img.write "test2.jpg"
end

image = ARGV[0]
filename = ARGV[1]
f = ARGV[2]
r = ARGV[3]
fisheyeconvert(image, filename, f.to_i, r.to_i)
ruby ./fisheyeconvert.rb deathmetal_fisheye2.jpg 230 400


埋まった!やったね!