bonanza-ruby-opencv 0.0.13.20140330211753
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +28 -0
- data/.yardopts +3 -0
- data/DEVELOPERS_NOTE.md +137 -0
- data/Gemfile +9 -0
- data/History.txt +5 -0
- data/License.txt +30 -0
- data/Manifest.txt +239 -0
- data/README.md +98 -0
- data/Rakefile +99 -0
- data/config.yml +7 -0
- data/examples/alpha_blend.rb +21 -0
- data/examples/contours/bitmap-contours-with-labels.png +0 -0
- data/examples/contours/bitmap-contours.png +0 -0
- data/examples/contours/bounding-box-detect-canny.rb +62 -0
- data/examples/contours/contour_retrieval_modes.rb +139 -0
- data/examples/contours/rotated-boxes.jpg +0 -0
- data/examples/convexhull.rb +47 -0
- data/examples/face_detect.rb +20 -0
- data/examples/facerec/create_csv.rb +43 -0
- data/examples/facerec/facerec_eigenfaces.rb +132 -0
- data/examples/facerec/facerec_fisherfaces.rb +131 -0
- data/examples/facerec/facerec_lbph.rb +116 -0
- data/examples/facerec/readme.md +111 -0
- data/examples/find_obj.rb +169 -0
- data/examples/houghcircle.rb +22 -0
- data/examples/images/box.png +0 -0
- data/examples/images/box_in_scene.png +0 -0
- data/examples/images/inpaint.png +0 -0
- data/examples/images/lena-256x256.jpg +0 -0
- data/examples/images/lena-eyes.jpg +0 -0
- data/examples/images/lenna-rotated.jpg +0 -0
- data/examples/images/lenna.jpg +0 -0
- data/examples/images/stuff.jpg +0 -0
- data/examples/images/tiffany.jpg +0 -0
- data/examples/inpaint.rb +57 -0
- data/examples/match_kdtree.rb +88 -0
- data/examples/match_template.rb +26 -0
- data/examples/paint.rb +70 -0
- data/examples/snake.rb +43 -0
- data/ext/opencv/algorithm.cpp +291 -0
- data/ext/opencv/algorithm.h +38 -0
- data/ext/opencv/curve.cpp +127 -0
- data/ext/opencv/curve.h +34 -0
- data/ext/opencv/cvavgcomp.cpp +64 -0
- data/ext/opencv/cvavgcomp.h +39 -0
- data/ext/opencv/cvbox2d.cpp +195 -0
- data/ext/opencv/cvbox2d.h +61 -0
- data/ext/opencv/cvcapture.cpp +607 -0
- data/ext/opencv/cvcapture.h +72 -0
- data/ext/opencv/cvchain.cpp +233 -0
- data/ext/opencv/cvchain.h +46 -0
- data/ext/opencv/cvcircle32f.cpp +126 -0
- data/ext/opencv/cvcircle32f.h +52 -0
- data/ext/opencv/cvconnectedcomp.cpp +156 -0
- data/ext/opencv/cvconnectedcomp.h +49 -0
- data/ext/opencv/cvcontour.cpp +332 -0
- data/ext/opencv/cvcontour.h +48 -0
- data/ext/opencv/cvcontourtree.cpp +96 -0
- data/ext/opencv/cvcontourtree.h +41 -0
- data/ext/opencv/cvconvexitydefect.cpp +92 -0
- data/ext/opencv/cvconvexitydefect.h +42 -0
- data/ext/opencv/cverror.cpp +115 -0
- data/ext/opencv/cverror.h +28 -0
- data/ext/opencv/cvfeaturetree.cpp +123 -0
- data/ext/opencv/cvfeaturetree.h +55 -0
- data/ext/opencv/cvfont.cpp +228 -0
- data/ext/opencv/cvfont.h +64 -0
- data/ext/opencv/cvhaarclassifiercascade.cpp +148 -0
- data/ext/opencv/cvhaarclassifiercascade.h +39 -0
- data/ext/opencv/cvhistogram.cpp +715 -0
- data/ext/opencv/cvhistogram.h +73 -0
- data/ext/opencv/cvhumoments.cpp +178 -0
- data/ext/opencv/cvhumoments.h +51 -0
- data/ext/opencv/cvline.cpp +159 -0
- data/ext/opencv/cvline.h +54 -0
- data/ext/opencv/cvmat.cpp +6829 -0
- data/ext/opencv/cvmat.h +323 -0
- data/ext/opencv/cvmemstorage.cpp +73 -0
- data/ext/opencv/cvmemstorage.h +53 -0
- data/ext/opencv/cvmoments.cpp +293 -0
- data/ext/opencv/cvmoments.h +75 -0
- data/ext/opencv/cvpoint.cpp +265 -0
- data/ext/opencv/cvpoint.h +67 -0
- data/ext/opencv/cvpoint2d32f.cpp +216 -0
- data/ext/opencv/cvpoint2d32f.h +63 -0
- data/ext/opencv/cvpoint3d32f.cpp +252 -0
- data/ext/opencv/cvpoint3d32f.h +66 -0
- data/ext/opencv/cvrect.cpp +441 -0
- data/ext/opencv/cvrect.h +88 -0
- data/ext/opencv/cvscalar.cpp +301 -0
- data/ext/opencv/cvscalar.h +76 -0
- data/ext/opencv/cvseq.cpp +605 -0
- data/ext/opencv/cvseq.h +74 -0
- data/ext/opencv/cvsize.cpp +227 -0
- data/ext/opencv/cvsize.h +65 -0
- data/ext/opencv/cvsize2d32f.cpp +215 -0
- data/ext/opencv/cvsize2d32f.h +64 -0
- data/ext/opencv/cvslice.cpp +126 -0
- data/ext/opencv/cvslice.h +61 -0
- data/ext/opencv/cvsurfparams.cpp +208 -0
- data/ext/opencv/cvsurfparams.h +58 -0
- data/ext/opencv/cvsurfpoint.cpp +279 -0
- data/ext/opencv/cvsurfpoint.h +54 -0
- data/ext/opencv/cvtermcriteria.cpp +198 -0
- data/ext/opencv/cvtermcriteria.h +71 -0
- data/ext/opencv/cvtwopoints.cpp +122 -0
- data/ext/opencv/cvtwopoints.h +51 -0
- data/ext/opencv/cvutils.cpp +221 -0
- data/ext/opencv/cvutils.h +31 -0
- data/ext/opencv/cvvideowriter.cpp +142 -0
- data/ext/opencv/cvvideowriter.h +43 -0
- data/ext/opencv/eigenfaces.cpp +75 -0
- data/ext/opencv/eigenfaces.h +30 -0
- data/ext/opencv/extconf.rb +82 -0
- data/ext/opencv/facerecognizer.cpp +181 -0
- data/ext/opencv/facerecognizer.h +46 -0
- data/ext/opencv/fisherfaces.cpp +75 -0
- data/ext/opencv/fisherfaces.h +30 -0
- data/ext/opencv/gui.cpp +71 -0
- data/ext/opencv/gui.h +30 -0
- data/ext/opencv/iplconvkernel.cpp +198 -0
- data/ext/opencv/iplconvkernel.h +71 -0
- data/ext/opencv/iplimage.cpp +666 -0
- data/ext/opencv/iplimage.h +75 -0
- data/ext/opencv/lbph.cpp +78 -0
- data/ext/opencv/lbph.h +30 -0
- data/ext/opencv/mouseevent.cpp +186 -0
- data/ext/opencv/mouseevent.h +56 -0
- data/ext/opencv/opencv.cpp +833 -0
- data/ext/opencv/opencv.h +405 -0
- data/ext/opencv/pointset.cpp +280 -0
- data/ext/opencv/pointset.h +68 -0
- data/ext/opencv/trackbar.cpp +127 -0
- data/ext/opencv/trackbar.h +69 -0
- data/ext/opencv/window.cpp +377 -0
- data/ext/opencv/window.h +66 -0
- data/images/CvMat_sobel.png +0 -0
- data/images/CvMat_sub_rect.png +0 -0
- data/images/CvSeq_relationmap.png +0 -0
- data/lib/opencv.rb +12 -0
- data/lib/opencv/psyched_yaml.rb +22 -0
- data/lib/opencv/version.rb +4 -0
- data/test/eigenfaces_save.xml +7524 -0
- data/test/fisherfaces_save.xml +7530 -0
- data/test/helper.rb +166 -0
- data/test/lbph_save.xml +4304 -0
- data/test/runner.rb +30 -0
- data/test/samples/airplane.jpg +0 -0
- data/test/samples/baboon.jpg +0 -0
- data/test/samples/baboon200.jpg +0 -0
- data/test/samples/baboon200_rotated.jpg +0 -0
- data/test/samples/blank0.jpg +0 -0
- data/test/samples/blank1.jpg +0 -0
- data/test/samples/blank2.jpg +0 -0
- data/test/samples/blank3.jpg +0 -0
- data/test/samples/blank4.jpg +0 -0
- data/test/samples/blank5.jpg +0 -0
- data/test/samples/blank6.jpg +0 -0
- data/test/samples/blank7.jpg +0 -0
- data/test/samples/blank8.jpg +0 -0
- data/test/samples/blank9.jpg +0 -0
- data/test/samples/cat.jpg +0 -0
- data/test/samples/chessboard.jpg +0 -0
- data/test/samples/contours.jpg +0 -0
- data/test/samples/fruits.jpg +0 -0
- data/test/samples/haarcascade_frontalface_alt.xml.gz +0 -0
- data/test/samples/inpaint-mask.bmp +0 -0
- data/test/samples/lena-256x256.jpg +0 -0
- data/test/samples/lena-32x32.jpg +0 -0
- data/test/samples/lena-eyes.jpg +0 -0
- data/test/samples/lena-inpaint.jpg +0 -0
- data/test/samples/lena.jpg +0 -0
- data/test/samples/lines.jpg +0 -0
- data/test/samples/messy0.jpg +0 -0
- data/test/samples/messy1.jpg +0 -0
- data/test/samples/movie_sample.avi +0 -0
- data/test/samples/one_way_train_0000.jpg +0 -0
- data/test/samples/one_way_train_0001.jpg +0 -0
- data/test/samples/partially_blank0.jpg +0 -0
- data/test/samples/partially_blank1.jpg +0 -0
- data/test/samples/smooth0.jpg +0 -0
- data/test/samples/smooth1.jpg +0 -0
- data/test/samples/smooth2.jpg +0 -0
- data/test/samples/smooth3.jpg +0 -0
- data/test/samples/smooth4.jpg +0 -0
- data/test/samples/smooth5.jpg +0 -0
- data/test/samples/smooth6.jpg +0 -0
- data/test/samples/str-cv-rotated.jpg +0 -0
- data/test/samples/str-cv.jpg +0 -0
- data/test/samples/str-ov.jpg +0 -0
- data/test/samples/stuff.jpg +0 -0
- data/test/test_curve.rb +43 -0
- data/test/test_cvavgcomp.rb +24 -0
- data/test/test_cvbox2d.rb +76 -0
- data/test/test_cvcapture.rb +183 -0
- data/test/test_cvchain.rb +108 -0
- data/test/test_cvcircle32f.rb +41 -0
- data/test/test_cvconnectedcomp.rb +61 -0
- data/test/test_cvcontour.rb +150 -0
- data/test/test_cvcontourtree.rb +43 -0
- data/test/test_cverror.rb +50 -0
- data/test/test_cvfeaturetree.rb +65 -0
- data/test/test_cvfont.rb +58 -0
- data/test/test_cvhaarclassifiercascade.rb +63 -0
- data/test/test_cvhistogram.rb +271 -0
- data/test/test_cvhumoments.rb +83 -0
- data/test/test_cvline.rb +50 -0
- data/test/test_cvmat.rb +3003 -0
- data/test/test_cvmat_drawing.rb +349 -0
- data/test/test_cvmat_dxt.rb +150 -0
- data/test/test_cvmat_imageprocessing.rb +2085 -0
- data/test/test_cvmoments.rb +180 -0
- data/test/test_cvpoint.rb +75 -0
- data/test/test_cvpoint2d32f.rb +75 -0
- data/test/test_cvpoint3d32f.rb +93 -0
- data/test/test_cvrect.rb +144 -0
- data/test/test_cvscalar.rb +113 -0
- data/test/test_cvseq.rb +295 -0
- data/test/test_cvsize.rb +75 -0
- data/test/test_cvsize2d32f.rb +75 -0
- data/test/test_cvslice.rb +31 -0
- data/test/test_cvsurfparams.rb +57 -0
- data/test/test_cvsurfpoint.rb +66 -0
- data/test/test_cvtermcriteria.rb +56 -0
- data/test/test_cvtwopoints.rb +40 -0
- data/test/test_cvvideowriter.rb +58 -0
- data/test/test_eigenfaces.rb +93 -0
- data/test/test_fisherfaces.rb +93 -0
- data/test/test_iplconvkernel.rb +54 -0
- data/test/test_iplimage.rb +232 -0
- data/test/test_lbph.rb +152 -0
- data/test/test_mouseevent.rb +17 -0
- data/test/test_opencv.rb +360 -0
- data/test/test_pointset.rb +128 -0
- data/test/test_preliminary.rb +130 -0
- data/test/test_trackbar.rb +47 -0
- data/test/test_window.rb +115 -0
- data/yard_extension.rb +5 -0
- metadata +399 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# face_detect.rb
|
3
|
+
require "rubygems"
|
4
|
+
require "opencv"
|
5
|
+
|
6
|
+
include OpenCV
|
7
|
+
|
8
|
+
window = GUI::Window.new("face detect")
|
9
|
+
capture = CvCapture.open
|
10
|
+
detector = CvHaarClassifierCascade::load("./data/haarcascades/haarcascade_frontalface_alt.xml")
|
11
|
+
|
12
|
+
loop {
|
13
|
+
image = capture.query
|
14
|
+
detector.detect_objects(image).each { |rect|
|
15
|
+
image.rectangle! rect.top_left, rect.bottom_right, color: CvColor::Red
|
16
|
+
}
|
17
|
+
window.show image
|
18
|
+
break if GUI::wait_key(100)
|
19
|
+
}
|
20
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- mode: ruby; coding: utf-8 -*-
|
3
|
+
|
4
|
+
# This is a tiny script to help you creating a CSV file from a face
|
5
|
+
# database with a similar hierarchie:
|
6
|
+
#
|
7
|
+
# philipp@mango:~/facerec/data/at$ tree
|
8
|
+
# .
|
9
|
+
# |-- README
|
10
|
+
# |-- s1
|
11
|
+
# | |-- 1.pgm
|
12
|
+
# | |-- ...
|
13
|
+
# | |-- 10.pgm
|
14
|
+
# |-- s2
|
15
|
+
# | |-- 1.pgm
|
16
|
+
# | |-- ...
|
17
|
+
# | |-- 10.pgm
|
18
|
+
# ...
|
19
|
+
# |-- s40
|
20
|
+
# | |-- 1.pgm
|
21
|
+
# | |-- ...
|
22
|
+
# | |-- 10.pgm
|
23
|
+
#
|
24
|
+
# See http://docs.opencv.org/trunk/modules/contrib/doc/facerec/facerec_tutorial.html
|
25
|
+
#
|
26
|
+
if ARGV.size != 1
|
27
|
+
puts "usage: ruby #{__FILE__} <base_path>"
|
28
|
+
exit
|
29
|
+
end
|
30
|
+
|
31
|
+
BASE_PATH = ARGV[0]
|
32
|
+
SEPARATOR = ';'
|
33
|
+
|
34
|
+
label = 0
|
35
|
+
Dir.glob("#{BASE_PATH}/*").each { |dir|
|
36
|
+
if FileTest::directory? dir
|
37
|
+
Dir.glob("#{dir}/*") { |filename|
|
38
|
+
puts "#{filename}#{SEPARATOR}#{label}"
|
39
|
+
}
|
40
|
+
label += 1
|
41
|
+
end
|
42
|
+
}
|
43
|
+
|
@@ -0,0 +1,132 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- mode: ruby; coding: utf-8 -*-
|
3
|
+
|
4
|
+
# Eigenfaces sample in ruby-opencv, equivalent to http://docs.opencv.org/trunk/_downloads/facerec_eigenfaces.cpp
|
5
|
+
# See http://docs.opencv.org/trunk/modules/contrib/doc/facerec/facerec_tutorial.html
|
6
|
+
require 'opencv'
|
7
|
+
include OpenCV
|
8
|
+
|
9
|
+
def norm_0_255(src)
|
10
|
+
dst = nil
|
11
|
+
case src.channel
|
12
|
+
when 1
|
13
|
+
dst = src.normalize(0, 255, CV_NORM_MINMAX, CV_8UC1)
|
14
|
+
when 2
|
15
|
+
dst = src.normalize(0, 255, CV_NORM_MINMAX, CV_8UC3)
|
16
|
+
else
|
17
|
+
dst = src.copy
|
18
|
+
end
|
19
|
+
|
20
|
+
dst
|
21
|
+
end
|
22
|
+
|
23
|
+
def read_csv(filename, sepalator = ';')
|
24
|
+
images = []
|
25
|
+
labels = []
|
26
|
+
open(filename, 'r') { |f|
|
27
|
+
f.each { |line|
|
28
|
+
path, label = line.chomp.split(sepalator)
|
29
|
+
images << CvMat.load(path, CV_LOAD_IMAGE_GRAYSCALE)
|
30
|
+
labels << label.to_i
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
[images, labels]
|
35
|
+
end
|
36
|
+
|
37
|
+
if ARGV.size < 1
|
38
|
+
puts "usage: ruby #{__FILE__} <csv.ext> <output_folder>"
|
39
|
+
exit 1
|
40
|
+
end
|
41
|
+
fn_csv = ARGV.shift
|
42
|
+
output_folder = ARGV.shift
|
43
|
+
|
44
|
+
images, labels = read_csv(fn_csv);
|
45
|
+
|
46
|
+
height = images[0].rows;
|
47
|
+
|
48
|
+
# The following lines simply get the last images from your dataset and remove it
|
49
|
+
# from the vector. This is done, so that the training data (which we learn the
|
50
|
+
# cv::FaceRecognizer on) and the test data we test the model with, do not overlap.
|
51
|
+
test_sample = images.pop
|
52
|
+
test_label = labels.pop
|
53
|
+
|
54
|
+
# The following lines create an Eigenfaces model for
|
55
|
+
# face recognition and train it with the images and
|
56
|
+
# labels read from the given CSV file.
|
57
|
+
# This here is a full PCA, if you just want to keep
|
58
|
+
# 10 principal components (read Eigenfaces), then call
|
59
|
+
# the factory method like this:
|
60
|
+
#
|
61
|
+
# EigenFaces.new(10)
|
62
|
+
#
|
63
|
+
# If you want to create a FaceRecognizer with a
|
64
|
+
# confidence threshold (e.g. 123.0), call it with:
|
65
|
+
#
|
66
|
+
# EigenFaces.new(10, 123.0)
|
67
|
+
#
|
68
|
+
# If you want to use _all_ Eigenfaces and have a threshold,
|
69
|
+
# then call the method like this:
|
70
|
+
#
|
71
|
+
# EigenFaces.new(0, 123.0)
|
72
|
+
#
|
73
|
+
model = EigenFaces.new
|
74
|
+
model.train(images, labels)
|
75
|
+
|
76
|
+
# The following line predicts the label of a given test image:
|
77
|
+
predicted_label, predicted_confidence = model.predict(test_sample)
|
78
|
+
|
79
|
+
puts "Predicted class: #{predicted_label} / Actual class: #{test_label}"
|
80
|
+
|
81
|
+
eigenvalues = model.get_mat('eigenvalues')
|
82
|
+
w = model.get_mat('eigenvectors');
|
83
|
+
mean = model.get_mat('mean')
|
84
|
+
|
85
|
+
if output_folder
|
86
|
+
norm_0_255(mean.reshape(1, images[0].rows)).save("#{output_folder}/mean.png")
|
87
|
+
else
|
88
|
+
w1 = GUI::Window.new('Predicted')
|
89
|
+
w2 = GUI::Window.new('Actual')
|
90
|
+
w3 = GUI::Window.new('mean')
|
91
|
+
|
92
|
+
w1.show images[predicted_label]
|
93
|
+
w2.show images[test_label]
|
94
|
+
w3.show norm_0_255(mean.reshape(1, images[0].rows))
|
95
|
+
end
|
96
|
+
|
97
|
+
# Display or save the Eigenfaces:
|
98
|
+
[w.cols, 10].min.times { |i|
|
99
|
+
puts "Eigenvalue ##{i} = #{eigenvalues[i][0]}"
|
100
|
+
ev = w.get_cols(i).clone()
|
101
|
+
grayscale = norm_0_255(ev.reshape(1, height))
|
102
|
+
|
103
|
+
# Show the image & apply a Jet colormap for better sensing.
|
104
|
+
cgrayscale = grayscale.apply_color_map(COLORMAP_JET)
|
105
|
+
if output_folder
|
106
|
+
norm_0_255(cgrayscale).save("#{output_folder}/eigenface_#{i}.png")
|
107
|
+
else
|
108
|
+
w4 = GUI::Window.new("eigenface_#{i}")
|
109
|
+
w4.show norm_0_255(cgrayscale)
|
110
|
+
end
|
111
|
+
}
|
112
|
+
|
113
|
+
[w.cols, 10].min.step([w.cols, 300].min, 15) { |num_components|
|
114
|
+
# slice the eigenvectors from the model
|
115
|
+
evs = w.get_cols(0..num_components)
|
116
|
+
projection = images[0].reshape(1, 1).subspace_project(evs, mean)
|
117
|
+
reconstruction = projection.subspace_reconstruct(evs, mean)
|
118
|
+
|
119
|
+
# Normalize the result:
|
120
|
+
reconstruction = norm_0_255(reconstruction.reshape(1, images[0].rows))
|
121
|
+
|
122
|
+
# Display or save:
|
123
|
+
if output_folder
|
124
|
+
norm_0_255(reconstruction).save("#{output_folder}/eigenface_reconstruction_#{num_components}.png")
|
125
|
+
else
|
126
|
+
w5 = GUI::Window.new("eigenface_reconstruction_#{num_components}")
|
127
|
+
w5.show norm_0_255(reconstruction)
|
128
|
+
end
|
129
|
+
}
|
130
|
+
|
131
|
+
GUI::wait_key unless output_folder
|
132
|
+
|
@@ -0,0 +1,131 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- mode: ruby; coding: utf-8 -*-
|
3
|
+
|
4
|
+
# Fisherfaces sample in ruby-opencv, equivalent to http://docs.opencv.org/trunk/_downloads/facerec_fisherfaces.cpp
|
5
|
+
# See http://docs.opencv.org/trunk/modules/contrib/doc/facerec/facerec_tutorial.html
|
6
|
+
require 'opencv'
|
7
|
+
include OpenCV
|
8
|
+
|
9
|
+
def norm_0_255(src)
|
10
|
+
dst = nil
|
11
|
+
case src.channel
|
12
|
+
when 1
|
13
|
+
dst = src.normalize(0, 255, CV_NORM_MINMAX, CV_8UC1)
|
14
|
+
when 2
|
15
|
+
dst = src.normalize(0, 255, CV_NORM_MINMAX, CV_8UC3)
|
16
|
+
else
|
17
|
+
dst = src.copy
|
18
|
+
end
|
19
|
+
|
20
|
+
dst
|
21
|
+
end
|
22
|
+
|
23
|
+
def read_csv(filename, sepalator = ';')
|
24
|
+
images = []
|
25
|
+
labels = []
|
26
|
+
open(filename, 'r') { |f|
|
27
|
+
f.each { |line|
|
28
|
+
path, label = line.chomp.split(sepalator)
|
29
|
+
images << CvMat.load(path, CV_LOAD_IMAGE_GRAYSCALE)
|
30
|
+
labels << label.to_i
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
[images, labels]
|
35
|
+
end
|
36
|
+
|
37
|
+
if ARGV.size < 1
|
38
|
+
puts "usage: ruby #{__FILE__} <csv.ext> <output_folder>"
|
39
|
+
exit 1
|
40
|
+
end
|
41
|
+
fn_csv = ARGV.shift
|
42
|
+
output_folder = ARGV.shift
|
43
|
+
|
44
|
+
images, labels = read_csv(fn_csv);
|
45
|
+
|
46
|
+
height = images[0].rows;
|
47
|
+
|
48
|
+
# The following lines simply get the last images from your dataset and remove it
|
49
|
+
# from the vector. This is done, so that the training data (which we learn the
|
50
|
+
# cv::FaceRecognizer on) and the test data we test the model with, do not overlap.
|
51
|
+
test_sample = images.pop
|
52
|
+
test_label = labels.pop
|
53
|
+
|
54
|
+
# The following lines create an Fisherfaces model for
|
55
|
+
# face recognition and train it with the images and
|
56
|
+
# labels read from the given CSV file.
|
57
|
+
# If you just want to keep 10 Fisherfaces, then call
|
58
|
+
# the factory method like this:
|
59
|
+
#
|
60
|
+
# FisherFaces.new(10)
|
61
|
+
#
|
62
|
+
# However it is not useful to discard Fisherfaces! Please
|
63
|
+
# always try to use _all_ available Fisherfaces for
|
64
|
+
# classification.
|
65
|
+
#
|
66
|
+
# If you want to create a FaceRecognizer with a
|
67
|
+
# confidence threshold (e.g. 123.0) and use _all_
|
68
|
+
# Fisherfaces, then call it with:
|
69
|
+
#
|
70
|
+
# FisherFaces.new(0, 123.0);
|
71
|
+
#
|
72
|
+
model = FisherFaces.new
|
73
|
+
model.train(images, labels)
|
74
|
+
|
75
|
+
# The following line predicts the label of a given test image:
|
76
|
+
predicted_label, predicted_confidence = model.predict(test_sample)
|
77
|
+
|
78
|
+
puts "Predicted class: #{predicted_label} / Actual class: #{test_label}"
|
79
|
+
|
80
|
+
eigenvalues = model.get_mat('eigenvalues')
|
81
|
+
w = model.get_mat('eigenvectors');
|
82
|
+
mean = model.get_mat('mean')
|
83
|
+
|
84
|
+
if output_folder
|
85
|
+
norm_0_255(mean.reshape(1, images[0].rows)).save("#{output_folder}/mean.png")
|
86
|
+
else
|
87
|
+
w1 = GUI::Window.new('Predicted')
|
88
|
+
w2 = GUI::Window.new('Actual')
|
89
|
+
w3 = GUI::Window.new('mean')
|
90
|
+
|
91
|
+
w1.show images[predicted_label]
|
92
|
+
w2.show images[test_label]
|
93
|
+
w3.show norm_0_255(mean.reshape(1, images[0].rows))
|
94
|
+
end
|
95
|
+
|
96
|
+
# Display or save the first, at most 16 Fisherfaces
|
97
|
+
[w.cols, 16].min.times { |i|
|
98
|
+
puts "Eigenvalue ##{i} = #{eigenvalues[i][0]}"
|
99
|
+
ev = w.get_cols(i).clone()
|
100
|
+
grayscale = norm_0_255(ev.reshape(1, height))
|
101
|
+
|
102
|
+
# Show the image & apply a Bone colormap for better sensing.
|
103
|
+
cgrayscale = grayscale.apply_color_map(COLORMAP_BONE)
|
104
|
+
if output_folder
|
105
|
+
norm_0_255(cgrayscale).save("#{output_folder}/fisherface_#{i}.png")
|
106
|
+
else
|
107
|
+
w4 = GUI::Window.new("fisherface_#{i}")
|
108
|
+
w4.show norm_0_255(cgrayscale)
|
109
|
+
end
|
110
|
+
}
|
111
|
+
|
112
|
+
[w.cols, 16].min.times { |num_component|
|
113
|
+
# Slice the Fisherface from the model
|
114
|
+
ev = w.get_cols(num_component)
|
115
|
+
projection = images[0].reshape(1, 1).subspace_project(ev, mean)
|
116
|
+
reconstruction = projection.subspace_reconstruct(ev, mean)
|
117
|
+
|
118
|
+
# Normalize the result:
|
119
|
+
reconstruction = norm_0_255(reconstruction.reshape(1, images[0].rows))
|
120
|
+
|
121
|
+
# Display or save:
|
122
|
+
if output_folder
|
123
|
+
norm_0_255(reconstruction).save("#{output_folder}/fisherface_reconstruction_#{num_component}.png")
|
124
|
+
else
|
125
|
+
w5 = GUI::Window.new("fisherface_reconstruction_#{num_component}")
|
126
|
+
w5.show norm_0_255(reconstruction)
|
127
|
+
end
|
128
|
+
}
|
129
|
+
|
130
|
+
GUI::wait_key unless output_folder
|
131
|
+
|
@@ -0,0 +1,116 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- mode: ruby; coding: utf-8 -*-
|
3
|
+
|
4
|
+
# LBPH sample in ruby-opencv, equivalent to http://docs.opencv.org/trunk/_downloads/facerec_lbph.cpp
|
5
|
+
# See http://docs.opencv.org/trunk/modules/contrib/doc/facerec/facerec_tutorial.html
|
6
|
+
require 'opencv'
|
7
|
+
include OpenCV
|
8
|
+
|
9
|
+
def read_csv(filename, sepalator = ';')
|
10
|
+
images = []
|
11
|
+
labels = []
|
12
|
+
open(filename, 'r') { |f|
|
13
|
+
f.each { |line|
|
14
|
+
path, label = line.chomp.split(sepalator)
|
15
|
+
images << CvMat.load(path, CV_LOAD_IMAGE_GRAYSCALE)
|
16
|
+
labels << label.to_i
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
[images, labels]
|
21
|
+
end
|
22
|
+
|
23
|
+
# Check for valid command line arguments, print usage
|
24
|
+
# if no arguments were given.
|
25
|
+
if ARGV.size < 1
|
26
|
+
puts "usage: ruby #{__FILE__} <csv.ext>"
|
27
|
+
exit 1
|
28
|
+
end
|
29
|
+
|
30
|
+
# Get the path to your CSV.
|
31
|
+
fn_csv = ARGV.shift
|
32
|
+
|
33
|
+
# Read in the data. This can fail if no valid
|
34
|
+
# input filename is given.
|
35
|
+
images, labels = read_csv(fn_csv);
|
36
|
+
|
37
|
+
# Quit if there are not enough images for this demo.
|
38
|
+
raise 'This demo needs at least 2 images to work. Please add more images to your data set!' if images.size <= 1
|
39
|
+
|
40
|
+
# Get the height from the first image. We'll need this
|
41
|
+
# later in code to reshape the images to their original size:
|
42
|
+
height = images[0].rows;
|
43
|
+
|
44
|
+
# The following lines simply get the last images from
|
45
|
+
# your dataset and remove it from the vector. This is
|
46
|
+
# done, so that the training data (which we learn the
|
47
|
+
# cv::FaceRecognizer on) and the test data we test
|
48
|
+
# the model with, do not overlap.
|
49
|
+
test_sample = images.pop
|
50
|
+
test_label = labels.pop
|
51
|
+
|
52
|
+
# The following lines create an LBPH model for
|
53
|
+
# face recognition and train it with the images and
|
54
|
+
# labels read from the given CSV file.
|
55
|
+
#
|
56
|
+
# The LBPHFaceRecognizer uses Extended Local Binary Patterns
|
57
|
+
# (it's probably configurable with other operators at a later
|
58
|
+
# point), and has the following default values
|
59
|
+
#
|
60
|
+
# radius = 1
|
61
|
+
# neighbors = 8
|
62
|
+
# grid_x = 8
|
63
|
+
# grid_y = 8
|
64
|
+
#
|
65
|
+
# So if you want a LBPH FaceRecognizer using a radius of
|
66
|
+
# 2 and 16 neighbors, call the factory method with:
|
67
|
+
#
|
68
|
+
# LBPH.new(2, 16);
|
69
|
+
#
|
70
|
+
# And if you want a threshold (e.g. 123.0) call it with its default values:
|
71
|
+
#
|
72
|
+
# LBPH.new(1,8,8,8,123.0)
|
73
|
+
#
|
74
|
+
model = LBPH.new
|
75
|
+
model.train(images, labels)
|
76
|
+
|
77
|
+
# The following line predicts the label of a given test image:
|
78
|
+
predicted_label, predicted_confidence = model.predict(test_sample)
|
79
|
+
|
80
|
+
# To get the confidence of a prediction call the model with:
|
81
|
+
#
|
82
|
+
# predicted_label = -1;
|
83
|
+
# confidence = 0.0;
|
84
|
+
# model.predict(test_sample, predicted_label, confidence)
|
85
|
+
#
|
86
|
+
puts "Predicted class: #{predicted_label} / Actual class: #{test_label}"
|
87
|
+
|
88
|
+
# Sometimes you'll need to get/set internal model data,
|
89
|
+
# which isn't exposed by the public FaceRecognizer.
|
90
|
+
# Since each FaceRecognizer is derived from a Algorithm,
|
91
|
+
# you can query the data.
|
92
|
+
#
|
93
|
+
# First we'll use it to set the threshold of the FaceRecognizer
|
94
|
+
# to 0.0 without retraining the model. This can be useful if
|
95
|
+
# you are evaluating the model:
|
96
|
+
model.set_double('threshold', 0.0);
|
97
|
+
|
98
|
+
# Now the threshold of this model is set to 0.0. A prediction
|
99
|
+
# now returns -1, as it's impossible to have a distance below it
|
100
|
+
predicted_label, predicted_confidence = model.predict(test_sample)
|
101
|
+
puts "Predicted class = #{predicted_label}"
|
102
|
+
|
103
|
+
# Show some informations about the model, as there's no cool
|
104
|
+
# Model data to display as in Eigenfaces/Fisherfaces.
|
105
|
+
# Due to efficiency reasons the LBP images are not stored
|
106
|
+
# within the model:
|
107
|
+
puts 'Model Information:'
|
108
|
+
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')})"
|
109
|
+
puts model_info
|
110
|
+
|
111
|
+
# We could get the histograms for example:
|
112
|
+
histgrams = model.get_matvector('histograms');
|
113
|
+
|
114
|
+
# But should I really visualize it? Probably the length is interesting:
|
115
|
+
puts "Size of the histograms: #{histgrams[0].dims.reduce(&:*)}"
|
116
|
+
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# Face recognition with ruby-opencv
|
2
|
+
|
3
|
+
This is a face recognition sample with ruby-opencv, which equivalent to the following OpenCV's tutorial.
|
4
|
+
|
5
|
+
[Face Recognition with OpenCV](http://docs.opencv.org/trunk/modules/contrib/doc/facerec/facerec_tutorial.html)
|
6
|
+
|
7
|
+
|
8
|
+
## Running samples
|
9
|
+
|
10
|
+
### 1. Get AT&T Facedatabase
|
11
|
+
|
12
|
+
Get AT&T Facedatabase from http://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html and unzip it.
|
13
|
+
|
14
|
+
```sh
|
15
|
+
$ wget http://www.cl.cam.ac.uk/Research/DTG/attarchive/pub/data/att_faces.zip
|
16
|
+
$ unzip att_faces.zip
|
17
|
+
```
|
18
|
+
|
19
|
+
### 2. Prepare the data
|
20
|
+
|
21
|
+
Create a CSV file to run samples.
|
22
|
+
|
23
|
+
```sh
|
24
|
+
$ ruby create_csv.rb att_faces > at.txt
|
25
|
+
```
|
26
|
+
|
27
|
+
You will get a CSV file which contains lines composed of a filename followed by a ; followed by the label (as integer number).
|
28
|
+
|
29
|
+
```sh
|
30
|
+
$ cat at.txt
|
31
|
+
att_faces/s34/2.pgm;0
|
32
|
+
att_faces/s34/3.pgm;0
|
33
|
+
att_faces/s34/8.pgm;0
|
34
|
+
att_faces/s34/4.pgm;0
|
35
|
+
att_faces/s34/5.pgm;0
|
36
|
+
att_faces/s34/10.pgm;0
|
37
|
+
att_faces/s34/9.pgm;0
|
38
|
+
att_faces/s34/7.pgm;0
|
39
|
+
att_faces/s34/6.pgm;0
|
40
|
+
att_faces/s34/1.pgm;0
|
41
|
+
...
|
42
|
+
```
|
43
|
+
|
44
|
+
### 3. Run sample codes
|
45
|
+
|
46
|
+
#### Eigenfaces
|
47
|
+
|
48
|
+
```sh
|
49
|
+
$ mkdir output-eigenfaces
|
50
|
+
$ ruby facerec_eigenfaces.rb at.txt output-eigenfaces
|
51
|
+
```
|
52
|
+
|
53
|
+
You will get the predicted class, actual class and eignvalues shown in console.
|
54
|
+
|
55
|
+
```sh
|
56
|
+
Predicted class: 39 / Actual class: 39
|
57
|
+
Eigenvalue #0 = 2823424.500638128
|
58
|
+
Eigenvalue #1 = 2062015.3818895558
|
59
|
+
Eigenvalue #2 = 1090171.0771557507
|
60
|
+
Eigenvalue #3 = 892019.3644237233
|
61
|
+
Eigenvalue #4 = 818537.7917991373
|
62
|
+
Eigenvalue #5 = 539058.2364753223
|
63
|
+
Eigenvalue #6 = 390359.3231975121
|
64
|
+
Eigenvalue #7 = 373809.5486713626
|
65
|
+
Eigenvalue #8 = 314658.94374918053
|
66
|
+
Eigenvalue #9 = 288764.63018440653
|
67
|
+
```
|
68
|
+
|
69
|
+
The result images will be stored in **output-eigenfaces** .
|
70
|
+
|
71
|
+
|
72
|
+
#### Fisherfaces
|
73
|
+
|
74
|
+
```sh
|
75
|
+
$ mkdir output-fisherfaces
|
76
|
+
$ ruby facerec_fisherfaces.rb at.txt output-fisherfaces
|
77
|
+
```
|
78
|
+
|
79
|
+
You will get the predicted class, actual class and eignvalues like Eigenfaces sample.
|
80
|
+
|
81
|
+
The result images will be stored in **output-fisherfaces** .
|
82
|
+
|
83
|
+
|
84
|
+
#### Local Binary Patterns Histograms
|
85
|
+
|
86
|
+
```sh
|
87
|
+
$ ruby facerec_lbph.rb at.txt
|
88
|
+
```
|
89
|
+
|
90
|
+
You will get the predicted class, actual class, model information and size of the histgrams.
|
91
|
+
|
92
|
+
```
|
93
|
+
Predicted class: 39 / Actual class: 39
|
94
|
+
Predicted class = -1
|
95
|
+
Model Information:
|
96
|
+
LBPH(radius=1, neighbors=8, grid_x=8, grid_y=8, threshold=0.0)
|
97
|
+
Size of the histograms: 16384
|
98
|
+
```
|
99
|
+
|
100
|
+
## Credits
|
101
|
+
|
102
|
+
### The Database of Faces
|
103
|
+
|
104
|
+
The Database of Faces, formerly The ORL Database of Faces, contains a set of face images taken between April 1992 and April 1994. The database was used in the context of a face recognition project carried out in collaboration with the Speech, Vision and Robotics Group of the Cambridge University Engineering Department.
|
105
|
+
|
106
|
+
There are ten different images of each of 40 distinct subjects. For some subjects, the images were taken at different times, varying the lighting, facial expressions (open / closed eyes, smiling / not smiling) and facial details (glasses / no glasses). All the images were taken against a dark homogeneous background with the subjects in an upright, frontal position (with tolerance for some side movement).
|
107
|
+
|
108
|
+
The files are in PGM format. The size of each image is 92x112 pixels, with 256 grey levels per pixel. The images are organised in 40 directories (one for each subject), which have names of the form sX, where X indicates the subject number (between 1 and 40). In each of these directories, there are ten different images of that subject, which have names of the form Y.pgm, where Y is the image number for that subject (between 1 and 10).
|
109
|
+
|
110
|
+
A copy of the database can be retrieved from: http://www.cl.cam.ac.uk/research/dtg/attarchive/pub/data/att_faces.zip.
|
111
|
+
|