def __init__(self, config, pca_components=25, n_components=2, metric='euclidean', face_detect=True, transform=None, img_size=(200, 180)): # type: (Config, int, int, str, bool, transforms.Compose, Tuple[int, int]) -> None self.pca_components = pca_components self.n_components = n_components self.metric = metric self.pca_lda = None self.label2name = None self.transform = transform if face_detect: self.face_detector = FaceDetector( prototxt='face_detect/checkpoints/deploy.prototxt.txt', checkpoint_path= 'face_detect/checkpoints/res10_300x300_ssd_iter_140000.caffemodel', img_size=img_size) else: self.face_detector = None self.result_writer = ResultWriter(config, 'fisher')
def __init__(self, config, C=100.0, random_state=42, max_iter=6000, num_points=24, radius=8, face_detect=False, transform=None, img_size=(200, 180)): # type: (Config, float, int, int, int, int, bool, transforms.Compose, Tuple[int, int])->None self.clf = LinearSVC(C=C, random_state=random_state, max_iter=max_iter) self.lbp = LocalBinaryPatterns(numPoints=num_points, radius=radius) self.transform = transform self.img_size = img_size if face_detect: self.face_detector = FaceDetector( prototxt='face_detect/checkpoints/deploy.prototxt.txt', checkpoint_path='face_detect/checkpoints/res10_300x300_ssd_iter_140000.caffemodel', img_size=img_size ) else: self.face_detector = None self.result_writer = ResultWriter(config=config, method='lbph')
def __init__(self, config, transform=None, distance_type='norm2', face_detect=True, img_size=(200, 180)): # type: (Config, transforms.Compose, str, bool, Tuple[int, int]) -> None assert distance_type == 'norm2' or distance_type == 'mahalanobis', f'invalid distance type: {distance_type}' self.distance_type = distance_type self.avg_face = None self.eigen_faces = None self.transform = transform self.classes_representation = [] if face_detect: self.face_detector = FaceDetector( prototxt='face_detect/checkpoints/deploy.prototxt.txt', checkpoint_path= 'face_detect/checkpoints/res10_300x300_ssd_iter_140000.caffemodel', img_size=img_size) else: self.face_detector = None self.result_writer = ResultWriter(config, 'eigen')
def __init__(self, config: Config): self.clf = svm.SVC(gamma='scale') self.result_writer = ResultWriter(config=config, method='face_rec')
class FaceRecognizer: def __init__(self, config: Config): self.clf = svm.SVC(gamma='scale') self.result_writer = ResultWriter(config=config, method='face_rec') def train(self, train_csv_path: str): assert os.path.exists(train_csv_path), f'invalid csv file path: {train_csv_path}' encodings = [] names = [] with open(train_csv_path, 'r') as csv_file: csv_reader = csv.reader(csv_file) print('get image encoding...') for img_file_path, person in tqdm(csv_reader): try: face = fr.load_image_file(img_file_path) face_enc = fr.face_encodings(face)[0] except: continue encodings.append(face_enc) names.append(person) print('start to fit...') self.clf.fit(encodings, names) def save_model(self, save_path: str): os.makedirs(os.path.dirname(save_path), exist_ok=True) with open(save_path, 'wb') as f: pickle.dump(self.clf, f) def load_model(self, pkl_path: str): with open(pkl_path, 'rb') as f: self.clf = pickle.load(f) def predict_img(self, img_path: str) -> str: assert os.path.exists(img_path), f'invalid image path: {img_path}' img = fr.load_image_file(img_path) img_enc = fr.face_encodings(img)[0] name = self.clf.predict([img_enc]) return name def predict(self, csv_file_path: str, data_type: str) -> float: assert os.path.exists(csv_file_path), f'invalid csv file path: {csv_file_path}' print('get image encoding...') right_num = 0 encoding, names, img_file_paths = [], [], [] with open(csv_file_path, 'r') as csv_file: csv_reader = csv.reader(csv_file) for img_file_path, person in tqdm(csv_reader): try: face = fr.load_image_file(img_file_path) face_enc = fr.face_encodings(face)[0] except: continue encoding.append(face_enc) img_file_paths.append(img_file_path) names.append(person) pred_names = self.clf.predict(encoding) for i in range(len(pred_names)): if pred_names[i] == names[i]: right_num += 1 else: print(f'pred: {pred_names[i]}, gt: {names[i]}') acc = right_num / len(pred_names) self.result_writer.write(image_paths=img_file_paths, pred_names=pred_names, names=names, accuracy=acc, fname=data_type + '.csv') return acc
class EigenFaceRecognizer: def __init__(self, config, transform=None, distance_type='norm2', face_detect=True, img_size=(200, 180)): # type: (Config, transforms.Compose, str, bool, Tuple[int, int]) -> None assert distance_type == 'norm2' or distance_type == 'mahalanobis', f'invalid distance type: {distance_type}' self.distance_type = distance_type self.avg_face = None self.eigen_faces = None self.transform = transform self.classes_representation = [] if face_detect: self.face_detector = FaceDetector( prototxt='face_detect/checkpoints/deploy.prototxt.txt', checkpoint_path= 'face_detect/checkpoints/res10_300x300_ssd_iter_140000.caffemodel', img_size=img_size) else: self.face_detector = None self.result_writer = ResultWriter(config, 'eigen') def get_representation(self, img_path: str): if self.avg_face is None or self.eigen_faces is None: raise ValueError('Model has not been trained') assert os.path.exists(img_path), f'invalid image path: {img_path}' image = Image.open(img_path) image = image.convert('RGB') if self.face_detector is not None: x_begin, x_end, y_begin, y_end = self.face_detector.detect( img_path) image = image.crop((x_begin, y_begin, x_end, y_end)) if self.transform is not None: image = self.transform(image) image = np.array(image, dtype=np.float32) image_vector = image.flatten().reshape((-1, 1)) image_vector -= self.avg_face.reshape((-1, 1)) return np.dot(self.eigen_faces, image_vector) def train(self, train_csv_path: str): with open(train_csv_path, 'r') as csv_file: csv_reader = csv.reader(csv_file) image_paths, names = [], [] for img_path, name in csv_reader: image_paths.append(img_path) names.append(name) eigen_faces, self.avg_face = self.get_eigen_face( image_paths, transform=self.transform) self.avg_face = self.avg_face.flatten() self.eigen_faces = np.zeros( (len(eigen_faces), len(eigen_faces[0]))) for i in range(len(eigen_faces)): self.eigen_faces[i, :] = eigen_faces[i].T with open(train_csv_path, 'r') as csv_file: csv_reader = csv.reader(csv_file) cur_rep, cur_num, cur_name = None, 0, None for img_path, name in tqdm(csv_reader): try: rep = self.get_representation(img_path) except: continue if cur_name is None: cur_name = name elif cur_name != name: self.classes_representation.append( (cur_name, cur_rep / cur_num)) cur_name = name cur_num = 0 cur_rep = None if cur_rep is None: cur_rep = rep else: cur_rep += rep cur_num += 1 def predict(self, csv_path: str, data_type: str) -> float: if self.avg_face is None or self.eigen_faces is None: raise ValueError('Model has not been trained') with open(csv_path, 'r') as csv_file: csv_reader = csv.reader(csv_file) img_representations, names, img_file_paths = [], [], [] for img_path, name in csv_reader: try: rep = self.get_representation(img_path) except: continue img_representations.append(rep.flatten()) names.append(name) img_file_paths.append(img_path) pred_names = [] for rep in img_representations: min_dist = None pred_name = None for name, class_rep in self.classes_representation: # X = np.vstack([rep, class_rep.flatten()]) # d = pdist(X, 'mahalanobis') d = np.sum((class_rep.flatten() - rep)**2) if min_dist is None or min_dist > d: min_dist = d pred_name = name pred_names.append(pred_name) right_num = 0 for i in range(len(names)): if names[i] != pred_names[i]: pass # print(f'pred: {pred_names[i]}, gt: {names[i]}') else: right_num += 1 acc = right_num / len(names) self.result_writer.write(image_paths=img_file_paths, pred_names=pred_names, names=names, accuracy=acc, fname=data_type + '.csv') return acc @staticmethod def _createDataMatrix(images): print("Creating data matrix", end=" ... ") ''' Allocate space for all images in one data matrix. The size of the data matrix is ( w * h * 3, numImages ) where, w = width of an image in the dataset. h = height of an image in the dataset. 3 is for the 3 color channels. ''' numImages = len(images) sz = images[0].shape data = np.zeros((numImages, sz[0] * sz[1] * sz[2]), dtype=np.float32) for i in range(0, numImages): image = images[i].flatten() data[i, :] = image print("DONE") return data.T @staticmethod def _PCA(matrix: np.ndarray, PCA_size=10): avg_face = np.average(matrix, axis=1) A = matrix.T - avg_face A = A.T eigen_values, eigen_vectors = np.linalg.eigh(np.dot(A.T, A)) sorted_indices = np.argsort(eigen_values) top_indices = sorted_indices[:-PCA_size - 1:-1] eigen_vectors = eigen_vectors[top_indices] ret_eigen_vectors = [ cv2.normalize(src=np.dot(A, eigen_vector).T, dst=None, norm_type=cv2.NORM_L2) for eigen_vector in eigen_vectors ] return avg_face, ret_eigen_vectors @staticmethod def get_eigen_face(image_paths: List[str], eigen_size: int = 100, transform=None): """ Generate eigen face for image :param eigen_size: size for eigen vector after PCA :param image_paths: the list of paths of images to process :param transform: transformation on image. Default is None. :return: eigen faces and average face """ images = [] print('Read images...') for image_path in tqdm(image_paths): assert os.path.exists( image_path), f'invalid image path: {image_path}' try: img = Image.open(image_path) img = img.convert('RGB') except: continue if transform is not None: img = transform(img) img = np.array(img) images.append(img) shape = images[0].shape img_data = EigenFaceRecognizer._createDataMatrix(images) print('Calculate PCA', end=' ... ') mean, eigen_vectors = EigenFaceRecognizer._PCA(img_data, eigen_size) print('DONE') avg_face = mean.reshape(shape) print('Get eigen faces for images', end=' ... ') eigen_faces = [] for eigen_vector in eigen_vectors: # eigen_face = eigen_vector.reshape(shape) eigen_faces.append(eigen_vector) print('DONE') return eigen_faces, avg_face def save_model(self, save_path: str): os.makedirs(os.path.dirname(save_path), exist_ok=True) with open(save_path, 'wb') as f: pickle.dump( { 'classes_representation': self.classes_representation, 'avg_face': self.avg_face, 'eigen_faces': self.eigen_faces }, f) def load_model(self, pkl_path: str): with open(pkl_path, 'rb') as f: checkpoint = pickle.load(f) self.classes_representation = checkpoint['classes_representation'] self.avg_face = checkpoint['avg_face'] self.eigen_faces = checkpoint['eigen_faces']
class PCALDAClassifier(Classifier): def __init__(self, config, pca_components=25, n_components=2, metric='euclidean', face_detect=True, transform=None, img_size=(200, 180)): # type: (Config, int, int, str, bool, transforms.Compose, Tuple[int, int]) -> None self.pca_components = pca_components self.n_components = n_components self.metric = metric self.pca_lda = None self.label2name = None self.transform = transform if face_detect: self.face_detector = FaceDetector( prototxt='face_detect/checkpoints/deploy.prototxt.txt', checkpoint_path= 'face_detect/checkpoints/res10_300x300_ssd_iter_140000.caffemodel', img_size=img_size) else: self.face_detector = None self.result_writer = ResultWriter(config, 'fisher') def _get_image_data_from_csv(self, csv_path): # type: (str) -> Tuple[np.ndarray, np.ndarray, Dict[int:str], List[str]] assert os.path.exists(csv_path), f'invalid path: {csv_path}' csv_file = open(csv_path, 'r') csv_reader = csv.reader(csv_file) label2name, name2label = {}, {} images, labels, imgs_paths = [], [], [] label = 0 print('Reading images ... ', end='') for image_path, name in csv_reader: try: img = Image.open(image_path).convert('RGB') if self.face_detector is not None: x_begin, x_end, y_begin, y_end = self.face_detector.detect( image_path, False) img = img.crop((x_begin, y_begin, x_end, y_end)) if self.transform is not None: img = self.transform(img) img = np.array(img) except: continue imgs_paths.append(image_path) images.append(np.ravel(img)) if name not in name2label: label2name[label] = name name2label[name] = label label += 1 labels.append(name2label[name]) csv_file.close() print('DONE') return np.vstack(images), np.hstack(labels), label2name, imgs_paths def train(self, X, y): # print(X.shape) print('start training ... ', end='') self.pca_lda = PCALDA( pca_components=self.pca_components, n_components=self.n_components, ).fit(X, y) print('DONE') return self def train_csv_file(self, csv_path: str): images, labels, label2name, _ = self._get_image_data_from_csv( csv_path=csv_path) self.train(images, labels) def predict(self, X, return_distances=False): assert self.pca_lda is not None, \ 'You must fit %s first' % self.__class__.__name__ # Find the nearest class mean to each new sample class_means = self.pca_lda.lda.class_means projected = self.pca_lda.project(np.atleast_2d(X)) distances = distance.cdist(projected, class_means, metric=self.metric) min_indices = np.argmin(distances, axis=1) if return_distances: return min_indices, distances return min_indices def predict_proba(self, X): indices, distances = self.predict(X, return_distances=True) # Perform softmax on negative distances because a good distance is a # low distance, and softmax does the inverse probs = softmax(-distances) return indices, probs def predict_csv_file(self, csv_path, data_type): # type: (str, str) -> float """ :return: accuracy """ assert os.path.exists(csv_path), f'invalid csv path: {csv_path}' assert self.pca_lda is not None, \ 'You must fit %s first' % self.__class__.__name__ images, labels, label2name, img_paths = self._get_image_data_from_csv( csv_path) pred_indices = self.predict(images) # print(f'pred_indices len: {len(pred_indices)}, names len: {len(names)}') right_num = 0 assert len(pred_indices) == len(labels) pred_names = [ label2name[pred_indices[i]] for i in range(len(pred_indices)) ] names = [label2name[labels[i]] for i in range(len(labels))] for i in range(len(pred_names)): if pred_names[i] == names[i]: right_num += 1 acc = right_num / len(pred_indices) self.result_writer.write(image_paths=img_paths, pred_names=pred_names, names=names, accuracy=acc, fname=data_type + '.csv') return acc def save_model(self, fname): dir_name = os.path.dirname(fname) os.makedirs(dir_name, exist_ok=True) face_detector = self.face_detector self.face_detector = None with open(fname, 'wb') as f: pickle.dump(self, f) self.face_detector = face_detector def load_model(self, fname): assert os.path.exists(fname), f'invalid file name: {fname}' with open(fname, 'rb') as f: checkpoint = pickle.load(f) self.pca_components = checkpoint.pca_components self.n_components = checkpoint.n_components self.metric = checkpoint.metric self.pca_lda = checkpoint.pca_lda self.label2name = checkpoint.label2name self.transform = checkpoint.transform
class LBPHRecognizer: def __init__(self, config, C=100.0, random_state=42, max_iter=6000, num_points=24, radius=8, face_detect=False, transform=None, img_size=(200, 180)): # type: (Config, float, int, int, int, int, bool, transforms.Compose, Tuple[int, int])->None self.clf = LinearSVC(C=C, random_state=random_state, max_iter=max_iter) self.lbp = LocalBinaryPatterns(numPoints=num_points, radius=radius) self.transform = transform self.img_size = img_size if face_detect: self.face_detector = FaceDetector( prototxt='face_detect/checkpoints/deploy.prototxt.txt', checkpoint_path='face_detect/checkpoints/res10_300x300_ssd_iter_140000.caffemodel', img_size=img_size ) else: self.face_detector = None self.result_writer = ResultWriter(config=config, method='lbph') def _get_data_names_for_csv(self, csv_path): assert os.path.exists(csv_path), f'invalid csv path: {csv_path}' data, names, image_paths = [], [], [] with open(csv_path, 'r') as csv_file: csv_reader = csv.reader(csv_file) for image_path, name in csv_reader: try: img = Image.open(image_path).convert('L') if self.face_detector is not None: x_begin, x_end, y_begin, y_end = self.face_detector.detect(image_path, False) img = img.crop((x_begin, y_begin, x_end, y_end)) if self.transform is not None: img = self.transform(img) img = self.transform(img) # img.show() img = np.array(img) except: continue image_paths.append(image_path) hist = self.lbp.describe(img) data.append(hist) names.append(name) return data, names, image_paths def train(self, csv_path): # type:(str)->None data, names, _ = self._get_data_names_for_csv(csv_path) self.clf.fit(data, names) def predict(self, csv_path, data_type): # type: (str, str) -> float """ :return: accuracy """ data, names, image_paths = self._get_data_names_for_csv(csv_path) pred_names = self.clf.predict(data) right_num = 0 assert len(names) == len(pred_names) for i in range(len(pred_names)): if names[i] == pred_names[i]: right_num += 1 acc = right_num / len(pred_names) self.result_writer.write(image_paths, pred_names, names, acc, data_type + '.csv') return acc def save_model(self, file_name: str): os.makedirs(os.path.dirname(file_name), exist_ok=True) face_detector = self.face_detector self.face_detector = None with open(file_name, 'wb') as f: pickle.dump(self, f) self.face_detector = face_detector def load_model(self, file_name: str): assert os.path.exists(file_name), f'invalid file name: {file_name}' with open(file_name, 'rb') as f: checkpoint = pickle.load(f) self.clf = checkpoint.clf self.lbp = checkpoint.lbp self.transform = checkpoint.transform