#!/usr/bin/env ruby
# -*- mode: ruby; coding: utf-8 -*-

# A sample of matching SURF feature points using kd-tree
# See http://tech.groups.yahoo.com/group/OpenCV/message/62318

require 'opencv'
include OpenCV

USE_EXTENDED_DESCRIPTOR = true
THRESHOLD = 1500
DESCRIPTOR_SIZE = USE_EXTENDED_DESCRIPTOR ? 128 : 64

img1 = CvMat.load('images/lenna.jpg', CV_LOAD_IMAGE_GRAYSCALE)
img2 = CvMat.load('images/lenna-rotated.jpg', CV_LOAD_IMAGE_GRAYSCALE)

puts 'Extracting features from img1 using SURF...'
param = CvSURFParams.new(THRESHOLD, USE_EXTENDED_DESCRIPTOR)
kp1, desc1 = img1.extract_surf(param)
puts "found #{kp1.size} keypoints from img1"

puts 'Extracting features from img2 using SURF...'
kp2, desc2 = img2.extract_surf(param)
puts "found #{kp2.size} keypoints from img2"

puts 'Matching keypoints...'
desc1mat = CvMat.new(kp1.size, DESCRIPTOR_SIZE, :cv32f, 1)
desc2mat = CvMat.new(kp2.size, DESCRIPTOR_SIZE, :cv32f, 1)
desc1.each_with_index { |desc, i|
  desc.each_with_index { |d, j|
    desc1mat[i, j] = CvScalar.new(d)
  }
}
desc2.each_with_index { |desc, i|
  desc.each_with_index { |d, j|
    desc2mat[i, j] = CvScalar.new(d)
  }
}

feature_tree = CvFeatureTree.new(desc1mat)
results, distances = feature_tree.find_features(desc2mat, 1, 250)

reverse_lookup = []
reverse_lookup_dist = []
kp1.size.times { |i|
  reverse_lookup << -1
  reverse_lookup_dist << Float::MAX
}

match_count = 0
kp2.size.times { |j|
  i = results[j][0].to_i
  d = distances[j][0]
  if (d < reverse_lookup_dist[i])
    match_count += 1 if reverse_lookup_dist[i] == Float::MAX
    reverse_lookup[i] = j
    reverse_lookup_dist[i] = d
  end
}
puts "found #{match_count} putative correspondences"

points1 = []
points2 = []
kp2.size.times { |j|
  i = results[j][0].to_i
  if (j == reverse_lookup[i])
    points1 << kp1[i].pt
    points2 << kp2[j].pt
  end
}

width = img1.cols + img2.cols
height = (img1.rows > img2.rows) ? img1.rows : img2.rows
correspond = IplImage.new(width, height, :cv8u, 1);
correspond.set_roi(CvRect.new(0, 0, img1.cols, img1.rows))
img1.copy(correspond)
correspond.set_roi(CvRect.new(img1.cols, 0, img1.cols + img2.cols, img2.rows))
img2.copy(correspond)
correspond.reset_roi

points1.zip(points2) { |pt1, pt2|
  pt2.x += img1.cols
  correspond.line!(pt1, pt2, :color => CvColor::White)
}

GUI::Window.new('Object Correspond').show correspond
GUI::wait_key

