Skip to content

Instantly share code, notes, and snippets.

@tyaslab
Created January 10, 2019 23:46
Show Gist options
  • Save tyaslab/2d3ec525fb0ccb334ba75abb8755b3c0 to your computer and use it in GitHub Desktop.
Save tyaslab/2d3ec525fb0ccb334ba75abb8755b3c0 to your computer and use it in GitHub Desktop.
Face Recognition
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