#!/usr/bin/env ruby # -*- mode: ruby; coding: utf-8 -*- # LBPH sample in ruby-opencv, equivalent to http://docs.opencv.org/trunk/_downloads/facerec_lbph.cpp # See http://docs.opencv.org/trunk/modules/contrib/doc/facerec/facerec_tutorial.html require 'opencv' include OpenCV def read_csv(filename, sepalator = ';') images = [] labels = [] open(filename, 'r') { |f| f.each { |line| path, label = line.chomp.split(sepalator) images << CvMat.load(path, CV_LOAD_IMAGE_GRAYSCALE) labels << label.to_i } } [images, labels] end # Check for valid command line arguments, print usage # if no arguments were given. if ARGV.size < 1 puts "usage: ruby #{__FILE__} " exit 1 end # Get the path to your CSV. fn_csv = ARGV.shift # Read in the data. This can fail if no valid # input filename is given. images, labels = read_csv(fn_csv); # Quit if there are not enough images for this demo. raise 'This demo needs at least 2 images to work. Please add more images to your data set!' if images.size <= 1 # Get the height from the first image. We'll need this # later in code to reshape the images to their original size: height = images[0].rows; # The following lines simply get the last images from # your dataset and remove it from the vector. This is # done, so that the training data (which we learn the # cv::FaceRecognizer on) and the test data we test # the model with, do not overlap. test_sample = images.pop test_label = labels.pop # The following lines create an LBPH model for # face recognition and train it with the images and # labels read from the given CSV file. # # The LBPHFaceRecognizer uses Extended Local Binary Patterns # (it's probably configurable with other operators at a later # point), and has the following default values # # radius = 1 # neighbors = 8 # grid_x = 8 # grid_y = 8 # # So if you want a LBPH FaceRecognizer using a radius of # 2 and 16 neighbors, call the factory method with: # # LBPH.new(2, 16); # # And if you want a threshold (e.g. 123.0) call it with its default values: # # LBPH.new(1,8,8,8,123.0) # model = LBPH.new model.train(images, labels) # The following line predicts the label of a given test image: predicted_label, predicted_confidence = model.predict(test_sample) # To get the confidence of a prediction call the model with: # # predicted_label = -1; # confidence = 0.0; # model.predict(test_sample, predicted_label, confidence) # puts "Predicted class: #{predicted_label} / Actual class: #{test_label}" # Sometimes you'll need to get/set internal model data, # which isn't exposed by the public FaceRecognizer. # Since each FaceRecognizer is derived from a Algorithm, # you can query the data. # # First we'll use it to set the threshold of the FaceRecognizer # to 0.0 without retraining the model. This can be useful if # you are evaluating the model: model.set_double('threshold', 0.0); # Now the threshold of this model is set to 0.0. A prediction # now returns -1, as it's impossible to have a distance below it predicted_label, predicted_confidence = model.predict(test_sample) puts "Predicted class = #{predicted_label}" # Show some informations about the model, as there's no cool # Model data to display as in Eigenfaces/Fisherfaces. # Due to efficiency reasons the LBP images are not stored # within the model: puts 'Model Information:' model_info = "\tLBPH(radius=#{model.get_int('radius')}, neighbors=#{model.get_int('neighbors')}, grid_x=#{model.get_int('grid_x')}, grid_y=#{model.get_int('grid_y')}, threshold=#{model.get_double('threshold')})" puts model_info # We could get the histograms for example: histgrams = model.get_matvector('histograms'); # But should I really visualize it? Probably the length is interesting: puts "Size of the histograms: #{histgrams[0].dims.reduce(&:*)}"