Permalink
Browse files

Unknown people benchmarking with lfw dataset. (#171)

* Added few classifiers to demos/classifier.py; demos/classifier_test_train.py - for test train accuracy estimation; demos/classifier_webcam.py for webcam face recognition

* Two stage classifier in webcam demo

* Renamed classifier_test_train; cleaned up demos/classifier_webcam.py

* Initial commit of working unknown evaluation. Clean up pending

* Clean up lfw-classification-unknown.py and fix flake8 warnings
1 parent 0d301b9 commit fa2a3a5d2097252b0612e1f8a58a9dd62217c9a0 @Vijayenthiran Vijayenthiran committed with bamos Jul 28, 2016
Showing with 803 additions and 21 deletions.
  1. +70 −21 demos/classifier.py
  2. +210 −0 demos/classifier_webcam.py
  3. +522 −0 evaluation/lfw-classification-unknown.py
  4. +1 −0 requirements.txt
View
@@ -40,6 +40,8 @@
from sklearn.preprocessing import LabelEncoder
from sklearn.svm import SVC
from sklearn.mixture import GMM
+from sklearn.tree import DecisionTreeClassifier
+from sklearn.naive_bayes import GaussianNB
fileDir = os.path.dirname(os.path.realpath(__file__))
modelDir = os.path.join(fileDir, '..', 'models')
@@ -69,8 +71,11 @@ def getRep(imgPath):
print("Face detection took {} seconds.".format(time.time() - start))
start = time.time()
- alignedFace = align.align(args.imgDim, rgbImg, bb,
- landmarkIndices=openface.AlignDlib.OUTER_EYES_AND_NOSE)
+ alignedFace = align.align(
+ args.imgDim,
+ rgbImg,
+ bb,
+ landmarkIndices=openface.AlignDlib.OUTER_EYES_AND_NOSE)
if alignedFace is None:
raise Exception("Unable to align image: {}".format(imgPath))
if args.verbose:
@@ -100,9 +105,34 @@ def train(args):
if args.classifier == 'LinearSvm':
clf = SVC(C=1, kernel='linear', probability=True)
- elif args.classifier == 'GMM':
+ elif args.classifier == 'GMM': # Doesn't work best
clf = GMM(n_components=nClasses)
+ # ref:
+ # http://scikit-learn.org/stable/auto_examples/classification/plot_classifier_comparison.html#example-classification-plot-classifier-comparison-py
+ elif args.classifier == 'RadialSvm': # Radial Basis Function kernel
+ # works better with C = 1 and gamma = 2
+ clf = SVC(C=1, kernel='rbf', probability=True, gamma=2)
+ elif args.classifier == 'DecisionTree': # Doesn't work best
+ clf = DecisionTreeClassifier(max_depth=20)
+ elif args.classifier == 'GaussianNB':
+ clf = GaussianNB()
+
+ # ref: https://jessesw.com/Deep-Learning/
+ elif args.classifier == 'DBN':
+ from nolearn.dbn import DBN
+ clf = DBN([embeddings.shape[1], 500, labelsNum[-1:][0] + 1], # i/p nodes, hidden nodes, o/p nodes
+ learn_rates=0.3,
+ # Smaller steps mean a possibly more accurate result, but the
+ # training will take longer
+ learn_rate_decays=0.9,
+ # a factor the initial learning rate will be multiplied by
+ # after each iteration of the training
+ epochs=300, # no of iternation
+ # dropouts = 0.25, # Express the percentage of nodes that
+ # will be randomly dropped as a decimal.
+ verbose=1)
+
if args.ldaDim > 0:
clf_final = clf
clf = Pipeline([('lda', LDA(n_components=args.ldaDim)),
@@ -140,13 +170,20 @@ def infer(args):
parser = argparse.ArgumentParser()
- parser.add_argument('--dlibFacePredictor', type=str,
- help="Path to dlib's face predictor.",
- default=os.path.join(dlibModelDir,
- "shape_predictor_68_face_landmarks.dat"))
- parser.add_argument('--networkModel', type=str,
- help="Path to Torch network model.",
- default=os.path.join(openfaceModelDir, 'nn4.small2.v1.t7'))
+ parser.add_argument(
+ '--dlibFacePredictor',
+ type=str,
+ help="Path to dlib's face predictor.",
+ default=os.path.join(
+ dlibModelDir,
+ "shape_predictor_68_face_landmarks.dat"))
+ parser.add_argument(
+ '--networkModel',
+ type=str,
+ help="Path to Torch network model.",
+ default=os.path.join(
+ openfaceModelDir,
+ 'nn4.small2.v1.t7'))
parser.add_argument('--imgDim', type=int,
help="Default image dimension.", default=96)
parser.add_argument('--cuda', action='store_true')
@@ -156,17 +193,29 @@ def infer(args):
trainParser = subparsers.add_parser('train',
help="Train a new classifier.")
trainParser.add_argument('--ldaDim', type=int, default=-1)
- trainParser.add_argument('--classifier', type=str,
- choices=['LinearSvm', 'GMM'],
- help='The type of classifier to use.',
- default='LinearSvm')
- trainParser.add_argument('workDir', type=str,
- help="The input work directory containing 'reps.csv' and 'labels.csv'. Obtained from aligning a directory with 'align-dlib' and getting the representations with 'batch-represent'.")
-
- inferParser = subparsers.add_parser('infer',
- help='Predict who an image contains from a trained classifier.')
- inferParser.add_argument('classifierModel', type=str,
- help='The Python pickle representing the classifier. This is NOT the Torch network model, which can be set with --networkModel.')
+ trainParser.add_argument(
+ '--classifier',
+ type=str,
+ choices=[
+ 'LinearSvm',
+ 'GMM',
+ 'RadialSvm',
+ 'DecisionTree',
+ 'GaussianNB',
+ 'DBN'],
+ help='The type of classifier to use.',
+ default='LinearSvm')
+ trainParser.add_argument(
+ 'workDir',
+ type=str,
+ help="The input work directory containing 'reps.csv' and 'labels.csv'. Obtained from aligning a directory with 'align-dlib' and getting the representations with 'batch-represent'.")
+
+ inferParser = subparsers.add_parser(
+ 'infer', help='Predict who an image contains from a trained classifier.')
+ inferParser.add_argument(
+ 'classifierModel',
+ type=str,
+ help='The Python pickle representing the classifier. This is NOT the Torch network model, which can be set with --networkModel.')
inferParser.add_argument('imgs', type=str, nargs='+',
help="Input image.")
@@ -0,0 +1,210 @@
+#!/usr/bin/env python2
+#
+# Example to run classifier on webcam stream.
+# Brandon Amos & Vijayenthiran
+# 2016/06/21
+#
+# Copyright 2015-2016 Carnegie Mellon University
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Contrib: Vijayenthiran
+# This example file shows to run a classifier on webcam stream. You need to
+# run the classifier.py to generate classifier with your own dataset.
+# To run this file from the openface home dir:
+# ./demo/classifier_webcam.py <path-to-your-classifier>
+
+
+import time
+
+start = time.time()
+
+import argparse
+import cv2
+import os
+import pickle
+
+import numpy as np
+np.set_printoptions(precision=2)
+from sklearn.mixture import GMM
+import openface
+
+fileDir = os.path.dirname(os.path.realpath(__file__))
+modelDir = os.path.join(fileDir, '..', 'models')
+dlibModelDir = os.path.join(modelDir, 'dlib')
+openfaceModelDir = os.path.join(modelDir, 'openface')
+
+
+def getRep(bgrImg):
+ start = time.time()
+ if bgrImg is None:
+ raise Exception("Unable to load image/frame")
+
+ rgbImg = cv2.cvtColor(bgrImg, cv2.COLOR_BGR2RGB)
+
+ if args.verbose:
+ print(" + Original size: {}".format(rgbImg.shape))
+ if args.verbose:
+ print("Loading the image took {} seconds.".format(time.time() - start))
+
+ start = time.time()
+
+ # Get the largest face bounding box
+ # bb = align.getLargestFaceBoundingBox(rgbImg) #Bounding box
+
+ # Get all bounding boxes
+ bb = align.getAllFaceBoundingBoxes(rgbImg)
+
+ if bb is None:
+ # raise Exception("Unable to find a face: {}".format(imgPath))
+ return None
+ if args.verbose:
+ print("Face detection took {} seconds.".format(time.time() - start))
+
+ start = time.time()
+
+ alignedFaces = []
+ for box in bb:
+ alignedFaces.append(
+ align.align(
+ args.imgDim,
+ rgbImg,
+ box,
+ landmarkIndices=openface.AlignDlib.OUTER_EYES_AND_NOSE))
+
+ if alignedFaces is None:
+ raise Exception("Unable to align the frame")
+ if args.verbose:
+ print("Alignment took {} seconds.".format(time.time() - start))
+
+ start = time.time()
+
+ reps = []
+ for alignedFace in alignedFaces:
+ reps.append(net.forward(alignedFace))
+
+ if args.verbose:
+ print("Neural network forward pass took {} seconds.".format(
+ time.time() - start))
+
+ # print reps
+ return reps
+
+
+def infer(img, args):
+ with open(args.classifierModel, 'r') as f:
+ (le, clf) = pickle.load(f) # le - label and clf - classifer
+
+ reps = getRep(img)
+ persons = []
+ confidences = []
+ for rep in reps:
+ try:
+ rep = rep.reshape(1, -1)
+ except:
+ print "No Face detected"
+ return (None, None)
+ start = time.time()
+ predictions = clf.predict_proba(rep).ravel()
+ # print predictions
+ maxI = np.argmax(predictions)
+ # max2 = np.argsort(predictions)[-3:][::-1][1]
+ persons.append(le.inverse_transform(maxI))
+ # print str(le.inverse_transform(max2)) + ": "+str( predictions [max2])
+ # ^ prints the second prediction
+ confidences.append(predictions[maxI])
+ if args.verbose:
+ print("Prediction took {} seconds.".format(time.time() - start))
+ pass
+ # print("Predict {} with {:.2f} confidence.".format(person, confidence))
+ if isinstance(clf, GMM):
+ dist = np.linalg.norm(rep - clf.means_[maxI])
+ print(" + Distance from the mean: {}".format(dist))
+ pass
+ return (persons, confidences)
+
+
+if __name__ == '__main__':
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ '--dlibFacePredictor',
+ type=str,
+ help="Path to dlib's face predictor.",
+ default=os.path.join(
+ dlibModelDir,
+ "shape_predictor_68_face_landmarks.dat"))
+ parser.add_argument(
+ '--networkModel',
+ type=str,
+ help="Path to Torch network model.",
+ default=os.path.join(
+ openfaceModelDir,
+ 'nn4.small2.v1.t7'))
+ parser.add_argument('--imgDim', type=int,
+ help="Default image dimension.", default=96)
+ parser.add_argument(
+ '--captureDevice',
+ type=int,
+ default=0,
+ help='Capture device. 0 for latop webcam and 1 for usb webcam')
+ parser.add_argument('--width', type=int, default=320)
+ parser.add_argument('--height', type=int, default=240)
+ parser.add_argument('--threshold', type=float, default=0.5)
+ parser.add_argument('--cuda', action='store_true')
+ parser.add_argument('--verbose', action='store_true')
+ parser.add_argument(
+ 'classifierModel',
+ type=str,
+ help='The Python pickle representing the classifier. This is NOT the Torch network model, which can be set with --networkModel.')
+
+ args = parser.parse_args()
+
+ align = openface.AlignDlib(args.dlibFacePredictor)
+ net = openface.TorchNeuralNet(
+ args.networkModel,
+ imgDim=args.imgDim,
+ cuda=args.cuda)
+
+ # Capture device. Usually 0 will be webcam and 1 will be usb cam.
+ video_capture = cv2.VideoCapture(args.captureDevice)
+ video_capture.set(3, args.width)
+ video_capture.set(4, args.height)
+
+ confidenceList = []
+ while True:
+ ret, frame = video_capture.read()
+ persons, confidences = infer(frame, args)
+ print "P: " + str(persons) + " C: " + str(confidences)
+ try:
+ # append with two floating point precision
+ confidenceList.append('%.2f' % confidences[0])
+ except:
+ # If there is no face detected, confidences matrix will be empty.
+ # We can simply ignore it.
+ pass
+
+ for i, c in enumerate(confidences):
+ if c <= args.threshold: # 0.5 is kept as threshold for known face.
+ persons[i] = "_unknown"
+
+ # Print the person name and conf value on the frame
+ cv2.putText(frame, "P: {} C: {}".format(persons, confidences),
+ (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
+ cv2.imshow('', frame)
+ # quit the program on the press of key 'q'
+ if cv2.waitKey(1) & 0xFF == ord('q'):
+ break
+ # When everything is done, release the capture
+ video_capture.release()
+ cv2.destroyAllWindows()
Oops, something went wrong.

0 comments on commit fa2a3a5

Please sign in to comment.