Created
January 10, 2019 23:46
-
-
Save tyaslab/2d3ec525fb0ccb334ba75abb8755b3c0 to your computer and use it in GitHub Desktop.
Face Recognition
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import cv2 | |
import numpy as np | |
class FaceRecognition(object): | |
pca = None | |
u = None | |
weight = None | |
training_image_filenames = [] | |
cache_dir = 'facerec_cache' | |
def __init__(self, **kwargs): | |
self.image_size = kwargs.pop('image_size', (50, 50)) | |
self.k_constant = kwargs.pop('k_constant', 0.5) | |
training_image_filenames_file_exists = os.path.exists( | |
os.path.join(self.cache_dir, 'training_image_filenames') | |
) | |
pca_file_exists = os.path.exists(os.path.join(self.cache_dir, 'pca.npy')) | |
u_file_exists = os.path.exists(os.path.join(self.cache_dir, 'u.npy')) | |
weight_file_exists = os.path.exists(os.path.join(self.cache_dir, 'weight.npy')) | |
# check if cache_dir dir exists | |
if not os.path.exists(self.cache_dir): | |
os.mkdir(self.cache_dir) | |
# get existing data | |
if training_image_filenames_file_exists and pca_file_exists and u_file_exists and weight_file_exists: | |
with open(os.path.join(self.cache_dir, 'training_image_filenames'), 'r') as f: | |
for line in f: | |
self.training_image_filenames.append(line.strip()) | |
self.pca = np.load(os.path.join(self.cache_dir, 'pca.npy')) | |
self.u = np.load(os.path.join(self.cache_dir, 'u.npy')) | |
self.weight = np.load(os.path.join(self.cache_dir, 'weight.npy')) | |
else: | |
print('Warning! no data available! You should train data before recognizing') | |
def train(self, training_image_filenames, show_vars=False): | |
image_height, image_width = self.image_size | |
N = image_height * image_width | |
M = len(training_image_filenames) | |
k = round(self.k_constant * M) | |
if show_vars: | |
print('N = ', N) | |
print('M = ', M) | |
# training matrix (should result N x M matrix) | |
face_vector_space = np.zeros((N, M), dtype=np.float) | |
for key, filename in enumerate(training_image_filenames): | |
img = cv2.imread(filename, cv2.IMREAD_GRAYSCALE) | |
# face_cascade = cv2.CascadeClassifier('/Volumes/Super/adityadarmawan/Git/django2/facerec/haarcascades/haarcascade_frontalface_default.xml') | |
# faces = face_cascade.detectMultiScale(img, 1.2, 5) # based uplon the book | |
# if type(faces) == tuple or faces.size == 0: | |
# print('no face in ', filename) | |
# i = 0 | |
# max_index = None | |
# max_x = None | |
# max_y = None | |
# max_w = None | |
# max_h = None | |
# for (x, y, w, h) in faces: | |
# if max_index is None or (max_w * max_h) < (w * h): | |
# max_index = i | |
# max_x = x | |
# max_y = y | |
# max_w = w | |
# max_h = h | |
# i += 1 | |
# img = img[max_y:max_y + max_h, max_x:max_x + max_w] | |
img = cv2.resize(img, (image_height, image_width)) | |
# img = cv2.equalizeHist(img) | |
# elliptical mask | |
# mask = np.zeros(self.image_size, dtype=np.uint8) | |
# mask[:,:] = 255 | |
# cv2.ellipse( | |
# mask, | |
# (round(image_height * 0.5), round(image_width * 0.4)), | |
# (round(image_height * 0.5), round(image_width * 0.8)), | |
# 0, 0, 360, 0, cv2.FILLED | |
# ) | |
# img = np.bitwise_and(img, mask) | |
face_vector_space[:,key] = img.flatten() | |
if show_vars: | |
print('face vector space = ', face_vector_space.shape) | |
# average face (should result N vector) | |
u = np.mean(face_vector_space, axis=1) | |
if show_vars: | |
print('mean = ', u.shape) | |
# show mean image to imshow | |
# u_image = u.reshape((image_height, image_width, 1)) | |
# u_image = np.uint8(u_image) | |
# cv2.imshow('mean', u_image) | |
# cv2.waitKey(0) | |
# # normalized (a.k.a. zero_mean) should result N x M matrix | |
zero_mean = np.zeros((N, M), dtype=np.float) | |
for i in range(0, M): | |
zero_mean[:,i] = face_vector_space[:,i] - u | |
if show_vars: | |
print('zero mean = ', zero_mean.shape) | |
# show zero mean image to imshow | |
# for i in range(zero_mean.shape[1]): | |
# zero_mean_image = zero_mean[:,i].reshape((image_height, image_width, 1)) | |
# zero_mean_image = np.uint8(zero_mean_image) | |
# cv2.imshow('zero mean %s' % (i,), zero_mean_image) | |
# cv2.waitKey(0) | |
# # covariance (dimension reductionality) should result N x N matrix | |
cov = np.dot(zero_mean.T, zero_mean) | |
if show_vars: | |
print('cov = ', cov.shape) | |
# eigenvalues and eigenvectors should result N vector and N x N vector, respectively | |
eigenvalues, eigenvectors = np.linalg.eig(cov) | |
# sort eigenvectors based on eigenvalues descending | |
idx = eigenvalues.argsort()[::-1] | |
idx = np.argsort(eigenvalues) | |
eigenvalues = eigenvalues[idx] | |
eigenvectors = eigenvectors[:,idx] | |
if show_vars: | |
print('eigenvalues = ', eigenvalues.shape) | |
print('eigenvectors = ', eigenvectors.shape) | |
# select only k eigenvectors | |
# eigenvectors = eigenvectors[0:k,] | |
pca = np.dot(zero_mean, eigenvectors) | |
if show_vars: | |
print('pca = ', pca.shape) | |
# show pca image to imshow | |
# for i in range(pca.shape[1]): | |
# pca_image = pca[:,i].reshape((image_height, image_width, 1)) | |
# pca_image = np.uint8(pca_image) | |
# cv2.imshow('pca %s' % (i,), pca_image) | |
# cv2.waitKey(0) | |
weight = np.zeros((M, M), dtype=np.float) # just to make sure that it contains vectors | |
for i in range(0, M): | |
weight[:,i] = np.dot(zero_mean[:, i], pca) | |
if show_vars: | |
print('weight = ', weight.shape) | |
self.training_image_filenames = training_image_filenames | |
self.pca = pca | |
self.u = u | |
self.weight = weight | |
# save training image filenames | |
with open(os.path.join(self.cache_dir, 'training_image_filenames'), 'w') as f: | |
for t in self.training_image_filenames: | |
f.write(t + '\n') | |
np.save(os.path.join(self.cache_dir, 'pca'), self.pca) | |
np.save(os.path.join(self.cache_dir, 'u'), self.u) | |
np.save(os.path.join(self.cache_dir, 'weight'), self.weight) | |
def recognize(self, test_image_filename): | |
if self.pca is None or self.u is None or self.weight is None: | |
raise Exception('No data available!') | |
image_height, image_width = self.image_size | |
N = image_height * image_width | |
Mtraining = self.pca.shape[1] | |
M = 1 # test_image_filename is NOT array | |
test_image = cv2.imread(test_image_filename, cv2.IMREAD_GRAYSCALE) | |
# face_cascade = cv2.CascadeClassifier('/Volumes/Super/adityadarmawan/Git/django2/facerec/haarcascades/haarcascade_frontalface_default.xml') | |
# faces = face_cascade.detectMultiScale(test_image, 1.2, 5) # based uplon the book | |
# if type(faces) == tuple or faces.size == 0: | |
# print('no face in ', test_image_filename) | |
# i = 0 | |
# max_index = None | |
# max_x = None | |
# max_y = None | |
# max_w = None | |
# max_h = None | |
# for (x, y, w, h) in faces: | |
# if max_index is None or (max_w * max_h) < (w * h): | |
# max_index = i | |
# max_x = x | |
# max_y = y | |
# max_w = w | |
# max_h = h | |
# i += 1 | |
# test_image = test_image[max_y:max_y + max_h, max_x:max_x + max_w] | |
test_image = cv2.resize(test_image, (image_height, image_width)) | |
# test_image = cv2.equalizeHist(test_image) | |
# elliptical mask | |
# mask = np.zeros(self.image_size, dtype=np.uint8) | |
# mask[:,:] = 255 | |
# cv2.ellipse( | |
# mask, | |
# (round(image_height * 0.5), round(image_width * 0.4)), | |
# (round(image_height * 0.5), round(image_width * 0.8)), | |
# 0, 0, 360, 0, cv2.FILLED | |
# ) | |
# test_image = np.bitwise_and(test_image, mask) | |
test_image = test_image.flatten() | |
# shoud be N x M matrix i.e. N x 1 vector | |
test_zero_mean = test_image - self.u # is a zero mean of test image | |
# show zero mean image to imshow | |
# test_zero_mean_image = test_zero_mean.reshape((image_height, image_width, 1)) | |
# test_zero_mean_image = np.uint8(test_zero_mean_image) | |
# cv2.imshow('test zero mean', test_zero_mean_image) | |
# cv2.waitKey(0) | |
# test weight (should be Mtraining x 1 vector) | |
test_weight = np.dot(test_zero_mean, self.pca) | |
# check distance | |
# TODO: simplify distance | |
distance_list = [] | |
for i in range(0, Mtraining): | |
distance = np.linalg.norm(test_weight - self.weight[:,i]) | |
distance_list.append(distance) | |
distance_list = np.asarray(distance_list) | |
shortest_distance_idx = np.argmin(distance_list) | |
shortest_distance = distance_list[shortest_distance_idx] | |
return { | |
'distance_list': distance_list, | |
'shortest_distance': shortest_distance, | |
'shortest_distance_idx': shortest_distance_idx, | |
'recognized_image': self.training_image_filenames[shortest_distance_idx] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment