vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
|
||||
|
||||
set(name "facerec")
|
||||
project(facerec_cpp_samples)
|
||||
|
||||
#SET(OpenCV_DIR /path/to/your/opencv/installation)
|
||||
|
||||
# packages
|
||||
find_package(OpenCV REQUIRED) # http://opencv.org
|
||||
|
||||
# probably you should loop through the sample files here
|
||||
add_executable(facerec_demo facerec_demo.cpp)
|
||||
target_link_libraries(facerec_demo opencv_core opencv_face opencv_imgproc opencv_highgui)
|
||||
|
||||
add_executable(facerec_video facerec_video.cpp)
|
||||
target_link_libraries(facerec_video opencv_face opencv_core opencv_imgproc opencv_highgui opencv_xobjdetect opencv_imgproc)
|
||||
|
||||
add_executable(facerec_eigenfaces facerec_eigenfaces.cpp)
|
||||
target_link_libraries(facerec_eigenfaces opencv_face opencv_core opencv_imgproc opencv_highgui)
|
||||
|
||||
add_executable(facerec_fisherfaces facerec_fisherfaces.cpp)
|
||||
target_link_libraries(facerec_fisherfaces opencv_face opencv_core opencv_imgproc opencv_highgui)
|
||||
|
||||
add_executable(facerec_lbph facerec_lbph.cpp)
|
||||
target_link_libraries(facerec_lbph opencv_face opencv_core opencv_imgproc opencv_highgui)
|
||||
|
||||
add_executable(mace_webcam mace_webcam.cpp)
|
||||
target_link_libraries(mace_webcam opencv_face opencv_core opencv_imgproc opencv_highgui opencv_videoio)
|
||||
@@ -0,0 +1,48 @@
|
||||
import org.opencv.core.*;
|
||||
import org.opencv.face.*;
|
||||
import org.opencv.imgcodecs.*;
|
||||
import org.opencv.imgproc.*;
|
||||
import org.opencv.xobjdetect.*;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
public class Facemark {
|
||||
static {
|
||||
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length < 3) {
|
||||
System.out.println("use: java Facemark [image file] [cascade file] [model file]");
|
||||
return;
|
||||
}
|
||||
// read the image
|
||||
Mat img = Imgcodecs.imread(args[0]);
|
||||
|
||||
// setup face detection
|
||||
CascadeClassifier cascade = new CascadeClassifier(args[1]);
|
||||
MatOfRect faces = new MatOfRect();
|
||||
// detect faces
|
||||
cascade.detectMultiScale(img, faces);
|
||||
|
||||
// setup landmarks detector
|
||||
Facemark fm = Face.createFacemarkKazemi();
|
||||
fm.loadModel(args[2]);
|
||||
|
||||
// fit landmarks for each found face
|
||||
ArrayList<MatOfPoint2f> landmarks = new ArrayList<MatOfPoint2f>();
|
||||
fm.fit(img, faces, landmarks);
|
||||
|
||||
// draw them
|
||||
for (int i=0; i<landmarks.size(); i++) {
|
||||
MatOfPoint2f lm = landmarks.get(i);
|
||||
for (int j=0; j<lm.rows(); j++) {
|
||||
double [] dp = lm.get(j,0);
|
||||
Point p = new Point(dp[0], dp[1]);
|
||||
Imgproc.circle(img,p,2,new Scalar(222),1);
|
||||
}
|
||||
}
|
||||
// save result
|
||||
Imgcodecs.imwrite("landmarks.jpg",img);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,400 @@
|
||||
/home/philipp/facerec/data/at/s13/2.pgm;12
|
||||
/home/philipp/facerec/data/at/s13/7.pgm;12
|
||||
/home/philipp/facerec/data/at/s13/6.pgm;12
|
||||
/home/philipp/facerec/data/at/s13/9.pgm;12
|
||||
/home/philipp/facerec/data/at/s13/5.pgm;12
|
||||
/home/philipp/facerec/data/at/s13/3.pgm;12
|
||||
/home/philipp/facerec/data/at/s13/4.pgm;12
|
||||
/home/philipp/facerec/data/at/s13/10.pgm;12
|
||||
/home/philipp/facerec/data/at/s13/8.pgm;12
|
||||
/home/philipp/facerec/data/at/s13/1.pgm;12
|
||||
/home/philipp/facerec/data/at/s17/2.pgm;16
|
||||
/home/philipp/facerec/data/at/s17/7.pgm;16
|
||||
/home/philipp/facerec/data/at/s17/6.pgm;16
|
||||
/home/philipp/facerec/data/at/s17/9.pgm;16
|
||||
/home/philipp/facerec/data/at/s17/5.pgm;16
|
||||
/home/philipp/facerec/data/at/s17/3.pgm;16
|
||||
/home/philipp/facerec/data/at/s17/4.pgm;16
|
||||
/home/philipp/facerec/data/at/s17/10.pgm;16
|
||||
/home/philipp/facerec/data/at/s17/8.pgm;16
|
||||
/home/philipp/facerec/data/at/s17/1.pgm;16
|
||||
/home/philipp/facerec/data/at/s32/2.pgm;31
|
||||
/home/philipp/facerec/data/at/s32/7.pgm;31
|
||||
/home/philipp/facerec/data/at/s32/6.pgm;31
|
||||
/home/philipp/facerec/data/at/s32/9.pgm;31
|
||||
/home/philipp/facerec/data/at/s32/5.pgm;31
|
||||
/home/philipp/facerec/data/at/s32/3.pgm;31
|
||||
/home/philipp/facerec/data/at/s32/4.pgm;31
|
||||
/home/philipp/facerec/data/at/s32/10.pgm;31
|
||||
/home/philipp/facerec/data/at/s32/8.pgm;31
|
||||
/home/philipp/facerec/data/at/s32/1.pgm;31
|
||||
/home/philipp/facerec/data/at/s10/2.pgm;9
|
||||
/home/philipp/facerec/data/at/s10/7.pgm;9
|
||||
/home/philipp/facerec/data/at/s10/6.pgm;9
|
||||
/home/philipp/facerec/data/at/s10/9.pgm;9
|
||||
/home/philipp/facerec/data/at/s10/5.pgm;9
|
||||
/home/philipp/facerec/data/at/s10/3.pgm;9
|
||||
/home/philipp/facerec/data/at/s10/4.pgm;9
|
||||
/home/philipp/facerec/data/at/s10/10.pgm;9
|
||||
/home/philipp/facerec/data/at/s10/8.pgm;9
|
||||
/home/philipp/facerec/data/at/s10/1.pgm;9
|
||||
/home/philipp/facerec/data/at/s27/2.pgm;26
|
||||
/home/philipp/facerec/data/at/s27/7.pgm;26
|
||||
/home/philipp/facerec/data/at/s27/6.pgm;26
|
||||
/home/philipp/facerec/data/at/s27/9.pgm;26
|
||||
/home/philipp/facerec/data/at/s27/5.pgm;26
|
||||
/home/philipp/facerec/data/at/s27/3.pgm;26
|
||||
/home/philipp/facerec/data/at/s27/4.pgm;26
|
||||
/home/philipp/facerec/data/at/s27/10.pgm;26
|
||||
/home/philipp/facerec/data/at/s27/8.pgm;26
|
||||
/home/philipp/facerec/data/at/s27/1.pgm;26
|
||||
/home/philipp/facerec/data/at/s5/2.pgm;4
|
||||
/home/philipp/facerec/data/at/s5/7.pgm;4
|
||||
/home/philipp/facerec/data/at/s5/6.pgm;4
|
||||
/home/philipp/facerec/data/at/s5/9.pgm;4
|
||||
/home/philipp/facerec/data/at/s5/5.pgm;4
|
||||
/home/philipp/facerec/data/at/s5/3.pgm;4
|
||||
/home/philipp/facerec/data/at/s5/4.pgm;4
|
||||
/home/philipp/facerec/data/at/s5/10.pgm;4
|
||||
/home/philipp/facerec/data/at/s5/8.pgm;4
|
||||
/home/philipp/facerec/data/at/s5/1.pgm;4
|
||||
/home/philipp/facerec/data/at/s20/2.pgm;19
|
||||
/home/philipp/facerec/data/at/s20/7.pgm;19
|
||||
/home/philipp/facerec/data/at/s20/6.pgm;19
|
||||
/home/philipp/facerec/data/at/s20/9.pgm;19
|
||||
/home/philipp/facerec/data/at/s20/5.pgm;19
|
||||
/home/philipp/facerec/data/at/s20/3.pgm;19
|
||||
/home/philipp/facerec/data/at/s20/4.pgm;19
|
||||
/home/philipp/facerec/data/at/s20/10.pgm;19
|
||||
/home/philipp/facerec/data/at/s20/8.pgm;19
|
||||
/home/philipp/facerec/data/at/s20/1.pgm;19
|
||||
/home/philipp/facerec/data/at/s30/2.pgm;29
|
||||
/home/philipp/facerec/data/at/s30/7.pgm;29
|
||||
/home/philipp/facerec/data/at/s30/6.pgm;29
|
||||
/home/philipp/facerec/data/at/s30/9.pgm;29
|
||||
/home/philipp/facerec/data/at/s30/5.pgm;29
|
||||
/home/philipp/facerec/data/at/s30/3.pgm;29
|
||||
/home/philipp/facerec/data/at/s30/4.pgm;29
|
||||
/home/philipp/facerec/data/at/s30/10.pgm;29
|
||||
/home/philipp/facerec/data/at/s30/8.pgm;29
|
||||
/home/philipp/facerec/data/at/s30/1.pgm;29
|
||||
/home/philipp/facerec/data/at/s39/2.pgm;38
|
||||
/home/philipp/facerec/data/at/s39/7.pgm;38
|
||||
/home/philipp/facerec/data/at/s39/6.pgm;38
|
||||
/home/philipp/facerec/data/at/s39/9.pgm;38
|
||||
/home/philipp/facerec/data/at/s39/5.pgm;38
|
||||
/home/philipp/facerec/data/at/s39/3.pgm;38
|
||||
/home/philipp/facerec/data/at/s39/4.pgm;38
|
||||
/home/philipp/facerec/data/at/s39/10.pgm;38
|
||||
/home/philipp/facerec/data/at/s39/8.pgm;38
|
||||
/home/philipp/facerec/data/at/s39/1.pgm;38
|
||||
/home/philipp/facerec/data/at/s35/2.pgm;34
|
||||
/home/philipp/facerec/data/at/s35/7.pgm;34
|
||||
/home/philipp/facerec/data/at/s35/6.pgm;34
|
||||
/home/philipp/facerec/data/at/s35/9.pgm;34
|
||||
/home/philipp/facerec/data/at/s35/5.pgm;34
|
||||
/home/philipp/facerec/data/at/s35/3.pgm;34
|
||||
/home/philipp/facerec/data/at/s35/4.pgm;34
|
||||
/home/philipp/facerec/data/at/s35/10.pgm;34
|
||||
/home/philipp/facerec/data/at/s35/8.pgm;34
|
||||
/home/philipp/facerec/data/at/s35/1.pgm;34
|
||||
/home/philipp/facerec/data/at/s23/2.pgm;22
|
||||
/home/philipp/facerec/data/at/s23/7.pgm;22
|
||||
/home/philipp/facerec/data/at/s23/6.pgm;22
|
||||
/home/philipp/facerec/data/at/s23/9.pgm;22
|
||||
/home/philipp/facerec/data/at/s23/5.pgm;22
|
||||
/home/philipp/facerec/data/at/s23/3.pgm;22
|
||||
/home/philipp/facerec/data/at/s23/4.pgm;22
|
||||
/home/philipp/facerec/data/at/s23/10.pgm;22
|
||||
/home/philipp/facerec/data/at/s23/8.pgm;22
|
||||
/home/philipp/facerec/data/at/s23/1.pgm;22
|
||||
/home/philipp/facerec/data/at/s4/2.pgm;3
|
||||
/home/philipp/facerec/data/at/s4/7.pgm;3
|
||||
/home/philipp/facerec/data/at/s4/6.pgm;3
|
||||
/home/philipp/facerec/data/at/s4/9.pgm;3
|
||||
/home/philipp/facerec/data/at/s4/5.pgm;3
|
||||
/home/philipp/facerec/data/at/s4/3.pgm;3
|
||||
/home/philipp/facerec/data/at/s4/4.pgm;3
|
||||
/home/philipp/facerec/data/at/s4/10.pgm;3
|
||||
/home/philipp/facerec/data/at/s4/8.pgm;3
|
||||
/home/philipp/facerec/data/at/s4/1.pgm;3
|
||||
/home/philipp/facerec/data/at/s9/2.pgm;8
|
||||
/home/philipp/facerec/data/at/s9/7.pgm;8
|
||||
/home/philipp/facerec/data/at/s9/6.pgm;8
|
||||
/home/philipp/facerec/data/at/s9/9.pgm;8
|
||||
/home/philipp/facerec/data/at/s9/5.pgm;8
|
||||
/home/philipp/facerec/data/at/s9/3.pgm;8
|
||||
/home/philipp/facerec/data/at/s9/4.pgm;8
|
||||
/home/philipp/facerec/data/at/s9/10.pgm;8
|
||||
/home/philipp/facerec/data/at/s9/8.pgm;8
|
||||
/home/philipp/facerec/data/at/s9/1.pgm;8
|
||||
/home/philipp/facerec/data/at/s37/2.pgm;36
|
||||
/home/philipp/facerec/data/at/s37/7.pgm;36
|
||||
/home/philipp/facerec/data/at/s37/6.pgm;36
|
||||
/home/philipp/facerec/data/at/s37/9.pgm;36
|
||||
/home/philipp/facerec/data/at/s37/5.pgm;36
|
||||
/home/philipp/facerec/data/at/s37/3.pgm;36
|
||||
/home/philipp/facerec/data/at/s37/4.pgm;36
|
||||
/home/philipp/facerec/data/at/s37/10.pgm;36
|
||||
/home/philipp/facerec/data/at/s37/8.pgm;36
|
||||
/home/philipp/facerec/data/at/s37/1.pgm;36
|
||||
/home/philipp/facerec/data/at/s24/2.pgm;23
|
||||
/home/philipp/facerec/data/at/s24/7.pgm;23
|
||||
/home/philipp/facerec/data/at/s24/6.pgm;23
|
||||
/home/philipp/facerec/data/at/s24/9.pgm;23
|
||||
/home/philipp/facerec/data/at/s24/5.pgm;23
|
||||
/home/philipp/facerec/data/at/s24/3.pgm;23
|
||||
/home/philipp/facerec/data/at/s24/4.pgm;23
|
||||
/home/philipp/facerec/data/at/s24/10.pgm;23
|
||||
/home/philipp/facerec/data/at/s24/8.pgm;23
|
||||
/home/philipp/facerec/data/at/s24/1.pgm;23
|
||||
/home/philipp/facerec/data/at/s19/2.pgm;18
|
||||
/home/philipp/facerec/data/at/s19/7.pgm;18
|
||||
/home/philipp/facerec/data/at/s19/6.pgm;18
|
||||
/home/philipp/facerec/data/at/s19/9.pgm;18
|
||||
/home/philipp/facerec/data/at/s19/5.pgm;18
|
||||
/home/philipp/facerec/data/at/s19/3.pgm;18
|
||||
/home/philipp/facerec/data/at/s19/4.pgm;18
|
||||
/home/philipp/facerec/data/at/s19/10.pgm;18
|
||||
/home/philipp/facerec/data/at/s19/8.pgm;18
|
||||
/home/philipp/facerec/data/at/s19/1.pgm;18
|
||||
/home/philipp/facerec/data/at/s8/2.pgm;7
|
||||
/home/philipp/facerec/data/at/s8/7.pgm;7
|
||||
/home/philipp/facerec/data/at/s8/6.pgm;7
|
||||
/home/philipp/facerec/data/at/s8/9.pgm;7
|
||||
/home/philipp/facerec/data/at/s8/5.pgm;7
|
||||
/home/philipp/facerec/data/at/s8/3.pgm;7
|
||||
/home/philipp/facerec/data/at/s8/4.pgm;7
|
||||
/home/philipp/facerec/data/at/s8/10.pgm;7
|
||||
/home/philipp/facerec/data/at/s8/8.pgm;7
|
||||
/home/philipp/facerec/data/at/s8/1.pgm;7
|
||||
/home/philipp/facerec/data/at/s21/2.pgm;20
|
||||
/home/philipp/facerec/data/at/s21/7.pgm;20
|
||||
/home/philipp/facerec/data/at/s21/6.pgm;20
|
||||
/home/philipp/facerec/data/at/s21/9.pgm;20
|
||||
/home/philipp/facerec/data/at/s21/5.pgm;20
|
||||
/home/philipp/facerec/data/at/s21/3.pgm;20
|
||||
/home/philipp/facerec/data/at/s21/4.pgm;20
|
||||
/home/philipp/facerec/data/at/s21/10.pgm;20
|
||||
/home/philipp/facerec/data/at/s21/8.pgm;20
|
||||
/home/philipp/facerec/data/at/s21/1.pgm;20
|
||||
/home/philipp/facerec/data/at/s1/2.pgm;0
|
||||
/home/philipp/facerec/data/at/s1/7.pgm;0
|
||||
/home/philipp/facerec/data/at/s1/6.pgm;0
|
||||
/home/philipp/facerec/data/at/s1/9.pgm;0
|
||||
/home/philipp/facerec/data/at/s1/5.pgm;0
|
||||
/home/philipp/facerec/data/at/s1/3.pgm;0
|
||||
/home/philipp/facerec/data/at/s1/4.pgm;0
|
||||
/home/philipp/facerec/data/at/s1/10.pgm;0
|
||||
/home/philipp/facerec/data/at/s1/8.pgm;0
|
||||
/home/philipp/facerec/data/at/s1/1.pgm;0
|
||||
/home/philipp/facerec/data/at/s7/2.pgm;6
|
||||
/home/philipp/facerec/data/at/s7/7.pgm;6
|
||||
/home/philipp/facerec/data/at/s7/6.pgm;6
|
||||
/home/philipp/facerec/data/at/s7/9.pgm;6
|
||||
/home/philipp/facerec/data/at/s7/5.pgm;6
|
||||
/home/philipp/facerec/data/at/s7/3.pgm;6
|
||||
/home/philipp/facerec/data/at/s7/4.pgm;6
|
||||
/home/philipp/facerec/data/at/s7/10.pgm;6
|
||||
/home/philipp/facerec/data/at/s7/8.pgm;6
|
||||
/home/philipp/facerec/data/at/s7/1.pgm;6
|
||||
/home/philipp/facerec/data/at/s16/2.pgm;15
|
||||
/home/philipp/facerec/data/at/s16/7.pgm;15
|
||||
/home/philipp/facerec/data/at/s16/6.pgm;15
|
||||
/home/philipp/facerec/data/at/s16/9.pgm;15
|
||||
/home/philipp/facerec/data/at/s16/5.pgm;15
|
||||
/home/philipp/facerec/data/at/s16/3.pgm;15
|
||||
/home/philipp/facerec/data/at/s16/4.pgm;15
|
||||
/home/philipp/facerec/data/at/s16/10.pgm;15
|
||||
/home/philipp/facerec/data/at/s16/8.pgm;15
|
||||
/home/philipp/facerec/data/at/s16/1.pgm;15
|
||||
/home/philipp/facerec/data/at/s36/2.pgm;35
|
||||
/home/philipp/facerec/data/at/s36/7.pgm;35
|
||||
/home/philipp/facerec/data/at/s36/6.pgm;35
|
||||
/home/philipp/facerec/data/at/s36/9.pgm;35
|
||||
/home/philipp/facerec/data/at/s36/5.pgm;35
|
||||
/home/philipp/facerec/data/at/s36/3.pgm;35
|
||||
/home/philipp/facerec/data/at/s36/4.pgm;35
|
||||
/home/philipp/facerec/data/at/s36/10.pgm;35
|
||||
/home/philipp/facerec/data/at/s36/8.pgm;35
|
||||
/home/philipp/facerec/data/at/s36/1.pgm;35
|
||||
/home/philipp/facerec/data/at/s25/2.pgm;24
|
||||
/home/philipp/facerec/data/at/s25/7.pgm;24
|
||||
/home/philipp/facerec/data/at/s25/6.pgm;24
|
||||
/home/philipp/facerec/data/at/s25/9.pgm;24
|
||||
/home/philipp/facerec/data/at/s25/5.pgm;24
|
||||
/home/philipp/facerec/data/at/s25/3.pgm;24
|
||||
/home/philipp/facerec/data/at/s25/4.pgm;24
|
||||
/home/philipp/facerec/data/at/s25/10.pgm;24
|
||||
/home/philipp/facerec/data/at/s25/8.pgm;24
|
||||
/home/philipp/facerec/data/at/s25/1.pgm;24
|
||||
/home/philipp/facerec/data/at/s14/2.pgm;13
|
||||
/home/philipp/facerec/data/at/s14/7.pgm;13
|
||||
/home/philipp/facerec/data/at/s14/6.pgm;13
|
||||
/home/philipp/facerec/data/at/s14/9.pgm;13
|
||||
/home/philipp/facerec/data/at/s14/5.pgm;13
|
||||
/home/philipp/facerec/data/at/s14/3.pgm;13
|
||||
/home/philipp/facerec/data/at/s14/4.pgm;13
|
||||
/home/philipp/facerec/data/at/s14/10.pgm;13
|
||||
/home/philipp/facerec/data/at/s14/8.pgm;13
|
||||
/home/philipp/facerec/data/at/s14/1.pgm;13
|
||||
/home/philipp/facerec/data/at/s34/2.pgm;33
|
||||
/home/philipp/facerec/data/at/s34/7.pgm;33
|
||||
/home/philipp/facerec/data/at/s34/6.pgm;33
|
||||
/home/philipp/facerec/data/at/s34/9.pgm;33
|
||||
/home/philipp/facerec/data/at/s34/5.pgm;33
|
||||
/home/philipp/facerec/data/at/s34/3.pgm;33
|
||||
/home/philipp/facerec/data/at/s34/4.pgm;33
|
||||
/home/philipp/facerec/data/at/s34/10.pgm;33
|
||||
/home/philipp/facerec/data/at/s34/8.pgm;33
|
||||
/home/philipp/facerec/data/at/s34/1.pgm;33
|
||||
/home/philipp/facerec/data/at/s11/2.pgm;10
|
||||
/home/philipp/facerec/data/at/s11/7.pgm;10
|
||||
/home/philipp/facerec/data/at/s11/6.pgm;10
|
||||
/home/philipp/facerec/data/at/s11/9.pgm;10
|
||||
/home/philipp/facerec/data/at/s11/5.pgm;10
|
||||
/home/philipp/facerec/data/at/s11/3.pgm;10
|
||||
/home/philipp/facerec/data/at/s11/4.pgm;10
|
||||
/home/philipp/facerec/data/at/s11/10.pgm;10
|
||||
/home/philipp/facerec/data/at/s11/8.pgm;10
|
||||
/home/philipp/facerec/data/at/s11/1.pgm;10
|
||||
/home/philipp/facerec/data/at/s26/2.pgm;25
|
||||
/home/philipp/facerec/data/at/s26/7.pgm;25
|
||||
/home/philipp/facerec/data/at/s26/6.pgm;25
|
||||
/home/philipp/facerec/data/at/s26/9.pgm;25
|
||||
/home/philipp/facerec/data/at/s26/5.pgm;25
|
||||
/home/philipp/facerec/data/at/s26/3.pgm;25
|
||||
/home/philipp/facerec/data/at/s26/4.pgm;25
|
||||
/home/philipp/facerec/data/at/s26/10.pgm;25
|
||||
/home/philipp/facerec/data/at/s26/8.pgm;25
|
||||
/home/philipp/facerec/data/at/s26/1.pgm;25
|
||||
/home/philipp/facerec/data/at/s18/2.pgm;17
|
||||
/home/philipp/facerec/data/at/s18/7.pgm;17
|
||||
/home/philipp/facerec/data/at/s18/6.pgm;17
|
||||
/home/philipp/facerec/data/at/s18/9.pgm;17
|
||||
/home/philipp/facerec/data/at/s18/5.pgm;17
|
||||
/home/philipp/facerec/data/at/s18/3.pgm;17
|
||||
/home/philipp/facerec/data/at/s18/4.pgm;17
|
||||
/home/philipp/facerec/data/at/s18/10.pgm;17
|
||||
/home/philipp/facerec/data/at/s18/8.pgm;17
|
||||
/home/philipp/facerec/data/at/s18/1.pgm;17
|
||||
/home/philipp/facerec/data/at/s29/2.pgm;28
|
||||
/home/philipp/facerec/data/at/s29/7.pgm;28
|
||||
/home/philipp/facerec/data/at/s29/6.pgm;28
|
||||
/home/philipp/facerec/data/at/s29/9.pgm;28
|
||||
/home/philipp/facerec/data/at/s29/5.pgm;28
|
||||
/home/philipp/facerec/data/at/s29/3.pgm;28
|
||||
/home/philipp/facerec/data/at/s29/4.pgm;28
|
||||
/home/philipp/facerec/data/at/s29/10.pgm;28
|
||||
/home/philipp/facerec/data/at/s29/8.pgm;28
|
||||
/home/philipp/facerec/data/at/s29/1.pgm;28
|
||||
/home/philipp/facerec/data/at/s33/2.pgm;32
|
||||
/home/philipp/facerec/data/at/s33/7.pgm;32
|
||||
/home/philipp/facerec/data/at/s33/6.pgm;32
|
||||
/home/philipp/facerec/data/at/s33/9.pgm;32
|
||||
/home/philipp/facerec/data/at/s33/5.pgm;32
|
||||
/home/philipp/facerec/data/at/s33/3.pgm;32
|
||||
/home/philipp/facerec/data/at/s33/4.pgm;32
|
||||
/home/philipp/facerec/data/at/s33/10.pgm;32
|
||||
/home/philipp/facerec/data/at/s33/8.pgm;32
|
||||
/home/philipp/facerec/data/at/s33/1.pgm;32
|
||||
/home/philipp/facerec/data/at/s12/2.pgm;11
|
||||
/home/philipp/facerec/data/at/s12/7.pgm;11
|
||||
/home/philipp/facerec/data/at/s12/6.pgm;11
|
||||
/home/philipp/facerec/data/at/s12/9.pgm;11
|
||||
/home/philipp/facerec/data/at/s12/5.pgm;11
|
||||
/home/philipp/facerec/data/at/s12/3.pgm;11
|
||||
/home/philipp/facerec/data/at/s12/4.pgm;11
|
||||
/home/philipp/facerec/data/at/s12/10.pgm;11
|
||||
/home/philipp/facerec/data/at/s12/8.pgm;11
|
||||
/home/philipp/facerec/data/at/s12/1.pgm;11
|
||||
/home/philipp/facerec/data/at/s6/2.pgm;5
|
||||
/home/philipp/facerec/data/at/s6/7.pgm;5
|
||||
/home/philipp/facerec/data/at/s6/6.pgm;5
|
||||
/home/philipp/facerec/data/at/s6/9.pgm;5
|
||||
/home/philipp/facerec/data/at/s6/5.pgm;5
|
||||
/home/philipp/facerec/data/at/s6/3.pgm;5
|
||||
/home/philipp/facerec/data/at/s6/4.pgm;5
|
||||
/home/philipp/facerec/data/at/s6/10.pgm;5
|
||||
/home/philipp/facerec/data/at/s6/8.pgm;5
|
||||
/home/philipp/facerec/data/at/s6/1.pgm;5
|
||||
/home/philipp/facerec/data/at/s22/2.pgm;21
|
||||
/home/philipp/facerec/data/at/s22/7.pgm;21
|
||||
/home/philipp/facerec/data/at/s22/6.pgm;21
|
||||
/home/philipp/facerec/data/at/s22/9.pgm;21
|
||||
/home/philipp/facerec/data/at/s22/5.pgm;21
|
||||
/home/philipp/facerec/data/at/s22/3.pgm;21
|
||||
/home/philipp/facerec/data/at/s22/4.pgm;21
|
||||
/home/philipp/facerec/data/at/s22/10.pgm;21
|
||||
/home/philipp/facerec/data/at/s22/8.pgm;21
|
||||
/home/philipp/facerec/data/at/s22/1.pgm;21
|
||||
/home/philipp/facerec/data/at/s15/2.pgm;14
|
||||
/home/philipp/facerec/data/at/s15/7.pgm;14
|
||||
/home/philipp/facerec/data/at/s15/6.pgm;14
|
||||
/home/philipp/facerec/data/at/s15/9.pgm;14
|
||||
/home/philipp/facerec/data/at/s15/5.pgm;14
|
||||
/home/philipp/facerec/data/at/s15/3.pgm;14
|
||||
/home/philipp/facerec/data/at/s15/4.pgm;14
|
||||
/home/philipp/facerec/data/at/s15/10.pgm;14
|
||||
/home/philipp/facerec/data/at/s15/8.pgm;14
|
||||
/home/philipp/facerec/data/at/s15/1.pgm;14
|
||||
/home/philipp/facerec/data/at/s2/2.pgm;1
|
||||
/home/philipp/facerec/data/at/s2/7.pgm;1
|
||||
/home/philipp/facerec/data/at/s2/6.pgm;1
|
||||
/home/philipp/facerec/data/at/s2/9.pgm;1
|
||||
/home/philipp/facerec/data/at/s2/5.pgm;1
|
||||
/home/philipp/facerec/data/at/s2/3.pgm;1
|
||||
/home/philipp/facerec/data/at/s2/4.pgm;1
|
||||
/home/philipp/facerec/data/at/s2/10.pgm;1
|
||||
/home/philipp/facerec/data/at/s2/8.pgm;1
|
||||
/home/philipp/facerec/data/at/s2/1.pgm;1
|
||||
/home/philipp/facerec/data/at/s31/2.pgm;30
|
||||
/home/philipp/facerec/data/at/s31/7.pgm;30
|
||||
/home/philipp/facerec/data/at/s31/6.pgm;30
|
||||
/home/philipp/facerec/data/at/s31/9.pgm;30
|
||||
/home/philipp/facerec/data/at/s31/5.pgm;30
|
||||
/home/philipp/facerec/data/at/s31/3.pgm;30
|
||||
/home/philipp/facerec/data/at/s31/4.pgm;30
|
||||
/home/philipp/facerec/data/at/s31/10.pgm;30
|
||||
/home/philipp/facerec/data/at/s31/8.pgm;30
|
||||
/home/philipp/facerec/data/at/s31/1.pgm;30
|
||||
/home/philipp/facerec/data/at/s28/2.pgm;27
|
||||
/home/philipp/facerec/data/at/s28/7.pgm;27
|
||||
/home/philipp/facerec/data/at/s28/6.pgm;27
|
||||
/home/philipp/facerec/data/at/s28/9.pgm;27
|
||||
/home/philipp/facerec/data/at/s28/5.pgm;27
|
||||
/home/philipp/facerec/data/at/s28/3.pgm;27
|
||||
/home/philipp/facerec/data/at/s28/4.pgm;27
|
||||
/home/philipp/facerec/data/at/s28/10.pgm;27
|
||||
/home/philipp/facerec/data/at/s28/8.pgm;27
|
||||
/home/philipp/facerec/data/at/s28/1.pgm;27
|
||||
/home/philipp/facerec/data/at/s40/2.pgm;39
|
||||
/home/philipp/facerec/data/at/s40/7.pgm;39
|
||||
/home/philipp/facerec/data/at/s40/6.pgm;39
|
||||
/home/philipp/facerec/data/at/s40/9.pgm;39
|
||||
/home/philipp/facerec/data/at/s40/5.pgm;39
|
||||
/home/philipp/facerec/data/at/s40/3.pgm;39
|
||||
/home/philipp/facerec/data/at/s40/4.pgm;39
|
||||
/home/philipp/facerec/data/at/s40/10.pgm;39
|
||||
/home/philipp/facerec/data/at/s40/8.pgm;39
|
||||
/home/philipp/facerec/data/at/s40/1.pgm;39
|
||||
/home/philipp/facerec/data/at/s3/2.pgm;2
|
||||
/home/philipp/facerec/data/at/s3/7.pgm;2
|
||||
/home/philipp/facerec/data/at/s3/6.pgm;2
|
||||
/home/philipp/facerec/data/at/s3/9.pgm;2
|
||||
/home/philipp/facerec/data/at/s3/5.pgm;2
|
||||
/home/philipp/facerec/data/at/s3/3.pgm;2
|
||||
/home/philipp/facerec/data/at/s3/4.pgm;2
|
||||
/home/philipp/facerec/data/at/s3/10.pgm;2
|
||||
/home/philipp/facerec/data/at/s3/8.pgm;2
|
||||
/home/philipp/facerec/data/at/s3/1.pgm;2
|
||||
/home/philipp/facerec/data/at/s38/2.pgm;37
|
||||
/home/philipp/facerec/data/at/s38/7.pgm;37
|
||||
/home/philipp/facerec/data/at/s38/6.pgm;37
|
||||
/home/philipp/facerec/data/at/s38/9.pgm;37
|
||||
/home/philipp/facerec/data/at/s38/5.pgm;37
|
||||
/home/philipp/facerec/data/at/s38/3.pgm;37
|
||||
/home/philipp/facerec/data/at/s38/4.pgm;37
|
||||
/home/philipp/facerec/data/at/s38/10.pgm;37
|
||||
/home/philipp/facerec/data/at/s38/8.pgm;37
|
||||
/home/philipp/facerec/data/at/s38/1.pgm;37
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
import os.path
|
||||
|
||||
# This is a tiny script to help you creating a CSV file from a face
|
||||
# database with a similar hierarchie:
|
||||
#
|
||||
# philipp@mango:~/facerec/data/at$ tree
|
||||
# .
|
||||
# |-- README
|
||||
# |-- s1
|
||||
# | |-- 1.pgm
|
||||
# | |-- ...
|
||||
# | |-- 10.pgm
|
||||
# |-- s2
|
||||
# | |-- 1.pgm
|
||||
# | |-- ...
|
||||
# | |-- 10.pgm
|
||||
# ...
|
||||
# |-- s40
|
||||
# | |-- 1.pgm
|
||||
# | |-- ...
|
||||
# | |-- 10.pgm
|
||||
#
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print "usage: create_csv <base_path>"
|
||||
sys.exit(1)
|
||||
|
||||
BASE_PATH=sys.argv[1]
|
||||
SEPARATOR=";"
|
||||
|
||||
label = 0
|
||||
for dirname, dirnames, filenames in os.walk(BASE_PATH):
|
||||
for subdirname in dirnames:
|
||||
subject_path = os.path.join(dirname, subdirname)
|
||||
for filename in os.listdir(subject_path):
|
||||
abs_path = "%s/%s" % (subject_path, filename)
|
||||
print "%s%s%d" % (abs_path, SEPARATOR, label)
|
||||
label = label + 1
|
||||
Executable
+112
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env python
|
||||
# Software License Agreement (BSD License)
|
||||
#
|
||||
# Copyright (c) 2012, Philipp Wagner
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
# * Neither the name of the author nor the names of its
|
||||
# contributors may be used to endorse or promote products derived
|
||||
# from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import sys, math, Image
|
||||
|
||||
def Distance(p1,p2):
|
||||
dx = p2[0] - p1[0]
|
||||
dy = p2[1] - p1[1]
|
||||
return math.sqrt(dx*dx+dy*dy)
|
||||
|
||||
def ScaleRotateTranslate(image, angle, center = None, new_center = None, scale = None, resample=Image.BICUBIC):
|
||||
if (scale is None) and (center is None):
|
||||
return image.rotate(angle=angle, resample=resample)
|
||||
nx,ny = x,y = center
|
||||
sx=sy=1.0
|
||||
if new_center:
|
||||
(nx,ny) = new_center
|
||||
if scale:
|
||||
(sx,sy) = (scale, scale)
|
||||
cosine = math.cos(angle)
|
||||
sine = math.sin(angle)
|
||||
a = cosine/sx
|
||||
b = sine/sx
|
||||
c = x-nx*a-ny*b
|
||||
d = -sine/sy
|
||||
e = cosine/sy
|
||||
f = y-nx*d-ny*e
|
||||
return image.transform(image.size, Image.AFFINE, (a,b,c,d,e,f), resample=resample)
|
||||
|
||||
def CropFace(image, eye_left=(0,0), eye_right=(0,0), offset_pct=(0.2,0.2), dest_sz = (70,70)):
|
||||
# calculate offsets in original image
|
||||
offset_h = math.floor(float(offset_pct[0])*dest_sz[0])
|
||||
offset_v = math.floor(float(offset_pct[1])*dest_sz[1])
|
||||
# get the direction
|
||||
eye_direction = (eye_right[0] - eye_left[0], eye_right[1] - eye_left[1])
|
||||
# calc rotation angle in radians
|
||||
rotation = -math.atan2(float(eye_direction[1]),float(eye_direction[0]))
|
||||
# distance between them
|
||||
dist = Distance(eye_left, eye_right)
|
||||
# calculate the reference eye-width
|
||||
reference = dest_sz[0] - 2.0*offset_h
|
||||
# scale factor
|
||||
scale = float(dist)/float(reference)
|
||||
# rotate original around the left eye
|
||||
image = ScaleRotateTranslate(image, center=eye_left, angle=rotation)
|
||||
# crop the rotated image
|
||||
crop_xy = (eye_left[0] - scale*offset_h, eye_left[1] - scale*offset_v)
|
||||
crop_size = (dest_sz[0]*scale, dest_sz[1]*scale)
|
||||
image = image.crop((int(crop_xy[0]), int(crop_xy[1]), int(crop_xy[0]+crop_size[0]), int(crop_xy[1]+crop_size[1])))
|
||||
# resize it
|
||||
image = image.resize(dest_sz, Image.ANTIALIAS)
|
||||
return image
|
||||
|
||||
def readFileNames():
|
||||
try:
|
||||
inFile = open('path_to_created_csv_file.csv')
|
||||
except:
|
||||
raise IOError('There is no file named path_to_created_csv_file.csv in current directory.')
|
||||
return False
|
||||
|
||||
picPath = []
|
||||
picIndex = []
|
||||
|
||||
for line in inFile.readlines():
|
||||
if line != '':
|
||||
fields = line.rstrip().split(';')
|
||||
picPath.append(fields[0])
|
||||
picIndex.append(int(fields[1]))
|
||||
|
||||
return (picPath, picIndex)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
[images, indexes]=readFileNames()
|
||||
if not os.path.exists("modified"):
|
||||
os.makedirs("modified")
|
||||
for img in images:
|
||||
image = Image.open(img)
|
||||
CropFace(image, eye_left=(252,364), eye_right=(420,366), offset_pct=(0.1,0.1), dest_sz=(200,200)).save("modified/"+img.rstrip().split('/')[1]+"_10_10_200_200.jpg")
|
||||
CropFace(image, eye_left=(252,364), eye_right=(420,366), offset_pct=(0.2,0.2), dest_sz=(200,200)).save("modified/"+img.rstrip().split('/')[1]+"_20_20_200_200.jpg")
|
||||
CropFace(image, eye_left=(252,364), eye_right=(420,366), offset_pct=(0.3,0.3), dest_sz=(200,200)).save("modified/"+img.rstrip().split('/')[1]+"_30_30_200_200.jpg")
|
||||
CropFace(image, eye_left=(252,364), eye_right=(420,366), offset_pct=(0.2,0.2)).save("modified/"+img.rstrip().split('/')[1]+"_20_20_70_70.jpg")
|
||||
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
This file was part of GSoC Project: Facemark API for OpenCV
|
||||
Final report: https://gist.github.com/kurnianggoro/74de9121e122ad0bd825176751d47ecc
|
||||
Student: Laksono Kurnianggoro
|
||||
Mentor: Delia Passalacqua
|
||||
*/
|
||||
|
||||
/*----------------------------------------------
|
||||
* Usage:
|
||||
* facemark_demo_aam <face_cascade_model> <eyes_cascade_model> <training_images> <annotation_files> [test_files]
|
||||
*
|
||||
* Example:
|
||||
* facemark_demo_aam ../face_cascade.xml ../eyes_cascade.xml ../images_train.txt ../points_train.txt ../test.txt
|
||||
*
|
||||
* Notes:
|
||||
* the user should provides the list of training images_train
|
||||
* accompanied by their corresponding landmarks location in separated files.
|
||||
* example of contents for images_train.txt:
|
||||
* ../trainset/image_0001.png
|
||||
* ../trainset/image_0002.png
|
||||
* example of contents for points_train.txt:
|
||||
* ../trainset/image_0001.pts
|
||||
* ../trainset/image_0002.pts
|
||||
* where the image_xxxx.pts contains the position of each face landmark.
|
||||
* example of the contents:
|
||||
* version: 1
|
||||
* n_points: 68
|
||||
* {
|
||||
* 115.167660 220.807529
|
||||
* 116.164839 245.721357
|
||||
* 120.208690 270.389841
|
||||
* ...
|
||||
* }
|
||||
* example of the dataset is available at https://ibug.doc.ic.ac.uk/download/annotations/lfpw.zip
|
||||
*--------------------------------------------------*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/geometry.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/face.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
using namespace cv::face;
|
||||
|
||||
bool myDetector( InputArray image, OutputArray ROIs, CascadeClassifier *face_cascade);
|
||||
bool getInitialFitting(Mat image, Rect face, std::vector<Point2f> s0,
|
||||
CascadeClassifier eyes_cascade, Mat & R, Point2f & Trans, float & scale);
|
||||
bool parseArguments(int argc, char** argv, String & cascade,
|
||||
String & model, String & images, String & annotations, String & testImages
|
||||
);
|
||||
|
||||
int main(int argc, char** argv )
|
||||
{
|
||||
String cascade_path,eyes_cascade_path,images_path, annotations_path, test_images_path;
|
||||
if(!parseArguments(argc, argv, cascade_path,eyes_cascade_path,images_path, annotations_path, test_images_path))
|
||||
return -1;
|
||||
|
||||
//! [instance_creation]
|
||||
/*create the facemark instance*/
|
||||
FacemarkAAM::Params params;
|
||||
params.scales.push_back(2.0);
|
||||
params.scales.push_back(4.0);
|
||||
params.model_filename = "AAM.yaml";
|
||||
Ptr<FacemarkAAM> facemark = FacemarkAAM::create(params);
|
||||
//! [instance_creation]
|
||||
|
||||
//! [load_dataset]
|
||||
/*Loads the dataset*/
|
||||
std::vector<String> images_train;
|
||||
std::vector<String> landmarks_train;
|
||||
loadDatasetList(images_path,annotations_path,images_train,landmarks_train);
|
||||
//! [load_dataset]
|
||||
|
||||
//! [add_samples]
|
||||
Mat image;
|
||||
std::vector<Point2f> facial_points;
|
||||
for(size_t i=0;i<images_train.size();i++){
|
||||
image = imread(images_train[i].c_str());
|
||||
loadFacePoints(landmarks_train[i],facial_points);
|
||||
facemark->addTrainingSample(image, facial_points);
|
||||
}
|
||||
//! [add_samples]
|
||||
|
||||
//! [training]
|
||||
/* trained model will be saved to AAM.yml */
|
||||
facemark->training();
|
||||
//! [training]
|
||||
|
||||
//! [load_test_images]
|
||||
/*test using some images*/
|
||||
String testFiles(images_path), testPts(annotations_path);
|
||||
if(!test_images_path.empty()){
|
||||
testFiles = test_images_path;
|
||||
testPts = test_images_path; //unused
|
||||
}
|
||||
std::vector<String> images;
|
||||
std::vector<String> facePoints;
|
||||
loadDatasetList(testFiles, testPts, images, facePoints);
|
||||
//! [load_test_images]
|
||||
|
||||
//! [trainsformation_variables]
|
||||
float scale ;
|
||||
Point2f T;
|
||||
Mat R;
|
||||
//! [trainsformation_variables]
|
||||
|
||||
//! [base_shape]
|
||||
FacemarkAAM::Data data;
|
||||
facemark->getData(&data);
|
||||
std::vector<Point2f> s0 = data.s0;
|
||||
//! [base_shape]
|
||||
|
||||
//! [fitting]
|
||||
/*fitting process*/
|
||||
std::vector<Rect> faces;
|
||||
//! [load_cascade_models]
|
||||
CascadeClassifier face_cascade(cascade_path);
|
||||
CascadeClassifier eyes_cascade(eyes_cascade_path);
|
||||
//! [load_cascade_models]
|
||||
for(int i=0;i<(int)images.size();i++){
|
||||
printf("image #%i ", i);
|
||||
//! [detect_face]
|
||||
image = imread(images[i]);
|
||||
myDetector(image, faces, &face_cascade);
|
||||
//! [detect_face]
|
||||
if(faces.size()>0){
|
||||
//! [get_initialization]
|
||||
std::vector<FacemarkAAM::Config> conf;
|
||||
std::vector<Rect> faces_eyes;
|
||||
for(unsigned j=0;j<faces.size();j++){
|
||||
if(getInitialFitting(image,faces[j],s0,eyes_cascade, R,T,scale)){
|
||||
conf.push_back(FacemarkAAM::Config(R,T,scale,(int)params.scales.size()-1));
|
||||
faces_eyes.push_back(faces[j]);
|
||||
}
|
||||
}
|
||||
//! [get_initialization]
|
||||
|
||||
//! [fitting_process]
|
||||
if(conf.size()>0){
|
||||
printf(" - face with eyes found %i ", (int)conf.size());
|
||||
std::vector<std::vector<Point2f> > landmarks;
|
||||
double newtime = (double)getTickCount();
|
||||
facemark->fitConfig(image, faces_eyes, landmarks, conf);
|
||||
double fittime = ((getTickCount() - newtime)/getTickFrequency());
|
||||
for(unsigned j=0;j<landmarks.size();j++){
|
||||
drawFacemarks(image, landmarks[j],Scalar(0,255,0));
|
||||
}
|
||||
printf("%f ms\n",fittime*1000);
|
||||
imshow("fitting", image);
|
||||
waitKey(0);
|
||||
}else{
|
||||
printf("initialization cannot be computed - skipping\n");
|
||||
}
|
||||
//! [fitting_process]
|
||||
}
|
||||
|
||||
} //for
|
||||
//! [fitting]
|
||||
}
|
||||
|
||||
bool myDetector(InputArray image, OutputArray faces, CascadeClassifier *face_cascade)
|
||||
{
|
||||
Mat gray;
|
||||
|
||||
if (image.channels() > 1)
|
||||
cvtColor(image, gray, COLOR_BGR2GRAY);
|
||||
else
|
||||
gray = image.getMat().clone();
|
||||
|
||||
equalizeHist(gray, gray);
|
||||
|
||||
std::vector<Rect> faces_;
|
||||
face_cascade->detectMultiScale(gray, faces_, 1.4, 2, CASCADE_SCALE_IMAGE, Size(30, 30));
|
||||
Mat(faces_).copyTo(faces);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getInitialFitting(Mat image, Rect face, std::vector<Point2f> s0 ,CascadeClassifier eyes_cascade, Mat & R, Point2f & Trans, float & scale){
|
||||
std::vector<Point2f> mybase;
|
||||
std::vector<Point2f> T;
|
||||
std::vector<Point2f> base = Mat(Mat(s0)+Scalar(image.cols/2,image.rows/2)).reshape(2);
|
||||
|
||||
std::vector<Point2f> base_shape,base_shape2 ;
|
||||
Point2f e1 = Point2f((float)((base[39].x+base[36].x)/2.0),(float)((base[39].y+base[36].y)/2.0)); //eye1
|
||||
Point2f e2 = Point2f((float)((base[45].x+base[42].x)/2.0),(float)((base[45].y+base[42].y)/2.0)); //eye2
|
||||
|
||||
if(face.width==0 || face.height==0) return false;
|
||||
|
||||
std::vector<Point2f> eye;
|
||||
bool found=false;
|
||||
|
||||
Mat faceROI = image( face);
|
||||
std::vector<Rect> eyes;
|
||||
|
||||
//-- In each face, detect eyes
|
||||
eyes_cascade.detectMultiScale( faceROI, eyes, 1.1, 2, CASCADE_SCALE_IMAGE, Size(20, 20) );
|
||||
if(eyes.size()==2){
|
||||
found = true;
|
||||
int j=0;
|
||||
Point2f c1( (float)(face.x + eyes[j].x + eyes[j].width*0.5), (float)(face.y + eyes[j].y + eyes[j].height*0.5));
|
||||
|
||||
j=1;
|
||||
Point2f c2( (float)(face.x + eyes[j].x + eyes[j].width*0.5), (float)(face.y + eyes[j].y + eyes[j].height*0.5));
|
||||
|
||||
Point2f pivot;
|
||||
double a0,a1;
|
||||
if(c1.x<c2.x){
|
||||
pivot = c1;
|
||||
a0 = atan2(c2.y-c1.y, c2.x-c1.x);
|
||||
}else{
|
||||
pivot = c2;
|
||||
a0 = atan2(c1.y-c2.y, c1.x-c2.x);
|
||||
}
|
||||
|
||||
scale = (float)(norm(Mat(c1)-Mat(c2))/norm(Mat(e1)-Mat(e2)));
|
||||
|
||||
mybase= Mat(Mat(s0)*scale).reshape(2);
|
||||
Point2f ey1 = Point2f((float)((mybase[39].x+mybase[36].x)/2.0),(float)((mybase[39].y+mybase[36].y)/2.0));
|
||||
Point2f ey2 = Point2f((float)((mybase[45].x+mybase[42].x)/2.0),(float)((mybase[45].y+mybase[42].y)/2.0));
|
||||
|
||||
|
||||
#define TO_DEGREE 180.0/3.14159265
|
||||
a1 = atan2(ey2.y-ey1.y, ey2.x-ey1.x);
|
||||
Mat rot = getRotationMatrix2D(Point2f(0,0), (a1-a0)*TO_DEGREE, 1.0);
|
||||
|
||||
rot(Rect(0,0,2,2)).convertTo(R, CV_32F);
|
||||
|
||||
base_shape = Mat(Mat(R*scale*Mat(Mat(s0).reshape(1)).t()).t()).reshape(2);
|
||||
ey1 = Point2f((float)((base_shape[39].x+base_shape[36].x)/2.0),(float)((base_shape[39].y+base_shape[36].y)/2.0));
|
||||
ey2 = Point2f((float)((base_shape[45].x+base_shape[42].x)/2.0),(float)((base_shape[45].y+base_shape[42].y)/2.0));
|
||||
|
||||
T.push_back(Point2f(pivot.x-ey1.x,pivot.y-ey1.y));
|
||||
Trans = Point2f(pivot.x-ey1.x,pivot.y-ey1.y);
|
||||
return true;
|
||||
}else{
|
||||
Trans = Point2f( (float)(face.x + face.width*0.5),(float)(face.y + face.height*0.5));
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
bool parseArguments(int argc, char** argv,
|
||||
String & cascade,
|
||||
String & model,
|
||||
String & images,
|
||||
String & annotations,
|
||||
String & test_images
|
||||
){
|
||||
const String keys =
|
||||
"{ @f face-cascade | | (required) path to the cascade model file for the face detector }"
|
||||
"{ @e eyes-cascade | | (required) path to the cascade model file for the eyes detector }"
|
||||
"{ @i images | | (required) path of a text file contains the list of paths to all training images}"
|
||||
"{ @a annotations | | (required) Path of a text file contains the list of paths to all annotations files}"
|
||||
"{ @t test-images | | Path of a text file contains the list of paths to the test images}"
|
||||
"{ help h usage ? | | facemark_demo_aam -face-cascade -eyes-cascade -images -annotations [-t]\n"
|
||||
" example: facemark_demo_aam ../face_cascade.xml ../eyes_cascade.xml ../images_train.txt ../points_train.txt ../test.txt}"
|
||||
;
|
||||
CommandLineParser parser(argc, argv,keys);
|
||||
parser.about("hello");
|
||||
|
||||
if (parser.has("help")){
|
||||
parser.printMessage();
|
||||
return false;
|
||||
}
|
||||
|
||||
cascade = String(parser.get<String>("face-cascade"));
|
||||
model = String(parser.get<string>("eyes-cascade"));
|
||||
images = String(parser.get<string>("images"));
|
||||
annotations = String(parser.get<string>("annotations"));
|
||||
test_images = String(parser.get<string>("test-images"));
|
||||
|
||||
if(cascade.empty() || model.empty() || images.empty() || annotations.empty()){
|
||||
std::cerr << "one or more required arguments are not found" << '\n';
|
||||
cout<<"face-cascade : "<<cascade.c_str()<<endl;
|
||||
cout<<"eyes-cascade : "<<model.c_str()<<endl;
|
||||
cout<<"images : "<<images.c_str()<<endl;
|
||||
cout<<"annotations : "<<annotations.c_str()<<endl;
|
||||
parser.printMessage();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
This file was part of GSoC Project: Facemark API for OpenCV
|
||||
Final report: https://gist.github.com/kurnianggoro/74de9121e122ad0bd825176751d47ecc
|
||||
Student: Laksono Kurnianggoro
|
||||
Mentor: Delia Passalacqua
|
||||
*/
|
||||
|
||||
/*----------------------------------------------
|
||||
* Usage:
|
||||
* facemark_demo_lbf <face_cascade_model> <saved_model_filename> <training_images> <annotation_files> [test_files]
|
||||
*
|
||||
* Example:
|
||||
* facemark_demo_lbf ../face_cascade.xml ../LBF.model ../images_train.txt ../points_train.txt ../test.txt
|
||||
*
|
||||
* Notes:
|
||||
* the user should provides the list of training images_train
|
||||
* accompanied by their corresponding landmarks location in separated files.
|
||||
* example of contents for images_train.txt:
|
||||
* ../trainset/image_0001.png
|
||||
* ../trainset/image_0002.png
|
||||
* example of contents for points_train.txt:
|
||||
* ../trainset/image_0001.pts
|
||||
* ../trainset/image_0002.pts
|
||||
* where the image_xxxx.pts contains the position of each face landmark.
|
||||
* example of the contents:
|
||||
* version: 1
|
||||
* n_points: 68
|
||||
* {
|
||||
* 115.167660 220.807529
|
||||
* 116.164839 245.721357
|
||||
* 120.208690 270.389841
|
||||
* ...
|
||||
* }
|
||||
* example of the dataset is available at https://ibug.doc.ic.ac.uk/download/annotations/ibug.zip
|
||||
*--------------------------------------------------*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/face.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
using namespace cv::face;
|
||||
|
||||
static bool myDetector( InputArray image, OutputArray roi, CascadeClassifier *face_detector);
|
||||
static bool parseArguments(int argc, char** argv, String & cascade,
|
||||
String & model, String & images, String & annotations, String & testImages
|
||||
);
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
String cascade_path,model_path,images_path, annotations_path, test_images_path;
|
||||
if(!parseArguments(argc, argv, cascade_path,model_path,images_path, annotations_path, test_images_path))
|
||||
return -1;
|
||||
|
||||
/*create the facemark instance*/
|
||||
FacemarkLBF::Params params;
|
||||
params.model_filename = model_path;
|
||||
params.cascade_face = cascade_path;
|
||||
Ptr<FacemarkLBF> facemark = FacemarkLBF::create(params);
|
||||
|
||||
CascadeClassifier face_cascade;
|
||||
face_cascade.load(params.cascade_face.c_str());
|
||||
facemark->setFaceDetector((FN_FaceDetector)myDetector, &face_cascade);
|
||||
|
||||
/*Loads the dataset*/
|
||||
std::vector<String> images_train;
|
||||
std::vector<String> landmarks_train;
|
||||
loadDatasetList(images_path,annotations_path,images_train,landmarks_train);
|
||||
|
||||
Mat image;
|
||||
std::vector<Point2f> facial_points;
|
||||
for(size_t i=0;i<images_train.size();i++){
|
||||
printf("%i/%i :: %s\n", (int)(i+1), (int)images_train.size(),images_train[i].c_str());
|
||||
image = imread(images_train[i].c_str());
|
||||
loadFacePoints(landmarks_train[i],facial_points);
|
||||
facemark->addTrainingSample(image, facial_points);
|
||||
}
|
||||
|
||||
/*train the Algorithm*/
|
||||
facemark->training();
|
||||
|
||||
/*test using some images*/
|
||||
String testFiles(images_path), testPts(annotations_path);
|
||||
if(!test_images_path.empty()){
|
||||
testFiles = test_images_path;
|
||||
testPts = test_images_path; //unused
|
||||
}
|
||||
std::vector<String> images;
|
||||
std::vector<String> facePoints;
|
||||
loadDatasetList(testFiles, testPts, images, facePoints);
|
||||
|
||||
std::vector<Rect> rects;
|
||||
CascadeClassifier cc(params.cascade_face.c_str());
|
||||
for(size_t i=0;i<images.size();i++){
|
||||
std::vector<std::vector<Point2f> > landmarks;
|
||||
cout<<images[i];
|
||||
Mat img = imread(images[i]);
|
||||
facemark->getFaces(img, rects);
|
||||
facemark->fit(img, rects, landmarks);
|
||||
|
||||
for(size_t j=0;j<rects.size();j++){
|
||||
drawFacemarks(img, landmarks[j], Scalar(0,0,255));
|
||||
rectangle(img, rects[j], Scalar(255,0,255));
|
||||
}
|
||||
|
||||
if(rects.size()>0){
|
||||
cout<<endl;
|
||||
imshow("result", img);
|
||||
waitKey(0);
|
||||
}else{
|
||||
cout<<"face not found"<<endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool myDetector(InputArray image, OutputArray faces, CascadeClassifier *face_cascade)
|
||||
{
|
||||
Mat gray;
|
||||
|
||||
if (image.channels() > 1)
|
||||
cvtColor(image, gray, COLOR_BGR2GRAY);
|
||||
else
|
||||
gray = image.getMat().clone();
|
||||
|
||||
equalizeHist(gray, gray);
|
||||
|
||||
std::vector<Rect> faces_;
|
||||
face_cascade->detectMultiScale(gray, faces_, 1.4, 2, CASCADE_SCALE_IMAGE, Size(30, 30));
|
||||
Mat(faces_).copyTo(faces);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parseArguments(int argc, char** argv,
|
||||
String & cascade,
|
||||
String & model,
|
||||
String & images,
|
||||
String & annotations,
|
||||
String & test_images
|
||||
){
|
||||
const String keys =
|
||||
"{ @c cascade | | (required) path to the face cascade xml file fo the face detector }"
|
||||
"{ @i images | | (required) path of a text file contains the list of paths to all training images}"
|
||||
"{ @a annotations | | (required) Path of a text file contains the list of paths to all annotations files}"
|
||||
"{ @m model | | (required) path to save the trained model }"
|
||||
"{ t test-images | | Path of a text file contains the list of paths to the test images}"
|
||||
"{ help h usage ? | | facemark_demo_lbf -cascade -images -annotations -model [-t] \n"
|
||||
" example: facemark_demo_lbf ../face_cascade.xml ../images_train.txt ../points_train.txt ../lbf.model}"
|
||||
;
|
||||
CommandLineParser parser(argc, argv,keys);
|
||||
parser.about("hello");
|
||||
|
||||
if (parser.has("help")){
|
||||
parser.printMessage();
|
||||
return false;
|
||||
}
|
||||
|
||||
cascade = String(parser.get<String>("cascade"));
|
||||
model = String(parser.get<string>("model"));
|
||||
images = String(parser.get<string>("images"));
|
||||
annotations = String(parser.get<string>("annotations"));
|
||||
test_images = String(parser.get<string>("t"));
|
||||
|
||||
cout<<"cascade : "<<cascade.c_str()<<endl;
|
||||
cout<<"model : "<<model.c_str()<<endl;
|
||||
cout<<"images : "<<images.c_str()<<endl;
|
||||
cout<<"annotations : "<<annotations.c_str()<<endl;
|
||||
|
||||
if(cascade.empty() || model.empty() || images.empty() || annotations.empty()){
|
||||
std::cerr << "one or more required arguments are not found" << '\n';
|
||||
|
||||
parser.printMessage();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
By downloading, copying, installing or using the software you agree to this
|
||||
license. If you do not agree to this license, do not download, install,
|
||||
copy or use the software.
|
||||
License Agreement
|
||||
For Open Source Computer Vision Library
|
||||
(3-clause BSD License)
|
||||
Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
Third party copyrights are property of their respective owners.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the names of the copyright holders nor the names of the contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
This software is provided by the copyright holders and contributors "as is" and
|
||||
any express or implied warranties, including, but not limited to, the implied
|
||||
warranties of merchantability and fitness for a particular purpose are
|
||||
disclaimed. In no event shall copyright holders or contributors be liable for
|
||||
any direct, indirect, incidental, special, exemplary, or consequential damages
|
||||
(including, but not limited to, procurement of substitute goods or services;
|
||||
loss of use, data, or profits; or business interruption) however caused
|
||||
and on any theory of liability, whether in contract, strict liability,
|
||||
or tort (including negligence or otherwise) arising in any way out of
|
||||
the use of this software, even if advised of the possibility of such damage.
|
||||
|
||||
This file was part of GSoC Project: Facemark API for OpenCV
|
||||
Final report: https://gist.github.com/kurnianggoro/74de9121e122ad0bd825176751d47ecc
|
||||
Student: Laksono Kurnianggoro
|
||||
Mentor: Delia Passalacqua
|
||||
*/
|
||||
|
||||
/*----------------------------------------------
|
||||
* Usage:
|
||||
* facemark_lbf_fitting <face_cascade_model> <lbf_model> <video_name>
|
||||
*
|
||||
* example:
|
||||
* facemark_lbf_fitting ../face_cascade.xml ../LBF.model ../video.mp4
|
||||
*
|
||||
* note: do not forget to provide the LBF_MODEL and DETECTOR_MODEL
|
||||
* the model are available at opencv_contrib/modules/face/data/
|
||||
*--------------------------------------------------*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/face.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
using namespace cv::face;
|
||||
|
||||
static bool myDetector(InputArray image, OutputArray ROIs, CascadeClassifier *face_cascade);
|
||||
static bool parseArguments(int argc, char** argv,
|
||||
String & cascade, String & model,String & video);
|
||||
|
||||
int main(int argc, char** argv ){
|
||||
String cascade_path,model_path,images_path, video_path;
|
||||
if(!parseArguments(argc, argv, cascade_path,model_path,video_path))
|
||||
return -1;
|
||||
|
||||
CascadeClassifier face_cascade;
|
||||
face_cascade.load(cascade_path);
|
||||
|
||||
FacemarkLBF::Params params;
|
||||
params.model_filename = model_path;
|
||||
params.cascade_face = cascade_path;
|
||||
|
||||
Ptr<FacemarkLBF> facemark = FacemarkLBF::create(params);
|
||||
facemark->setFaceDetector((FN_FaceDetector)myDetector, &face_cascade);
|
||||
facemark->loadModel(params.model_filename.c_str());
|
||||
|
||||
VideoCapture capture(video_path);
|
||||
Mat frame;
|
||||
|
||||
if( !capture.isOpened() ){
|
||||
printf("Error when reading vide\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Mat img;
|
||||
String text;
|
||||
char buff[255];
|
||||
double fittime;
|
||||
int nfaces;
|
||||
std::vector<Rect> rects,rects_scaled;
|
||||
std::vector<std::vector<Point2f> > landmarks;
|
||||
CascadeClassifier cc(params.cascade_face.c_str());
|
||||
namedWindow( "w", 1);
|
||||
for( ; ; )
|
||||
{
|
||||
capture >> frame;
|
||||
if(frame.empty())
|
||||
break;
|
||||
|
||||
double __time__ = (double)getTickCount();
|
||||
|
||||
float scale = (float)(400.0/frame.cols);
|
||||
resize(frame, img, Size((int)(frame.cols*scale), (int)(frame.rows*scale)), 0, 0, INTER_LINEAR_EXACT);
|
||||
|
||||
facemark->getFaces(img, rects);
|
||||
rects_scaled.clear();
|
||||
|
||||
for(int j=0;j<(int)rects.size();j++){
|
||||
rects_scaled.push_back(Rect(
|
||||
(int)(rects[j].x/scale),
|
||||
(int)(rects[j].y/scale),
|
||||
(int)(rects[j].width/scale),
|
||||
(int)(rects[j].height/scale)));
|
||||
}
|
||||
rects = rects_scaled;
|
||||
fittime=0;
|
||||
nfaces = (int)rects.size();
|
||||
if(rects.size()>0){
|
||||
double newtime = (double)getTickCount();
|
||||
|
||||
facemark->fit(frame, rects, landmarks);
|
||||
|
||||
|
||||
fittime = ((getTickCount() - newtime)/getTickFrequency());
|
||||
for(int j=0;j<(int)rects.size();j++){
|
||||
landmarks[j] = Mat(Mat(landmarks[j]));
|
||||
drawFacemarks(frame, landmarks[j], Scalar(0,0,255));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double fps = (getTickFrequency()/(getTickCount() - __time__));
|
||||
sprintf(buff, "faces: %i %03.2f fps, fit:%03.0f ms",nfaces,fps,fittime*1000);
|
||||
text = buff;
|
||||
putText(frame, text, Point(20,40), FONT_HERSHEY_PLAIN , 2.0,Scalar::all(255), 2, 8);
|
||||
|
||||
imshow("w", frame);
|
||||
waitKey(1); // waits to display frame
|
||||
}
|
||||
waitKey(0); // key press to close window
|
||||
}
|
||||
|
||||
bool myDetector(InputArray image, OutputArray faces, CascadeClassifier *face_cascade)
|
||||
{
|
||||
Mat gray;
|
||||
|
||||
if (image.channels() > 1)
|
||||
cvtColor(image, gray, COLOR_BGR2GRAY);
|
||||
else
|
||||
gray = image.getMat().clone();
|
||||
|
||||
equalizeHist(gray, gray);
|
||||
|
||||
std::vector<Rect> faces_;
|
||||
face_cascade->detectMultiScale(gray, faces_, 1.4, 2, CASCADE_SCALE_IMAGE, Size(30, 30));
|
||||
Mat(faces_).copyTo(faces);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parseArguments(int argc, char** argv,
|
||||
String & cascade,
|
||||
String & model,
|
||||
String & video
|
||||
){
|
||||
const String keys =
|
||||
"{ @c cascade | | (required) path to the cascade model file for the face detector }"
|
||||
"{ @m model | | (required) path to the trained model }"
|
||||
"{ @v video | | (required) path input video}"
|
||||
"{ help h usage ? | | facemark_lbf_fitting -cascade -model -video [-t]\n"
|
||||
" example: facemark_lbf_fitting ../face_cascade.xml ../LBF.model ../video.mp4}"
|
||||
;
|
||||
CommandLineParser parser(argc, argv,keys);
|
||||
parser.about("hello");
|
||||
|
||||
if (parser.has("help")){
|
||||
parser.printMessage();
|
||||
return false;
|
||||
}
|
||||
|
||||
cascade = String(parser.get<String>("cascade"));
|
||||
model = String(parser.get<string>("model"));
|
||||
video = String(parser.get<string>("video"));
|
||||
|
||||
|
||||
if(cascade.empty() || model.empty() || video.empty() ){
|
||||
std::cerr << "one or more required arguments are not found" << '\n';
|
||||
cout<<"cascade : "<<cascade.c_str()<<endl;
|
||||
cout<<"model : "<<model.c_str()<<endl;
|
||||
cout<<"video : "<<video.c_str()<<endl;
|
||||
parser.printMessage();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright (c) 2011. Philipp Wagner <bytefish[at]gmx[dot]de>.
|
||||
* Released to public domain under terms of the BSD Simplified license.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the organization nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* See <http://www.opensource.org/licenses/bsd-license>
|
||||
*/
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/face.hpp"
|
||||
#include "opencv2/core/utility.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
using namespace cv;
|
||||
using namespace cv::face;
|
||||
using namespace std;
|
||||
|
||||
static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, std::map<int, string>& labelsInfo, char separator = ';') {
|
||||
ifstream csv(filename.c_str());
|
||||
if (!csv) CV_Error(Error::StsBadArg, "No valid input file was given, please check the given filename.");
|
||||
string line, path, classlabel, info;
|
||||
while (getline(csv, line)) {
|
||||
stringstream liness(line);
|
||||
path.clear(); classlabel.clear(); info.clear();
|
||||
getline(liness, path, separator);
|
||||
getline(liness, classlabel, separator);
|
||||
getline(liness, info, separator);
|
||||
if(!path.empty() && !classlabel.empty()) {
|
||||
cout << "Processing " << path << endl;
|
||||
int label = atoi(classlabel.c_str());
|
||||
if(!info.empty())
|
||||
labelsInfo.insert(std::make_pair(label, info));
|
||||
// 'path' can be file, dir or wildcard path
|
||||
String root(path.c_str());
|
||||
vector<String> files;
|
||||
glob(root, files, true);
|
||||
for(vector<String>::const_iterator f = files.begin(); f != files.end(); ++f) {
|
||||
cout << "\t" << *f << endl;
|
||||
Mat img = imread(*f, IMREAD_GRAYSCALE);
|
||||
static int w=-1, h=-1;
|
||||
static bool showSmallSizeWarning = true;
|
||||
if(w>0 && h>0 && (w!=img.cols || h!=img.rows)) cout << "\t* Warning: images should be of the same size!" << endl;
|
||||
if(showSmallSizeWarning && (img.cols<50 || img.rows<50)) {
|
||||
cout << "* Warning: for better results images should be not smaller than 50x50!" << endl;
|
||||
showSmallSizeWarning = false;
|
||||
}
|
||||
images.push_back(img);
|
||||
labels.push_back(label);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
// Check for valid command line arguments, print usage
|
||||
// if no arguments were given.
|
||||
if (argc != 2 && argc != 3) {
|
||||
cout << "Usage: " << argv[0] << " <csv> [arg2]\n"
|
||||
<< "\t<csv> - path to config file in CSV format\n"
|
||||
<< "\targ2 - if the 2nd argument is provided (with any value) "
|
||||
<< "the advanced stuff is run and shown to console.\n"
|
||||
<< "The CSV config file consists of the following lines:\n"
|
||||
<< "<path>;<label>[;<comment>]\n"
|
||||
<< "\t<path> - file, dir or wildcard path\n"
|
||||
<< "\t<label> - non-negative integer person label\n"
|
||||
<< "\t<comment> - optional comment string (e.g. person name)"
|
||||
<< endl;
|
||||
exit(1);
|
||||
}
|
||||
// Get the path to your CSV.
|
||||
string fn_csv = string(argv[1]);
|
||||
// These vectors hold the images and corresponding labels.
|
||||
vector<Mat> images;
|
||||
vector<int> labels;
|
||||
std::map<int, string> labelsInfo;
|
||||
// Read in the data. This can fail if no valid
|
||||
// input filename is given.
|
||||
try {
|
||||
read_csv(fn_csv, images, labels, labelsInfo);
|
||||
} catch (const cv::Exception& e) {
|
||||
cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl;
|
||||
// nothing more we can do
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Quit if there are not enough images for this demo.
|
||||
if(images.size() <= 1) {
|
||||
string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!";
|
||||
CV_Error(Error::StsError, error_message);
|
||||
}
|
||||
// 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.
|
||||
Mat testSample = images[images.size() - 1];
|
||||
int nlabels = (int)labels.size();
|
||||
int testLabel = labels[nlabels-1];
|
||||
images.pop_back();
|
||||
labels.pop_back();
|
||||
// The following lines create an Eigenfaces model for
|
||||
// face recognition and train it with the images and
|
||||
// labels read from the given CSV file.
|
||||
// This here is a full PCA, if you just want to keep
|
||||
// 10 principal components (read Eigenfaces), then call
|
||||
// the factory method like this:
|
||||
//
|
||||
// EigenFaceRecognizer::create(10);
|
||||
//
|
||||
// If you want to create a FaceRecognizer with a
|
||||
// confidennce threshold, call it with:
|
||||
//
|
||||
// EigenFaceRecognizer::create(10, 123.0);
|
||||
//
|
||||
Ptr<EigenFaceRecognizer> model = EigenFaceRecognizer::create();
|
||||
for( int i = 0; i < nlabels; i++ )
|
||||
model->setLabelInfo(i, labelsInfo[i]);
|
||||
model->train(images, labels);
|
||||
string saveModelPath = "face-rec-model.txt";
|
||||
cout << "Saving the trained model to " << saveModelPath << endl;
|
||||
model->save(saveModelPath);
|
||||
|
||||
// The following line predicts the label of a given
|
||||
// test image:
|
||||
int predictedLabel = model->predict(testSample);
|
||||
//
|
||||
// To get the confidence of a prediction call the model with:
|
||||
//
|
||||
// int predictedLabel = -1;
|
||||
// double confidence = 0.0;
|
||||
// model->predict(testSample, predictedLabel, confidence);
|
||||
//
|
||||
string result_message = format("Predicted class = %d / Actual class = %d.", predictedLabel, testLabel);
|
||||
cout << result_message << endl;
|
||||
if( (predictedLabel == testLabel) && !model->getLabelInfo(predictedLabel).empty() )
|
||||
cout << format("%d-th label's info: %s", predictedLabel, model->getLabelInfo(predictedLabel).c_str()) << endl;
|
||||
|
||||
// advanced stuff
|
||||
if(argc>2) {
|
||||
// Sometimes you'll need to get/set internal model data,
|
||||
// which isn't exposed by the public cv::FaceRecognizer.
|
||||
// Since each cv::FaceRecognizer is derived from a
|
||||
// cv::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->setThreshold(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
|
||||
predictedLabel = model->predict(testSample);
|
||||
cout << "Predicted class = " << predictedLabel << endl;
|
||||
// Here is how to get the eigenvalues of this Eigenfaces model:
|
||||
Mat eigenvalues = model->getEigenValues();
|
||||
// And we can do the same to display the Eigenvectors (read Eigenfaces):
|
||||
Mat W = model->getEigenVectors();
|
||||
// From this we will display the (at most) first 10 Eigenfaces:
|
||||
for (int i = 0; i < min(10, W.cols); i++) {
|
||||
string msg = format("Eigenvalue #%d = %.5f", i, eigenvalues.at<double>(i));
|
||||
cout << msg << endl;
|
||||
// get eigenvector #i
|
||||
Mat ev = W.col(i).clone();
|
||||
// Reshape to original size & normalize to [0...255] for imshow.
|
||||
Mat grayscale;
|
||||
normalize(ev.reshape(1), grayscale, 0, 255, NORM_MINMAX, CV_8UC1);
|
||||
// Show the image & apply a Jet colormap for better sensing.
|
||||
Mat cgrayscale;
|
||||
applyColorMap(grayscale, cgrayscale, COLORMAP_JET);
|
||||
imshow(format("%d", i), cgrayscale);
|
||||
}
|
||||
waitKey(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright (c) 2011. Philipp Wagner <bytefish[at]gmx[dot]de>.
|
||||
* Released to public domain under terms of the BSD Simplified license.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the organization nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* See <http://www.opensource.org/licenses/bsd-license>
|
||||
*/
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/face.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace cv::face;
|
||||
using namespace std;
|
||||
|
||||
static Mat norm_0_255(InputArray _src) {
|
||||
Mat src = _src.getMat();
|
||||
// Create and return normalized image:
|
||||
Mat dst;
|
||||
switch(src.channels()) {
|
||||
case 1:
|
||||
cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC1);
|
||||
break;
|
||||
case 3:
|
||||
cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC3);
|
||||
break;
|
||||
default:
|
||||
src.copyTo(dst);
|
||||
break;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') {
|
||||
std::ifstream file(filename.c_str(), ifstream::in);
|
||||
if (!file) {
|
||||
string error_message = "No valid input file was given, please check the given filename.";
|
||||
CV_Error(Error::StsBadArg, error_message);
|
||||
}
|
||||
string line, path, classlabel;
|
||||
while (getline(file, line)) {
|
||||
stringstream liness(line);
|
||||
getline(liness, path, separator);
|
||||
getline(liness, classlabel);
|
||||
if(!path.empty() && !classlabel.empty()) {
|
||||
images.push_back(imread(path, 0));
|
||||
labels.push_back(atoi(classlabel.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
// Check for valid command line arguments, print usage
|
||||
// if no arguments were given.
|
||||
if (argc < 2) {
|
||||
cout << "usage: " << argv[0] << " <csv.ext> <output_folder> " << endl;
|
||||
exit(1);
|
||||
}
|
||||
string output_folder = ".";
|
||||
if (argc == 3) {
|
||||
output_folder = string(argv[2]);
|
||||
}
|
||||
// Get the path to your CSV.
|
||||
string fn_csv = string(argv[1]);
|
||||
// These vectors hold the images and corresponding labels.
|
||||
vector<Mat> images;
|
||||
vector<int> labels;
|
||||
// Read in the data. This can fail if no valid
|
||||
// input filename is given.
|
||||
try {
|
||||
read_csv(fn_csv, images, labels);
|
||||
} catch (const cv::Exception& e) {
|
||||
cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl;
|
||||
// nothing more we can do
|
||||
exit(1);
|
||||
}
|
||||
// Quit if there are not enough images for this demo.
|
||||
if(images.size() <= 1) {
|
||||
string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!";
|
||||
CV_Error(Error::StsError, error_message);
|
||||
}
|
||||
// Get the height from the first image. We'll need this
|
||||
// later in code to reshape the images to their original
|
||||
// size:
|
||||
int 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::BasicFaceRecognizer on) and the test data we test
|
||||
// the model with, do not overlap.
|
||||
Mat testSample = images[images.size() - 1];
|
||||
int testLabel = labels[labels.size() - 1];
|
||||
images.pop_back();
|
||||
labels.pop_back();
|
||||
// The following lines create an Eigenfaces model for
|
||||
// face recognition and train it with the images and
|
||||
// labels read from the given CSV file.
|
||||
// This here is a full PCA, if you just want to keep
|
||||
// 10 principal components (read Eigenfaces), then call
|
||||
// the factory method like this:
|
||||
//
|
||||
// EigenFaceRecognizer::create(10);
|
||||
//
|
||||
// If you want to create a FaceRecognizer with a
|
||||
// confidence threshold (e.g. 123.0), call it with:
|
||||
//
|
||||
// EigenFaceRecognizer::create(10, 123.0);
|
||||
//
|
||||
// If you want to use _all_ Eigenfaces and have a threshold,
|
||||
// then call the method like this:
|
||||
//
|
||||
// EigenFaceRecognizer::create(0, 123.0);
|
||||
//
|
||||
Ptr<EigenFaceRecognizer> model = EigenFaceRecognizer::create();
|
||||
model->train(images, labels);
|
||||
// The following line predicts the label of a given
|
||||
// test image:
|
||||
int predictedLabel = model->predict(testSample);
|
||||
//
|
||||
// To get the confidence of a prediction call the model with:
|
||||
//
|
||||
// int predictedLabel = -1;
|
||||
// double confidence = 0.0;
|
||||
// model->predict(testSample, predictedLabel, confidence);
|
||||
//
|
||||
string result_message = format("Predicted class = %d / Actual class = %d.", predictedLabel, testLabel);
|
||||
cout << result_message << endl;
|
||||
// Here is how to get the eigenvalues of this Eigenfaces model:
|
||||
Mat eigenvalues = model->getEigenValues();
|
||||
// And we can do the same to display the Eigenvectors (read Eigenfaces):
|
||||
Mat W = model->getEigenVectors();
|
||||
// Get the sample mean from the training data
|
||||
Mat mean = model->getMean();
|
||||
// Display or save:
|
||||
if(argc == 2) {
|
||||
imshow("mean", norm_0_255(mean.reshape(1, images[0].rows)));
|
||||
} else {
|
||||
imwrite(format("%s/mean.png", output_folder.c_str()), norm_0_255(mean.reshape(1, images[0].rows)));
|
||||
}
|
||||
// Display or save the Eigenfaces:
|
||||
for (int i = 0; i < min(10, W.cols); i++) {
|
||||
string msg = format("Eigenvalue #%d = %.5f", i, eigenvalues.at<double>(i));
|
||||
cout << msg << endl;
|
||||
// get eigenvector #i
|
||||
Mat ev = W.col(i).clone();
|
||||
// Reshape to original size & normalize to [0...255] for imshow.
|
||||
Mat grayscale = norm_0_255(ev.reshape(1, height));
|
||||
// Show the image & apply a Jet colormap for better sensing.
|
||||
Mat cgrayscale;
|
||||
applyColorMap(grayscale, cgrayscale, COLORMAP_JET);
|
||||
// Display or save:
|
||||
if(argc == 2) {
|
||||
imshow(format("eigenface_%d", i), cgrayscale);
|
||||
} else {
|
||||
imwrite(format("%s/eigenface_%d.png", output_folder.c_str(), i), norm_0_255(cgrayscale));
|
||||
}
|
||||
}
|
||||
|
||||
// Display or save the image reconstruction at some predefined steps:
|
||||
for(int num_components = min(W.cols, 10); num_components < min(W.cols, 300); num_components+=15) {
|
||||
// slice the eigenvectors from the model
|
||||
Mat evs = Mat(W, Range::all(), Range(0, num_components));
|
||||
Mat projection = LDA::subspaceProject(evs, mean, images[0].reshape(1,1));
|
||||
Mat reconstruction = LDA::subspaceReconstruct(evs, mean, projection);
|
||||
// Normalize the result:
|
||||
reconstruction = norm_0_255(reconstruction.reshape(1, images[0].rows));
|
||||
// Display or save:
|
||||
if(argc == 2) {
|
||||
imshow(format("eigenface_reconstruction_%d", num_components), reconstruction);
|
||||
} else {
|
||||
imwrite(format("%s/eigenface_reconstruction_%d.png", output_folder.c_str(), num_components), reconstruction);
|
||||
}
|
||||
}
|
||||
// Display if we are not writing to an output folder:
|
||||
if(argc == 2) {
|
||||
waitKey(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (c) 2011. Philipp Wagner <bytefish[at]gmx[dot]de>.
|
||||
* Released to public domain under terms of the BSD Simplified license.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the organization nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* See <http://www.opensource.org/licenses/bsd-license>
|
||||
*/
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/face.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace cv::face;
|
||||
using namespace std;
|
||||
|
||||
static Mat norm_0_255(InputArray _src) {
|
||||
Mat src = _src.getMat();
|
||||
// Create and return normalized image:
|
||||
Mat dst;
|
||||
switch(src.channels()) {
|
||||
case 1:
|
||||
cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC1);
|
||||
break;
|
||||
case 3:
|
||||
cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC3);
|
||||
break;
|
||||
default:
|
||||
src.copyTo(dst);
|
||||
break;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') {
|
||||
std::ifstream file(filename.c_str(), ifstream::in);
|
||||
if (!file) {
|
||||
string error_message = "No valid input file was given, please check the given filename.";
|
||||
CV_Error(Error::StsBadArg, error_message);
|
||||
}
|
||||
string line, path, classlabel;
|
||||
while (getline(file, line)) {
|
||||
stringstream liness(line);
|
||||
getline(liness, path, separator);
|
||||
getline(liness, classlabel);
|
||||
if(!path.empty() && !classlabel.empty()) {
|
||||
images.push_back(imread(path, 0));
|
||||
labels.push_back(atoi(classlabel.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
// Check for valid command line arguments, print usage
|
||||
// if no arguments were given.
|
||||
if (argc < 2) {
|
||||
cout << "usage: " << argv[0] << " <csv.ext> <output_folder> " << endl;
|
||||
exit(1);
|
||||
}
|
||||
string output_folder = ".";
|
||||
if (argc == 3) {
|
||||
output_folder = string(argv[2]);
|
||||
}
|
||||
// Get the path to your CSV.
|
||||
string fn_csv = string(argv[1]);
|
||||
// These vectors hold the images and corresponding labels.
|
||||
vector<Mat> images;
|
||||
vector<int> labels;
|
||||
// Read in the data. This can fail if no valid
|
||||
// input filename is given.
|
||||
try {
|
||||
read_csv(fn_csv, images, labels);
|
||||
} catch (const cv::Exception& e) {
|
||||
cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl;
|
||||
// nothing more we can do
|
||||
exit(1);
|
||||
}
|
||||
// Quit if there are not enough images for this demo.
|
||||
if(images.size() <= 1) {
|
||||
string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!";
|
||||
CV_Error(Error::StsError, error_message);
|
||||
}
|
||||
// Get the height from the first image. We'll need this
|
||||
// later in code to reshape the images to their original
|
||||
// size:
|
||||
int 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::BasicFaceRecognizer on) and the test data we test
|
||||
// the model with, do not overlap.
|
||||
Mat testSample = images[images.size() - 1];
|
||||
int testLabel = labels[labels.size() - 1];
|
||||
images.pop_back();
|
||||
labels.pop_back();
|
||||
// The following lines create an Fisherfaces model for
|
||||
// face recognition and train it with the images and
|
||||
// labels read from the given CSV file.
|
||||
// If you just want to keep 10 Fisherfaces, then call
|
||||
// the factory method like this:
|
||||
//
|
||||
// FisherFaceRecognizer::create(10);
|
||||
//
|
||||
// However it is not useful to discard Fisherfaces! Please
|
||||
// always try to use _all_ available Fisherfaces for
|
||||
// classification.
|
||||
//
|
||||
// If you want to create a FaceRecognizer with a
|
||||
// confidence threshold (e.g. 123.0) and use _all_
|
||||
// Fisherfaces, then call it with:
|
||||
//
|
||||
// FisherFaceRecognizer::create(0, 123.0);
|
||||
//
|
||||
Ptr<FisherFaceRecognizer> model = FisherFaceRecognizer::create();
|
||||
model->train(images, labels);
|
||||
// The following line predicts the label of a given
|
||||
// test image:
|
||||
int predictedLabel = model->predict(testSample);
|
||||
//
|
||||
// To get the confidence of a prediction call the model with:
|
||||
//
|
||||
// int predictedLabel = -1;
|
||||
// double confidence = 0.0;
|
||||
// model->predict(testSample, predictedLabel, confidence);
|
||||
//
|
||||
string result_message = format("Predicted class = %d / Actual class = %d.", predictedLabel, testLabel);
|
||||
cout << result_message << endl;
|
||||
// Here is how to get the eigenvalues of this Eigenfaces model:
|
||||
Mat eigenvalues = model->getEigenValues();
|
||||
// And we can do the same to display the Eigenvectors (read Eigenfaces):
|
||||
Mat W = model->getEigenVectors();
|
||||
// Get the sample mean from the training data
|
||||
Mat mean = model->getMean();
|
||||
// Display or save:
|
||||
if(argc == 2) {
|
||||
imshow("mean", norm_0_255(mean.reshape(1, images[0].rows)));
|
||||
} else {
|
||||
imwrite(format("%s/mean.png", output_folder.c_str()), norm_0_255(mean.reshape(1, images[0].rows)));
|
||||
}
|
||||
// Display or save the first, at most 16 Fisherfaces:
|
||||
for (int i = 0; i < min(16, W.cols); i++) {
|
||||
string msg = format("Eigenvalue #%d = %.5f", i, eigenvalues.at<double>(i));
|
||||
cout << msg << endl;
|
||||
// get eigenvector #i
|
||||
Mat ev = W.col(i).clone();
|
||||
// Reshape to original size & normalize to [0...255] for imshow.
|
||||
Mat grayscale = norm_0_255(ev.reshape(1, height));
|
||||
// Show the image & apply a Bone colormap for better sensing.
|
||||
Mat cgrayscale;
|
||||
applyColorMap(grayscale, cgrayscale, COLORMAP_BONE);
|
||||
// Display or save:
|
||||
if(argc == 2) {
|
||||
imshow(format("fisherface_%d", i), cgrayscale);
|
||||
} else {
|
||||
imwrite(format("%s/fisherface_%d.png", output_folder.c_str(), i), norm_0_255(cgrayscale));
|
||||
}
|
||||
}
|
||||
// Display or save the image reconstruction at some predefined steps:
|
||||
for(int num_component = 0; num_component < min(16, W.cols); num_component++) {
|
||||
// Slice the Fisherface from the model:
|
||||
Mat ev = W.col(num_component);
|
||||
Mat projection = LDA::subspaceProject(ev, mean, images[0].reshape(1,1));
|
||||
Mat reconstruction = LDA::subspaceReconstruct(ev, mean, projection);
|
||||
// Normalize the result:
|
||||
reconstruction = norm_0_255(reconstruction.reshape(1, images[0].rows));
|
||||
// Display or save:
|
||||
if(argc == 2) {
|
||||
imshow(format("fisherface_reconstruction_%d", num_component), reconstruction);
|
||||
} else {
|
||||
imwrite(format("%s/fisherface_reconstruction_%d.png", output_folder.c_str(), num_component), reconstruction);
|
||||
}
|
||||
}
|
||||
// Display if we are not writing to an output folder:
|
||||
if(argc == 2) {
|
||||
waitKey(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2011. Philipp Wagner <bytefish[at]gmx[dot]de>.
|
||||
* Released to public domain under terms of the BSD Simplified license.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the organization nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* See <http://www.opensource.org/licenses/bsd-license>
|
||||
*/
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/face.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace cv::face;
|
||||
using namespace std;
|
||||
|
||||
static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') {
|
||||
std::ifstream file(filename.c_str(), ifstream::in);
|
||||
if (!file) {
|
||||
string error_message = "No valid input file was given, please check the given filename.";
|
||||
CV_Error(Error::StsBadArg, error_message);
|
||||
}
|
||||
string line, path, classlabel;
|
||||
while (getline(file, line)) {
|
||||
stringstream liness(line);
|
||||
getline(liness, path, separator);
|
||||
getline(liness, classlabel);
|
||||
if(!path.empty() && !classlabel.empty()) {
|
||||
images.push_back(imread(path, 0));
|
||||
labels.push_back(atoi(classlabel.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
// Check for valid command line arguments, print usage
|
||||
// if no arguments were given.
|
||||
if (argc != 2) {
|
||||
cout << "usage: " << argv[0] << " <csv.ext>" << endl;
|
||||
exit(1);
|
||||
}
|
||||
// Get the path to your CSV.
|
||||
string fn_csv = string(argv[1]);
|
||||
// These vectors hold the images and corresponding labels.
|
||||
vector<Mat> images;
|
||||
vector<int> labels;
|
||||
// Read in the data. This can fail if no valid
|
||||
// input filename is given.
|
||||
try {
|
||||
read_csv(fn_csv, images, labels);
|
||||
} catch (const cv::Exception& e) {
|
||||
cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl;
|
||||
// nothing more we can do
|
||||
exit(1);
|
||||
}
|
||||
// Quit if there are not enough images for this demo.
|
||||
if(images.size() <= 1) {
|
||||
string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!";
|
||||
CV_Error(Error::StsError, error_message);
|
||||
}
|
||||
// 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::LBPHFaceRecognizer on) and the test data we test
|
||||
// the model with, do not overlap.
|
||||
Mat testSample = images[images.size() - 1];
|
||||
int testLabel = labels[labels.size() - 1];
|
||||
images.pop_back();
|
||||
labels.pop_back();
|
||||
// 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:
|
||||
//
|
||||
// cv::face::LBPHFaceRecognizer::create(2, 16);
|
||||
//
|
||||
// And if you want a threshold (e.g. 123.0) call it with its default values:
|
||||
//
|
||||
// cv::face::LBPHFaceRecognizer::create(1,8,8,8,123.0)
|
||||
//
|
||||
Ptr<LBPHFaceRecognizer> model = LBPHFaceRecognizer::create();
|
||||
model->train(images, labels);
|
||||
// The following line predicts the label of a given
|
||||
// test image:
|
||||
int predictedLabel = model->predict(testSample);
|
||||
//
|
||||
// To get the confidence of a prediction call the model with:
|
||||
//
|
||||
// int predictedLabel = -1;
|
||||
// double confidence = 0.0;
|
||||
// model->predict(testSample, predictedLabel, confidence);
|
||||
//
|
||||
string result_message = format("Predicted class = %d / Actual class = %d.", predictedLabel, testLabel);
|
||||
cout << result_message << endl;
|
||||
// First we'll use it to set the threshold of the LBPHFaceRecognizer
|
||||
// to 0.0 without retraining the model. This can be useful if
|
||||
// you are evaluating the model:
|
||||
//
|
||||
model->setThreshold(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
|
||||
predictedLabel = model->predict(testSample);
|
||||
cout << "Predicted class = " << predictedLabel << endl;
|
||||
// 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:
|
||||
cout << "Model Information:" << endl;
|
||||
string model_info = format("\tLBPH(radius=%i, neighbors=%i, grid_x=%i, grid_y=%i, threshold=%.2f)",
|
||||
model->getRadius(),
|
||||
model->getNeighbors(),
|
||||
model->getGridX(),
|
||||
model->getGridY(),
|
||||
model->getThreshold());
|
||||
cout << model_info << endl;
|
||||
// We could get the histograms for example:
|
||||
vector<Mat> histograms = model->getHistograms();
|
||||
// But should I really visualize it? Probably the length is interesting:
|
||||
cout << "Size of the histograms: " << histograms[0].total() << endl;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright (c) 2011. Philipp Wagner <bytefish[at]gmx[dot]de>.
|
||||
* Released to public domain under terms of the BSD Simplified license.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the organization nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* See <http://www.opensource.org/licenses/bsd-license>
|
||||
*/
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/face.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace cv::face;
|
||||
using namespace std;
|
||||
|
||||
static Mat norm_0_255(InputArray _src) {
|
||||
Mat src = _src.getMat();
|
||||
// Create and return normalized image:
|
||||
Mat dst;
|
||||
switch(src.channels()) {
|
||||
case 1:
|
||||
cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC1);
|
||||
break;
|
||||
case 3:
|
||||
cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC3);
|
||||
break;
|
||||
default:
|
||||
src.copyTo(dst);
|
||||
break;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') {
|
||||
std::ifstream file(filename.c_str(), ifstream::in);
|
||||
if (!file) {
|
||||
string error_message = "No valid input file was given, please check the given filename.";
|
||||
CV_Error(Error::StsBadArg, error_message);
|
||||
}
|
||||
string line, path, classlabel;
|
||||
while (getline(file, line)) {
|
||||
stringstream liness(line);
|
||||
getline(liness, path, separator);
|
||||
getline(liness, classlabel);
|
||||
if(!path.empty() && !classlabel.empty()) {
|
||||
images.push_back(imread(path, 0));
|
||||
labels.push_back(atoi(classlabel.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
// Check for valid command line arguments, print usage
|
||||
// if no arguments were given.
|
||||
if (argc < 2) {
|
||||
cout << "usage: " << argv[0] << " <csv.ext> <output_folder> " << endl;
|
||||
exit(1);
|
||||
}
|
||||
string output_folder = ".";
|
||||
if (argc == 3) {
|
||||
output_folder = string(argv[2]);
|
||||
}
|
||||
// Get the path to your CSV.
|
||||
string fn_csv = string(argv[1]);
|
||||
// These vectors hold the images and corresponding labels.
|
||||
vector<Mat> images;
|
||||
vector<int> labels;
|
||||
// Read in the data. This can fail if no valid
|
||||
// input filename is given.
|
||||
try {
|
||||
read_csv(fn_csv, images, labels);
|
||||
} catch (const cv::Exception& e) {
|
||||
cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl;
|
||||
// nothing more we can do
|
||||
exit(1);
|
||||
}
|
||||
// Quit if there are not enough images for this demo.
|
||||
if(images.size() <= 1) {
|
||||
string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!";
|
||||
CV_Error(Error::StsError, error_message);
|
||||
}
|
||||
// Get the height from the first image. We'll need this
|
||||
// later in code to reshape the images to their original
|
||||
// size:
|
||||
int 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.
|
||||
Mat testSample = images[images.size() - 1];
|
||||
int testLabel = labels[labels.size() - 1];
|
||||
images.pop_back();
|
||||
labels.pop_back();
|
||||
// The following lines create an Eigenfaces model for
|
||||
// face recognition and train it with the images and
|
||||
// labels read from the given CSV file.
|
||||
// This here is a full PCA, if you just want to keep
|
||||
// 10 principal components (read Eigenfaces), then call
|
||||
// the factory method like this:
|
||||
//
|
||||
// cv::face::EigenFaceRecognizer::create(10);
|
||||
//
|
||||
// If you want to create a FaceRecognizer with a
|
||||
// confidence threshold (e.g. 123.0), call it with:
|
||||
//
|
||||
// cv::face::EigenFaceRecognizer::create(10, 123.0);
|
||||
//
|
||||
// If you want to use _all_ Eigenfaces and have a threshold,
|
||||
// then call the method like this:
|
||||
//
|
||||
// cv::face::EigenFaceRecognizer::create(0, 123.0);
|
||||
//
|
||||
Ptr<EigenFaceRecognizer> model0 = EigenFaceRecognizer::create();
|
||||
model0->train(images, labels);
|
||||
// save the model to eigenfaces_at.yaml
|
||||
model0->save("eigenfaces_at.yml");
|
||||
//
|
||||
//
|
||||
// Now create a new Eigenfaces Recognizer
|
||||
//
|
||||
Ptr<EigenFaceRecognizer> model1 = Algorithm::load<EigenFaceRecognizer>("eigenfaces_at.yml");
|
||||
// The following line predicts the label of a given
|
||||
// test image:
|
||||
int predictedLabel = model1->predict(testSample);
|
||||
//
|
||||
// To get the confidence of a prediction call the model with:
|
||||
//
|
||||
// int predictedLabel = -1;
|
||||
// double confidence = 0.0;
|
||||
// model->predict(testSample, predictedLabel, confidence);
|
||||
//
|
||||
string result_message = format("Predicted class = %d / Actual class = %d.", predictedLabel, testLabel);
|
||||
cout << result_message << endl;
|
||||
// Here is how to get the eigenvalues of this Eigenfaces model:
|
||||
Mat eigenvalues = model1->getEigenValues();
|
||||
// And we can do the same to display the Eigenvectors (read Eigenfaces):
|
||||
Mat W = model1->getEigenVectors();
|
||||
// Get the sample mean from the training data
|
||||
Mat mean = model1->getMean();
|
||||
// Display or save:
|
||||
if(argc == 2) {
|
||||
imshow("mean", norm_0_255(mean.reshape(1, images[0].rows)));
|
||||
} else {
|
||||
imwrite(format("%s/mean.png", output_folder.c_str()), norm_0_255(mean.reshape(1, images[0].rows)));
|
||||
}
|
||||
// Display or save the Eigenfaces:
|
||||
for (int i = 0; i < min(10, W.cols); i++) {
|
||||
string msg = format("Eigenvalue #%d = %.5f", i, eigenvalues.at<double>(i));
|
||||
cout << msg << endl;
|
||||
// get eigenvector #i
|
||||
Mat ev = W.col(i).clone();
|
||||
// Reshape to original size & normalize to [0...255] for imshow.
|
||||
Mat grayscale = norm_0_255(ev.reshape(1, height));
|
||||
// Show the image & apply a Jet colormap for better sensing.
|
||||
Mat cgrayscale;
|
||||
applyColorMap(grayscale, cgrayscale, COLORMAP_JET);
|
||||
// Display or save:
|
||||
if(argc == 2) {
|
||||
imshow(format("eigenface_%d", i), cgrayscale);
|
||||
} else {
|
||||
imwrite(format("%s/eigenface_%d.png", output_folder.c_str(), i), norm_0_255(cgrayscale));
|
||||
}
|
||||
}
|
||||
// Display or save the image reconstruction at some predefined steps:
|
||||
for(int num_components = 10; num_components < 300; num_components+=15) {
|
||||
// slice the eigenvectors from the model
|
||||
Mat evs = Mat(W, Range::all(), Range(0, num_components));
|
||||
Mat projection = LDA::subspaceProject(evs, mean, images[0].reshape(1,1));
|
||||
Mat reconstruction = LDA::subspaceReconstruct(evs, mean, projection);
|
||||
// Normalize the result:
|
||||
reconstruction = norm_0_255(reconstruction.reshape(1, images[0].rows));
|
||||
// Display or save:
|
||||
if(argc == 2) {
|
||||
imshow(format("eigenface_reconstruction_%d", num_components), reconstruction);
|
||||
} else {
|
||||
imwrite(format("%s/eigenface_reconstruction_%d.png", output_folder.c_str(), num_components), reconstruction);
|
||||
}
|
||||
}
|
||||
// Display if we are not writing to an output folder:
|
||||
if(argc == 2) {
|
||||
waitKey(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (c) 2011. Philipp Wagner <bytefish[at]gmx[dot]de>.
|
||||
* Released to public domain under terms of the BSD Simplified license.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the organization nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* See <http://www.opensource.org/licenses/bsd-license>
|
||||
*/
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/face.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/xobjdetect.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace cv::face;
|
||||
using namespace std;
|
||||
|
||||
static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') {
|
||||
std::ifstream file(filename.c_str(), ifstream::in);
|
||||
if (!file) {
|
||||
string error_message = "No valid input file was given, please check the given filename.";
|
||||
CV_Error(Error::StsBadArg, error_message);
|
||||
}
|
||||
string line, path, classlabel;
|
||||
while (getline(file, line)) {
|
||||
stringstream liness(line);
|
||||
getline(liness, path, separator);
|
||||
getline(liness, classlabel);
|
||||
if(!path.empty() && !classlabel.empty()) {
|
||||
images.push_back(imread(path, 0));
|
||||
labels.push_back(atoi(classlabel.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
// Check for valid command line arguments, print usage
|
||||
// if no arguments were given.
|
||||
if (argc != 4) {
|
||||
cout << "usage: " << argv[0] << " </path/to/haar_cascade> </path/to/csv.ext> </path/to/device id>" << endl;
|
||||
cout << "\t </path/to/haar_cascade> -- Path to the Haar Cascade for face detection." << endl;
|
||||
cout << "\t </path/to/csv.ext> -- Path to the CSV file with the face database." << endl;
|
||||
cout << "\t <device id> -- The webcam device id to grab frames from." << endl;
|
||||
exit(1);
|
||||
}
|
||||
// Get the path to your CSV:
|
||||
string fn_haar = string(argv[1]);
|
||||
string fn_csv = string(argv[2]);
|
||||
int deviceId = atoi(argv[3]);
|
||||
// These vectors hold the images and corresponding labels:
|
||||
vector<Mat> images;
|
||||
vector<int> labels;
|
||||
// Read in the data (fails if no valid input filename is given, but you'll get an error message):
|
||||
try {
|
||||
read_csv(fn_csv, images, labels);
|
||||
} catch (const cv::Exception& e) {
|
||||
cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl;
|
||||
// nothing more we can do
|
||||
exit(1);
|
||||
}
|
||||
// Get the height from the first image. We'll need this
|
||||
// later in code to reshape the images to their original
|
||||
// size AND we need to reshape incoming faces to this size:
|
||||
int im_width = images[0].cols;
|
||||
int im_height = images[0].rows;
|
||||
// Create a FaceRecognizer and train it on the given images:
|
||||
Ptr<FisherFaceRecognizer> model = FisherFaceRecognizer::create();
|
||||
model->train(images, labels);
|
||||
// That's it for learning the Face Recognition model. You now
|
||||
// need to create the classifier for the task of Face Detection.
|
||||
// We are going to use the haar cascade you have specified in the
|
||||
// command line arguments:
|
||||
//
|
||||
CascadeClassifier haar_cascade;
|
||||
haar_cascade.load(fn_haar);
|
||||
// Get a handle to the Video device:
|
||||
VideoCapture cap(deviceId);
|
||||
// Check if we can use this device at all:
|
||||
if(!cap.isOpened()) {
|
||||
cerr << "Capture Device ID " << deviceId << "cannot be opened." << endl;
|
||||
return -1;
|
||||
}
|
||||
// Holds the current frame from the Video device:
|
||||
Mat frame;
|
||||
for(;;) {
|
||||
cap >> frame;
|
||||
// Clone the current frame:
|
||||
Mat original = frame.clone();
|
||||
// Convert the current frame to grayscale:
|
||||
Mat gray;
|
||||
cvtColor(original, gray, COLOR_BGR2GRAY);
|
||||
// Find the faces in the frame:
|
||||
vector< Rect_<int> > faces;
|
||||
haar_cascade.detectMultiScale(gray, faces);
|
||||
// At this point you have the position of the faces in
|
||||
// faces. Now we'll get the faces, make a prediction and
|
||||
// annotate it in the video. Cool or what?
|
||||
for(size_t i = 0; i < faces.size(); i++) {
|
||||
// Process face by face:
|
||||
Rect face_i = faces[i];
|
||||
// Crop the face from the image. So simple with OpenCV C++:
|
||||
Mat face = gray(face_i);
|
||||
// Resizing the face is necessary for Eigenfaces and Fisherfaces. You can easily
|
||||
// verify this, by reading through the face recognition tutorial coming with OpenCV.
|
||||
// Resizing IS NOT NEEDED for Local Binary Patterns Histograms, so preparing the
|
||||
// input data really depends on the algorithm used.
|
||||
//
|
||||
// I strongly encourage you to play around with the algorithms. See which work best
|
||||
// in your scenario, LBPH should always be a contender for robust face recognition.
|
||||
//
|
||||
// Since I am showing the Fisherfaces algorithm here, I also show how to resize the
|
||||
// face you have just found:
|
||||
Mat face_resized;
|
||||
cv::resize(face, face_resized, Size(im_width, im_height), 1.0, 1.0, INTER_CUBIC);
|
||||
// Now perform the prediction, see how easy that is:
|
||||
int prediction = model->predict(face_resized);
|
||||
// And finally write all we've found out to the original image!
|
||||
// First of all draw a green rectangle around the detected face:
|
||||
rectangle(original, face_i, Scalar(0, 255,0), 1);
|
||||
// Create the text we will annotate the box with:
|
||||
string box_text = format("Prediction = %d", prediction);
|
||||
// Calculate the position for annotated text (make sure we don't
|
||||
// put illegal values in there):
|
||||
int pos_x = std::max(face_i.tl().x - 10, 0);
|
||||
int pos_y = std::max(face_i.tl().y - 10, 0);
|
||||
// And now put it into the image:
|
||||
putText(original, box_text, Point(pos_x, pos_y), FONT_HERSHEY_PLAIN, 1.0, Scalar(0,255,0), 2);
|
||||
}
|
||||
// Show the result:
|
||||
imshow("face_recognizer", original);
|
||||
// And display it:
|
||||
char key = (char) waitKey(20);
|
||||
// Exit this loop on escape:
|
||||
if(key == 27)
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import random
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
|
||||
frame1 = cv.imread(cv.samples.findFile('lena.jpg'))
|
||||
if frame1 is None:
|
||||
print("image not found")
|
||||
exit()
|
||||
frame = np.vstack((frame1,frame1))
|
||||
facemark = cv.face.createFacemarkLBF()
|
||||
try:
|
||||
facemark.loadModel(cv.samples.findFile('lbfmodel.yaml'))
|
||||
except cv.error:
|
||||
print("Model not found\nlbfmodel.yaml can be download at")
|
||||
print("https://raw.githubusercontent.com/kurnianggoro/GSOC2017/master/data/lbfmodel.yaml")
|
||||
cascade = cv.CascadeClassifier(cv.samples.findFile('lbpcascade_frontalface_improved.xml'))
|
||||
if cascade.empty() :
|
||||
print("cascade not found")
|
||||
exit()
|
||||
faces = cascade.detectMultiScale(frame, 1.05, 3, cv.CASCADE_SCALE_IMAGE, (30, 30))
|
||||
if len(faces) == 0:
|
||||
print('no faces detected')
|
||||
landmarks = []
|
||||
else:
|
||||
ok, landmarks = facemark.fit(frame, faces=faces)
|
||||
cv.imshow("Image", frame)
|
||||
for marks in landmarks:
|
||||
couleur = (random.randint(0,255),
|
||||
random.randint(0,255),
|
||||
random.randint(0,255))
|
||||
cv.face.drawFacemarks(frame, marks, couleur)
|
||||
cv.imshow("Image Landmarks", frame)
|
||||
cv.waitKey()
|
||||
@@ -0,0 +1,137 @@
|
||||
// This file is part of the OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/xobjdetect.hpp"
|
||||
#include "opencv2/face/mace.hpp"
|
||||
#include <iostream>
|
||||
using namespace cv;
|
||||
using namespace cv::face;
|
||||
using namespace std;
|
||||
|
||||
|
||||
enum STATE {
|
||||
NEUTRAL,
|
||||
RECORD,
|
||||
PREDICT
|
||||
};
|
||||
|
||||
const char *help =
|
||||
"press 'r' to record images. once N trainimages were recorded, train the mace filter\n"
|
||||
"press 'p' to predict (twofactor mode will switch back to neutral after each prediction attempt)\n"
|
||||
"press 's' to save a trained model\n"
|
||||
"press 'esc' to return\n"
|
||||
"any other key will reset to neutral state\n";
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
CommandLineParser parser(argc, argv,
|
||||
"{ help h usage ? || show this help message }"
|
||||
"{ cascade c || (required) path to a cascade file for face detection }"
|
||||
"{ pre p || load a pretrained mace filter file, saved from previous session (e.g. my.xml.gz) }"
|
||||
"{ num n |50| num train images }"
|
||||
"{ size s |64| image size }"
|
||||
"{ twofactor t || pass phrase(text) for 2 factor authentification.\n"
|
||||
" (random convolute images seeded with the crc of this)\n"
|
||||
" users will get prompted to guess the secrect, additional to the image. }"
|
||||
);
|
||||
String cascade = parser.get<String>("cascade");
|
||||
if (parser.has("help") || cascade.empty()) {
|
||||
parser.printMessage();
|
||||
return 1;
|
||||
} else {
|
||||
cout << help << endl;
|
||||
}
|
||||
String defname = "mace.xml.gz";
|
||||
String pre = parser.get<String>("pre");
|
||||
String two = parser.get<String>("twofactor");
|
||||
int N = parser.get<int>("num");
|
||||
int Z = parser.get<int>("size");
|
||||
int state = NEUTRAL;
|
||||
|
||||
Ptr<MACE> mace;
|
||||
if (! pre.empty()) { // load pretrained model, if available
|
||||
mace = MACE::load(pre);
|
||||
if (mace->empty()) {
|
||||
cerr << "loading the MACE failed !" << endl;
|
||||
return -1;
|
||||
}
|
||||
state = PREDICT;
|
||||
} else {
|
||||
mace = MACE::create(Z);
|
||||
if (! two.empty()) {
|
||||
cout << "'" << two << "' initial passphrase" << endl;
|
||||
mace->salt(two);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CascadeClassifier head(cascade);
|
||||
if (head.empty()) {
|
||||
cerr << "loading the cascade failed !" << endl;
|
||||
return -2;
|
||||
}
|
||||
|
||||
VideoCapture cap(0);
|
||||
if (! cap.isOpened()) {
|
||||
cerr << "VideoCapture could not be opened !" << endl;
|
||||
return -3;
|
||||
}
|
||||
|
||||
vector<Mat> train_img;
|
||||
while(1) {
|
||||
Mat frame;
|
||||
cap >> frame;
|
||||
|
||||
vector<Rect> rects;
|
||||
head.detectMultiScale(frame,rects);
|
||||
if (rects.size()>0) {
|
||||
Scalar col = Scalar(0,120,0);
|
||||
|
||||
if (state == RECORD) {
|
||||
if (train_img.size() >= size_t(N)) {
|
||||
mace->train(train_img);
|
||||
train_img.clear();
|
||||
state = PREDICT;
|
||||
} else {
|
||||
train_img.push_back(frame(rects[0]).clone());
|
||||
}
|
||||
col = Scalar(200,0,0);
|
||||
}
|
||||
|
||||
if (state == PREDICT) {
|
||||
if (! two.empty()) { // prompt for secret on console
|
||||
cout << "enter passphrase: ";
|
||||
string pass;
|
||||
getline(cin, pass);
|
||||
mace->salt(pass);
|
||||
state = NEUTRAL;
|
||||
cout << "'" << pass << "' : ";
|
||||
}
|
||||
bool same = mace->same(frame(rects[0]));
|
||||
if (same) col = Scalar(0,220,220);
|
||||
else col = Scalar(60,60,60);
|
||||
if (! two.empty()) {
|
||||
cout << (same ? "accepted." : "denied.") << endl;
|
||||
}
|
||||
}
|
||||
|
||||
rectangle(frame, rects[0], col, 2);
|
||||
}
|
||||
|
||||
imshow("MACE",frame);
|
||||
int k = waitKey(10);
|
||||
switch (k) {
|
||||
case -1 : break;
|
||||
case 27 : return 0;
|
||||
default : state = NEUTRAL; break;
|
||||
case 'r': state = RECORD; break;
|
||||
case 'p': state = PREDICT; break;
|
||||
case 's': mace->save(defname); break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
#include "opencv2/face.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/xobjdetect.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
using namespace cv::face;
|
||||
|
||||
static bool myDetector(InputArray image, OutputArray faces, CascadeClassifier *face_cascade)
|
||||
{
|
||||
Mat gray;
|
||||
|
||||
if (image.channels() > 1)
|
||||
cvtColor(image, gray, COLOR_BGR2GRAY);
|
||||
else
|
||||
gray = image.getMat().clone();
|
||||
|
||||
equalizeHist(gray, gray);
|
||||
|
||||
std::vector<Rect> faces_;
|
||||
face_cascade->detectMultiScale(gray, faces_, 1.4, 2, CASCADE_SCALE_IMAGE, Size(30, 30));
|
||||
Mat(faces_).copyTo(faces);
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc,char** argv){
|
||||
//Give the path to the directory containing all the files containing data
|
||||
CommandLineParser parser(argc, argv,
|
||||
"{ help h usage ? | | give the following arguments in following format }"
|
||||
"{ model_filename f | | (required) path to binary file storing the trained model which is to be loaded [example - /data/file.dat]}"
|
||||
"{ image i | | (required) path to image in which face landmarks have to be detected.[example - /data/image.jpg] }"
|
||||
"{ face_cascade c | | Path to the face cascade xml file which you want to use as a detector}"
|
||||
);
|
||||
// Read in the input arguments
|
||||
if (parser.has("help")){
|
||||
parser.printMessage();
|
||||
cerr << "TIP: Use absolute paths to avoid any problems with the software!" << endl;
|
||||
return 0;
|
||||
}
|
||||
string filename(parser.get<string>("model_filename"));
|
||||
if (filename.empty()){
|
||||
parser.printMessage();
|
||||
cerr << "The name of the model file to be loaded for detecting landmarks is not found" << endl;
|
||||
return -1;
|
||||
}
|
||||
string image(parser.get<string>("image"));
|
||||
if (image.empty()){
|
||||
parser.printMessage();
|
||||
cerr << "The name of the image file in which landmarks have to be detected is not found" << endl;
|
||||
return -1;
|
||||
}
|
||||
string cascade_name(parser.get<string>("face_cascade"));
|
||||
if (cascade_name.empty()){
|
||||
parser.printMessage();
|
||||
cerr << "The name of the cascade classifier to be loaded to detect faces is not found" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Mat img = imread(image);
|
||||
|
||||
//pass the face cascade xml file which you want to pass as a detector
|
||||
CascadeClassifier face_cascade;
|
||||
face_cascade.load(cascade_name);
|
||||
FacemarkKazemi::Params params;
|
||||
Ptr<FacemarkKazemi> facemark = FacemarkKazemi::create(params);
|
||||
facemark->setFaceDetector((FN_FaceDetector)myDetector, &face_cascade);
|
||||
facemark->loadModel(filename);
|
||||
cout<<"Loaded model"<<endl;
|
||||
vector<Rect> faces;
|
||||
resize(img,img,Size(460,460), 0, 0, INTER_LINEAR_EXACT);
|
||||
facemark->getFaces(img,faces);
|
||||
vector< vector<Point2f> > shapes;
|
||||
|
||||
// Check if faces detected or not
|
||||
// Helps in proper exception handling when writing images to the directories.
|
||||
if(faces.size() != 0) {
|
||||
if(facemark->fit(img,faces,shapes))
|
||||
{
|
||||
for( size_t i = 0; i < faces.size(); i++ )
|
||||
{
|
||||
cv::rectangle(img,faces[i],Scalar( 255, 0, 0 ));
|
||||
}
|
||||
for(unsigned long i=0;i<faces.size();i++){
|
||||
for(unsigned long k=0;k<shapes[i].size();k++)
|
||||
cv::circle(img,shapes[i][k],5,cv::Scalar(0,0,255),FILLED);
|
||||
}
|
||||
namedWindow("Detected_shape");
|
||||
imshow("Detected_shape",img);
|
||||
waitKey(0);
|
||||
}
|
||||
} else {
|
||||
cout << "Faces not detected." << endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
#include "opencv2/face.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/xobjdetect.hpp"
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
using namespace cv::face;
|
||||
|
||||
static bool myDetector(InputArray image, OutputArray faces, CascadeClassifier *face_cascade)
|
||||
{
|
||||
Mat gray;
|
||||
|
||||
if (image.channels() > 1)
|
||||
cvtColor(image, gray, COLOR_BGR2GRAY);
|
||||
else
|
||||
gray = image.getMat().clone();
|
||||
|
||||
equalizeHist(gray, gray);
|
||||
|
||||
std::vector<Rect> faces_;
|
||||
face_cascade->detectMultiScale(gray, faces_, 1.4, 2, CASCADE_SCALE_IMAGE, Size(30, 30));
|
||||
Mat(faces_).copyTo(faces);
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc,char** argv){
|
||||
//Give the path to the directory containing all the files containing data
|
||||
CommandLineParser parser(argc, argv,
|
||||
"{ help h usage ? | | give the following arguments in following format }"
|
||||
"{ model_filename f | | (required) path to binary file storing the trained model which is to be loaded [example - /data/file.dat]}"
|
||||
"{ video v | | (required) path to video in which face landmarks have to be detected.[example - /data/video.avi] }"
|
||||
"{ face_cascade c | | Path to the face cascade xml file which you want to use as a detector}"
|
||||
);
|
||||
// Read in the input arguments
|
||||
if (parser.has("help")){
|
||||
parser.printMessage();
|
||||
cerr << "TIP: Use absolute paths to avoid any problems with the software!" << endl;
|
||||
return 0;
|
||||
}
|
||||
string filename(parser.get<string>("model_filename"));
|
||||
if (filename.empty()){
|
||||
parser.printMessage();
|
||||
cerr << "The name of the model file to be loaded for detecting landmarks is not found" << endl;
|
||||
return -1;
|
||||
}
|
||||
string video(parser.get<string>("video"));
|
||||
if (video.empty()){
|
||||
parser.printMessage();
|
||||
cerr << "The name of the video file in which landmarks have to be detected is not found" << endl;
|
||||
return -1;
|
||||
}
|
||||
string cascade_name(parser.get<string>("face_cascade"));
|
||||
if (cascade_name.empty()){
|
||||
parser.printMessage();
|
||||
cerr << "The name of the cascade classifier to be loaded to detect faces is not found" << endl;
|
||||
return -1;
|
||||
}
|
||||
VideoCapture cap(video);
|
||||
if(!cap.isOpened()){
|
||||
cerr<<"Video cannot be loaded. Give correct path"<<endl;
|
||||
return -1;
|
||||
}
|
||||
//pass the face cascade xml file which you want to pass as a detector
|
||||
CascadeClassifier face_cascade;
|
||||
face_cascade.load(cascade_name);
|
||||
FacemarkKazemi::Params params;
|
||||
Ptr<FacemarkKazemi> facemark = FacemarkKazemi::create(params);
|
||||
facemark->setFaceDetector((FN_FaceDetector)myDetector, &face_cascade);
|
||||
facemark->loadModel(filename);
|
||||
cout<<"Loaded model"<<endl;
|
||||
//vector to store the faces detected in the image
|
||||
vector<Rect> faces;
|
||||
vector< vector<Point2f> > shapes;
|
||||
Mat img;
|
||||
while(1){
|
||||
faces.clear();
|
||||
shapes.clear();
|
||||
cap>>img;
|
||||
//Detect faces in the current image
|
||||
resize(img,img,Size(600,600), 0, 0, INTER_LINEAR_EXACT);
|
||||
facemark->getFaces(img,faces);
|
||||
if(faces.size()==0){
|
||||
cout<<"No faces found in this frame"<<endl;
|
||||
}
|
||||
else{
|
||||
for( size_t i = 0; i < faces.size(); i++ )
|
||||
{
|
||||
cv::rectangle(img,faces[i],Scalar( 255, 0, 0 ));
|
||||
}
|
||||
//vector to store the landmarks of all the faces in the image
|
||||
if(facemark->fit(img,faces,shapes))
|
||||
{
|
||||
for(unsigned long i=0;i<faces.size();i++){
|
||||
for(unsigned long k=0;k<shapes[i].size();k++)
|
||||
cv::circle(img,shapes[i][k],3,cv::Scalar(0,0,255),FILLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
namedWindow("Detected_shape");
|
||||
imshow("Detected_shape",img);
|
||||
if(waitKey(1) >= 0) break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- cascade_depth stores the depth of cascade of regressors used for training.
|
||||
tree_depth stores the depth of trees created as weak learners during gradient boosting.
|
||||
num_trees_per_cascade_level stores number of trees required per cascade level.
|
||||
learning_rate stores the learning rate for gradient boosting.This is required to prevent overfitting using shrinkage.
|
||||
oversampling_amount stores the oversampling amount for the samples.
|
||||
num_test_coordinates stores number of test coordinates to be generated as samples to decide for making the split.
|
||||
lambda stores the value used for calculating the probabilty which helps to select closer pixels for making the split.
|
||||
num_test_splits stores the number of test splits to be generated before making the best split.
|
||||
-->
|
||||
<opencv_storage>
|
||||
<cascade_depth>15</cascade_depth>
|
||||
<tree_depth>4</tree_depth>
|
||||
<num_trees_per_cascade_level>500</num_trees_per_cascade_level>
|
||||
<learning_rate>1.0000000149011612e-01</learning_rate>
|
||||
<oversampling_amount>20</oversampling_amount>
|
||||
<num_test_coordinates>400</num_test_coordinates>
|
||||
<lambda>1.0000000149011612e-01</lambda>
|
||||
<num_test_splits>20</num_test_splits>
|
||||
</opencv_storage>
|
||||
@@ -0,0 +1,205 @@
|
||||
#include "opencv2/face.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/xobjdetect.hpp"
|
||||
#include "opencv2/photo.hpp" // seamlessClone()
|
||||
#include "opencv2/geometry.hpp" // Subdiv2D()
|
||||
#include <iostream>
|
||||
using namespace cv;
|
||||
using namespace cv::face;
|
||||
using namespace std;
|
||||
|
||||
static bool myDetector(InputArray image, OutputArray faces, CascadeClassifier *face_cascade)
|
||||
{
|
||||
Mat gray;
|
||||
|
||||
if (image.channels() > 1)
|
||||
cvtColor(image, gray, COLOR_BGR2GRAY);
|
||||
else
|
||||
gray = image.getMat().clone();
|
||||
|
||||
equalizeHist(gray, gray);
|
||||
|
||||
std::vector<Rect> faces_;
|
||||
face_cascade->detectMultiScale(gray, faces_, 1.4, 2, CASCADE_SCALE_IMAGE, Size(30, 30));
|
||||
Mat(faces_).copyTo(faces);
|
||||
return true;
|
||||
}
|
||||
|
||||
void divideIntoTriangles(Rect rect, vector<Point2f> &points, vector< vector<int> > &delaunayTri);
|
||||
void warpTriangle(Mat &img1, Mat &img2, vector<Point2f> &triangle1, vector<Point2f> &triangle2);
|
||||
|
||||
//Divide the face into triangles for warping
|
||||
void divideIntoTriangles(Rect rect, vector<Point2f> &points, vector< vector<int> > &Tri){
|
||||
|
||||
// Create an instance of Subdiv2D
|
||||
Subdiv2D subdiv(rect);
|
||||
// Insert points into subdiv
|
||||
for( vector<Point2f>::iterator it = points.begin(); it != points.end(); it++)
|
||||
subdiv.insert(*it);
|
||||
vector<Vec6f> triangleList;
|
||||
subdiv.getTriangleList(triangleList);
|
||||
vector<Point2f> pt(3);
|
||||
vector<int> ind(3);
|
||||
for( size_t i = 0; i < triangleList.size(); i++ )
|
||||
{
|
||||
Vec6f triangle = triangleList[i];
|
||||
pt[0] = Point2f(triangle[0], triangle[1]);
|
||||
pt[1] = Point2f(triangle[2], triangle[3]);
|
||||
pt[2] = Point2f(triangle[4], triangle[5]);
|
||||
// Workaround for https://github.com/opencv/opencv/issues/26016
|
||||
// To keep its behaviour, pt casts to Point_<int>.
|
||||
if ( rect.contains(Point_<int>(pt[0])) && rect.contains(Point_<int>(pt[1])) && rect.contains(Point_<int>(pt[2]))){
|
||||
for(int j = 0; j < 3; j++)
|
||||
for(size_t k = 0; k < points.size(); k++)
|
||||
if(abs(pt[j].x - points[k].x) < 1.0 && abs(pt[j].y - points[k].y) < 1)
|
||||
ind[j] =(int) k;
|
||||
Tri.push_back(ind);
|
||||
}
|
||||
}
|
||||
}
|
||||
void warpTriangle(Mat &img1, Mat &img2, vector<Point2f> &triangle1, vector<Point2f> &triangle2)
|
||||
{
|
||||
Rect rectangle1 = boundingRect(triangle1);
|
||||
Rect rectangle2 = boundingRect(triangle2);
|
||||
// Offset points by left top corner of the respective rectangles
|
||||
vector<Point2f> triangle1Rect, triangle2Rect;
|
||||
vector<Point> triangle2RectInt;
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
triangle1Rect.push_back( Point2f( triangle1[i].x - rectangle1.x, triangle1[i].y - rectangle1.y) );
|
||||
triangle2Rect.push_back( Point2f( triangle2[i].x - rectangle2.x, triangle2[i].y - rectangle2.y) );
|
||||
triangle2RectInt.push_back( Point((int)(triangle2[i].x - rectangle2.x),(int) (triangle2[i].y - rectangle2.y))); // for fillConvexPoly
|
||||
}
|
||||
// Get mask by filling triangle
|
||||
Mat mask = Mat::zeros(rectangle2.height, rectangle2.width, CV_32FC3);
|
||||
fillConvexPoly(mask, triangle2RectInt, Scalar(1.0, 1.0, 1.0), 16, 0);
|
||||
// Apply warpImage to small rectangular patches
|
||||
Mat img1Rect;
|
||||
img1(rectangle1).copyTo(img1Rect);
|
||||
Mat img2Rect = Mat::zeros(rectangle2.height, rectangle2.width, img1Rect.type());
|
||||
Mat warp_mat = getAffineTransform(triangle1Rect, triangle2Rect);
|
||||
warpAffine( img1Rect, img2Rect, warp_mat, img2Rect.size(), INTER_LINEAR, BORDER_REFLECT_101);
|
||||
multiply(img2Rect,mask, img2Rect);
|
||||
multiply(img2(rectangle2), Scalar(1.0,1.0,1.0) - mask, img2(rectangle2));
|
||||
img2(rectangle2) = img2(rectangle2) + img2Rect;
|
||||
}
|
||||
int main( int argc, char** argv)
|
||||
{
|
||||
//Give the path to the directory containing all the files containing data
|
||||
CommandLineParser parser(argc, argv,
|
||||
"{ help h usage ? | | give the following arguments in following format }"
|
||||
"{ image1 i1 | | (required) path to the first image file in which you want to apply swapping }"
|
||||
"{ image2 i2 | | (required) path to the second image file in which you want to apply face swapping }"
|
||||
"{ model m | | (required) path to the file containing model to be loaded for face landmark detection}"
|
||||
"{ face_cascade f | | Path to the face cascade xml file which you want to use as a detector}"
|
||||
);
|
||||
// Read in the input arguments
|
||||
if (parser.has("help")){
|
||||
parser.printMessage();
|
||||
cerr << "TIP: Use absolute paths to avoid any problems with the software!" << endl;
|
||||
return 0;
|
||||
}
|
||||
Mat img1=imread(parser.get<string>("image1"));
|
||||
Mat img2=imread(parser.get<string>("image2"));
|
||||
if (img1.empty()||img2.empty()){
|
||||
if(img1.empty()){
|
||||
parser.printMessage();
|
||||
cerr << parser.get<string>("image1")<<" not found" << endl;
|
||||
return -1;
|
||||
}
|
||||
if (img2.empty()){
|
||||
parser.printMessage();
|
||||
cerr << parser.get<string>("image2")<<" not found" << endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
string modelfile_name(parser.get<string>("model"));
|
||||
if (modelfile_name.empty()){
|
||||
parser.printMessage();
|
||||
cerr << "Model file name not found." << endl;
|
||||
return -1;
|
||||
}
|
||||
string cascade_name(parser.get<string>("face_cascade"));
|
||||
if (cascade_name.empty()){
|
||||
parser.printMessage();
|
||||
cerr << "The name of the cascade classifier to be loaded to detect faces is not found" << endl;
|
||||
return -1;
|
||||
}
|
||||
//create a pointer to call the base class
|
||||
//pass the face cascade xml file which you want to pass as a detector
|
||||
CascadeClassifier face_cascade;
|
||||
face_cascade.load(cascade_name);
|
||||
FacemarkKazemi::Params params;
|
||||
Ptr<FacemarkKazemi> facemark = FacemarkKazemi::create(params);
|
||||
facemark->setFaceDetector((FN_FaceDetector)myDetector, &face_cascade);
|
||||
facemark->loadModel(modelfile_name);
|
||||
cout<<"Loaded model"<<endl;
|
||||
//vector to store the faces detected in the image
|
||||
vector<Rect> faces1,faces2;
|
||||
vector< vector<Point2f> > shape1,shape2;
|
||||
//Detect faces in the current image
|
||||
float ratio1 = (float)img1.cols/(float)img1.rows;
|
||||
float ratio2 = (float)img2.cols/(float)img2.rows;
|
||||
resize(img1,img1,Size((int)(640*ratio1),(int)(640*ratio1)), 0, 0, INTER_LINEAR_EXACT);
|
||||
resize(img2,img2,Size((int)(640*ratio2),(int)(640*ratio2)), 0, 0, INTER_LINEAR_EXACT);
|
||||
Mat img1Warped = img2.clone();
|
||||
facemark->getFaces(img1,faces1);
|
||||
facemark->getFaces(img2,faces2);
|
||||
//Initialise the shape of the faces
|
||||
facemark->fit(img1,faces1,shape1);
|
||||
facemark->fit(img2,faces2,shape2);
|
||||
unsigned long numswaps = (unsigned long)min((unsigned long)shape1.size(),(unsigned long)shape2.size());
|
||||
for(unsigned long z=0;z<numswaps;z++){
|
||||
vector<Point2f> points1 = shape1[z];
|
||||
vector<Point2f> points2 = shape2[z];
|
||||
img1.convertTo(img1, CV_32F);
|
||||
img1Warped.convertTo(img1Warped, CV_32F);
|
||||
// Find convex hull
|
||||
vector<Point2f> boundary_image1;
|
||||
vector<Point2f> boundary_image2;
|
||||
vector<int> index;
|
||||
convexHull(Mat(points2),index, false, false);
|
||||
for(size_t i = 0; i < index.size(); i++)
|
||||
{
|
||||
boundary_image1.push_back(points1[index[i]]);
|
||||
boundary_image2.push_back(points2[index[i]]);
|
||||
}
|
||||
// Triangulation for points on the convex hull
|
||||
vector< vector<int> > triangles;
|
||||
Rect rect(0, 0, img1Warped.cols, img1Warped.rows);
|
||||
divideIntoTriangles(rect, boundary_image2, triangles);
|
||||
// Apply affine transformation to Delaunay triangles
|
||||
for(size_t i = 0; i < triangles.size(); i++)
|
||||
{
|
||||
vector<Point2f> triangle1, triangle2;
|
||||
// Get points for img1, img2 corresponding to the triangles
|
||||
for(int j = 0; j < 3; j++)
|
||||
{
|
||||
triangle1.push_back(boundary_image1[triangles[i][j]]);
|
||||
triangle2.push_back(boundary_image2[triangles[i][j]]);
|
||||
}
|
||||
warpTriangle(img1, img1Warped, triangle1, triangle2);
|
||||
}
|
||||
// Calculate mask
|
||||
vector<Point> hull;
|
||||
for(size_t i = 0; i < boundary_image2.size(); i++)
|
||||
{
|
||||
Point pt((int)boundary_image2[i].x,(int)boundary_image2[i].y);
|
||||
hull.push_back(pt);
|
||||
}
|
||||
Mat mask = Mat::zeros(img2.rows, img2.cols, img2.depth());
|
||||
fillConvexPoly(mask,&hull[0],(int)hull.size(), Scalar(255,255,255));
|
||||
// Clone seamlessly.
|
||||
Rect r = boundingRect(boundary_image2);
|
||||
Point center = (r.tl() + r.br()) / 2;
|
||||
Mat output;
|
||||
img1Warped.convertTo(img1Warped, CV_8UC3);
|
||||
seamlessClone(img1Warped,img2, mask, center, output, NORMAL_CLONE);
|
||||
imshow("Face_Swapped", output);
|
||||
waitKey(0);
|
||||
destroyAllWindows();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
#include "opencv2/face.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/xobjdetect.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
using namespace cv::face;
|
||||
|
||||
static bool myDetector(InputArray image, OutputArray faces, CascadeClassifier *face_cascade)
|
||||
{
|
||||
Mat gray;
|
||||
|
||||
if (image.channels() > 1)
|
||||
cvtColor(image, gray, COLOR_BGR2GRAY);
|
||||
else
|
||||
gray = image.getMat().clone();
|
||||
|
||||
equalizeHist(gray, gray);
|
||||
|
||||
std::vector<Rect> faces_;
|
||||
face_cascade->detectMultiScale(gray, faces_, 1.4, 2, CASCADE_SCALE_IMAGE, Size(30, 30));
|
||||
Mat(faces_).copyTo(faces);
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc,char** argv){
|
||||
//Give the path to the directory containing all the files containing data
|
||||
CommandLineParser parser(argc, argv,
|
||||
"{ help h usage ? | | give the following arguments in following format }"
|
||||
"{ annotations a |. | (required) path to annotations txt file [example - /data/annotations.txt] }"
|
||||
"{ config c | | (required) path to configuration xml file containing parameters for training.[ example - /data/config.xml] }"
|
||||
"{ model m | | (required) path to configuration xml file containing parameters for training.[ example - /data/model.dat] }"
|
||||
"{ width w | 460 | The width which you want all images to get to scale the annotations. large images are slow to process [default = 460] }"
|
||||
"{ height h | 460 | The height which you want all images to get to scale the annotations. large images are slow to process [default = 460] }"
|
||||
"{ face_cascade f | | Path to the face cascade xml file which you want to use as a detector}"
|
||||
);
|
||||
//Read in the input arguments
|
||||
if (parser.has("help")){
|
||||
parser.printMessage();
|
||||
cerr << "TIP: Use absolute paths to avoid any problems with the software!" << endl;
|
||||
return 0;
|
||||
}
|
||||
string directory(parser.get<string>("annotations"));
|
||||
//default initialisation
|
||||
Size scale(460,460);
|
||||
scale = Size(parser.get<int>("width"),parser.get<int>("height"));
|
||||
if (directory.empty()){
|
||||
parser.printMessage();
|
||||
cerr << "The name of the directory from which annotations have to be found is empty" << endl;
|
||||
return -1;
|
||||
}
|
||||
string configfile_name(parser.get<string>("config"));
|
||||
if (configfile_name.empty()){
|
||||
parser.printMessage();
|
||||
cerr << "No configuration file name found which contains the parameters for training" << endl;
|
||||
return -1;
|
||||
}
|
||||
string modelfile_name(parser.get<string>("model"));
|
||||
if (modelfile_name.empty()){
|
||||
parser.printMessage();
|
||||
cerr << "No name for the model_file found in which the trained model has to be saved" << endl;
|
||||
return -1;
|
||||
}
|
||||
string cascade_name(parser.get<string>("face_cascade"));
|
||||
if (cascade_name.empty()){
|
||||
parser.printMessage();
|
||||
cerr << "The name of the cascade classifier to be loaded to detect faces is not found" << endl;
|
||||
return -1;
|
||||
}
|
||||
//create a vector to store names of files in which annotations
|
||||
//and image names are found
|
||||
/*The format of the file containing annotations should be of following format
|
||||
/data/abc/abc.jpg
|
||||
123.45,345.65
|
||||
321.67,543.89
|
||||
The above format is similar to HELEN dataset which is used for training model
|
||||
*/
|
||||
vector<String> filenames;
|
||||
//reading the files from the given directory
|
||||
glob(directory + "*.txt",filenames);
|
||||
//create a pointer to call the base class
|
||||
//pass the face cascade xml file which you want to pass as a detector
|
||||
CascadeClassifier face_cascade;
|
||||
face_cascade.load(cascade_name);
|
||||
FacemarkKazemi::Params params;
|
||||
params.configfile = configfile_name;
|
||||
Ptr<FacemarkKazemi> facemark = FacemarkKazemi::create(params);
|
||||
facemark->setFaceDetector((FN_FaceDetector)myDetector, &face_cascade);
|
||||
//create a vector to store image names
|
||||
vector<String> imagenames;
|
||||
//create object to get landmarks
|
||||
vector< vector<Point2f> > trainlandmarks,Trainlandmarks;
|
||||
//gets landmarks and corresponding image names in both the vectors
|
||||
//vector to store images
|
||||
vector<Mat> trainimages;
|
||||
loadTrainingData(filenames,trainlandmarks,imagenames);
|
||||
for(unsigned long i=0;i<300;i++){
|
||||
string imgname = imagenames[i].substr(0, imagenames[i].size()-1);
|
||||
string img = directory + string(imgname) + ".jpg";
|
||||
Mat src = imread(img);
|
||||
if(src.empty()){
|
||||
cerr<<string("Image "+img+" not found\n.")<<endl;
|
||||
continue;
|
||||
}
|
||||
trainimages.push_back(src);
|
||||
Trainlandmarks.push_back(trainlandmarks[i]);
|
||||
}
|
||||
cout<<"Got data"<<endl;
|
||||
facemark->training(trainimages,Trainlandmarks,configfile_name,scale,modelfile_name);
|
||||
cout<<"Training complete"<<endl;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*----------------------------------------------
|
||||
* the user should provide the list of training images_train,
|
||||
* accompanied by their corresponding landmarks location in separated files.
|
||||
* example of contents for images.txt:
|
||||
* ../trainset/image_0001.png
|
||||
* ../trainset/image_0002.png
|
||||
* example of contents for annotation.txt:
|
||||
* ../trainset/image_0001.pts
|
||||
* ../trainset/image_0002.pts
|
||||
* where the image_xxxx.pts contains the position of each face landmark.
|
||||
* example of the contents:
|
||||
* version: 1
|
||||
* n_points: 68
|
||||
* {
|
||||
* 115.167660 220.807529
|
||||
* 116.164839 245.721357
|
||||
* 120.208690 270.389841
|
||||
* ...
|
||||
* }
|
||||
* example of the dataset is available at https://ibug.doc.ic.ac.uk/resources/facial-point-annotations/
|
||||
*--------------------------------------------------*/
|
||||
#include "opencv2/face.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/xobjdetect.hpp"
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
using namespace cv::face;
|
||||
|
||||
static bool myDetector(InputArray image, OutputArray faces, CascadeClassifier *face_cascade)
|
||||
{
|
||||
Mat gray;
|
||||
|
||||
if (image.channels() > 1)
|
||||
cvtColor(image, gray, COLOR_BGR2GRAY);
|
||||
else
|
||||
gray = image.getMat().clone();
|
||||
|
||||
equalizeHist(gray, gray);
|
||||
|
||||
std::vector<Rect> faces_;
|
||||
face_cascade->detectMultiScale(gray, faces_, 1.4, 2, CASCADE_SCALE_IMAGE, Size(30, 30));
|
||||
Mat(faces_).copyTo(faces);
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc,char** argv){
|
||||
//Give the path to the directory containing all the files containing data
|
||||
CommandLineParser parser(argc, argv,
|
||||
"{ help h usage ? | | give the following arguments in following format }"
|
||||
"{ images i | | (required) path to images txt file [example - /data/images.txt] }"
|
||||
"{ annotations a |. | (required) path to annotations txt file [example - /data/annotations.txt] }"
|
||||
"{ config c | | (required) path to configuration xml file containing parameters for training.[example - /data/config.xml] }"
|
||||
"{ model m | | (required) path to file containing trained model for face landmark detection[example - /data/model.dat] }"
|
||||
"{ width w | 460 | The width which you want all images to get to scale the annotations. large images are slow to process [default = 460] }"
|
||||
"{ height h | 460 | The height which you want all images to get to scale the annotations. large images are slow to process [default = 460] }"
|
||||
"{ face_cascade f | | Path to the face cascade xml file which you want to use as a detector}"
|
||||
);
|
||||
// Read in the input arguments
|
||||
if (parser.has("help")){
|
||||
parser.printMessage();
|
||||
cerr << "TIP: Use absolute paths to avoid any problems with the software!" << endl;
|
||||
return 0;
|
||||
}
|
||||
string annotations(parser.get<string>("annotations"));
|
||||
string imagesList(parser.get<string>("images"));
|
||||
//default initialisation
|
||||
Size scale(460,460);
|
||||
scale = Size(parser.get<int>("width"),parser.get<int>("height"));
|
||||
if (annotations.empty()){
|
||||
parser.printMessage();
|
||||
cerr << "Name for annotations file not found. Aborting...." << endl;
|
||||
return -1;
|
||||
}
|
||||
if (imagesList.empty()){
|
||||
parser.printMessage();
|
||||
cerr << "Name for file containing image list not found. Aborting....." << endl;
|
||||
return -1;
|
||||
}
|
||||
string configfile_name(parser.get<string>("config"));
|
||||
if (configfile_name.empty()){
|
||||
parser.printMessage();
|
||||
cerr << "No configuration file name found which contains the parameters for training" << endl;
|
||||
return -1;
|
||||
}
|
||||
string modelfile_name(parser.get<string>("model"));
|
||||
if (modelfile_name.empty()){
|
||||
parser.printMessage();
|
||||
cerr << "No name for the model_file found in which the trained model has to be saved" << endl;
|
||||
return -1;
|
||||
}
|
||||
string cascade_name(parser.get<string>("face_cascade"));
|
||||
if (cascade_name.empty()){
|
||||
parser.printMessage();
|
||||
cerr << "The name of the cascade classifier to be loaded to detect faces is not found" << endl;
|
||||
return -1;
|
||||
}
|
||||
//create a pointer to call the base class
|
||||
//pass the face cascade xml file which you want to pass as a detector
|
||||
CascadeClassifier face_cascade;
|
||||
face_cascade.load(cascade_name);
|
||||
FacemarkKazemi::Params params;
|
||||
params.configfile = configfile_name;
|
||||
Ptr<FacemarkKazemi> facemark = FacemarkKazemi::create(params);
|
||||
facemark->setFaceDetector((FN_FaceDetector)myDetector, &face_cascade);
|
||||
|
||||
std::vector<String> images;
|
||||
std::vector<std::vector<Point2f> > facePoints;
|
||||
loadTrainingData(imagesList, annotations, images, facePoints, 0.0);
|
||||
//gets landmarks and corresponding image names in both the vectors
|
||||
vector<Mat> Trainimages;
|
||||
std::vector<std::vector<Point2f> > Trainlandmarks;
|
||||
//vector to store images
|
||||
Mat src;
|
||||
for(unsigned long i=0;i<images.size();i++){
|
||||
src = imread(images[i]);
|
||||
if(src.empty()){
|
||||
cout<<images[i]<<endl;
|
||||
cerr<<string("Image not found\n.Aborting...")<<endl;
|
||||
continue;
|
||||
}
|
||||
Trainimages.push_back(src);
|
||||
Trainlandmarks.push_back(facePoints[i]);
|
||||
}
|
||||
cout<<"Got data"<<endl;
|
||||
facemark->training(Trainimages,Trainlandmarks,configfile_name,scale,modelfile_name);
|
||||
cout<<"Training complete"<<endl;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#include "opencv2/core.hpp"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
int main(int argc,const char ** argv){
|
||||
CommandLineParser parser(argc, argv,
|
||||
"{ help h usage ? | | give the following arguments in following format }"
|
||||
"{ filename f |. | (required) path to file which you want to create as config file [example - /data/config.xml] }"
|
||||
"{ cascade_depth cd | 10 | (required) This stores the depth of cascade of regressors used for training.}"
|
||||
"{ tree_depth td | 4 | (required) This stores the depth of trees created as weak learners during gradient boosting.}"
|
||||
"{ num_trees_per_cascade_level| 500 | (required) This stores number of trees required per cascade level.}"
|
||||
"{ learning_rate | 0.1 | (required) This stores the learning rate for gradient boosting.}"
|
||||
"{ oversampling_amount | 20 | (required) This stores the oversampling amount for the samples.}"
|
||||
"{ num_test_coordinates | 400 | (required) This stores number of test coordinates required for making the split.}"
|
||||
"{ lambda | 0.1 | (required) This stores the value used for calculating the probabilty.}"
|
||||
"{ num_test_splits | 20 | (required) This stores the number of test splits to be generated before making the best split.}"
|
||||
);
|
||||
// Read in the input arguments
|
||||
if (parser.has("help")){
|
||||
parser.printMessage();
|
||||
cerr << "TIP: Use absolute paths to avoid any problems with the software!" << endl;
|
||||
return 0;
|
||||
}
|
||||
//These variables have been initialised as defined in the research paper "One millisecond face alignment" CVPR 2014
|
||||
int cascade_depth = 15;
|
||||
int tree_depth = 4;
|
||||
int num_trees_per_cascade_level = 500;
|
||||
float learning_rate = float(0.1);
|
||||
int oversampling_amount = 20;
|
||||
int num_test_coordinates = 400;
|
||||
float lambda = float(0.1);
|
||||
int num_test_splits = 20;
|
||||
|
||||
cascade_depth = parser.get<int>("cascade_depth");
|
||||
tree_depth = parser.get<int>("tree_depth");
|
||||
num_trees_per_cascade_level = parser.get<int>("num_trees_per_cascade_level");
|
||||
learning_rate = parser.get<float>("learning_rate");
|
||||
oversampling_amount = parser.get<int>("oversampling_amount");
|
||||
num_test_coordinates = parser.get<int>("num_test_coordinates");
|
||||
lambda = parser.get<float>("lambda");
|
||||
num_test_splits = parser.get<int>("num_test_splits");
|
||||
string filename(parser.get<string>("filename"));
|
||||
FileStorage fs(filename, FileStorage::WRITE);
|
||||
if (!fs.isOpened())
|
||||
{
|
||||
cerr << "Failed to open " << filename << endl;
|
||||
parser.printMessage();
|
||||
return -1;
|
||||
}
|
||||
fs << "cascade_depth" << cascade_depth;
|
||||
fs << "tree_depth"<< tree_depth;
|
||||
fs << "num_trees_per_cascade_level" << num_trees_per_cascade_level;
|
||||
fs << "learning_rate" << learning_rate;
|
||||
fs << "oversampling_amount" << oversampling_amount;
|
||||
fs << "num_test_coordinates" << num_test_coordinates;
|
||||
fs << "lambda" << lambda ;
|
||||
fs << "num_test_splits"<< num_test_splits;
|
||||
fs.release();
|
||||
cout << "Write Done." << endl;
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user