updated
| 
						 | 
					@ -0,0 +1,54 @@
 | 
				
			||||||
 | 
					# Byte-compiled / optimized / DLL files
 | 
				
			||||||
 | 
					__pycache__/
 | 
				
			||||||
 | 
					*.py[cod]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# C extensions
 | 
				
			||||||
 | 
					*.so
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Distribution / packaging
 | 
				
			||||||
 | 
					.Python
 | 
				
			||||||
 | 
					env/
 | 
				
			||||||
 | 
					bin/
 | 
				
			||||||
 | 
					build/
 | 
				
			||||||
 | 
					develop-eggs/
 | 
				
			||||||
 | 
					dist/
 | 
				
			||||||
 | 
					eggs/
 | 
				
			||||||
 | 
					lib/
 | 
				
			||||||
 | 
					lib64/
 | 
				
			||||||
 | 
					parts/
 | 
				
			||||||
 | 
					sdist/
 | 
				
			||||||
 | 
					var/
 | 
				
			||||||
 | 
					*.egg-info/
 | 
				
			||||||
 | 
					.installed.cfg
 | 
				
			||||||
 | 
					*.egg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Installer logs
 | 
				
			||||||
 | 
					pip-log.txt
 | 
				
			||||||
 | 
					pip-delete-this-directory.txt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Unit test / coverage reports
 | 
				
			||||||
 | 
					htmlcov/
 | 
				
			||||||
 | 
					.tox/
 | 
				
			||||||
 | 
					.coverage
 | 
				
			||||||
 | 
					.cache
 | 
				
			||||||
 | 
					nosetests.xml
 | 
				
			||||||
 | 
					coverage.xml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Translations
 | 
				
			||||||
 | 
					*.mo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Mr Developer
 | 
				
			||||||
 | 
					.mr.developer.cfg
 | 
				
			||||||
 | 
					.project
 | 
				
			||||||
 | 
					.pydevproject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Rope
 | 
				
			||||||
 | 
					.ropeproject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Django stuff:
 | 
				
			||||||
 | 
					*.log
 | 
				
			||||||
 | 
					*.pot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Sphinx documentation
 | 
				
			||||||
 | 
					docs/_build/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,32 @@
 | 
				
			||||||
 | 
					This implements a face alignment method, as preprocessing to tasks such as age and gender estimation, 
 | 
				
			||||||
 | 
					and face recognition as described in [1] and [2].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The code calls an executable of facial landmarks detection, by X.Zhu and D. Ramanan, implementing the algorithm described in [3]. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(The rest of this text is a quotation from their code: Copyright (C) 2012 Xiangxin Zhu, Deva Ramanan)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It includes pre-trained face models. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Much of the detection code is built on top of part-based model implementation of [4]. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The training code implements a quadratic program (QP) solver described in [5].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In the training code, we use the positive samples from MultiPIE dataset (available at www.multipie.org) and the negative images from the INRIAPerson dataset [6] (included in the package). 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Acknowledgements: We graciously thank the authors of the previous code releases and image benchmarks for making them publicly available.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					References
 | 
				
			||||||
 | 
					==========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[1] E. Eidinger, R. Enbar, T. Hassner, Age and Gender Estimation of Unfiltered Faces, submitted to IEEE TRANSACTIONS ON INFORMATION FORENSICS AND SECURITY, 2014
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[2] http://www.openu.ac.il/home/hassner/Adience/links.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[3] X. Zhu, D. Ramanan. Face Detection, Pose Estimation and Landmark Localization in the Wild. CVPR 2012.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[4] P. Felzenszwalb, R. Girshick, D. McAllester. Discriminatively Trained Deformable Part Models. http://people.cs.uchicago.edu/~pff/latent.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[5] D. Ramanan. Dual Coordinate Descent Solvers for Large Structured Prediction Problems. UCI Technical Report, to appear.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[6] N. Dalal, B. Triggs. Histograms of Oriented Gradients for Human Detection. CVPR 2005.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,21 @@
 | 
				
			||||||
 | 
					Copyright (C) 2014 Adience SER Ltd. (www.adience.com)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Permission is hereby granted, free of charge, to any person obtaining
 | 
				
			||||||
 | 
					a copy of this software and associated documentation files (the
 | 
				
			||||||
 | 
					"Software"), to deal in the Software without restriction, including
 | 
				
			||||||
 | 
					without limitation the rights to use, copy, modify, merge, publish,
 | 
				
			||||||
 | 
					distribute, sublicense, and/or sell copies of the Software, and to
 | 
				
			||||||
 | 
					permit persons to whom the Software is furnished to do so, subject to
 | 
				
			||||||
 | 
					the following conditions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The above copyright notice and this permission notice shall be
 | 
				
			||||||
 | 
					included in all copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | 
				
			||||||
 | 
					EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | 
				
			||||||
 | 
					MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 | 
				
			||||||
 | 
					NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 | 
				
			||||||
 | 
					LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 | 
				
			||||||
 | 
					OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 | 
				
			||||||
 | 
					WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,49 @@
 | 
				
			||||||
 | 
					adience_align
 | 
				
			||||||
 | 
					========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This project provides alignment tools for faces, to be used as a preprocessing step before computer vision tasks on face images.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Homepage for the project: http://www.openu.ac.il/home/hassner/Adience/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					See the test for example usage.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Specificaly, the "pipeline" test, shows how to use the full process (just remember to change the location of the model files to where you stor the *.xml and other model files)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Installation
 | 
				
			||||||
 | 
					=========
 | 
				
			||||||
 | 
					in the root of the repository:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					python setup.py sdist
 | 
				
			||||||
 | 
					sudo pip install dist/adience-<version_number>.tar.gz
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CopyRight
 | 
				
			||||||
 | 
					=========
 | 
				
			||||||
 | 
					(contact: Eran Eidinger (eran@adience.com), Roee Enbar (roee.e@adience.com))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					See the LICENSE.txt file (basically, an MIT license).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					With any publication that uses this alignment code, or it's derivative, we kindly ask that you cite the paper:
 | 
				
			||||||
 | 
					E. Eidinger, R. Enbar, and T. Hassner, Age and Gender Estimation of Unfiltered Faces, Transactions on Information Forensics and Security (IEEE-TIFS), special issue on Face Recognition in the Wild
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For more details, please see:
 | 
				
			||||||
 | 
					http://www.openu.ac.il/home/hassner/Adience/publications.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Compilation notes
 | 
				
			||||||
 | 
					========
 | 
				
			||||||
 | 
					1. The shared objects were compiled for linux 64bit on Ubuntu 13.10
 | 
				
			||||||
 | 
					2. The SO uses boost-1.53, so make sure it is installed on your system and available at /usr/local/, or use LD_LIBRARY_PATH="yourpath" to point it at the right place. Alternatively, place "libboost_system.so.1.53.0" and "libboost_filesystem.so.1.53.0". at the "adiencealign/resources/" subfolder
 | 
				
			||||||
 | 
					3. For landmarks detection, we use the file libPartsBasedDetector.so, compiled from the project https://github.com/wg-perception/PartsBasedDetector. You can either compile it yourselves, or use the version under "resources" subfolder, compiled with boost 1.53, on a linux ubuntu 14.04 machine.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					We will release the source code for the shared object in the near future
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Running the test
 | 
				
			||||||
 | 
					========
 | 
				
			||||||
 | 
					1. run ```./clear_test.sh``` to delete results of old tests.
 | 
				
			||||||
 | 
					2. run ```python test_pipeline.py```
 | 
				
			||||||
 | 
					3. results are in the "outputs" subfolder
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					Created on May 7, 2014
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@author: eran
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					from adiencealign.common.landmarks import fidu_transform, shift_vector,\
 | 
				
			||||||
 | 
					    WEIGHTS3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AffineAligner(object):
 | 
				
			||||||
 | 
					    def __init__(self, fidu_model_file, ):
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.shift = ( 0.25, 0.25 )
 | 
				
			||||||
 | 
					        fidu_model = [(int(x.split(',')[1]),int(x.split(',')[2])) for x in file(fidu_model_file,'r')]
 | 
				
			||||||
 | 
					        self.fidu_model = shift_vector(fidu_model, self.shift)
 | 
				
			||||||
 | 
					        self.WEIGHTS3 = WEIGHTS3
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def align(self, img, fidu_points):
 | 
				
			||||||
 | 
					        # create bs1 image
 | 
				
			||||||
 | 
					        funneled_img, R = fidu_transform(self.fidu_model, fidu_points, WEIGHTS3, img, self.shift)
 | 
				
			||||||
 | 
					        return funneled_img, R      
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,354 @@
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					Created on May 7, 2014
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@author: eran
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					import cv2
 | 
				
			||||||
 | 
					import pickle
 | 
				
			||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					from shapely.geometry.polygon import Polygon
 | 
				
			||||||
 | 
					import math
 | 
				
			||||||
 | 
					from adiencealign.common.files import make_path, expand_path
 | 
				
			||||||
 | 
					from adiencealign.common.images import pad_image_for_rotation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CascadeDetector(object):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    This is a haar cascade classifier capable of detecting in multiple angles
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    def __init__(self, cascade_file = './resources/haarcascade_frontalface_default.xml', 
 | 
				
			||||||
 | 
					                 min_size = (10, 10),
 | 
				
			||||||
 | 
					                 min_neighbors = 20,
 | 
				
			||||||
 | 
					                 scale_factor = 1.04,
 | 
				
			||||||
 | 
					                 angles = [0],
 | 
				
			||||||
 | 
					                 thr = 0.4, 
 | 
				
			||||||
 | 
					                 cascade_type = 'haar'):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        cascade_type - is a string defining the type of cascade
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        print(expand_path('.'))
 | 
				
			||||||
 | 
					        self.cascade_file = cascade_file.rsplit('/',1)[1]
 | 
				
			||||||
 | 
					        self._cascade_classifier = cv2.CascadeClassifier(cascade_file)
 | 
				
			||||||
 | 
					        self.scale_factor = scale_factor
 | 
				
			||||||
 | 
					        self.min_neighbors = min_neighbors
 | 
				
			||||||
 | 
					        self.min_size = min_size
 | 
				
			||||||
 | 
					        self.cascade_type = cascade_type
 | 
				
			||||||
 | 
					        self.angles = angles
 | 
				
			||||||
 | 
					        self.thr = thr
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return ''.join([str(x) for x in ['cascade_file:',self.cascade_file,
 | 
				
			||||||
 | 
					                          ',scale_factor:',self.scale_factor,
 | 
				
			||||||
 | 
					                          ',min_neighbors:',self.min_neighbors,
 | 
				
			||||||
 | 
					                          ',min_neighbors:',self.min_neighbors,
 | 
				
			||||||
 | 
					                          ',cascade_type:',self.cascade_type
 | 
				
			||||||
 | 
					                          ]])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def save_configuration(self, target_file):
 | 
				
			||||||
 | 
					        file_path = target_file.rsplit('/',1)[0]
 | 
				
			||||||
 | 
					        make_path(file_path)
 | 
				
			||||||
 | 
					        config = {'min_size':self.min_size, 'min_neighbours':self.min_neighbors, 'scale_factor':self.scale_factor, 'cascade_file':self.cascade_file}
 | 
				
			||||||
 | 
					        pickle.dump(obj=config, file = open(target_file,'w'), protocol = 2)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def load_configuration(target_file):
 | 
				
			||||||
 | 
					        return pickle.load(open(target_file,'r'))
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def detectMultiScaleWithScores(self, img, scaleFactor = None, minNeighbors = None, minSize = None, flags = 4):
 | 
				
			||||||
 | 
					        scaleFactor = self.scale_factor if not scaleFactor else scaleFactor
 | 
				
			||||||
 | 
					        minNeighbors = self.min_neighbors if not minNeighbors else minNeighbors
 | 
				
			||||||
 | 
					        minSize = self.min_size if not minSize else minSize
 | 
				
			||||||
 | 
					        return self._cascade_classifier.detectMultiScale(img, 
 | 
				
			||||||
 | 
					                                                         scaleFactor = scaleFactor, 
 | 
				
			||||||
 | 
					                                                         minNeighbors = minNeighbors, 
 | 
				
			||||||
 | 
					                                                         minSize = minSize, 
 | 
				
			||||||
 | 
					                                                         flags = flags)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def detectWithAngles(self, img, angels = None, resolve = True, thr = None ):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        angles - a list of angles to test. If None, default to the value created at the constructor (which defaults to [0])
 | 
				
			||||||
 | 
					        resolve - a boolean flag, whether or not to cluster the boxes, and resolve cluster by highest score.
 | 
				
			||||||
 | 
					        thr - the maximum area covered with objects, before we break from the angles loop
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        returns - a list of CascadeResult() objects
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if thr == None:
 | 
				
			||||||
 | 
					            thr = self.thr
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        original_size = img.shape[0] * img.shape[0]
 | 
				
			||||||
 | 
					        if angels == None:
 | 
				
			||||||
 | 
					            angels = self.angles
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        results = []
 | 
				
			||||||
 | 
					        total_area = 0
 | 
				
			||||||
 | 
					        for angle in angels:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # the diagonal of the image is the diameter of the rotated image, so the big_image needs to bound this circle
 | 
				
			||||||
 | 
					            # by being that big
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            big_image, x_shift, y_shift, diag, rot_center = pad_image_for_rotation(img)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            # find the rotation and the inverse rotation matrix, to allow translations between old and new coordinates and vice versa
 | 
				
			||||||
 | 
					            rot_mat = cv2.getRotationMatrix2D(rot_center, angle, scale = 1.0)
 | 
				
			||||||
 | 
					            inv_rot_mat = cv2.invertAffineTransform(rot_mat)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            # rotate the image by the desired angle
 | 
				
			||||||
 | 
					            rot_image = cv2.warpAffine(big_image, rot_mat, (big_image.shape[1],big_image.shape[0]), flags=cv2.INTER_CUBIC)
 | 
				
			||||||
 | 
					            faces = self.detectMultiScaleWithScores(rot_image, scaleFactor = 1.03, minNeighbors = 20, minSize = (15,15), flags = 4)
 | 
				
			||||||
 | 
					            for face in faces:
 | 
				
			||||||
 | 
					                xp = face[0]
 | 
				
			||||||
 | 
					                dx = face[2]
 | 
				
			||||||
 | 
					                yp = face[1]
 | 
				
			||||||
 | 
					                dy = face[3]
 | 
				
			||||||
 | 
					                score = 1
 | 
				
			||||||
 | 
					                dots = np.matrix([[xp,xp+dx,xp+dx,xp], [yp,yp,yp+dy,yp+dy], [1, 1, 1, 1]])
 | 
				
			||||||
 | 
					                # these are the original coordinates in the "big_image"
 | 
				
			||||||
 | 
					#                print dots
 | 
				
			||||||
 | 
					                originals_in_big = inv_rot_mat * dots
 | 
				
			||||||
 | 
					#                print originals_in_big
 | 
				
			||||||
 | 
					                shifter = np.matrix([[x_shift]*4, [y_shift]*4])
 | 
				
			||||||
 | 
					#                print shifter
 | 
				
			||||||
 | 
					                # these are the original coordinate in the original image
 | 
				
			||||||
 | 
					                originals = originals_in_big - shifter
 | 
				
			||||||
 | 
					#                print originals
 | 
				
			||||||
 | 
					                points = np.array(originals.transpose())
 | 
				
			||||||
 | 
					                x = points[0,0]
 | 
				
			||||||
 | 
					                y = points[0,1]
 | 
				
			||||||
 | 
					                box_with_score = ([x,y,dx,dy], score)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                cascade_result = CascadeResult.from_polygon_points(points, score, self.cascade_type)
 | 
				
			||||||
 | 
					#                print cascade_result
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                results.append(cascade_result)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            #################
 | 
				
			||||||
 | 
					            # test and see, if we found enough objects, break out and don't waste our time
 | 
				
			||||||
 | 
					                total_area += cascade_result.area     
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        if resolve:
 | 
				
			||||||
 | 
					            return resolve_angles(results, width = img.shape[1], height = img.shape[0])
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return results
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BoxInImage(object):
 | 
				
			||||||
 | 
					    def __init__(self, originals, dx, dy, score = None, angle = 0):
 | 
				
			||||||
 | 
					        self.originals = originals
 | 
				
			||||||
 | 
					        self.dx = dx
 | 
				
			||||||
 | 
					        self.dy = dy
 | 
				
			||||||
 | 
					        self.score = score
 | 
				
			||||||
 | 
					        self.angle = angle
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return ",".join([str(x) for x in [self.originals, self.dx, self.dy, self.score, self.angle]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def resolve_angles(list_of_results, width, height, thr = 0.3):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    we want to cluster the boxes into clusters, and then choose the best box in each cluster by score
 | 
				
			||||||
 | 
					        * thr - decides what the maximum distance is for a box to join a cluster, in the sense of how much of it's area is covered by the best box in the cluster
 | 
				
			||||||
 | 
					              note, that two squares, centered, with 45 degrees rotation, will overlap on 77% of their area (thr == 0.22)    
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    clusters = []
 | 
				
			||||||
 | 
					    for box in list_of_results:
 | 
				
			||||||
 | 
					#        total_polygon = Polygon([(0,0), (width,0), (width,height), (0,height)])
 | 
				
			||||||
 | 
					#        if box.polygon.intersection(total_polygon).area < box.area:
 | 
				
			||||||
 | 
					#            # this means the box is outside the image somehow
 | 
				
			||||||
 | 
					#            continue
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        area = box.area
 | 
				
			||||||
 | 
					        closest_cluster = None
 | 
				
			||||||
 | 
					        dist_to_closest_cluster = 1.0
 | 
				
			||||||
 | 
					        for n,cluster in enumerate(clusters):
 | 
				
			||||||
 | 
					            dist = 1.0
 | 
				
			||||||
 | 
					            for cluster_box in cluster:
 | 
				
			||||||
 | 
					                local_dist = 1.0 - box.overlap(cluster_box)/area
 | 
				
			||||||
 | 
					                dist = min(dist, local_dist)
 | 
				
			||||||
 | 
					            if dist < dist_to_closest_cluster:
 | 
				
			||||||
 | 
					                dist_to_closest_cluster = dist
 | 
				
			||||||
 | 
					                closest_cluster = n
 | 
				
			||||||
 | 
					        if closest_cluster == None or dist_to_closest_cluster > thr:
 | 
				
			||||||
 | 
					            # no good cluster was found, open a new cluster
 | 
				
			||||||
 | 
					            clusters.append([box])
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            clusters[n].append(box)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    centroids = []
 | 
				
			||||||
 | 
					    for cluster in clusters:
 | 
				
			||||||
 | 
					        centroids.append(sorted(cluster,key=lambda x: x.score)[-1])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return centroids
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					def resolve_boxes(dict_of_list_of_cascade_results, min_overlap = 0.7):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    Say you tried two different cascades to detect faces.
 | 
				
			||||||
 | 
					    enter a dictionary (the key is a string describing a cascade type) of detected objects
 | 
				
			||||||
 | 
					    This function returns a unified results list, where it resolves overlapping boxes, and chooses one of them.
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    The bigger boxes are selected instead of smaller ones, whether they contain them, or enough of them, determined by min_overlap
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    final_faces = []
 | 
				
			||||||
 | 
					    for cascade_str, faces in dict_of_list_of_cascade_results.items():
 | 
				
			||||||
 | 
					        # go through each cascade type
 | 
				
			||||||
 | 
					        for face in faces:
 | 
				
			||||||
 | 
					            if type(face) == CascadeResult:
 | 
				
			||||||
 | 
					                new_res = face
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                new_res = CascadeResult(face,cascade_type = cascade_str)
 | 
				
			||||||
 | 
					            to_add = True
 | 
				
			||||||
 | 
					            for old_index,old_res in enumerate(final_faces):
 | 
				
			||||||
 | 
					                ratio = new_res.area / old_res.area
 | 
				
			||||||
 | 
					                if ratio >1.0:
 | 
				
			||||||
 | 
					                    # new_box is bigger
 | 
				
			||||||
 | 
					                    if new_res.overlap(old_res)/old_res.area > min_overlap:
 | 
				
			||||||
 | 
					                        # the new box contains the old one, we want to replace it:
 | 
				
			||||||
 | 
					                        final_faces[old_index] = new_res
 | 
				
			||||||
 | 
					                        to_add = False
 | 
				
			||||||
 | 
					                        break
 | 
				
			||||||
 | 
					                if ratio <=1.0:
 | 
				
			||||||
 | 
					                    # the new_box is smaller
 | 
				
			||||||
 | 
					                    if new_res.overlap(old_res)/new_res.area > min_overlap:
 | 
				
			||||||
 | 
					                        # the old box contains the new one, we therefore dont need to add the new box:
 | 
				
			||||||
 | 
					                        to_add = False
 | 
				
			||||||
 | 
					                        break
 | 
				
			||||||
 | 
					            if to_add:
 | 
				
			||||||
 | 
					                # if there was no hit, this is a new face, we can add it
 | 
				
			||||||
 | 
					                final_faces.append(new_res)
 | 
				
			||||||
 | 
					    return final_faces
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def most_centered_box( cascade_results, xxx_todo_changeme ):
 | 
				
			||||||
 | 
					    ( rows, cols ) = xxx_todo_changeme
 | 
				
			||||||
 | 
					    best_err = 1e10
 | 
				
			||||||
 | 
					    for i, cascade in enumerate( cascade_results ):
 | 
				
			||||||
 | 
					        err = ( cascade.x + cascade.dx / 2 - cols / 2 ) ** 2 + ( cascade.y + cascade.dy / 2 - rows / 2 ) ** 2
 | 
				
			||||||
 | 
					        if err < best_err:
 | 
				
			||||||
 | 
					            index = i
 | 
				
			||||||
 | 
					    return cascade_results[ index ]
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					class CascadeResult(object):
 | 
				
			||||||
 | 
					    def __init__(self, box_with_score, cascade_type = None, angle = 0):
 | 
				
			||||||
 | 
					        self.x = box_with_score[0][0]
 | 
				
			||||||
 | 
					        self.y = box_with_score[0][1]
 | 
				
			||||||
 | 
					        self.dx = box_with_score[0][2]
 | 
				
			||||||
 | 
					        self.dy = box_with_score[0][3]
 | 
				
			||||||
 | 
					        self.score = box_with_score[1]
 | 
				
			||||||
 | 
					        self.cascade_type = cascade_type
 | 
				
			||||||
 | 
					        self.angle = angle
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def from_polygon_points(points, score, cascade_type = None):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        an alternative generator, allows giving the polygon points instead of [x,y,dx,dy]
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        x = points[0,0]
 | 
				
			||||||
 | 
					        y = points[0,1]
 | 
				
			||||||
 | 
					        top = points[1,] - points[0,]
 | 
				
			||||||
 | 
					        left = points[3,] - points[0,]
 | 
				
			||||||
 | 
					        dx = math.sqrt(sum([i*i for i in top]))
 | 
				
			||||||
 | 
					        dy = math.sqrt(sum([i*i for i in left]))
 | 
				
			||||||
 | 
					        angle = math.atan(float(top[1])/top[0]) * 180 / math.pi if top[0] != 0 else (970 if top[1] >0 else -90)
 | 
				
			||||||
 | 
					        return CascadeResult(([x,y,dx,dy],score), cascade_type, angle)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return ''.join([str(x) for x in ['center:',self.center,
 | 
				
			||||||
 | 
					                                         ',\nx:',self.x,
 | 
				
			||||||
 | 
					                                         ',\ny:',self.y,
 | 
				
			||||||
 | 
					                                         ',\ndx:',self.dx,
 | 
				
			||||||
 | 
					                                         ',\ndy:',self.dy,
 | 
				
			||||||
 | 
					                                         ',\nscore:',self.score,
 | 
				
			||||||
 | 
					                                         ',\nangle:',self.angle,
 | 
				
			||||||
 | 
					                                         ',\ncascade_type:',self.cascade_type,
 | 
				
			||||||
 | 
					                                         ',\npoints_int:\n',self.points_int
 | 
				
			||||||
 | 
					                                         ]])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def points(self):
 | 
				
			||||||
 | 
					        x = self.x
 | 
				
			||||||
 | 
					        y = self.y
 | 
				
			||||||
 | 
					        dx = self.dx
 | 
				
			||||||
 | 
					        dy = self.dy
 | 
				
			||||||
 | 
					        a = self.angle/180.0*math.pi
 | 
				
			||||||
 | 
					        dots = np.matrix([[x,y,1],[x+dx,y,1],[x+dx,y+dy,1],[x,y+dy,1]])
 | 
				
			||||||
 | 
					        dots = dots.transpose()
 | 
				
			||||||
 | 
					        rot_mat = cv2.getRotationMatrix2D((dots[0,0],dots[1,0]), -self.angle, scale = 1.0)
 | 
				
			||||||
 | 
					        points = rot_mat * dots
 | 
				
			||||||
 | 
					        points = points.transpose() 
 | 
				
			||||||
 | 
					        return points
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def center(self):
 | 
				
			||||||
 | 
					        return tuple(int(x) for x in (self.points.sum(0)/4.0).tolist()[0])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def points_int(self):
 | 
				
			||||||
 | 
					        return self.points.astype(int)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def score_with_type(self):
 | 
				
			||||||
 | 
					        if self.cascade_type:
 | 
				
			||||||
 | 
					            return self.cascade_type + ' ' + str(self.score)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return str(self.score)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def filename_encode(self):
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return '_'.join([str(x) for x in ['loct'] + self.cvformat_result[0] + ['ang', int(self.angle),self.cascade_type, self.score]]) 
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def cvformat_coords(self):
 | 
				
			||||||
 | 
					        if self.angle == 0:
 | 
				
			||||||
 | 
					            return [int(x) for x in [self.x, self.y, self.dx, self.dy]]
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            raise Exception('cannot return [x,y,dx,dy] for a box with angle, use cvformat_result() instead')
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def cvformat_result(self):
 | 
				
			||||||
 | 
					        return ([int(x) for x in [self.x, self.y, self.dx, self.dy]], self.score, self.angle)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					#    @property
 | 
				
			||||||
 | 
					#    def rot_matrix(self):
 | 
				
			||||||
 | 
					#        return array([[cos(math.radians(self.angle)), -sin(math.radians(self.angle))], 
 | 
				
			||||||
 | 
					#                   [sin(math.radians(self.angle)),  cos(math.radians(self.angle))]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def top_left(self):
 | 
				
			||||||
 | 
					        return tuple(self.points[0,].tolist()[0])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def top_right(self):
 | 
				
			||||||
 | 
					        return tuple(self.points[1,].tolist()[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def bottom_right(self):
 | 
				
			||||||
 | 
					        return tuple(self.points[2,].tolist()[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def bottom_left(self):
 | 
				
			||||||
 | 
					        return tuple(self.points[3,].tolist()[0])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def polygon(self):
 | 
				
			||||||
 | 
					        return Polygon([self.top_left, self.top_right, self.bottom_right, self.bottom_left])
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def overlap(self, otherRect):      
 | 
				
			||||||
 | 
					        return float(self.polygon.intersection(otherRect.polygon).area)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def area(self):
 | 
				
			||||||
 | 
					        return float(self.polygon.area)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def __gt__(self,b):
 | 
				
			||||||
 | 
					        return self.area>b.area
 | 
				
			||||||
 | 
					    def __ge__(self,b):
 | 
				
			||||||
 | 
					        return self.area>=b.area
 | 
				
			||||||
 | 
					    def __lt__(self,b):
 | 
				
			||||||
 | 
					        return self.area<b.area    
 | 
				
			||||||
 | 
					    def __le__(self,b):
 | 
				
			||||||
 | 
					        return self.area<=b.area
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,353 @@
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					Created on May 7, 2014
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@author: eran
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					import cv2
 | 
				
			||||||
 | 
					import pickle
 | 
				
			||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					from shapely.geometry.polygon import Polygon
 | 
				
			||||||
 | 
					import math
 | 
				
			||||||
 | 
					from adiencealign.common.files import make_path, expand_path
 | 
				
			||||||
 | 
					from adiencealign.common.images import pad_image_for_rotation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CascadeDetector(object):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    This is a haar cascade classifier capable of detecting in multiple angles
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    def __init__(self, cascade_file = './resources/haarcascade_frontalface_default.xml', 
 | 
				
			||||||
 | 
					                 min_size = (10, 10),
 | 
				
			||||||
 | 
					                 min_neighbors = 20,
 | 
				
			||||||
 | 
					                 scale_factor = 1.04,
 | 
				
			||||||
 | 
					                 angles = [0],
 | 
				
			||||||
 | 
					                 thr = 0.4, 
 | 
				
			||||||
 | 
					                 cascade_type = 'haar'):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        cascade_type - is a string defining the type of cascade
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        print expand_path('.')
 | 
				
			||||||
 | 
					        self.cascade_file = cascade_file.rsplit('/',1)[1]
 | 
				
			||||||
 | 
					        self._cascade_classifier = cv2.CascadeClassifier(cascade_file)
 | 
				
			||||||
 | 
					        self.scale_factor = scale_factor
 | 
				
			||||||
 | 
					        self.min_neighbors = min_neighbors
 | 
				
			||||||
 | 
					        self.min_size = min_size
 | 
				
			||||||
 | 
					        self.cascade_type = cascade_type
 | 
				
			||||||
 | 
					        self.angles = angles
 | 
				
			||||||
 | 
					        self.thr = thr
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return ''.join([str(x) for x in ['cascade_file:',self.cascade_file,
 | 
				
			||||||
 | 
					                          ',scale_factor:',self.scale_factor,
 | 
				
			||||||
 | 
					                          ',min_neighbors:',self.min_neighbors,
 | 
				
			||||||
 | 
					                          ',min_neighbors:',self.min_neighbors,
 | 
				
			||||||
 | 
					                          ',cascade_type:',self.cascade_type
 | 
				
			||||||
 | 
					                          ]])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def save_configuration(self, target_file):
 | 
				
			||||||
 | 
					        file_path = target_file.rsplit('/',1)[0]
 | 
				
			||||||
 | 
					        make_path(file_path)
 | 
				
			||||||
 | 
					        config = {'min_size':self.min_size, 'min_neighbours':self.min_neighbors, 'scale_factor':self.scale_factor, 'cascade_file':self.cascade_file}
 | 
				
			||||||
 | 
					        pickle.dump(obj=config, file = open(target_file,'w'), protocol = 2)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def load_configuration(target_file):
 | 
				
			||||||
 | 
					        return pickle.load(open(target_file,'r'))
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def detectMultiScaleWithScores(self, img, scaleFactor = None, minNeighbors = None, minSize = None, flags = 4):
 | 
				
			||||||
 | 
					        scaleFactor = self.scale_factor if not scaleFactor else scaleFactor
 | 
				
			||||||
 | 
					        minNeighbors = self.min_neighbors if not minNeighbors else minNeighbors
 | 
				
			||||||
 | 
					        minSize = self.min_size if not minSize else minSize
 | 
				
			||||||
 | 
					        return self._cascade_classifier.detectMultiScale(img, 
 | 
				
			||||||
 | 
					                                                         scaleFactor = scaleFactor, 
 | 
				
			||||||
 | 
					                                                         minNeighbors = minNeighbors, 
 | 
				
			||||||
 | 
					                                                         minSize = minSize, 
 | 
				
			||||||
 | 
					                                                         flags = flags)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def detectWithAngles(self, img, angels = None, resolve = True, thr = None ):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        angles - a list of angles to test. If None, default to the value created at the constructor (which defaults to [0])
 | 
				
			||||||
 | 
					        resolve - a boolean flag, whether or not to cluster the boxes, and resolve cluster by highest score.
 | 
				
			||||||
 | 
					        thr - the maximum area covered with objects, before we break from the angles loop
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        returns - a list of CascadeResult() objects
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if thr == None:
 | 
				
			||||||
 | 
					            thr = self.thr
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        original_size = img.shape[0] * img.shape[0]
 | 
				
			||||||
 | 
					        if angels == None:
 | 
				
			||||||
 | 
					            angels = self.angles
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        results = []
 | 
				
			||||||
 | 
					        total_area = 0
 | 
				
			||||||
 | 
					        for angle in angels:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # the diagonal of the image is the diameter of the rotated image, so the big_image needs to bound this circle
 | 
				
			||||||
 | 
					            # by being that big
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            big_image, x_shift, y_shift, diag, rot_center = pad_image_for_rotation(img)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            # find the rotation and the inverse rotation matrix, to allow translations between old and new coordinates and vice versa
 | 
				
			||||||
 | 
					            rot_mat = cv2.getRotationMatrix2D(rot_center, angle, scale = 1.0)
 | 
				
			||||||
 | 
					            inv_rot_mat = cv2.invertAffineTransform(rot_mat)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            # rotate the image by the desired angle
 | 
				
			||||||
 | 
					            rot_image = cv2.warpAffine(big_image, rot_mat, (big_image.shape[1],big_image.shape[0]), flags=cv2.INTER_CUBIC)
 | 
				
			||||||
 | 
					            faces = self.detectMultiScaleWithScores(rot_image, scaleFactor = 1.03, minNeighbors = 20, minSize = (15,15), flags = 4)
 | 
				
			||||||
 | 
					            for face in faces:
 | 
				
			||||||
 | 
					                xp = face[0]
 | 
				
			||||||
 | 
					                dx = face[2]
 | 
				
			||||||
 | 
					                yp = face[1]
 | 
				
			||||||
 | 
					                dy = face[3]
 | 
				
			||||||
 | 
					                score = 1
 | 
				
			||||||
 | 
					                dots = np.matrix([[xp,xp+dx,xp+dx,xp], [yp,yp,yp+dy,yp+dy], [1, 1, 1, 1]])
 | 
				
			||||||
 | 
					                # these are the original coordinates in the "big_image"
 | 
				
			||||||
 | 
					#                print dots
 | 
				
			||||||
 | 
					                originals_in_big = inv_rot_mat * dots
 | 
				
			||||||
 | 
					#                print originals_in_big
 | 
				
			||||||
 | 
					                shifter = np.matrix([[x_shift]*4, [y_shift]*4])
 | 
				
			||||||
 | 
					#                print shifter
 | 
				
			||||||
 | 
					                # these are the original coordinate in the original image
 | 
				
			||||||
 | 
					                originals = originals_in_big - shifter
 | 
				
			||||||
 | 
					#                print originals
 | 
				
			||||||
 | 
					                points = np.array(originals.transpose())
 | 
				
			||||||
 | 
					                x = points[0,0]
 | 
				
			||||||
 | 
					                y = points[0,1]
 | 
				
			||||||
 | 
					                box_with_score = ([x,y,dx,dy], score)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                cascade_result = CascadeResult.from_polygon_points(points, score, self.cascade_type)
 | 
				
			||||||
 | 
					#                print cascade_result
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                results.append(cascade_result)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            #################
 | 
				
			||||||
 | 
					            # test and see, if we found enough objects, break out and don't waste our time
 | 
				
			||||||
 | 
					                total_area += cascade_result.area     
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        if resolve:
 | 
				
			||||||
 | 
					            return resolve_angles(results, width = img.shape[1], height = img.shape[0])
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return results
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BoxInImage(object):
 | 
				
			||||||
 | 
					    def __init__(self, originals, dx, dy, score = None, angle = 0):
 | 
				
			||||||
 | 
					        self.originals = originals
 | 
				
			||||||
 | 
					        self.dx = dx
 | 
				
			||||||
 | 
					        self.dy = dy
 | 
				
			||||||
 | 
					        self.score = score
 | 
				
			||||||
 | 
					        self.angle = angle
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return ",".join([str(x) for x in [self.originals, self.dx, self.dy, self.score, self.angle]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def resolve_angles(list_of_results, width, height, thr = 0.3):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    we want to cluster the boxes into clusters, and then choose the best box in each cluster by score
 | 
				
			||||||
 | 
					        * thr - decides what the maximum distance is for a box to join a cluster, in the sense of how much of it's area is covered by the best box in the cluster
 | 
				
			||||||
 | 
					              note, that two squares, centered, with 45 degrees rotation, will overlap on 77% of their area (thr == 0.22)    
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    clusters = []
 | 
				
			||||||
 | 
					    for box in list_of_results:
 | 
				
			||||||
 | 
					#        total_polygon = Polygon([(0,0), (width,0), (width,height), (0,height)])
 | 
				
			||||||
 | 
					#        if box.polygon.intersection(total_polygon).area < box.area:
 | 
				
			||||||
 | 
					#            # this means the box is outside the image somehow
 | 
				
			||||||
 | 
					#            continue
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        area = box.area
 | 
				
			||||||
 | 
					        closest_cluster = None
 | 
				
			||||||
 | 
					        dist_to_closest_cluster = 1.0
 | 
				
			||||||
 | 
					        for n,cluster in enumerate(clusters):
 | 
				
			||||||
 | 
					            dist = 1.0
 | 
				
			||||||
 | 
					            for cluster_box in cluster:
 | 
				
			||||||
 | 
					                local_dist = 1.0 - box.overlap(cluster_box)/area
 | 
				
			||||||
 | 
					                dist = min(dist, local_dist)
 | 
				
			||||||
 | 
					            if dist < dist_to_closest_cluster:
 | 
				
			||||||
 | 
					                dist_to_closest_cluster = dist
 | 
				
			||||||
 | 
					                closest_cluster = n
 | 
				
			||||||
 | 
					        if closest_cluster == None or dist_to_closest_cluster > thr:
 | 
				
			||||||
 | 
					            # no good cluster was found, open a new cluster
 | 
				
			||||||
 | 
					            clusters.append([box])
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            clusters[n].append(box)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    centroids = []
 | 
				
			||||||
 | 
					    for cluster in clusters:
 | 
				
			||||||
 | 
					        centroids.append(sorted(cluster,key=lambda x: x.score)[-1])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return centroids
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					def resolve_boxes(dict_of_list_of_cascade_results, min_overlap = 0.7):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    Say you tried two different cascades to detect faces.
 | 
				
			||||||
 | 
					    enter a dictionary (the key is a string describing a cascade type) of detected objects
 | 
				
			||||||
 | 
					    This function returns a unified results list, where it resolves overlapping boxes, and chooses one of them.
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    The bigger boxes are selected instead of smaller ones, whether they contain them, or enough of them, determined by min_overlap
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    final_faces = []
 | 
				
			||||||
 | 
					    for cascade_str, faces in dict_of_list_of_cascade_results.iteritems():
 | 
				
			||||||
 | 
					        # go through each cascade type
 | 
				
			||||||
 | 
					        for face in faces:
 | 
				
			||||||
 | 
					            if type(face) == CascadeResult:
 | 
				
			||||||
 | 
					                new_res = face
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                new_res = CascadeResult(face,cascade_type = cascade_str)
 | 
				
			||||||
 | 
					            to_add = True
 | 
				
			||||||
 | 
					            for old_index,old_res in enumerate(final_faces):
 | 
				
			||||||
 | 
					                ratio = new_res.area / old_res.area
 | 
				
			||||||
 | 
					                if ratio >1.0:
 | 
				
			||||||
 | 
					                    # new_box is bigger
 | 
				
			||||||
 | 
					                    if new_res.overlap(old_res)/old_res.area > min_overlap:
 | 
				
			||||||
 | 
					                        # the new box contains the old one, we want to replace it:
 | 
				
			||||||
 | 
					                        final_faces[old_index] = new_res
 | 
				
			||||||
 | 
					                        to_add = False
 | 
				
			||||||
 | 
					                        break
 | 
				
			||||||
 | 
					                if ratio <=1.0:
 | 
				
			||||||
 | 
					                    # the new_box is smaller
 | 
				
			||||||
 | 
					                    if new_res.overlap(old_res)/new_res.area > min_overlap:
 | 
				
			||||||
 | 
					                        # the old box contains the new one, we therefore dont need to add the new box:
 | 
				
			||||||
 | 
					                        to_add = False
 | 
				
			||||||
 | 
					                        break
 | 
				
			||||||
 | 
					            if to_add:
 | 
				
			||||||
 | 
					                # if there was no hit, this is a new face, we can add it
 | 
				
			||||||
 | 
					                final_faces.append(new_res)
 | 
				
			||||||
 | 
					    return final_faces
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def most_centered_box( cascade_results, ( rows, cols ) ):
 | 
				
			||||||
 | 
					    best_err = 1e10
 | 
				
			||||||
 | 
					    for i, cascade in enumerate( cascade_results ):
 | 
				
			||||||
 | 
					        err = ( cascade.x + cascade.dx / 2 - cols / 2 ) ** 2 + ( cascade.y + cascade.dy / 2 - rows / 2 ) ** 2
 | 
				
			||||||
 | 
					        if err < best_err:
 | 
				
			||||||
 | 
					            index = i
 | 
				
			||||||
 | 
					    return cascade_results[ index ]
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					class CascadeResult(object):
 | 
				
			||||||
 | 
					    def __init__(self, box_with_score, cascade_type = None, angle = 0):
 | 
				
			||||||
 | 
					        self.x = box_with_score[0][0]
 | 
				
			||||||
 | 
					        self.y = box_with_score[0][1]
 | 
				
			||||||
 | 
					        self.dx = box_with_score[0][2]
 | 
				
			||||||
 | 
					        self.dy = box_with_score[0][3]
 | 
				
			||||||
 | 
					        self.score = box_with_score[1]
 | 
				
			||||||
 | 
					        self.cascade_type = cascade_type
 | 
				
			||||||
 | 
					        self.angle = angle
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def from_polygon_points(points, score, cascade_type = None):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        an alternative generator, allows giving the polygon points instead of [x,y,dx,dy]
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        x = points[0,0]
 | 
				
			||||||
 | 
					        y = points[0,1]
 | 
				
			||||||
 | 
					        top = points[1,] - points[0,]
 | 
				
			||||||
 | 
					        left = points[3,] - points[0,]
 | 
				
			||||||
 | 
					        dx = math.sqrt(sum([i*i for i in top]))
 | 
				
			||||||
 | 
					        dy = math.sqrt(sum([i*i for i in left]))
 | 
				
			||||||
 | 
					        angle = math.atan(float(top[1])/top[0]) * 180 / math.pi if top[0] != 0 else (970 if top[1] >0 else -90)
 | 
				
			||||||
 | 
					        return CascadeResult(([x,y,dx,dy],score), cascade_type, angle)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return ''.join([str(x) for x in ['center:',self.center,
 | 
				
			||||||
 | 
					                                         ',\nx:',self.x,
 | 
				
			||||||
 | 
					                                         ',\ny:',self.y,
 | 
				
			||||||
 | 
					                                         ',\ndx:',self.dx,
 | 
				
			||||||
 | 
					                                         ',\ndy:',self.dy,
 | 
				
			||||||
 | 
					                                         ',\nscore:',self.score,
 | 
				
			||||||
 | 
					                                         ',\nangle:',self.angle,
 | 
				
			||||||
 | 
					                                         ',\ncascade_type:',self.cascade_type,
 | 
				
			||||||
 | 
					                                         ',\npoints_int:\n',self.points_int
 | 
				
			||||||
 | 
					                                         ]])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def points(self):
 | 
				
			||||||
 | 
					        x = self.x
 | 
				
			||||||
 | 
					        y = self.y
 | 
				
			||||||
 | 
					        dx = self.dx
 | 
				
			||||||
 | 
					        dy = self.dy
 | 
				
			||||||
 | 
					        a = self.angle/180.0*math.pi
 | 
				
			||||||
 | 
					        dots = np.matrix([[x,y,1],[x+dx,y,1],[x+dx,y+dy,1],[x,y+dy,1]])
 | 
				
			||||||
 | 
					        dots = dots.transpose()
 | 
				
			||||||
 | 
					        rot_mat = cv2.getRotationMatrix2D((dots[0,0],dots[1,0]), -self.angle, scale = 1.0)
 | 
				
			||||||
 | 
					        points = rot_mat * dots
 | 
				
			||||||
 | 
					        points = points.transpose() 
 | 
				
			||||||
 | 
					        return points
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def center(self):
 | 
				
			||||||
 | 
					        return tuple(int(x) for x in (self.points.sum(0)/4.0).tolist()[0])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def points_int(self):
 | 
				
			||||||
 | 
					        return self.points.astype(int)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def score_with_type(self):
 | 
				
			||||||
 | 
					        if self.cascade_type:
 | 
				
			||||||
 | 
					            return self.cascade_type + ' ' + str(self.score)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return str(self.score)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def filename_encode(self):
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return '_'.join([str(x) for x in ['loct'] + self.cvformat_result[0] + ['ang', int(self.angle),self.cascade_type, self.score]]) 
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def cvformat_coords(self):
 | 
				
			||||||
 | 
					        if self.angle == 0:
 | 
				
			||||||
 | 
					            return [int(x) for x in [self.x, self.y, self.dx, self.dy]]
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            raise Exception('cannot return [x,y,dx,dy] for a box with angle, use cvformat_result() instead')
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def cvformat_result(self):
 | 
				
			||||||
 | 
					        return ([int(x) for x in [self.x, self.y, self.dx, self.dy]], self.score, self.angle)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					#    @property
 | 
				
			||||||
 | 
					#    def rot_matrix(self):
 | 
				
			||||||
 | 
					#        return array([[cos(math.radians(self.angle)), -sin(math.radians(self.angle))], 
 | 
				
			||||||
 | 
					#                   [sin(math.radians(self.angle)),  cos(math.radians(self.angle))]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def top_left(self):
 | 
				
			||||||
 | 
					        return tuple(self.points[0,].tolist()[0])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def top_right(self):
 | 
				
			||||||
 | 
					        return tuple(self.points[1,].tolist()[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def bottom_right(self):
 | 
				
			||||||
 | 
					        return tuple(self.points[2,].tolist()[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def bottom_left(self):
 | 
				
			||||||
 | 
					        return tuple(self.points[3,].tolist()[0])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def polygon(self):
 | 
				
			||||||
 | 
					        return Polygon([self.top_left, self.top_right, self.bottom_right, self.bottom_left])
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def overlap(self, otherRect):      
 | 
				
			||||||
 | 
					        return float(self.polygon.intersection(otherRect.polygon).area)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def area(self):
 | 
				
			||||||
 | 
					        return float(self.polygon.area)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def __gt__(self,b):
 | 
				
			||||||
 | 
					        return self.area>b.area
 | 
				
			||||||
 | 
					    def __ge__(self,b):
 | 
				
			||||||
 | 
					        return self.area>=b.area
 | 
				
			||||||
 | 
					    def __lt__(self,b):
 | 
				
			||||||
 | 
					        return self.area<b.area    
 | 
				
			||||||
 | 
					    def __le__(self,b):
 | 
				
			||||||
 | 
					        return self.area<=b.area
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,153 @@
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					Created on May 7, 2014
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@author: eran
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					from adiencealign.common.images import extract_box
 | 
				
			||||||
 | 
					import glob
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
 | 
					from adiencealign.cascade_detection.cascade_detector import CascadeDetector,\
 | 
				
			||||||
 | 
					    resolve_boxes, CascadeResult
 | 
				
			||||||
 | 
					import cv2
 | 
				
			||||||
 | 
					import csv
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					Created on Dec 18, 2013
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@author: eran
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					Created on Nov 26, 2013
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@author: eran
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CascadeFaceFinder(object):
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def __init__(self, 
 | 
				
			||||||
 | 
					                 min_size = 32, 
 | 
				
			||||||
 | 
					                 drawn_target_res = 360*360, 
 | 
				
			||||||
 | 
					                 hangles = [0, -22, 22], 
 | 
				
			||||||
 | 
					                 langles = [0,-45,-22,22,45], 
 | 
				
			||||||
 | 
					                 haar_file = 'haarcascade_frontalface_default.xml',
 | 
				
			||||||
 | 
					                 lbp_file = 'lbpcascade_frontalface.xml'):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        finder = CascadeFaceFinder(min_size = 32, drawn_target_res = 360*360, hangles = [0], langles = [0,-45,-22,22,45], parts_threshold = 0)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        finder.get_faces_in_folder(input_folder, output_dir, drawn_folder, is_small_drawn)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        or
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        finder.get_faces_in_photo(full_file, output_dir, drawn_folder, is_small_drawn)
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        self.min_size = (min_size,min_size)
 | 
				
			||||||
 | 
					        self.drawn_target_res = drawn_target_res
 | 
				
			||||||
 | 
					        self._hangles = hangles
 | 
				
			||||||
 | 
					        self._langles = langles
 | 
				
			||||||
 | 
					        self.recalc_detectors(haar_file, lbp_file)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					#         self.funnel = FaceFunnel()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def hangles(self):
 | 
				
			||||||
 | 
					        return self._hangles
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @hangles.setter
 | 
				
			||||||
 | 
					    def hangles(self,hangles):
 | 
				
			||||||
 | 
					        self._hangles = hangles
 | 
				
			||||||
 | 
					        self.recalc_detectors()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def langles(self):
 | 
				
			||||||
 | 
					        return self._langles
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @langles.setter
 | 
				
			||||||
 | 
					    def langles(self,langles):
 | 
				
			||||||
 | 
					        self._langles = langles
 | 
				
			||||||
 | 
					        self.recalc_detectors()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def recalc_detectors(self, haar_file, lbp_file):
 | 
				
			||||||
 | 
					        self.haar_dtct = CascadeDetector(cascade_file = haar_file,
 | 
				
			||||||
 | 
					                                      min_size = self.min_size,
 | 
				
			||||||
 | 
					                                      min_neighbors = 20,
 | 
				
			||||||
 | 
					                                      scale_factor = 1.03,
 | 
				
			||||||
 | 
					                                      cascade_type = 'haar',
 | 
				
			||||||
 | 
					                                      thr = 0.4,
 | 
				
			||||||
 | 
					                                      angles = self.hangles)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        self.lbp_dtct = CascadeDetector(cascade_file = lbp_file,
 | 
				
			||||||
 | 
					                                   min_size = self.min_size,
 | 
				
			||||||
 | 
					                                   min_neighbors = 15,
 | 
				
			||||||
 | 
					                                   scale_factor = 1.04,
 | 
				
			||||||
 | 
					                                   cascade_type = 'lbp',
 | 
				
			||||||
 | 
					                                   thr = 0.4,
 | 
				
			||||||
 | 
					                                   angles = self.langles)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def get_faces_list_in_photo(self, img):
 | 
				
			||||||
 | 
					        if self.hangles:
 | 
				
			||||||
 | 
					                haar_faces = self.haar_dtct.detectWithAngles(img, resolve = True)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            haar_faces = []
 | 
				
			||||||
 | 
					        lbp_faces = self.lbp_dtct.detectWithAngles(img, resolve = True)
 | 
				
			||||||
 | 
					        faces = resolve_boxes({'haar':haar_faces, 'lbp':lbp_faces}, min_overlap = 0.6)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return faces
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def create_faces_file(self, fname, is_overwrite = False, target_file = None):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        Runs facial detection on fname (say a.jpg, or a.png), and creates a results file (a.faces.txt)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        target_file - override, and specify a specific target file
 | 
				
			||||||
 | 
					        is_overwrite - allow overwriting an existing results file
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        faces = self.get_faces_list_in_photo(cv2.imread(fname))
 | 
				
			||||||
 | 
					        results_file = fname.rsplit('.',1)[0] + '.faces.txt' if target_file is None else target_file
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if os.path.exists(results_file) and not is_overwrite:
 | 
				
			||||||
 | 
					            print("Warning, faces result file", results_file, "exists")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            with open(results_file,'w') as csvfile:
 | 
				
			||||||
 | 
					                csv_writer = csv.writer(csvfile, delimiter=',')
 | 
				
			||||||
 | 
					                header = ['x', 'y','dx','dy', 'score', 'angle', 'type']
 | 
				
			||||||
 | 
					                csv_writer.writerow(header)
 | 
				
			||||||
 | 
					                for face in faces:
 | 
				
			||||||
 | 
					                    csv_writer.writerow([str(i) for i in [int(face.x), int(face.y), int(face.dx), int(face.dy), face.score, face.angle, face.cascade_type]])
 | 
				
			||||||
 | 
					        return results_file
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def get_sub_images_from_file(self,original_image_file, faces_file):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        extracts all the face sub-images from an image file, based on the results in a faces file
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        returns - the list of face images (numpy arrays)
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        img = cv2.imread(original_image_file)
 | 
				
			||||||
 | 
					        faces_reader = csv.reader(open(faces_file))
 | 
				
			||||||
 | 
					        next(faces_reader)  # discard the headings
 | 
				
			||||||
 | 
					        padded_face_images = []
 | 
				
			||||||
 | 
					        for line in faces_reader:
 | 
				
			||||||
 | 
					            x, y, dx, dy, score, angle, cascade_type = line
 | 
				
			||||||
 | 
					            [x,y,dx,dy,score, angle] = [int(float(i)) for i in [x,y,dx,dy,score, angle]]
 | 
				
			||||||
 | 
					            face = CascadeResult(([x,y,dx,dy], score), cascade_type, angle)
 | 
				
			||||||
 | 
					            padded_face, bounding_box_in_padded_face, _, _ = extract_box(img, face, padding_factor = 0.25)
 | 
				
			||||||
 | 
					            padded_face_images.append(padded_face)
 | 
				
			||||||
 | 
					        return padded_face_images
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def create_sub_images_from_file(self, original_image_file, faces_file, target_folder = None, img_type = 'png'):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        reads a faces file, created by "self.create_faces_file" and extracts padded faces from the original image
 | 
				
			||||||
 | 
					        The faces will be created in the same folder as the faces file, unless specified otherwise by "target_folder"
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        returns - the list of face files (strings)
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        target_folder = os.path.split(faces_file)[0] if target_folder is None else target_folder
 | 
				
			||||||
 | 
					        padded_face_images = self.get_sub_images_from_file(original_image_file, faces_file)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        base_image_name = os.path.split(faces_file)[1].split('.')[0]
 | 
				
			||||||
 | 
					        face_files = []
 | 
				
			||||||
 | 
					        for n_face, face_img in enumerate(padded_face_images):
 | 
				
			||||||
 | 
					            face_file = os.path.join(target_folder, base_image_name + '_face_%d.%s' %(n_face, img_type))
 | 
				
			||||||
 | 
					            cv2.imwrite( face_file , face_img )
 | 
				
			||||||
 | 
					            face_files.append(face_file)
 | 
				
			||||||
 | 
					        return face_files
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,153 @@
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					Created on May 7, 2014
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@author: eran
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					from adiencealign.common.images import extract_box
 | 
				
			||||||
 | 
					import glob
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
 | 
					from adiencealign.cascade_detection.cascade_detector import CascadeDetector,\
 | 
				
			||||||
 | 
					    resolve_boxes, CascadeResult
 | 
				
			||||||
 | 
					import cv2
 | 
				
			||||||
 | 
					import csv
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					Created on Dec 18, 2013
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@author: eran
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					Created on Nov 26, 2013
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@author: eran
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CascadeFaceFinder(object):
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def __init__(self, 
 | 
				
			||||||
 | 
					                 min_size = 32, 
 | 
				
			||||||
 | 
					                 drawn_target_res = 360*360, 
 | 
				
			||||||
 | 
					                 hangles = [0, -22, 22], 
 | 
				
			||||||
 | 
					                 langles = [0,-45,-22,22,45], 
 | 
				
			||||||
 | 
					                 haar_file = 'haarcascade_frontalface_default.xml',
 | 
				
			||||||
 | 
					                 lbp_file = 'lbpcascade_frontalface.xml'):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        finder = CascadeFaceFinder(min_size = 32, drawn_target_res = 360*360, hangles = [0], langles = [0,-45,-22,22,45], parts_threshold = 0)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        finder.get_faces_in_folder(input_folder, output_dir, drawn_folder, is_small_drawn)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        or
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        finder.get_faces_in_photo(full_file, output_dir, drawn_folder, is_small_drawn)
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        self.min_size = (min_size,min_size)
 | 
				
			||||||
 | 
					        self.drawn_target_res = drawn_target_res
 | 
				
			||||||
 | 
					        self._hangles = hangles
 | 
				
			||||||
 | 
					        self._langles = langles
 | 
				
			||||||
 | 
					        self.recalc_detectors(haar_file, lbp_file)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					#         self.funnel = FaceFunnel()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def hangles(self):
 | 
				
			||||||
 | 
					        return self._hangles
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @hangles.setter
 | 
				
			||||||
 | 
					    def hangles(self,hangles):
 | 
				
			||||||
 | 
					        self._hangles = hangles
 | 
				
			||||||
 | 
					        self.recalc_detectors()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def langles(self):
 | 
				
			||||||
 | 
					        return self._langles
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @langles.setter
 | 
				
			||||||
 | 
					    def langles(self,langles):
 | 
				
			||||||
 | 
					        self._langles = langles
 | 
				
			||||||
 | 
					        self.recalc_detectors()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def recalc_detectors(self, haar_file, lbp_file):
 | 
				
			||||||
 | 
					        self.haar_dtct = CascadeDetector(cascade_file = haar_file,
 | 
				
			||||||
 | 
					                                      min_size = self.min_size,
 | 
				
			||||||
 | 
					                                      min_neighbors = 20,
 | 
				
			||||||
 | 
					                                      scale_factor = 1.03,
 | 
				
			||||||
 | 
					                                      cascade_type = 'haar',
 | 
				
			||||||
 | 
					                                      thr = 0.4,
 | 
				
			||||||
 | 
					                                      angles = self.hangles)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        self.lbp_dtct = CascadeDetector(cascade_file = lbp_file,
 | 
				
			||||||
 | 
					                                   min_size = self.min_size,
 | 
				
			||||||
 | 
					                                   min_neighbors = 15,
 | 
				
			||||||
 | 
					                                   scale_factor = 1.04,
 | 
				
			||||||
 | 
					                                   cascade_type = 'lbp',
 | 
				
			||||||
 | 
					                                   thr = 0.4,
 | 
				
			||||||
 | 
					                                   angles = self.langles)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def get_faces_list_in_photo(self, img):
 | 
				
			||||||
 | 
					        if self.hangles:
 | 
				
			||||||
 | 
					                haar_faces = self.haar_dtct.detectWithAngles(img, resolve = True)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            haar_faces = []
 | 
				
			||||||
 | 
					        lbp_faces = self.lbp_dtct.detectWithAngles(img, resolve = True)
 | 
				
			||||||
 | 
					        faces = resolve_boxes({'haar':haar_faces, 'lbp':lbp_faces}, min_overlap = 0.6)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return faces
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def create_faces_file(self, fname, is_overwrite = False, target_file = None):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        Runs facial detection on fname (say a.jpg, or a.png), and creates a results file (a.faces.txt)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        target_file - override, and specify a specific target file
 | 
				
			||||||
 | 
					        is_overwrite - allow overwriting an existing results file
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        faces = self.get_faces_list_in_photo(cv2.imread(fname))
 | 
				
			||||||
 | 
					        results_file = fname.rsplit('.',1)[0] + '.faces.txt' if target_file is None else target_file
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if os.path.exists(results_file) and not is_overwrite:
 | 
				
			||||||
 | 
					            print "Warning, faces result file", results_file, "exists"
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            with open(results_file,'w') as csvfile:
 | 
				
			||||||
 | 
					                csv_writer = csv.writer(csvfile, delimiter=',')
 | 
				
			||||||
 | 
					                header = ['x', 'y','dx','dy', 'score', 'angle', 'type']
 | 
				
			||||||
 | 
					                csv_writer.writerow(header)
 | 
				
			||||||
 | 
					                for face in faces:
 | 
				
			||||||
 | 
					                    csv_writer.writerow([str(i) for i in [int(face.x), int(face.y), int(face.dx), int(face.dy), face.score, face.angle, face.cascade_type]])
 | 
				
			||||||
 | 
					        return results_file
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def get_sub_images_from_file(self,original_image_file, faces_file):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        extracts all the face sub-images from an image file, based on the results in a faces file
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        returns - the list of face images (numpy arrays)
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        img = cv2.imread(original_image_file)
 | 
				
			||||||
 | 
					        faces_reader = csv.reader(open(faces_file))
 | 
				
			||||||
 | 
					        faces_reader.next()  # discard the headings
 | 
				
			||||||
 | 
					        padded_face_images = []
 | 
				
			||||||
 | 
					        for line in faces_reader:
 | 
				
			||||||
 | 
					            x, y, dx, dy, score, angle, cascade_type = line
 | 
				
			||||||
 | 
					            [x,y,dx,dy,score, angle] = [int(float(i)) for i in [x,y,dx,dy,score, angle]]
 | 
				
			||||||
 | 
					            face = CascadeResult(([x,y,dx,dy], score), cascade_type, angle)
 | 
				
			||||||
 | 
					            padded_face, bounding_box_in_padded_face, _, _ = extract_box(img, face, padding_factor = 0.25)
 | 
				
			||||||
 | 
					            padded_face_images.append(padded_face)
 | 
				
			||||||
 | 
					        return padded_face_images
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def create_sub_images_from_file(self, original_image_file, faces_file, target_folder = None, img_type = 'png'):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        reads a faces file, created by "self.create_faces_file" and extracts padded faces from the original image
 | 
				
			||||||
 | 
					        The faces will be created in the same folder as the faces file, unless specified otherwise by "target_folder"
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        returns - the list of face files (strings)
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        target_folder = os.path.split(faces_file)[0] if target_folder is None else target_folder
 | 
				
			||||||
 | 
					        padded_face_images = self.get_sub_images_from_file(original_image_file, faces_file)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        base_image_name = os.path.split(faces_file)[1].split('.')[0]
 | 
				
			||||||
 | 
					        face_files = []
 | 
				
			||||||
 | 
					        for n_face, face_img in enumerate(padded_face_images):
 | 
				
			||||||
 | 
					            face_file = os.path.join(target_folder, base_image_name + '_face_%d.%s' %(n_face, img_type))
 | 
				
			||||||
 | 
					            cv2.imwrite( face_file , face_img )
 | 
				
			||||||
 | 
					            face_files.append(face_file)
 | 
				
			||||||
 | 
					        return face_files
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,33 @@
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					Created on May 7, 2014
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@author: eran
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					import cv2
 | 
				
			||||||
 | 
					from adiencealign.cascade_detection.cascade_detector import CascadeResult
 | 
				
			||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def draw_rect(img, r, angle = 0, color=(255,255,255), thickness = 4, alpha = 0.5):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    accepts:
 | 
				
			||||||
 | 
					    1. a (x,y,dx,dy) list
 | 
				
			||||||
 | 
					    4. a [(x,y,dx,dy),score] list, as returned by cv2.CascadeClassifier.detectMultiScaleWithScores()
 | 
				
			||||||
 | 
					    5. a CascadeResult object
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    if type(r) == CascadeResult:
 | 
				
			||||||
 | 
					        color = tuple(list(color) + [alpha])
 | 
				
			||||||
 | 
					        cv2.polylines(img, pts = [r.points_int], isClosed = True, color = color, thickness = thickness)
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    elif len(r)==4 or len(r)==2: # [x,y,dx,dy]
 | 
				
			||||||
 | 
					        if len(r)==2:
 | 
				
			||||||
 | 
					            if len(r[0]) == 4:
 | 
				
			||||||
 | 
					                r = r[0]
 | 
				
			||||||
 | 
					            else: 
 | 
				
			||||||
 | 
					                raise Exception("bad input to draw_rect...")
 | 
				
			||||||
 | 
					        pt1 = int(round(r[0])), int(round(r[1]))
 | 
				
			||||||
 | 
					        pt2 = int(round(r[0]+r[2])), int(round(r[1]+r[3]))
 | 
				
			||||||
 | 
					        color = tuple(list(color) + [alpha])
 | 
				
			||||||
 | 
					        cv2.rectangle(img, pt1, pt2, color, thickness = thickness)
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        raise Exception("bad input to draw_rect...")
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					Created on May 7, 2014
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@author: eran
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import shutil
 | 
				
			||||||
 | 
					from os.path import expanduser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def make_path(path, delete_content_if_exists = False):
 | 
				
			||||||
 | 
					    if not os.path.exists(path):
 | 
				
			||||||
 | 
					        os.makedirs(path)
 | 
				
			||||||
 | 
					    elif delete_content_if_exists:
 | 
				
			||||||
 | 
					        shutil.rmtree(path)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					def expand_path(file_or_path):
 | 
				
			||||||
 | 
					    if file_or_path.startswith('~/'):
 | 
				
			||||||
 | 
					        home = expanduser("~")
 | 
				
			||||||
 | 
					        file_at = os.path.join(home,file_or_path[2:])
 | 
				
			||||||
 | 
					        return file_at
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        return file_or_path        
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,87 @@
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					Created on May 7, 2014
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@author: eran
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					import math
 | 
				
			||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					import cv2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def pad_image_for_rotation(img):
 | 
				
			||||||
 | 
					    # we pad the image so, when we rotate it, it would never be clipped
 | 
				
			||||||
 | 
					    rot_y,rot_x = img.shape[:2]
 | 
				
			||||||
 | 
					    rot_x = int(rot_x / 2.0)
 | 
				
			||||||
 | 
					    rot_y = int(rot_y / 2.0)
 | 
				
			||||||
 | 
					    diag = int(math.sqrt(sum([math.pow(x,2) for x in img.shape])))
 | 
				
			||||||
 | 
					    diag = int(math.ceil(diag / 2.0) * 2.0) # make sure it is even
 | 
				
			||||||
 | 
					    if len(img.shape) == 3:
 | 
				
			||||||
 | 
					        big_image = np.zeros((diag, diag, 3), dtype=np.uint8)
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        big_image = np.zeros((diag, diag), dtype=np.uint8)
 | 
				
			||||||
 | 
					    x_shift = int(diag/2-rot_x)
 | 
				
			||||||
 | 
					    y_shift = int(diag/2-rot_y)
 | 
				
			||||||
 | 
					    # the shift of the old image within big_image
 | 
				
			||||||
 | 
					    if len(img.shape) == 3:
 | 
				
			||||||
 | 
					        big_image[y_shift :y_shift+img.shape[0], x_shift:x_shift+img.shape[1], :] = img
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        big_image[y_shift :y_shift+img.shape[0], x_shift:x_shift+img.shape[1]] = img
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    # the rotation center is no the radius (half the old image diagonal)
 | 
				
			||||||
 | 
					    rot_center = diag/2, diag/2
 | 
				
			||||||
 | 
					    return big_image, x_shift, y_shift, diag, rot_center
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def extract_rect(img, rect, factor = 0.2):
 | 
				
			||||||
 | 
					    (x,y,dx,dy) = rect
 | 
				
			||||||
 | 
					    new_x = max(0, int(x-dx*factor))
 | 
				
			||||||
 | 
					    new_y = max(0, int(y-dy*factor))
 | 
				
			||||||
 | 
					    new_dx = min(int(dx+2*factor*dx), img.shape[1] - new_x)
 | 
				
			||||||
 | 
					    new_dy = min(int(dy+2*factor*dy), img.shape[0] - new_y)
 | 
				
			||||||
 | 
					    Dx = x - new_x
 | 
				
			||||||
 | 
					    Dy = y - new_y
 | 
				
			||||||
 | 
					    #return [new_x, new_y, new_dx, new_dy]
 | 
				
			||||||
 | 
					    return img[new_y:new_y+new_dy,new_x:new_x+new_dx,:], Dx, Dy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def extract_box(img, box, padding_factor = 0.2):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    we can search for whatever we want in the rotated bordered image, 
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    Any point found can be translated back to the original image by:
 | 
				
			||||||
 | 
					    1. adding the origins of the bordered area,
 | 
				
			||||||
 | 
					    2. rotating the point using the inverse rotation matrix    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if box.angle != 0:
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        b_w = max(img.shape)*2
 | 
				
			||||||
 | 
					        b_h = b_w
 | 
				
			||||||
 | 
					        dx_center = b_w / 2 - box.center[0]
 | 
				
			||||||
 | 
					        dy_center = b_h / 2 - box.center[1]
 | 
				
			||||||
 | 
					        new_img = np.zeros((b_w, b_h, 3), dtype = img.dtype)
 | 
				
			||||||
 | 
					        new_img[dy_center:(dy_center + img.shape[0]), dx_center:(dx_center + img.shape[1]), :] = img
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        box_in_big_image = box.points + np.c_[np.ones((4,1)) * dx_center, np.ones((4,1)) * dy_center]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rot_mat = cv2.getRotationMatrix2D((b_w/2, b_h/2), box.angle, scale = 1.0)
 | 
				
			||||||
 | 
					        inv_rot_mat = cv2.invertAffineTransform(rot_mat)
 | 
				
			||||||
 | 
					        rot_image = cv2.warpAffine(new_img, rot_mat, (new_img.shape[1],new_img.shape[0]), flags=cv2.INTER_CUBIC)
 | 
				
			||||||
 | 
					        box_UL_in_rotated = (rot_mat * np.matrix([box_in_big_image[0,0], box_in_big_image[0,1], 1]).transpose()).transpose().tolist()[0] 
 | 
				
			||||||
 | 
					        box_coords_in_rotated = np.matrix(np.c_[box_in_big_image, np.ones((4,1))]) * rot_mat.T
 | 
				
			||||||
 | 
					        box_coords_in_rotated = box_coords_in_rotated[0,:].tolist()[0] + [box.dx, box.dy]
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        rot_mat = cv2.getRotationMatrix2D(box.center, box.angle, scale = 1.0)
 | 
				
			||||||
 | 
					        inv_rot_mat = cv2.invertAffineTransform(rot_mat)
 | 
				
			||||||
 | 
					        # for efficiency
 | 
				
			||||||
 | 
					        rot_image = img.copy()
 | 
				
			||||||
 | 
					        box_UL_in_rotated = (rot_mat * np.matrix([box.points[0,0], box.points[0,1], 1]).transpose()).transpose().tolist()[0] 
 | 
				
			||||||
 | 
					        box_coords_in_rotated = box_UL_in_rotated + [box.dx, box.dy]
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    img_with_border, Dx, Dy = extract_rect(rot_image, box_coords_in_rotated, padding_factor)
 | 
				
			||||||
 | 
					    box_coords_in_bordered = [Dx, Dy] + [box.dx, box.dy]
 | 
				
			||||||
 | 
					    border_UL_in_rotated = [box_UL_in_rotated[0]-Dx, box_UL_in_rotated[1]-Dy]
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return img_with_border, box_coords_in_bordered, border_UL_in_rotated, inv_rot_mat
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,113 @@
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					Created on May 7, 2014
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@author: eran
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import csv
 | 
				
			||||||
 | 
					import cv2
 | 
				
			||||||
 | 
					from numpy import linalg
 | 
				
			||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WEIGHTS3 = [11.1088851746,15.8721645013,12.3189439894,15.9467104922,13.9265119716,17.2447706133,11.4118267639,17.0728365324,12.7831886739,17.1908773151,9.6639887492,13.8443342456,8.76890470223,11.4441704453,7.52083144762,10.3245662427,6.35563072919,7.55739887985,6.42340544936,7.48786881875,10.8720924456,8.1349958353,12.3664410374,9.58137800608,6.29390307208,9.47697088783,8.49859202931,9.43946799727,7.92920023102,10.6126442536,10.2953809171,11.299323189,11.1181958685,12.9374719654,12.3764338392,14.7823225327,13.086272904,16.0571795811,15.079169884,17.5936174739,8.39112414861,7.68532826996,8.89386612449,8.70173923211,10.0826620269,8.70286074207,8.13121344224,9.80805203263,7.76044090777,9.2502084627,7.61334683331,10.4813589698,8.64831020289,11.0452512508,9.19528177019,13.0171747152,10.1204323102,14.0189765809,11.0232436734,14.7355286373,12.4881579947,15.4279914333,11.5785971474,16.7942051778,12.4916161829,17.57726411,14.3422306002,19.3015061859,16.3109851665,23.7227227093,17.7687071538,22.6848438204,14.9879312002,18.6763354368,12.927920123,17.7652660198,10.3584444834,15.5584775245,10.660322225,15.4351684107,11.6468441007,13.7962556973,12.9019472625,16.6407866045,13.1946878458,16.4137518526,9.86525395127,11.6687513083,10.4858060411,12.8407630953,9.24210197996,10.9728479778,9.37639005327,12.3418022852,12.2786533953,12.0629300205,14.8495857728,15.4667996708,14.7414922143,15.2761005039,8.5837102275,10.8010609515,6.55275411638,14.4240347981,10.4200283162,17.6888997346,11.4480670185,22.4669420211,13.1705102756,29.3073334802,16.9922725597,35.4031969543,18.7102372238,41.7466671473,21.7036998929,47.1495172267,24.4179633642,51.9023425203,26.6870471848,57.5921966087,7.71654443362,18.3796425232,9.84932443383,23.3915673615,15.7135746598,31.5768046636,18.159161567,39.0502675506,20.5154926286,44.6961338521,22.8541610324,50.9071591504,26.5569627651,54.4338495899,29.1062390164,61.5990210977]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def read_fidu(fidu_name):
 | 
				
			||||||
 | 
					    fidu_reader = csv.reader(open(fidu_name))
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        line = next(fidu_reader)
 | 
				
			||||||
 | 
					        score, yaw_angle = line
 | 
				
			||||||
 | 
					        next(fidu_reader)
 | 
				
			||||||
 | 
					        fidu_points = [[int(float(field)) for field in row[-2:]] for row in fidu_reader]
 | 
				
			||||||
 | 
					        return int(score), int(yaw_angle), fidu_points
 | 
				
			||||||
 | 
					    except:
 | 
				
			||||||
 | 
					        if line[0] == 'nothing found':
 | 
				
			||||||
 | 
					            return -1000, None, None
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            raise Exception('Corrupt Fidu File ' + fidu_name)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					def draw_fidu(img, fidu_points, radius = 3, color = (255,0,0), thickness = 1, draw_numbers_color = None):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    set draw_numbers_color = (COLOR_BLUE), for example, to draw the point numbers
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    for n,point in enumerate(fidu_points):
 | 
				
			||||||
 | 
					        cv2.circle(img, tuple([int(x) for x in point]),radius,color, thickness)
 | 
				
			||||||
 | 
					        if draw_numbers_color:
 | 
				
			||||||
 | 
					            cv2.putText( img, 
 | 
				
			||||||
 | 
					              '%d' %n,
 | 
				
			||||||
 | 
					             tuple([int(point[0])-4, int(point[1])-4]), 
 | 
				
			||||||
 | 
					             cv2.FONT_HERSHEY_COMPLEX, 
 | 
				
			||||||
 | 
					             0.35, 
 | 
				
			||||||
 | 
					             draw_numbers_color, 
 | 
				
			||||||
 | 
					             thickness = 1 )
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _compute_affine_transform_cvpy(refpoints, points, w = None): # Copied from the book
 | 
				
			||||||
 | 
					    if w == None:
 | 
				
			||||||
 | 
					        w = [1] * (len(points) * 2)
 | 
				
			||||||
 | 
					    assert(len(w) == 2*len(points))
 | 
				
			||||||
 | 
					    y = []
 | 
				
			||||||
 | 
					    for n, p in enumerate(refpoints):
 | 
				
			||||||
 | 
					        y += [p[0]/w[n*2], p[1]/w[n*2+1]]
 | 
				
			||||||
 | 
					    A = []
 | 
				
			||||||
 | 
					    for n, p in enumerate(points):
 | 
				
			||||||
 | 
					        A.extend([ [p[0]/w[n*2], p[1]/w[n*2], 0, 0, 1/w[n*2], 0], [0, 0, p[0]/w[n*2+1], p[1]/w[n*2+1], 0, 1/w[n*2+1]] ])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    lstsq = linalg.lstsq(A,y)
 | 
				
			||||||
 | 
					    h11, h12, h21, h22, dx, dy = lstsq[0]
 | 
				
			||||||
 | 
					    err = lstsq[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    R = np.array([[h11, h12, dx], [h21, h22, dy]])
 | 
				
			||||||
 | 
					    return R, err
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _compute_affine_transform_ocvlsq(refpoints, points, w = None):
 | 
				
			||||||
 | 
					    if w == None:
 | 
				
			||||||
 | 
					        w = [1] * (len(points) * 2)
 | 
				
			||||||
 | 
					    assert(len(w) == 2*len(points))
 | 
				
			||||||
 | 
					    y = []
 | 
				
			||||||
 | 
					    for n, p in enumerate(refpoints):
 | 
				
			||||||
 | 
					        y += [p[0]/w[n*2], p[1]/w[n*2+1]]
 | 
				
			||||||
 | 
					    A = []
 | 
				
			||||||
 | 
					    for n, p in enumerate(points):
 | 
				
			||||||
 | 
					        A.extend([ [p[0]/w[n*2], p[1]/w[n*2], 0, 0, 1/w[n*2], 0], [0, 0, p[0]/w[n*2+1], p[1]/w[n*2+1], 0, 1/w[n*2+1]] ])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    lstsq = cv2.solve(np.array(A), np.array(y), flags=cv2.DECOMP_SVD)
 | 
				
			||||||
 | 
					    h11, h12, h21, h22, dx, dy = lstsq[1]
 | 
				
			||||||
 | 
					    err = 0#lstsq[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #R = np.array([[h11, h12, dx], [h21, h22, dy]])
 | 
				
			||||||
 | 
					    # The row above works too - but creates a redundant dimension
 | 
				
			||||||
 | 
					    R = np.array([[h11[0], h12[0], dx[0]], [h21[0], h22[0], dy[0]]])
 | 
				
			||||||
 | 
					    return R, err
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					def fidu_transform(fidu_model, fidu_points, weights, img, shift=(0.0,0.0), use_ocvlsq=False):
 | 
				
			||||||
 | 
					    FIDU_SIZE = 544
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    SHIFT_Y = int(FIDU_SIZE * shift[0])
 | 
				
			||||||
 | 
					    SHIFT_X = int(FIDU_SIZE * shift[1])
 | 
				
			||||||
 | 
					    if not use_ocvlsq:
 | 
				
			||||||
 | 
					        R, err = _compute_affine_transform_cvpy(fidu_model, fidu_points, weights)
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        R, err = _compute_affine_transform_ocvlsq(fidu_model, fidu_points, weights)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    funneled_img = cv2.warpAffine(img, R, (FIDU_SIZE + SHIFT_Y*2, FIDU_SIZE + SHIFT_X*2), flags=cv2.INTER_CUBIC)
 | 
				
			||||||
 | 
					    return funneled_img, R
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def shift_vector(points, shift):
 | 
				
			||||||
 | 
					    FIDU_SIZE = 544
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    SHIFT_Y = int(FIDU_SIZE * shift[0])
 | 
				
			||||||
 | 
					    SHIFT_X = int(FIDU_SIZE * shift[1])
 | 
				
			||||||
 | 
					    SHIFT = (SHIFT_X, SHIFT_Y)
 | 
				
			||||||
 | 
					    s_points = [(p[0] + SHIFT[0], p[1] + SHIFT[1]) for p in points]
 | 
				
			||||||
 | 
					    return s_points
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def unwarp_fidu(orig_fidu_points, unwarp_mat):    
 | 
				
			||||||
 | 
					    points = np.array(orig_fidu_points).T
 | 
				
			||||||
 | 
					    points_h = np.r_[points, np.ones((1,points.shape[1]))]
 | 
				
			||||||
 | 
					    orig_fidu_points_in_aligned = np.dot(unwarp_mat,points_h).T.astype(np.int16)
 | 
				
			||||||
 | 
					    return orig_fidu_points_in_aligned 
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,113 @@
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					Created on May 7, 2014
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@author: eran
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import csv
 | 
				
			||||||
 | 
					import cv2
 | 
				
			||||||
 | 
					from numpy import linalg
 | 
				
			||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WEIGHTS3 = [11.1088851746,15.8721645013,12.3189439894,15.9467104922,13.9265119716,17.2447706133,11.4118267639,17.0728365324,12.7831886739,17.1908773151,9.6639887492,13.8443342456,8.76890470223,11.4441704453,7.52083144762,10.3245662427,6.35563072919,7.55739887985,6.42340544936,7.48786881875,10.8720924456,8.1349958353,12.3664410374,9.58137800608,6.29390307208,9.47697088783,8.49859202931,9.43946799727,7.92920023102,10.6126442536,10.2953809171,11.299323189,11.1181958685,12.9374719654,12.3764338392,14.7823225327,13.086272904,16.0571795811,15.079169884,17.5936174739,8.39112414861,7.68532826996,8.89386612449,8.70173923211,10.0826620269,8.70286074207,8.13121344224,9.80805203263,7.76044090777,9.2502084627,7.61334683331,10.4813589698,8.64831020289,11.0452512508,9.19528177019,13.0171747152,10.1204323102,14.0189765809,11.0232436734,14.7355286373,12.4881579947,15.4279914333,11.5785971474,16.7942051778,12.4916161829,17.57726411,14.3422306002,19.3015061859,16.3109851665,23.7227227093,17.7687071538,22.6848438204,14.9879312002,18.6763354368,12.927920123,17.7652660198,10.3584444834,15.5584775245,10.660322225,15.4351684107,11.6468441007,13.7962556973,12.9019472625,16.6407866045,13.1946878458,16.4137518526,9.86525395127,11.6687513083,10.4858060411,12.8407630953,9.24210197996,10.9728479778,9.37639005327,12.3418022852,12.2786533953,12.0629300205,14.8495857728,15.4667996708,14.7414922143,15.2761005039,8.5837102275,10.8010609515,6.55275411638,14.4240347981,10.4200283162,17.6888997346,11.4480670185,22.4669420211,13.1705102756,29.3073334802,16.9922725597,35.4031969543,18.7102372238,41.7466671473,21.7036998929,47.1495172267,24.4179633642,51.9023425203,26.6870471848,57.5921966087,7.71654443362,18.3796425232,9.84932443383,23.3915673615,15.7135746598,31.5768046636,18.159161567,39.0502675506,20.5154926286,44.6961338521,22.8541610324,50.9071591504,26.5569627651,54.4338495899,29.1062390164,61.5990210977]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def read_fidu(fidu_name):
 | 
				
			||||||
 | 
					    fidu_reader = csv.reader(open(fidu_name))
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        line = fidu_reader.next()
 | 
				
			||||||
 | 
					        score, yaw_angle = line
 | 
				
			||||||
 | 
					        fidu_reader.next()
 | 
				
			||||||
 | 
					        fidu_points = [[int(float(field)) for field in row[-2:]] for row in fidu_reader]
 | 
				
			||||||
 | 
					        return int(score), int(yaw_angle), fidu_points
 | 
				
			||||||
 | 
					    except:
 | 
				
			||||||
 | 
					        if line[0] == 'nothing found':
 | 
				
			||||||
 | 
					            return -1000, None, None
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            raise Exception('Corrupt Fidu File ' + fidu_name)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					def draw_fidu(img, fidu_points, radius = 3, color = (255,0,0), thickness = 1, draw_numbers_color = None):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    set draw_numbers_color = (COLOR_BLUE), for example, to draw the point numbers
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    for n,point in enumerate(fidu_points):
 | 
				
			||||||
 | 
					        cv2.circle(img, tuple([int(x) for x in point]),radius,color, thickness)
 | 
				
			||||||
 | 
					        if draw_numbers_color:
 | 
				
			||||||
 | 
					            cv2.putText( img, 
 | 
				
			||||||
 | 
					              '%d' %n,
 | 
				
			||||||
 | 
					             tuple([int(point[0])-4, int(point[1])-4]), 
 | 
				
			||||||
 | 
					             cv2.FONT_HERSHEY_COMPLEX, 
 | 
				
			||||||
 | 
					             0.35, 
 | 
				
			||||||
 | 
					             draw_numbers_color, 
 | 
				
			||||||
 | 
					             thickness = 1 )
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _compute_affine_transform_cvpy(refpoints, points, w = None): # Copied from the book
 | 
				
			||||||
 | 
					    if w == None:
 | 
				
			||||||
 | 
					        w = [1] * (len(points) * 2)
 | 
				
			||||||
 | 
					    assert(len(w) == 2*len(points))
 | 
				
			||||||
 | 
					    y = []
 | 
				
			||||||
 | 
					    for n, p in enumerate(refpoints):
 | 
				
			||||||
 | 
					        y += [p[0]/w[n*2], p[1]/w[n*2+1]]
 | 
				
			||||||
 | 
					    A = []
 | 
				
			||||||
 | 
					    for n, p in enumerate(points):
 | 
				
			||||||
 | 
					        A.extend([ [p[0]/w[n*2], p[1]/w[n*2], 0, 0, 1/w[n*2], 0], [0, 0, p[0]/w[n*2+1], p[1]/w[n*2+1], 0, 1/w[n*2+1]] ])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    lstsq = linalg.lstsq(A,y)
 | 
				
			||||||
 | 
					    h11, h12, h21, h22, dx, dy = lstsq[0]
 | 
				
			||||||
 | 
					    err = lstsq[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    R = np.array([[h11, h12, dx], [h21, h22, dy]])
 | 
				
			||||||
 | 
					    return R, err
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _compute_affine_transform_ocvlsq(refpoints, points, w = None):
 | 
				
			||||||
 | 
					    if w == None:
 | 
				
			||||||
 | 
					        w = [1] * (len(points) * 2)
 | 
				
			||||||
 | 
					    assert(len(w) == 2*len(points))
 | 
				
			||||||
 | 
					    y = []
 | 
				
			||||||
 | 
					    for n, p in enumerate(refpoints):
 | 
				
			||||||
 | 
					        y += [p[0]/w[n*2], p[1]/w[n*2+1]]
 | 
				
			||||||
 | 
					    A = []
 | 
				
			||||||
 | 
					    for n, p in enumerate(points):
 | 
				
			||||||
 | 
					        A.extend([ [p[0]/w[n*2], p[1]/w[n*2], 0, 0, 1/w[n*2], 0], [0, 0, p[0]/w[n*2+1], p[1]/w[n*2+1], 0, 1/w[n*2+1]] ])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    lstsq = cv2.solve(np.array(A), np.array(y), flags=cv2.DECOMP_SVD)
 | 
				
			||||||
 | 
					    h11, h12, h21, h22, dx, dy = lstsq[1]
 | 
				
			||||||
 | 
					    err = 0#lstsq[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #R = np.array([[h11, h12, dx], [h21, h22, dy]])
 | 
				
			||||||
 | 
					    # The row above works too - but creates a redundant dimension
 | 
				
			||||||
 | 
					    R = np.array([[h11[0], h12[0], dx[0]], [h21[0], h22[0], dy[0]]])
 | 
				
			||||||
 | 
					    return R, err
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					def fidu_transform(fidu_model, fidu_points, weights, img, shift=(0.0,0.0), use_ocvlsq=False):
 | 
				
			||||||
 | 
					    FIDU_SIZE = 544
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    SHIFT_Y = int(FIDU_SIZE * shift[0])
 | 
				
			||||||
 | 
					    SHIFT_X = int(FIDU_SIZE * shift[1])
 | 
				
			||||||
 | 
					    if not use_ocvlsq:
 | 
				
			||||||
 | 
					        R, err = _compute_affine_transform_cvpy(fidu_model, fidu_points, weights)
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        R, err = _compute_affine_transform_ocvlsq(fidu_model, fidu_points, weights)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    funneled_img = cv2.warpAffine(img, R, (FIDU_SIZE + SHIFT_Y*2, FIDU_SIZE + SHIFT_X*2), flags=cv2.INTER_CUBIC)
 | 
				
			||||||
 | 
					    return funneled_img, R
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def shift_vector(points, shift):
 | 
				
			||||||
 | 
					    FIDU_SIZE = 544
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    SHIFT_Y = int(FIDU_SIZE * shift[0])
 | 
				
			||||||
 | 
					    SHIFT_X = int(FIDU_SIZE * shift[1])
 | 
				
			||||||
 | 
					    SHIFT = (SHIFT_X, SHIFT_Y)
 | 
				
			||||||
 | 
					    s_points = [(p[0] + SHIFT[0], p[1] + SHIFT[1]) for p in points]
 | 
				
			||||||
 | 
					    return s_points
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def unwarp_fidu(orig_fidu_points, unwarp_mat):    
 | 
				
			||||||
 | 
					    points = np.array(orig_fidu_points).T
 | 
				
			||||||
 | 
					    points_h = np.r_[points, np.ones((1,points.shape[1]))]
 | 
				
			||||||
 | 
					    orig_fidu_points_in_aligned = np.dot(unwarp_mat,points_h).T.astype(np.int16)
 | 
				
			||||||
 | 
					    return orig_fidu_points_in_aligned 
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					Created on May 7, 2014
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@author: eran
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					import subprocess
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def detect_landmarks(fname, max_size = 400*400, min_size = 50*50, fidu_exec_dir = os.path.abspath('../resources/')):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    optional flags:
 | 
				
			||||||
 | 
					    max_size - If exceeds this pixel size, image is resized to that before landmark detection
 | 
				
			||||||
 | 
					    min_size - If below this pixel size, image is resized to that before landmark detection
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    max_size = str(max_size) if max_size is not None else ''
 | 
				
			||||||
 | 
					    min_size = str(min_size) if min_size is not None else ''
 | 
				
			||||||
 | 
					    fidu_cmd = './FiducialFaceDetector.sh Face_small_146filters_-0.65thr.xml %s %s %s' %(fname, min_size, max_size)    
 | 
				
			||||||
 | 
					    print(fidu_cmd)
 | 
				
			||||||
 | 
					    x = subprocess.call(fidu_cmd, shell=True, cwd = fidu_exec_dir)
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					Created on May 7, 2014
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@author: eran
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					import subprocess
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def detect_landmarks(fname, max_size = 400*400, min_size = 50*50, fidu_exec_dir = os.path.abspath('../resources/')):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    optional flags:
 | 
				
			||||||
 | 
					    max_size - If exceeds this pixel size, image is resized to that before landmark detection
 | 
				
			||||||
 | 
					    min_size - If below this pixel size, image is resized to that before landmark detection
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    max_size = str(max_size) if max_size is not None else ''
 | 
				
			||||||
 | 
					    min_size = str(min_size) if min_size is not None else ''
 | 
				
			||||||
 | 
					    fidu_cmd = './FiducialFaceDetector.sh Face_small_146filters_-0.65thr.xml %s %s %s' %(fname, min_size, max_size)    
 | 
				
			||||||
 | 
					    print fidu_cmd
 | 
				
			||||||
 | 
					    x = subprocess.call(fidu_cmd, shell=True, cwd = fidu_exec_dir)
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,148 @@
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					Created on May 8, 2014
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@author: eran
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					from adiencealign.cascade_detection.cascade_face_finder import CascadeFaceFinder
 | 
				
			||||||
 | 
					from adiencealign.affine_alignment.affine_aligner import AffineAligner
 | 
				
			||||||
 | 
					import glob
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					from adiencealign.landmarks_detection.landmarks_detector import detect_landmarks
 | 
				
			||||||
 | 
					from adiencealign.common.landmarks import read_fidu, unwarp_fidu, draw_fidu
 | 
				
			||||||
 | 
					import cv2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CascadeFaceAligner(object):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    classdocs
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, haar_file = '../resources/haarcascade_frontalface_default.xml', 
 | 
				
			||||||
 | 
					                 lbp_file = '../resources/lbpcascade_frontalface.xml', 
 | 
				
			||||||
 | 
					                 fidu_model_file = '../resources/model_ang_0.txt',
 | 
				
			||||||
 | 
					                 fidu_exec_dir = '../resources/'):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        Constructor
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        self.face_finder = CascadeFaceFinder(haar_file = haar_file,
 | 
				
			||||||
 | 
					                                            lbp_file = lbp_file)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.aligner = AffineAligner(fidu_model_file = fidu_model_file)
 | 
				
			||||||
 | 
					        self.fidu_exec_dir = fidu_exec_dir
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.valid_angles = [-45,-30,-15,0,15,30,45]
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def detect_faces(self, input_folder, output_folder, mark_dones = True):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        mark_dones - if True, will create a hidden file, marking this file as done with a hidden file, starting with '.done.'
 | 
				
			||||||
 | 
					        '''        
 | 
				
			||||||
 | 
					        input_files1 = glob.glob(os.path.join(input_folder, '*.jpg'))
 | 
				
			||||||
 | 
					        input_files2 = glob.glob(os.path.join(input_folder, '*.png'))
 | 
				
			||||||
 | 
					        input_files = input_files1 + input_files2
 | 
				
			||||||
 | 
					        N = len(input_files)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for n_file, input_file in enumerate(input_files):
 | 
				
			||||||
 | 
					            a,b = os.path.split(input_file)
 | 
				
			||||||
 | 
					            done_file = os.path.join(a, '.done.' + b.rsplit('.',1)[0])
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if os.path.exists(done_file):
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            print("... processing", input_file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            target_faces_file = os.path.join( output_folder, os.path.split(input_file)[1].rsplit('.',1)[0] + '.faces.txt')
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            faces_file = self.face_finder.create_faces_file( input_file, is_overwrite = False, target_file = target_faces_file )
 | 
				
			||||||
 | 
					            sub_images_files = self.face_finder.create_sub_images_from_file( original_image_file = input_file, 
 | 
				
			||||||
 | 
					                                                    faces_file = faces_file, 
 | 
				
			||||||
 | 
					                                                    target_folder = output_folder,
 | 
				
			||||||
 | 
					                                                    img_type = 'jpg')
 | 
				
			||||||
 | 
					            #touch
 | 
				
			||||||
 | 
					            open(done_file,'w').close()
 | 
				
			||||||
 | 
					            print("Detected on %d / %d files" %(n_file, N))
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def align_faces(self, input_images, output_path, fidu_max_size = None, fidu_min_size = None, is_align = True, is_draw_fidu = False, delete_no_fidu = False):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        input_images - can be either a folder (all *.jpgs in it) or a list of filenames
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        , fidu_max_size = None, fidu_min_size = None):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        if type(input_images) == type(''):
 | 
				
			||||||
 | 
					            input_images = glob.glob(os.path.join(input_images, '*.jpg'))
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for input_image in input_images:
 | 
				
			||||||
 | 
					            detect_landmarks(fname = os.path.abspath(input_image),
 | 
				
			||||||
 | 
					                                 max_size = fidu_max_size,
 | 
				
			||||||
 | 
					                                 min_size = fidu_min_size,
 | 
				
			||||||
 | 
					                                 fidu_exec_dir = self.fidu_exec_dir)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            fidu_file = input_image.rsplit('.',1)[0] + '.cfidu'
 | 
				
			||||||
 | 
					            fidu_score, yaw_angle, fidu_points = read_fidu(fidu_file)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if not (fidu_score is not None and yaw_angle in self.valid_angles):
 | 
				
			||||||
 | 
					                # skip face
 | 
				
			||||||
 | 
					                if delete_no_fidu:
 | 
				
			||||||
 | 
					                    os.remove(fidu_file)
 | 
				
			||||||
 | 
					                    os.remove(input_image)
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if is_align:
 | 
				
			||||||
 | 
					                # save the aligned image
 | 
				
			||||||
 | 
					                sub_img = cv2.imread(input_image)
 | 
				
			||||||
 | 
					                _, base_fname = os.path.split(input_image) 
 | 
				
			||||||
 | 
					                aligned_img, R = self.aligner.align(sub_img, fidu_points)
 | 
				
			||||||
 | 
					                aligned_img_file = os.path.join(output_path, base_fname.rsplit('.',1)[0] + '.aligned.png')
 | 
				
			||||||
 | 
					                cv2.imwrite(aligned_img_file, aligned_img)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                # save a copy of the aligned image, with the landmarks drawn
 | 
				
			||||||
 | 
					                if is_draw_fidu:
 | 
				
			||||||
 | 
					                    aligned_img_file = os.path.join(output_path, base_fname.rsplit('.',1)[0] + '.aligned.withpoints.png') 
 | 
				
			||||||
 | 
					                    fidu_points_in_aligned = unwarp_fidu(orig_fidu_points = fidu_points, unwarp_mat = R)
 | 
				
			||||||
 | 
					                    draw_fidu(aligned_img, fidu_points_in_aligned, radius = 9, color = (255,0,0), thickness = 3)
 | 
				
			||||||
 | 
					                    cv2.imwrite(aligned_img_file, aligned_img)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					#             
 | 
				
			||||||
 | 
					#     def align_faces2(self, input_folder, output_folder, detect_landmarks = True, delete_no_fidu = True, is_align = True, is_draw_fidu = False, fidu_max_size = None, fidu_min_size = None):
 | 
				
			||||||
 | 
					#         '''
 | 
				
			||||||
 | 
					#         delete_no_fidu - deletes the .cfidu and sub_img if no fidu was found
 | 
				
			||||||
 | 
					#         is_draw_fidu - creates a copy of the aligned images with the original fiducial points on it
 | 
				
			||||||
 | 
					#         '''
 | 
				
			||||||
 | 
					#         input_files = glob.glob(os.path.join(input_folder, '*'))
 | 
				
			||||||
 | 
					#         for input_file in input_files:
 | 
				
			||||||
 | 
					#             print "... processing", input_file
 | 
				
			||||||
 | 
					#             target_faces_file = os.path.join( output_folder, os.path.split(input_file)[1].rsplit('.',1)[0] + '.faces.txt')
 | 
				
			||||||
 | 
					#             
 | 
				
			||||||
 | 
					#             faces_file = self.face_finder.create_faces_file( input_file, is_overwrite = False, target_file = target_faces_file )
 | 
				
			||||||
 | 
					#             sub_images_files = self.face_finder.create_sub_images_from_file( original_image_file = input_file, 
 | 
				
			||||||
 | 
					#                                                     faces_file = faces_file, 
 | 
				
			||||||
 | 
					#                                                     target_folder = output_folder,
 | 
				
			||||||
 | 
					#                                                     img_type = 'jpg')
 | 
				
			||||||
 | 
					#             
 | 
				
			||||||
 | 
					#             for sub_image_file in sub_images_files:
 | 
				
			||||||
 | 
					#                 detect_landmarks(fname = os.path.abspath(sub_image_file),
 | 
				
			||||||
 | 
					#                                  max_size = fidu_max_size,
 | 
				
			||||||
 | 
					#                                  min_size = fidu_min_size,
 | 
				
			||||||
 | 
					#                                  fidu_exec_dir = self.fidu_exec_dir)
 | 
				
			||||||
 | 
					#                 
 | 
				
			||||||
 | 
					#                 fidu_file = sub_image_file.rsplit('.',1)[0] + '.cfidu'
 | 
				
			||||||
 | 
					#                 fidu_score, yaw_angle, fidu_points = read_fidu(fidu_file)
 | 
				
			||||||
 | 
					#                 
 | 
				
			||||||
 | 
					#                 if not (fidu_score is not None and yaw_angle in self.valid_angles):
 | 
				
			||||||
 | 
					#                     # skip face
 | 
				
			||||||
 | 
					#                     os.remove(fidu_file)
 | 
				
			||||||
 | 
					#                     os.remove(sub_image_file)
 | 
				
			||||||
 | 
					#                     continue
 | 
				
			||||||
 | 
					#                 
 | 
				
			||||||
 | 
					#                 if is_align:
 | 
				
			||||||
 | 
					#                     # save the aligned image
 | 
				
			||||||
 | 
					#                     sub_img = cv2.imread(sub_image_file)
 | 
				
			||||||
 | 
					#                     aligned_img, R = self.aligner.align(sub_img, fidu_points)
 | 
				
			||||||
 | 
					#                     aligned_img_file = sub_image_file.rsplit('.',1)[0] + '.aligned.png'
 | 
				
			||||||
 | 
					#                     cv2.imwrite(aligned_img_file, aligned_img)
 | 
				
			||||||
 | 
					#                     
 | 
				
			||||||
 | 
					#                     # save a copy of the aligned image, with the landmarks drawn
 | 
				
			||||||
 | 
					#                     if is_draw_fidu:
 | 
				
			||||||
 | 
					#                         aligned_img_file = sub_image_file.rsplit('.',1)[0] + '.aligned.withpoints.png'
 | 
				
			||||||
 | 
					#                         fidu_points_in_aligned = unwarp_fidu(orig_fidu_points = fidu_points, unwarp_mat = R)
 | 
				
			||||||
 | 
					#                         draw_fidu(aligned_img, fidu_points_in_aligned, radius = 9, color = (255,0,0), thickness = 3)
 | 
				
			||||||
 | 
					#                         cv2.imwrite(aligned_img_file, aligned_img)
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,148 @@
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					Created on May 8, 2014
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@author: eran
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					from adiencealign.cascade_detection.cascade_face_finder import CascadeFaceFinder
 | 
				
			||||||
 | 
					from adiencealign.affine_alignment.affine_aligner import AffineAligner
 | 
				
			||||||
 | 
					import glob
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					from adiencealign.landmarks_detection.landmarks_detector import detect_landmarks
 | 
				
			||||||
 | 
					from adiencealign.common.landmarks import read_fidu, unwarp_fidu, draw_fidu
 | 
				
			||||||
 | 
					import cv2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CascadeFaceAligner(object):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    classdocs
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, haar_file = '../resources/haarcascade_frontalface_default.xml', 
 | 
				
			||||||
 | 
					                 lbp_file = '../resources/lbpcascade_frontalface.xml', 
 | 
				
			||||||
 | 
					                 fidu_model_file = '../resources/model_ang_0.txt',
 | 
				
			||||||
 | 
					                 fidu_exec_dir = '../resources/'):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        Constructor
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        self.face_finder = CascadeFaceFinder(haar_file = haar_file,
 | 
				
			||||||
 | 
					                                            lbp_file = lbp_file)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.aligner = AffineAligner(fidu_model_file = fidu_model_file)
 | 
				
			||||||
 | 
					        self.fidu_exec_dir = fidu_exec_dir
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.valid_angles = [-45,-30,-15,0,15,30,45]
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def detect_faces(self, input_folder, output_folder, mark_dones = True):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        mark_dones - if True, will create a hidden file, marking this file as done with a hidden file, starting with '.done.'
 | 
				
			||||||
 | 
					        '''        
 | 
				
			||||||
 | 
					        input_files1 = glob.glob(os.path.join(input_folder, '*.jpg'))
 | 
				
			||||||
 | 
					        input_files2 = glob.glob(os.path.join(input_folder, '*.png'))
 | 
				
			||||||
 | 
					        input_files = input_files1 + input_files2
 | 
				
			||||||
 | 
					        N = len(input_files)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for n_file, input_file in enumerate(input_files):
 | 
				
			||||||
 | 
					            a,b = os.path.split(input_file)
 | 
				
			||||||
 | 
					            done_file = os.path.join(a, '.done.' + b.rsplit('.',1)[0])
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if os.path.exists(done_file):
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            print "... processing", input_file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            target_faces_file = os.path.join( output_folder, os.path.split(input_file)[1].rsplit('.',1)[0] + '.faces.txt')
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            faces_file = self.face_finder.create_faces_file( input_file, is_overwrite = False, target_file = target_faces_file )
 | 
				
			||||||
 | 
					            sub_images_files = self.face_finder.create_sub_images_from_file( original_image_file = input_file, 
 | 
				
			||||||
 | 
					                                                    faces_file = faces_file, 
 | 
				
			||||||
 | 
					                                                    target_folder = output_folder,
 | 
				
			||||||
 | 
					                                                    img_type = 'jpg')
 | 
				
			||||||
 | 
					            #touch
 | 
				
			||||||
 | 
					            open(done_file,'w').close()
 | 
				
			||||||
 | 
					            print "Detected on %d / %d files" %(n_file, N)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def align_faces(self, input_images, output_path, fidu_max_size = None, fidu_min_size = None, is_align = True, is_draw_fidu = False, delete_no_fidu = False):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        input_images - can be either a folder (all *.jpgs in it) or a list of filenames
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        , fidu_max_size = None, fidu_min_size = None):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        if type(input_images) == type(''):
 | 
				
			||||||
 | 
					            input_images = glob.glob(os.path.join(input_images, '*.jpg'))
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for input_image in input_images:
 | 
				
			||||||
 | 
					            detect_landmarks(fname = os.path.abspath(input_image),
 | 
				
			||||||
 | 
					                                 max_size = fidu_max_size,
 | 
				
			||||||
 | 
					                                 min_size = fidu_min_size,
 | 
				
			||||||
 | 
					                                 fidu_exec_dir = self.fidu_exec_dir)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            fidu_file = input_image.rsplit('.',1)[0] + '.cfidu'
 | 
				
			||||||
 | 
					            fidu_score, yaw_angle, fidu_points = read_fidu(fidu_file)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if not (fidu_score is not None and yaw_angle in self.valid_angles):
 | 
				
			||||||
 | 
					                # skip face
 | 
				
			||||||
 | 
					                if delete_no_fidu:
 | 
				
			||||||
 | 
					                    os.remove(fidu_file)
 | 
				
			||||||
 | 
					                    os.remove(input_image)
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if is_align:
 | 
				
			||||||
 | 
					                # save the aligned image
 | 
				
			||||||
 | 
					                sub_img = cv2.imread(input_image)
 | 
				
			||||||
 | 
					                _, base_fname = os.path.split(input_image) 
 | 
				
			||||||
 | 
					                aligned_img, R = self.aligner.align(sub_img, fidu_points)
 | 
				
			||||||
 | 
					                aligned_img_file = os.path.join(output_path, base_fname.rsplit('.',1)[0] + '.aligned.png')
 | 
				
			||||||
 | 
					                cv2.imwrite(aligned_img_file, aligned_img)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                # save a copy of the aligned image, with the landmarks drawn
 | 
				
			||||||
 | 
					                if is_draw_fidu:
 | 
				
			||||||
 | 
					                    aligned_img_file = os.path.join(output_path, base_fname.rsplit('.',1)[0] + '.aligned.withpoints.png') 
 | 
				
			||||||
 | 
					                    fidu_points_in_aligned = unwarp_fidu(orig_fidu_points = fidu_points, unwarp_mat = R)
 | 
				
			||||||
 | 
					                    draw_fidu(aligned_img, fidu_points_in_aligned, radius = 9, color = (255,0,0), thickness = 3)
 | 
				
			||||||
 | 
					                    cv2.imwrite(aligned_img_file, aligned_img)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					#             
 | 
				
			||||||
 | 
					#     def align_faces2(self, input_folder, output_folder, detect_landmarks = True, delete_no_fidu = True, is_align = True, is_draw_fidu = False, fidu_max_size = None, fidu_min_size = None):
 | 
				
			||||||
 | 
					#         '''
 | 
				
			||||||
 | 
					#         delete_no_fidu - deletes the .cfidu and sub_img if no fidu was found
 | 
				
			||||||
 | 
					#         is_draw_fidu - creates a copy of the aligned images with the original fiducial points on it
 | 
				
			||||||
 | 
					#         '''
 | 
				
			||||||
 | 
					#         input_files = glob.glob(os.path.join(input_folder, '*'))
 | 
				
			||||||
 | 
					#         for input_file in input_files:
 | 
				
			||||||
 | 
					#             print "... processing", input_file
 | 
				
			||||||
 | 
					#             target_faces_file = os.path.join( output_folder, os.path.split(input_file)[1].rsplit('.',1)[0] + '.faces.txt')
 | 
				
			||||||
 | 
					#             
 | 
				
			||||||
 | 
					#             faces_file = self.face_finder.create_faces_file( input_file, is_overwrite = False, target_file = target_faces_file )
 | 
				
			||||||
 | 
					#             sub_images_files = self.face_finder.create_sub_images_from_file( original_image_file = input_file, 
 | 
				
			||||||
 | 
					#                                                     faces_file = faces_file, 
 | 
				
			||||||
 | 
					#                                                     target_folder = output_folder,
 | 
				
			||||||
 | 
					#                                                     img_type = 'jpg')
 | 
				
			||||||
 | 
					#             
 | 
				
			||||||
 | 
					#             for sub_image_file in sub_images_files:
 | 
				
			||||||
 | 
					#                 detect_landmarks(fname = os.path.abspath(sub_image_file),
 | 
				
			||||||
 | 
					#                                  max_size = fidu_max_size,
 | 
				
			||||||
 | 
					#                                  min_size = fidu_min_size,
 | 
				
			||||||
 | 
					#                                  fidu_exec_dir = self.fidu_exec_dir)
 | 
				
			||||||
 | 
					#                 
 | 
				
			||||||
 | 
					#                 fidu_file = sub_image_file.rsplit('.',1)[0] + '.cfidu'
 | 
				
			||||||
 | 
					#                 fidu_score, yaw_angle, fidu_points = read_fidu(fidu_file)
 | 
				
			||||||
 | 
					#                 
 | 
				
			||||||
 | 
					#                 if not (fidu_score is not None and yaw_angle in self.valid_angles):
 | 
				
			||||||
 | 
					#                     # skip face
 | 
				
			||||||
 | 
					#                     os.remove(fidu_file)
 | 
				
			||||||
 | 
					#                     os.remove(sub_image_file)
 | 
				
			||||||
 | 
					#                     continue
 | 
				
			||||||
 | 
					#                 
 | 
				
			||||||
 | 
					#                 if is_align:
 | 
				
			||||||
 | 
					#                     # save the aligned image
 | 
				
			||||||
 | 
					#                     sub_img = cv2.imread(sub_image_file)
 | 
				
			||||||
 | 
					#                     aligned_img, R = self.aligner.align(sub_img, fidu_points)
 | 
				
			||||||
 | 
					#                     aligned_img_file = sub_image_file.rsplit('.',1)[0] + '.aligned.png'
 | 
				
			||||||
 | 
					#                     cv2.imwrite(aligned_img_file, aligned_img)
 | 
				
			||||||
 | 
					#                     
 | 
				
			||||||
 | 
					#                     # save a copy of the aligned image, with the landmarks drawn
 | 
				
			||||||
 | 
					#                     if is_draw_fidu:
 | 
				
			||||||
 | 
					#                         aligned_img_file = sub_image_file.rsplit('.',1)[0] + '.aligned.withpoints.png'
 | 
				
			||||||
 | 
					#                         fidu_points_in_aligned = unwarp_fidu(orig_fidu_points = fidu_points, unwarp_mat = R)
 | 
				
			||||||
 | 
					#                         draw_fidu(aligned_img, fidu_points_in_aligned, radius = 9, color = (255,0,0), thickness = 3)
 | 
				
			||||||
 | 
					#                         cv2.imwrite(aligned_img_file, aligned_img)
 | 
				
			||||||
							
								
								
									
										65460
									
								
								adience_align-master/adiencealign(old)/resources/Face_small_146filters_-0.65thr.xml
								
								
								
								
									Executable file
								
							
							
						
						| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					#!/bin/sh
 | 
				
			||||||
 | 
					export LD_LIBRARY_PATH=/usr/local/lib:.
 | 
				
			||||||
 | 
					"./FiducialFaceDetector" $1 $2 $3 $4
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,68 @@
 | 
				
			||||||
 | 
					1,272,272
 | 
				
			||||||
 | 
					2,252,272
 | 
				
			||||||
 | 
					3,232,272
 | 
				
			||||||
 | 
					4,292,272
 | 
				
			||||||
 | 
					5,312,272
 | 
				
			||||||
 | 
					6,272,232
 | 
				
			||||||
 | 
					7,272,192
 | 
				
			||||||
 | 
					8,272,152
 | 
				
			||||||
 | 
					9,272,112
 | 
				
			||||||
 | 
					10,212,112
 | 
				
			||||||
 | 
					11,192,112
 | 
				
			||||||
 | 
					12,172,112
 | 
				
			||||||
 | 
					13,192,92
 | 
				
			||||||
 | 
					14,172,92
 | 
				
			||||||
 | 
					15,152,112
 | 
				
			||||||
 | 
					16,112,72
 | 
				
			||||||
 | 
					17,132,52
 | 
				
			||||||
 | 
					18,172,52
 | 
				
			||||||
 | 
					19,212,52
 | 
				
			||||||
 | 
					20,252,52
 | 
				
			||||||
 | 
					21,332,112
 | 
				
			||||||
 | 
					22,352,112
 | 
				
			||||||
 | 
					23,372,112
 | 
				
			||||||
 | 
					24,352,92
 | 
				
			||||||
 | 
					25,372,92
 | 
				
			||||||
 | 
					26,392,112
 | 
				
			||||||
 | 
					27,432,72
 | 
				
			||||||
 | 
					28,412,52
 | 
				
			||||||
 | 
					29,372,52
 | 
				
			||||||
 | 
					30,332,52
 | 
				
			||||||
 | 
					31,292,52
 | 
				
			||||||
 | 
					32,272,312
 | 
				
			||||||
 | 
					33,252,312
 | 
				
			||||||
 | 
					34,232,332
 | 
				
			||||||
 | 
					35,212,352
 | 
				
			||||||
 | 
					36,232,352
 | 
				
			||||||
 | 
					37,252,332
 | 
				
			||||||
 | 
					38,272,332
 | 
				
			||||||
 | 
					39,312,312
 | 
				
			||||||
 | 
					40,332,332
 | 
				
			||||||
 | 
					41,352,352
 | 
				
			||||||
 | 
					42,332,352
 | 
				
			||||||
 | 
					43,312,332
 | 
				
			||||||
 | 
					44,332,372
 | 
				
			||||||
 | 
					45,312,352
 | 
				
			||||||
 | 
					46,312,372
 | 
				
			||||||
 | 
					47,272,352
 | 
				
			||||||
 | 
					48,252,372
 | 
				
			||||||
 | 
					49,252,352
 | 
				
			||||||
 | 
					50,232,372
 | 
				
			||||||
 | 
					51,272,372
 | 
				
			||||||
 | 
					52,272,492
 | 
				
			||||||
 | 
					53,212,492
 | 
				
			||||||
 | 
					54,172,472
 | 
				
			||||||
 | 
					55,132,432
 | 
				
			||||||
 | 
					56,92,392
 | 
				
			||||||
 | 
					57,72,332
 | 
				
			||||||
 | 
					58,52,272
 | 
				
			||||||
 | 
					59,52,212
 | 
				
			||||||
 | 
					60,52,152
 | 
				
			||||||
 | 
					61,332,472
 | 
				
			||||||
 | 
					62,372,452
 | 
				
			||||||
 | 
					63,412,412
 | 
				
			||||||
 | 
					64,452,372
 | 
				
			||||||
 | 
					65,472,312
 | 
				
			||||||
 | 
					66,472,252
 | 
				
			||||||
 | 
					67,472,192
 | 
				
			||||||
 | 
					68,472,132
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 696 KiB  | 
| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					rm outputs/pipeline/faces/*
 | 
				
			||||||
 | 
					rm outputs/pipeline/aligned/*
 | 
				
			||||||
 | 
					rm outputs/cascade/1/*
 | 
				
			||||||
 | 
					rm outputs/cascade/2/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					rm resources/pipeline/.done*
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					rm outputs/pipeline/faces/*
 | 
				
			||||||
 | 
					rm outputs/pipeline/aligned/*
 | 
				
			||||||
 | 
					rm outputs/cascade/1/*
 | 
				
			||||||
 | 
					rm outputs/cascade/2/*
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 61 KiB  | 
| 
		 After Width: | Height: | Size: 65 KiB  | 
| 
		 After Width: | Height: | Size: 57 KiB  | 
| 
		 After Width: | Height: | Size: 44 KiB  | 
| 
		 After Width: | Height: | Size: 44 KiB  | 
| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					x,y,dx,dy,score,angle,type
 | 
				
			||||||
 | 
					61,62,132,132,344,0.0,haar
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 61 KiB  | 
| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					x,y,dx,dy,score,angle,type
 | 
				
			||||||
 | 
					327,101,121,121,154,0.0,lbp
 | 
				
			||||||
 | 
					237,106,113,113,139,0.0,lbp
 | 
				
			||||||
 | 
					164,49,91,91,49,0.0,lbp
 | 
				
			||||||
 | 
					434,86,94,94,95,0.0,lbp
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 68 KiB  | 
| 
		 After Width: | Height: | Size: 60 KiB  | 
| 
		 After Width: | Height: | Size: 42 KiB  | 
| 
		 After Width: | Height: | Size: 43 KiB  | 
| 
		 After Width: | Height: | Size: 436 KiB  | 
| 
		 After Width: | Height: | Size: 449 KiB  | 
| 
		 After Width: | Height: | Size: 696 KiB  | 
| 
		 After Width: | Height: | Size: 703 KiB  | 
| 
		 After Width: | Height: | Size: 645 KiB  | 
| 
		 After Width: | Height: | Size: 649 KiB  | 
| 
		 After Width: | Height: | Size: 586 KiB  | 
| 
		 After Width: | Height: | Size: 586 KiB  | 
| 
		 After Width: | Height: | Size: 720 KiB  | 
| 
		 After Width: | Height: | Size: 722 KiB  | 
| 
		 After Width: | Height: | Size: 742 KiB  | 
| 
		 After Width: | Height: | Size: 741 KiB  | 
| 
		 After Width: | Height: | Size: 750 KiB  | 
| 
		 After Width: | Height: | Size: 750 KiB  | 
| 
		 After Width: | Height: | Size: 783 KiB  | 
| 
		 After Width: | Height: | Size: 781 KiB  | 
| 
		 After Width: | Height: | Size: 696 KiB  | 
| 
		 After Width: | Height: | Size: 696 KiB  | 
| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					x,y,dx,dy,score,angle,type
 | 
				
			||||||
 | 
					61,62,132,132,344,0.0,haar
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,70 @@
 | 
				
			||||||
 | 
					233,0
 | 
				
			||||||
 | 
					x,y,dx,dy,x_c,y_c
 | 
				
			||||||
 | 
					87.00,106.00,22.00,22.00,98.00,117.00
 | 
				
			||||||
 | 
					83.00,106.00,22.00,22.00,94.00,117.00
 | 
				
			||||||
 | 
					78.00,106.00,22.00,22.00,89.00,117.00
 | 
				
			||||||
 | 
					92.00,106.00,22.00,22.00,103.00,117.00
 | 
				
			||||||
 | 
					96.00,106.00,22.00,22.00,107.00,117.00
 | 
				
			||||||
 | 
					87.00,96.00,22.00,22.00,98.00,107.00
 | 
				
			||||||
 | 
					87.00,87.00,22.00,22.00,98.00,98.00
 | 
				
			||||||
 | 
					87.00,78.00,22.00,22.00,98.00,89.00
 | 
				
			||||||
 | 
					87.00,74.00,22.00,22.00,98.00,85.00
 | 
				
			||||||
 | 
					74.00,74.00,22.00,22.00,85.00,85.00
 | 
				
			||||||
 | 
					69.00,74.00,22.00,22.00,80.00,85.00
 | 
				
			||||||
 | 
					64.00,74.00,22.00,22.00,75.00,85.00
 | 
				
			||||||
 | 
					69.00,69.00,22.00,22.00,80.00,80.00
 | 
				
			||||||
 | 
					64.00,69.00,22.00,22.00,75.00,80.00
 | 
				
			||||||
 | 
					60.00,74.00,22.00,22.00,71.00,85.00
 | 
				
			||||||
 | 
					51.00,64.00,22.00,22.00,62.00,75.00
 | 
				
			||||||
 | 
					55.00,55.00,22.00,22.00,66.00,66.00
 | 
				
			||||||
 | 
					60.00,51.00,22.00,22.00,71.00,62.00
 | 
				
			||||||
 | 
					69.00,55.00,22.00,22.00,80.00,66.00
 | 
				
			||||||
 | 
					74.00,60.00,22.00,22.00,85.00,71.00
 | 
				
			||||||
 | 
					96.00,74.00,22.00,22.00,107.00,85.00
 | 
				
			||||||
 | 
					101.00,74.00,22.00,22.00,112.00,85.00
 | 
				
			||||||
 | 
					106.00,74.00,22.00,22.00,117.00,85.00
 | 
				
			||||||
 | 
					101.00,69.00,22.00,22.00,112.00,80.00
 | 
				
			||||||
 | 
					106.00,69.00,22.00,22.00,117.00,80.00
 | 
				
			||||||
 | 
					110.00,74.00,22.00,22.00,121.00,85.00
 | 
				
			||||||
 | 
					119.00,64.00,22.00,22.00,130.00,75.00
 | 
				
			||||||
 | 
					115.00,60.00,22.00,22.00,126.00,71.00
 | 
				
			||||||
 | 
					106.00,55.00,22.00,22.00,117.00,66.00
 | 
				
			||||||
 | 
					101.00,55.00,22.00,22.00,112.00,66.00
 | 
				
			||||||
 | 
					92.00,55.00,22.00,22.00,103.00,66.00
 | 
				
			||||||
 | 
					87.00,110.00,22.00,22.00,98.00,121.00
 | 
				
			||||||
 | 
					78.00,110.00,22.00,22.00,89.00,121.00
 | 
				
			||||||
 | 
					74.00,115.00,22.00,22.00,85.00,126.00
 | 
				
			||||||
 | 
					69.00,119.00,22.00,22.00,80.00,130.00
 | 
				
			||||||
 | 
					69.00,119.00,22.00,22.00,80.00,130.00
 | 
				
			||||||
 | 
					78.00,115.00,22.00,22.00,89.00,126.00
 | 
				
			||||||
 | 
					87.00,115.00,22.00,22.00,98.00,126.00
 | 
				
			||||||
 | 
					96.00,110.00,22.00,22.00,107.00,121.00
 | 
				
			||||||
 | 
					101.00,115.00,22.00,22.00,112.00,126.00
 | 
				
			||||||
 | 
					106.00,119.00,22.00,22.00,117.00,130.00
 | 
				
			||||||
 | 
					101.00,115.00,22.00,22.00,112.00,126.00
 | 
				
			||||||
 | 
					101.00,115.00,22.00,22.00,112.00,126.00
 | 
				
			||||||
 | 
					101.00,124.00,22.00,22.00,112.00,135.00
 | 
				
			||||||
 | 
					96.00,119.00,22.00,22.00,107.00,130.00
 | 
				
			||||||
 | 
					96.00,129.00,22.00,22.00,107.00,140.00
 | 
				
			||||||
 | 
					87.00,124.00,22.00,22.00,98.00,135.00
 | 
				
			||||||
 | 
					78.00,129.00,22.00,22.00,89.00,140.00
 | 
				
			||||||
 | 
					78.00,119.00,22.00,22.00,89.00,130.00
 | 
				
			||||||
 | 
					74.00,124.00,22.00,22.00,85.00,135.00
 | 
				
			||||||
 | 
					87.00,133.00,22.00,22.00,98.00,144.00
 | 
				
			||||||
 | 
					87.00,156.00,22.00,22.00,98.00,167.00
 | 
				
			||||||
 | 
					78.00,152.00,22.00,22.00,89.00,163.00
 | 
				
			||||||
 | 
					69.00,147.00,22.00,22.00,80.00,158.00
 | 
				
			||||||
 | 
					60.00,138.00,22.00,22.00,71.00,149.00
 | 
				
			||||||
 | 
					55.00,129.00,22.00,22.00,66.00,140.00
 | 
				
			||||||
 | 
					51.00,115.00,22.00,22.00,62.00,126.00
 | 
				
			||||||
 | 
					51.00,106.00,22.00,22.00,62.00,117.00
 | 
				
			||||||
 | 
					46.00,92.00,22.00,22.00,57.00,103.00
 | 
				
			||||||
 | 
					46.00,83.00,22.00,22.00,57.00,94.00
 | 
				
			||||||
 | 
					101.00,152.00,22.00,22.00,112.00,163.00
 | 
				
			||||||
 | 
					110.00,142.00,22.00,22.00,121.00,153.00
 | 
				
			||||||
 | 
					119.00,133.00,22.00,22.00,130.00,144.00
 | 
				
			||||||
 | 
					124.00,119.00,22.00,22.00,135.00,130.00
 | 
				
			||||||
 | 
					129.00,110.00,22.00,22.00,140.00,121.00
 | 
				
			||||||
 | 
					133.00,101.00,22.00,22.00,144.00,112.00
 | 
				
			||||||
 | 
					138.00,87.00,22.00,22.00,149.00,98.00
 | 
				
			||||||
 | 
					138.00,74.00,22.00,22.00,149.00,85.00
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 12 KiB  | 
| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					x,y,dx,dy,score,angle,type
 | 
				
			||||||
 | 
					69,69,109,109,306,0.0,haar
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,70 @@
 | 
				
			||||||
 | 
					130,0
 | 
				
			||||||
 | 
					x,y,dx,dy,x_c,y_c
 | 
				
			||||||
 | 
					68.00,92.00,19.00,19.00,77.50,101.50
 | 
				
			||||||
 | 
					68.00,92.00,19.00,19.00,77.50,101.50
 | 
				
			||||||
 | 
					68.00,88.00,19.00,19.00,77.50,97.50
 | 
				
			||||||
 | 
					72.00,92.00,19.00,19.00,81.50,101.50
 | 
				
			||||||
 | 
					76.00,92.00,19.00,19.00,85.50,101.50
 | 
				
			||||||
 | 
					68.00,84.00,19.00,19.00,77.50,93.50
 | 
				
			||||||
 | 
					68.00,76.00,19.00,19.00,77.50,85.50
 | 
				
			||||||
 | 
					68.00,68.00,19.00,19.00,77.50,77.50
 | 
				
			||||||
 | 
					72.00,60.00,19.00,19.00,81.50,69.50
 | 
				
			||||||
 | 
					60.00,60.00,19.00,19.00,69.50,69.50
 | 
				
			||||||
 | 
					56.00,60.00,19.00,19.00,65.50,69.50
 | 
				
			||||||
 | 
					52.00,60.00,19.00,19.00,61.50,69.50
 | 
				
			||||||
 | 
					56.00,56.00,19.00,19.00,65.50,65.50
 | 
				
			||||||
 | 
					52.00,56.00,19.00,19.00,61.50,65.50
 | 
				
			||||||
 | 
					48.00,56.00,19.00,19.00,57.50,65.50
 | 
				
			||||||
 | 
					40.00,48.00,19.00,19.00,49.50,57.50
 | 
				
			||||||
 | 
					44.00,44.00,19.00,19.00,53.50,53.50
 | 
				
			||||||
 | 
					48.00,40.00,19.00,19.00,57.50,49.50
 | 
				
			||||||
 | 
					56.00,44.00,19.00,19.00,65.50,53.50
 | 
				
			||||||
 | 
					60.00,48.00,19.00,19.00,69.50,57.50
 | 
				
			||||||
 | 
					84.00,64.00,19.00,19.00,93.50,73.50
 | 
				
			||||||
 | 
					88.00,64.00,19.00,19.00,97.50,73.50
 | 
				
			||||||
 | 
					92.00,64.00,19.00,19.00,101.50,73.50
 | 
				
			||||||
 | 
					88.00,60.00,19.00,19.00,97.50,69.50
 | 
				
			||||||
 | 
					92.00,60.00,19.00,19.00,101.50,69.50
 | 
				
			||||||
 | 
					96.00,64.00,19.00,19.00,105.50,73.50
 | 
				
			||||||
 | 
					104.00,56.00,19.00,19.00,113.50,65.50
 | 
				
			||||||
 | 
					100.00,52.00,19.00,19.00,109.50,61.50
 | 
				
			||||||
 | 
					92.00,52.00,19.00,19.00,101.50,61.50
 | 
				
			||||||
 | 
					84.00,52.00,19.00,19.00,93.50,61.50
 | 
				
			||||||
 | 
					76.00,52.00,19.00,19.00,85.50,61.50
 | 
				
			||||||
 | 
					68.00,96.00,19.00,19.00,77.50,105.50
 | 
				
			||||||
 | 
					64.00,96.00,19.00,19.00,73.50,105.50
 | 
				
			||||||
 | 
					60.00,96.00,19.00,19.00,69.50,105.50
 | 
				
			||||||
 | 
					56.00,100.00,19.00,19.00,65.50,109.50
 | 
				
			||||||
 | 
					56.00,100.00,19.00,19.00,65.50,109.50
 | 
				
			||||||
 | 
					64.00,100.00,19.00,19.00,73.50,109.50
 | 
				
			||||||
 | 
					68.00,100.00,19.00,19.00,77.50,109.50
 | 
				
			||||||
 | 
					76.00,96.00,19.00,19.00,85.50,105.50
 | 
				
			||||||
 | 
					80.00,100.00,19.00,19.00,89.50,109.50
 | 
				
			||||||
 | 
					84.00,100.00,19.00,19.00,93.50,109.50
 | 
				
			||||||
 | 
					80.00,100.00,19.00,19.00,89.50,109.50
 | 
				
			||||||
 | 
					80.00,100.00,19.00,19.00,89.50,109.50
 | 
				
			||||||
 | 
					80.00,104.00,19.00,19.00,89.50,113.50
 | 
				
			||||||
 | 
					76.00,100.00,19.00,19.00,85.50,109.50
 | 
				
			||||||
 | 
					76.00,108.00,19.00,19.00,85.50,117.50
 | 
				
			||||||
 | 
					68.00,104.00,19.00,19.00,77.50,113.50
 | 
				
			||||||
 | 
					60.00,108.00,19.00,19.00,69.50,117.50
 | 
				
			||||||
 | 
					60.00,100.00,19.00,19.00,69.50,109.50
 | 
				
			||||||
 | 
					56.00,104.00,19.00,19.00,65.50,113.50
 | 
				
			||||||
 | 
					68.00,108.00,19.00,19.00,77.50,117.50
 | 
				
			||||||
 | 
					72.00,128.00,19.00,19.00,81.50,137.50
 | 
				
			||||||
 | 
					60.00,124.00,19.00,19.00,69.50,133.50
 | 
				
			||||||
 | 
					52.00,120.00,19.00,19.00,61.50,129.50
 | 
				
			||||||
 | 
					44.00,112.00,19.00,19.00,53.50,121.50
 | 
				
			||||||
 | 
					40.00,104.00,19.00,19.00,49.50,113.50
 | 
				
			||||||
 | 
					36.00,92.00,19.00,19.00,45.50,101.50
 | 
				
			||||||
 | 
					32.00,80.00,19.00,19.00,41.50,89.50
 | 
				
			||||||
 | 
					28.00,68.00,19.00,19.00,37.50,77.50
 | 
				
			||||||
 | 
					28.00,56.00,19.00,19.00,37.50,65.50
 | 
				
			||||||
 | 
					80.00,124.00,19.00,19.00,89.50,133.50
 | 
				
			||||||
 | 
					88.00,120.00,19.00,19.00,97.50,129.50
 | 
				
			||||||
 | 
					96.00,112.00,19.00,19.00,105.50,121.50
 | 
				
			||||||
 | 
					100.00,96.00,19.00,19.00,109.50,105.50
 | 
				
			||||||
 | 
					104.00,84.00,19.00,19.00,113.50,93.50
 | 
				
			||||||
 | 
					108.00,76.00,19.00,19.00,117.50,85.50
 | 
				
			||||||
 | 
					116.00,64.00,19.00,19.00,125.50,73.50
 | 
				
			||||||
 | 
					116.00,56.00,19.00,19.00,125.50,65.50
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 11 KiB  | 
| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					x,y,dx,dy,score,angle,type
 | 
				
			||||||
 | 
					254,40,107,107,345,0.0,haar
 | 
				
			||||||
 | 
					159,61,87,86,90,22.0,haar
 | 
				
			||||||
 | 
					20,63,82,82,117,0.0,lbp
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,70 @@
 | 
				
			||||||
 | 
					157,0
 | 
				
			||||||
 | 
					x,y,dx,dy,x_c,y_c
 | 
				
			||||||
 | 
					68.00,88.00,19.00,19.00,77.50,97.50
 | 
				
			||||||
 | 
					64.00,88.00,19.00,19.00,73.50,97.50
 | 
				
			||||||
 | 
					64.00,88.00,19.00,19.00,73.50,97.50
 | 
				
			||||||
 | 
					76.00,84.00,19.00,19.00,85.50,93.50
 | 
				
			||||||
 | 
					80.00,84.00,19.00,19.00,89.50,93.50
 | 
				
			||||||
 | 
					68.00,80.00,19.00,19.00,77.50,89.50
 | 
				
			||||||
 | 
					68.00,76.00,19.00,19.00,77.50,85.50
 | 
				
			||||||
 | 
					68.00,68.00,19.00,19.00,77.50,77.50
 | 
				
			||||||
 | 
					68.00,60.00,19.00,19.00,77.50,69.50
 | 
				
			||||||
 | 
					56.00,60.00,19.00,19.00,65.50,69.50
 | 
				
			||||||
 | 
					56.00,64.00,19.00,19.00,65.50,73.50
 | 
				
			||||||
 | 
					52.00,64.00,19.00,19.00,61.50,73.50
 | 
				
			||||||
 | 
					52.00,60.00,19.00,19.00,61.50,69.50
 | 
				
			||||||
 | 
					52.00,60.00,19.00,19.00,61.50,69.50
 | 
				
			||||||
 | 
					48.00,64.00,19.00,19.00,57.50,73.50
 | 
				
			||||||
 | 
					36.00,56.00,19.00,19.00,45.50,65.50
 | 
				
			||||||
 | 
					40.00,52.00,19.00,19.00,49.50,61.50
 | 
				
			||||||
 | 
					48.00,48.00,19.00,19.00,57.50,57.50
 | 
				
			||||||
 | 
					56.00,48.00,19.00,19.00,65.50,57.50
 | 
				
			||||||
 | 
					64.00,48.00,19.00,19.00,73.50,57.50
 | 
				
			||||||
 | 
					80.00,56.00,19.00,19.00,89.50,65.50
 | 
				
			||||||
 | 
					84.00,56.00,19.00,19.00,93.50,65.50
 | 
				
			||||||
 | 
					88.00,56.00,19.00,19.00,97.50,65.50
 | 
				
			||||||
 | 
					84.00,52.00,19.00,19.00,93.50,61.50
 | 
				
			||||||
 | 
					88.00,52.00,19.00,19.00,97.50,61.50
 | 
				
			||||||
 | 
					92.00,52.00,19.00,19.00,101.50,61.50
 | 
				
			||||||
 | 
					104.00,44.00,19.00,19.00,113.50,53.50
 | 
				
			||||||
 | 
					104.00,40.00,19.00,19.00,113.50,49.50
 | 
				
			||||||
 | 
					96.00,40.00,19.00,19.00,105.50,49.50
 | 
				
			||||||
 | 
					88.00,44.00,19.00,19.00,97.50,53.50
 | 
				
			||||||
 | 
					80.00,48.00,19.00,19.00,89.50,57.50
 | 
				
			||||||
 | 
					68.00,92.00,19.00,19.00,77.50,101.50
 | 
				
			||||||
 | 
					64.00,92.00,19.00,19.00,73.50,101.50
 | 
				
			||||||
 | 
					60.00,96.00,19.00,19.00,69.50,105.50
 | 
				
			||||||
 | 
					56.00,100.00,19.00,19.00,65.50,109.50
 | 
				
			||||||
 | 
					60.00,100.00,19.00,19.00,69.50,109.50
 | 
				
			||||||
 | 
					64.00,96.00,19.00,19.00,73.50,105.50
 | 
				
			||||||
 | 
					68.00,96.00,19.00,19.00,77.50,105.50
 | 
				
			||||||
 | 
					76.00,88.00,19.00,19.00,85.50,97.50
 | 
				
			||||||
 | 
					84.00,92.00,19.00,19.00,93.50,101.50
 | 
				
			||||||
 | 
					88.00,96.00,19.00,19.00,97.50,105.50
 | 
				
			||||||
 | 
					88.00,92.00,19.00,19.00,97.50,101.50
 | 
				
			||||||
 | 
					80.00,92.00,19.00,19.00,89.50,101.50
 | 
				
			||||||
 | 
					88.00,108.00,19.00,19.00,97.50,117.50
 | 
				
			||||||
 | 
					84.00,104.00,19.00,19.00,93.50,113.50
 | 
				
			||||||
 | 
					84.00,112.00,19.00,19.00,93.50,121.50
 | 
				
			||||||
 | 
					76.00,108.00,19.00,19.00,85.50,117.50
 | 
				
			||||||
 | 
					68.00,112.00,19.00,19.00,77.50,121.50
 | 
				
			||||||
 | 
					68.00,104.00,19.00,19.00,77.50,113.50
 | 
				
			||||||
 | 
					64.00,104.00,19.00,19.00,73.50,113.50
 | 
				
			||||||
 | 
					76.00,112.00,19.00,19.00,85.50,121.50
 | 
				
			||||||
 | 
					76.00,128.00,19.00,19.00,85.50,137.50
 | 
				
			||||||
 | 
					68.00,124.00,19.00,19.00,77.50,133.50
 | 
				
			||||||
 | 
					60.00,116.00,19.00,19.00,69.50,125.50
 | 
				
			||||||
 | 
					52.00,108.00,19.00,19.00,61.50,117.50
 | 
				
			||||||
 | 
					44.00,104.00,19.00,19.00,53.50,113.50
 | 
				
			||||||
 | 
					40.00,96.00,19.00,19.00,49.50,105.50
 | 
				
			||||||
 | 
					40.00,88.00,19.00,19.00,49.50,97.50
 | 
				
			||||||
 | 
					36.00,76.00,19.00,19.00,45.50,85.50
 | 
				
			||||||
 | 
					36.00,68.00,19.00,19.00,45.50,77.50
 | 
				
			||||||
 | 
					84.00,124.00,19.00,19.00,93.50,133.50
 | 
				
			||||||
 | 
					92.00,116.00,19.00,19.00,101.50,125.50
 | 
				
			||||||
 | 
					100.00,112.00,19.00,19.00,109.50,121.50
 | 
				
			||||||
 | 
					104.00,100.00,19.00,19.00,113.50,109.50
 | 
				
			||||||
 | 
					108.00,92.00,19.00,19.00,117.50,101.50
 | 
				
			||||||
 | 
					112.00,80.00,19.00,19.00,121.50,89.50
 | 
				
			||||||
 | 
					112.00,68.00,19.00,19.00,121.50,77.50
 | 
				
			||||||
 | 
					112.00,60.00,19.00,19.00,121.50,69.50
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 11 KiB  | 
| 
						 | 
					@ -0,0 +1,70 @@
 | 
				
			||||||
 | 
					67,0
 | 
				
			||||||
 | 
					x,y,dx,dy,x_c,y_c
 | 
				
			||||||
 | 
					60.00,76.00,19.00,19.00,69.50,85.50
 | 
				
			||||||
 | 
					60.00,76.00,19.00,19.00,69.50,85.50
 | 
				
			||||||
 | 
					56.00,72.00,19.00,19.00,65.50,81.50
 | 
				
			||||||
 | 
					64.00,72.00,19.00,19.00,73.50,81.50
 | 
				
			||||||
 | 
					64.00,72.00,19.00,19.00,73.50,81.50
 | 
				
			||||||
 | 
					60.00,68.00,19.00,19.00,69.50,77.50
 | 
				
			||||||
 | 
					60.00,64.00,19.00,19.00,69.50,73.50
 | 
				
			||||||
 | 
					56.00,56.00,19.00,19.00,65.50,65.50
 | 
				
			||||||
 | 
					56.00,52.00,19.00,19.00,65.50,61.50
 | 
				
			||||||
 | 
					44.00,52.00,19.00,19.00,53.50,61.50
 | 
				
			||||||
 | 
					40.00,56.00,19.00,19.00,49.50,65.50
 | 
				
			||||||
 | 
					36.00,56.00,19.00,19.00,45.50,65.50
 | 
				
			||||||
 | 
					40.00,52.00,19.00,19.00,49.50,61.50
 | 
				
			||||||
 | 
					40.00,52.00,19.00,19.00,49.50,61.50
 | 
				
			||||||
 | 
					36.00,56.00,19.00,19.00,45.50,65.50
 | 
				
			||||||
 | 
					24.00,48.00,19.00,19.00,33.50,57.50
 | 
				
			||||||
 | 
					28.00,44.00,19.00,19.00,37.50,53.50
 | 
				
			||||||
 | 
					32.00,40.00,19.00,19.00,41.50,49.50
 | 
				
			||||||
 | 
					40.00,44.00,19.00,19.00,49.50,53.50
 | 
				
			||||||
 | 
					48.00,44.00,19.00,19.00,57.50,53.50
 | 
				
			||||||
 | 
					64.00,48.00,19.00,19.00,73.50,57.50
 | 
				
			||||||
 | 
					68.00,48.00,19.00,19.00,77.50,57.50
 | 
				
			||||||
 | 
					72.00,48.00,19.00,19.00,81.50,57.50
 | 
				
			||||||
 | 
					68.00,44.00,19.00,19.00,77.50,53.50
 | 
				
			||||||
 | 
					72.00,44.00,19.00,19.00,81.50,53.50
 | 
				
			||||||
 | 
					76.00,44.00,19.00,19.00,85.50,53.50
 | 
				
			||||||
 | 
					84.00,36.00,19.00,19.00,93.50,45.50
 | 
				
			||||||
 | 
					80.00,32.00,19.00,19.00,89.50,41.50
 | 
				
			||||||
 | 
					72.00,28.00,19.00,19.00,81.50,37.50
 | 
				
			||||||
 | 
					68.00,28.00,19.00,19.00,77.50,37.50
 | 
				
			||||||
 | 
					64.00,32.00,19.00,19.00,73.50,41.50
 | 
				
			||||||
 | 
					60.00,80.00,19.00,19.00,69.50,89.50
 | 
				
			||||||
 | 
					56.00,84.00,19.00,19.00,65.50,93.50
 | 
				
			||||||
 | 
					52.00,88.00,19.00,19.00,61.50,97.50
 | 
				
			||||||
 | 
					52.00,96.00,19.00,19.00,61.50,105.50
 | 
				
			||||||
 | 
					52.00,96.00,19.00,19.00,61.50,105.50
 | 
				
			||||||
 | 
					56.00,84.00,19.00,19.00,65.50,93.50
 | 
				
			||||||
 | 
					60.00,84.00,19.00,19.00,69.50,93.50
 | 
				
			||||||
 | 
					68.00,80.00,19.00,19.00,77.50,89.50
 | 
				
			||||||
 | 
					72.00,80.00,19.00,19.00,81.50,89.50
 | 
				
			||||||
 | 
					76.00,80.00,19.00,19.00,85.50,89.50
 | 
				
			||||||
 | 
					72.00,84.00,19.00,19.00,81.50,93.50
 | 
				
			||||||
 | 
					68.00,84.00,19.00,19.00,77.50,93.50
 | 
				
			||||||
 | 
					76.00,84.00,19.00,19.00,85.50,93.50
 | 
				
			||||||
 | 
					72.00,80.00,19.00,19.00,81.50,89.50
 | 
				
			||||||
 | 
					72.00,84.00,19.00,19.00,81.50,93.50
 | 
				
			||||||
 | 
					64.00,80.00,19.00,19.00,73.50,89.50
 | 
				
			||||||
 | 
					56.00,84.00,19.00,19.00,65.50,93.50
 | 
				
			||||||
 | 
					56.00,76.00,19.00,19.00,65.50,85.50
 | 
				
			||||||
 | 
					52.00,76.00,19.00,19.00,61.50,85.50
 | 
				
			||||||
 | 
					64.00,84.00,19.00,19.00,73.50,93.50
 | 
				
			||||||
 | 
					64.00,100.00,19.00,19.00,73.50,109.50
 | 
				
			||||||
 | 
					56.00,96.00,19.00,19.00,65.50,105.50
 | 
				
			||||||
 | 
					48.00,88.00,19.00,19.00,57.50,97.50
 | 
				
			||||||
 | 
					40.00,76.00,19.00,19.00,49.50,85.50
 | 
				
			||||||
 | 
					32.00,68.00,19.00,19.00,41.50,77.50
 | 
				
			||||||
 | 
					28.00,56.00,19.00,19.00,37.50,65.50
 | 
				
			||||||
 | 
					24.00,44.00,19.00,19.00,33.50,53.50
 | 
				
			||||||
 | 
					20.00,32.00,19.00,19.00,29.50,41.50
 | 
				
			||||||
 | 
					20.00,20.00,19.00,19.00,29.50,29.50
 | 
				
			||||||
 | 
					72.00,96.00,19.00,19.00,81.50,105.50
 | 
				
			||||||
 | 
					80.00,88.00,19.00,19.00,89.50,97.50
 | 
				
			||||||
 | 
					84.00,80.00,19.00,19.00,93.50,89.50
 | 
				
			||||||
 | 
					88.00,68.00,19.00,19.00,97.50,77.50
 | 
				
			||||||
 | 
					88.00,56.00,19.00,19.00,97.50,65.50
 | 
				
			||||||
 | 
					92.00,44.00,19.00,19.00,101.50,53.50
 | 
				
			||||||
 | 
					92.00,32.00,19.00,19.00,101.50,41.50
 | 
				
			||||||
 | 
					92.00,20.00,19.00,19.00,101.50,29.50
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 8.1 KiB  | 
| 
						 | 
					@ -0,0 +1,70 @@
 | 
				
			||||||
 | 
					145,0
 | 
				
			||||||
 | 
					x,y,dx,dy,x_c,y_c
 | 
				
			||||||
 | 
					48.00,64.00,19.00,19.00,57.50,73.50
 | 
				
			||||||
 | 
					48.00,64.00,19.00,19.00,57.50,73.50
 | 
				
			||||||
 | 
					44.00,64.00,19.00,19.00,53.50,73.50
 | 
				
			||||||
 | 
					52.00,64.00,19.00,19.00,61.50,73.50
 | 
				
			||||||
 | 
					56.00,64.00,19.00,19.00,65.50,73.50
 | 
				
			||||||
 | 
					48.00,60.00,19.00,19.00,57.50,69.50
 | 
				
			||||||
 | 
					48.00,56.00,19.00,19.00,57.50,65.50
 | 
				
			||||||
 | 
					48.00,48.00,19.00,19.00,57.50,57.50
 | 
				
			||||||
 | 
					52.00,44.00,19.00,19.00,61.50,53.50
 | 
				
			||||||
 | 
					40.00,44.00,19.00,19.00,49.50,53.50
 | 
				
			||||||
 | 
					36.00,44.00,19.00,19.00,45.50,53.50
 | 
				
			||||||
 | 
					36.00,44.00,19.00,19.00,45.50,53.50
 | 
				
			||||||
 | 
					36.00,40.00,19.00,19.00,45.50,49.50
 | 
				
			||||||
 | 
					36.00,40.00,19.00,19.00,45.50,49.50
 | 
				
			||||||
 | 
					32.00,44.00,19.00,19.00,41.50,53.50
 | 
				
			||||||
 | 
					20.00,36.00,19.00,19.00,29.50,45.50
 | 
				
			||||||
 | 
					24.00,32.00,19.00,19.00,33.50,41.50
 | 
				
			||||||
 | 
					28.00,28.00,19.00,19.00,37.50,37.50
 | 
				
			||||||
 | 
					36.00,32.00,19.00,19.00,45.50,41.50
 | 
				
			||||||
 | 
					40.00,36.00,19.00,19.00,49.50,45.50
 | 
				
			||||||
 | 
					60.00,44.00,19.00,19.00,69.50,53.50
 | 
				
			||||||
 | 
					64.00,44.00,19.00,19.00,73.50,53.50
 | 
				
			||||||
 | 
					64.00,44.00,19.00,19.00,73.50,53.50
 | 
				
			||||||
 | 
					64.00,40.00,19.00,19.00,73.50,49.50
 | 
				
			||||||
 | 
					64.00,40.00,19.00,19.00,73.50,49.50
 | 
				
			||||||
 | 
					68.00,44.00,19.00,19.00,77.50,53.50
 | 
				
			||||||
 | 
					76.00,36.00,19.00,19.00,85.50,45.50
 | 
				
			||||||
 | 
					72.00,36.00,19.00,19.00,81.50,45.50
 | 
				
			||||||
 | 
					68.00,32.00,19.00,19.00,77.50,41.50
 | 
				
			||||||
 | 
					64.00,32.00,19.00,19.00,73.50,41.50
 | 
				
			||||||
 | 
					56.00,32.00,19.00,19.00,65.50,41.50
 | 
				
			||||||
 | 
					48.00,68.00,19.00,19.00,57.50,77.50
 | 
				
			||||||
 | 
					44.00,64.00,19.00,19.00,53.50,73.50
 | 
				
			||||||
 | 
					40.00,68.00,19.00,19.00,49.50,77.50
 | 
				
			||||||
 | 
					36.00,72.00,19.00,19.00,45.50,81.50
 | 
				
			||||||
 | 
					36.00,72.00,19.00,19.00,45.50,81.50
 | 
				
			||||||
 | 
					44.00,68.00,19.00,19.00,53.50,77.50
 | 
				
			||||||
 | 
					48.00,72.00,19.00,19.00,57.50,81.50
 | 
				
			||||||
 | 
					56.00,64.00,19.00,19.00,65.50,73.50
 | 
				
			||||||
 | 
					60.00,68.00,19.00,19.00,69.50,77.50
 | 
				
			||||||
 | 
					64.00,68.00,19.00,19.00,73.50,77.50
 | 
				
			||||||
 | 
					60.00,72.00,19.00,19.00,69.50,81.50
 | 
				
			||||||
 | 
					60.00,68.00,19.00,19.00,69.50,77.50
 | 
				
			||||||
 | 
					60.00,76.00,19.00,19.00,69.50,85.50
 | 
				
			||||||
 | 
					56.00,72.00,19.00,19.00,65.50,81.50
 | 
				
			||||||
 | 
					56.00,80.00,19.00,19.00,65.50,89.50
 | 
				
			||||||
 | 
					48.00,76.00,19.00,19.00,57.50,85.50
 | 
				
			||||||
 | 
					40.00,76.00,19.00,19.00,49.50,85.50
 | 
				
			||||||
 | 
					40.00,68.00,19.00,19.00,49.50,77.50
 | 
				
			||||||
 | 
					36.00,72.00,19.00,19.00,45.50,81.50
 | 
				
			||||||
 | 
					48.00,80.00,19.00,19.00,57.50,89.50
 | 
				
			||||||
 | 
					48.00,100.00,19.00,19.00,57.50,109.50
 | 
				
			||||||
 | 
					40.00,96.00,19.00,19.00,49.50,105.50
 | 
				
			||||||
 | 
					36.00,92.00,19.00,19.00,45.50,101.50
 | 
				
			||||||
 | 
					28.00,84.00,19.00,19.00,37.50,93.50
 | 
				
			||||||
 | 
					24.00,80.00,19.00,19.00,33.50,89.50
 | 
				
			||||||
 | 
					20.00,72.00,19.00,19.00,29.50,81.50
 | 
				
			||||||
 | 
					20.00,64.00,19.00,19.00,29.50,73.50
 | 
				
			||||||
 | 
					16.00,52.00,19.00,19.00,25.50,61.50
 | 
				
			||||||
 | 
					16.00,44.00,19.00,19.00,25.50,53.50
 | 
				
			||||||
 | 
					56.00,96.00,19.00,19.00,65.50,105.50
 | 
				
			||||||
 | 
					64.00,88.00,19.00,19.00,73.50,97.50
 | 
				
			||||||
 | 
					72.00,84.00,19.00,19.00,81.50,93.50
 | 
				
			||||||
 | 
					76.00,76.00,19.00,19.00,85.50,85.50
 | 
				
			||||||
 | 
					76.00,68.00,19.00,19.00,85.50,77.50
 | 
				
			||||||
 | 
					76.00,60.00,19.00,19.00,85.50,69.50
 | 
				
			||||||
 | 
					80.00,48.00,19.00,19.00,89.50,57.50
 | 
				
			||||||
 | 
					80.00,40.00,19.00,19.00,89.50,49.50
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 8.3 KiB  | 
| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					x,y,dx,dy,score,angle,type
 | 
				
			||||||
 | 
					327,101,121,121,154,0.0,lbp
 | 
				
			||||||
 | 
					237,106,113,113,139,0.0,lbp
 | 
				
			||||||
 | 
					164,49,91,91,49,0.0,lbp
 | 
				
			||||||
 | 
					434,86,94,94,95,0.0,lbp
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,70 @@
 | 
				
			||||||
 | 
					119,0
 | 
				
			||||||
 | 
					x,y,dx,dy,x_c,y_c
 | 
				
			||||||
 | 
					74.00,84.00,25.00,25.00,86.50,96.50
 | 
				
			||||||
 | 
					69.00,84.00,25.00,25.00,81.50,96.50
 | 
				
			||||||
 | 
					69.00,79.00,25.00,25.00,81.50,91.50
 | 
				
			||||||
 | 
					79.00,79.00,25.00,25.00,91.50,91.50
 | 
				
			||||||
 | 
					84.00,79.00,25.00,25.00,96.50,91.50
 | 
				
			||||||
 | 
					74.00,79.00,25.00,25.00,86.50,91.50
 | 
				
			||||||
 | 
					74.00,74.00,25.00,25.00,86.50,86.50
 | 
				
			||||||
 | 
					74.00,69.00,25.00,25.00,86.50,81.50
 | 
				
			||||||
 | 
					74.00,63.00,25.00,25.00,86.50,75.50
 | 
				
			||||||
 | 
					58.00,63.00,25.00,25.00,70.50,75.50
 | 
				
			||||||
 | 
					58.00,63.00,25.00,25.00,70.50,75.50
 | 
				
			||||||
 | 
					53.00,69.00,25.00,25.00,65.50,81.50
 | 
				
			||||||
 | 
					53.00,63.00,25.00,25.00,65.50,75.50
 | 
				
			||||||
 | 
					53.00,63.00,25.00,25.00,65.50,75.50
 | 
				
			||||||
 | 
					48.00,69.00,25.00,25.00,60.50,81.50
 | 
				
			||||||
 | 
					37.00,58.00,25.00,25.00,49.50,70.50
 | 
				
			||||||
 | 
					42.00,53.00,25.00,25.00,54.50,65.50
 | 
				
			||||||
 | 
					48.00,48.00,25.00,25.00,60.50,60.50
 | 
				
			||||||
 | 
					58.00,53.00,25.00,25.00,70.50,65.50
 | 
				
			||||||
 | 
					63.00,53.00,25.00,25.00,75.50,65.50
 | 
				
			||||||
 | 
					90.00,63.00,25.00,25.00,102.50,75.50
 | 
				
			||||||
 | 
					95.00,63.00,25.00,25.00,107.50,75.50
 | 
				
			||||||
 | 
					100.00,63.00,25.00,25.00,112.50,75.50
 | 
				
			||||||
 | 
					95.00,63.00,25.00,25.00,107.50,75.50
 | 
				
			||||||
 | 
					100.00,63.00,25.00,25.00,112.50,75.50
 | 
				
			||||||
 | 
					106.00,63.00,25.00,25.00,118.50,75.50
 | 
				
			||||||
 | 
					116.00,53.00,25.00,25.00,128.50,65.50
 | 
				
			||||||
 | 
					111.00,53.00,25.00,25.00,123.50,65.50
 | 
				
			||||||
 | 
					100.00,48.00,25.00,25.00,112.50,60.50
 | 
				
			||||||
 | 
					95.00,48.00,25.00,25.00,107.50,60.50
 | 
				
			||||||
 | 
					84.00,53.00,25.00,25.00,96.50,65.50
 | 
				
			||||||
 | 
					74.00,90.00,25.00,25.00,86.50,102.50
 | 
				
			||||||
 | 
					63.00,90.00,25.00,25.00,75.50,102.50
 | 
				
			||||||
 | 
					58.00,95.00,25.00,25.00,70.50,107.50
 | 
				
			||||||
 | 
					53.00,100.00,25.00,25.00,65.50,112.50
 | 
				
			||||||
 | 
					58.00,106.00,25.00,25.00,70.50,118.50
 | 
				
			||||||
 | 
					63.00,95.00,25.00,25.00,75.50,107.50
 | 
				
			||||||
 | 
					74.00,90.00,25.00,25.00,86.50,102.50
 | 
				
			||||||
 | 
					84.00,90.00,25.00,25.00,96.50,102.50
 | 
				
			||||||
 | 
					95.00,95.00,25.00,25.00,107.50,107.50
 | 
				
			||||||
 | 
					100.00,100.00,25.00,25.00,112.50,112.50
 | 
				
			||||||
 | 
					100.00,100.00,25.00,25.00,112.50,112.50
 | 
				
			||||||
 | 
					84.00,95.00,25.00,25.00,96.50,107.50
 | 
				
			||||||
 | 
					95.00,111.00,25.00,25.00,107.50,123.50
 | 
				
			||||||
 | 
					90.00,106.00,25.00,25.00,102.50,118.50
 | 
				
			||||||
 | 
					90.00,116.00,25.00,25.00,102.50,128.50
 | 
				
			||||||
 | 
					79.00,111.00,25.00,25.00,91.50,123.50
 | 
				
			||||||
 | 
					63.00,116.00,25.00,25.00,75.50,128.50
 | 
				
			||||||
 | 
					63.00,106.00,25.00,25.00,75.50,118.50
 | 
				
			||||||
 | 
					58.00,111.00,25.00,25.00,70.50,123.50
 | 
				
			||||||
 | 
					79.00,116.00,25.00,25.00,91.50,128.50
 | 
				
			||||||
 | 
					84.00,143.00,25.00,25.00,96.50,155.50
 | 
				
			||||||
 | 
					69.00,137.00,25.00,25.00,81.50,149.50
 | 
				
			||||||
 | 
					58.00,127.00,25.00,25.00,70.50,139.50
 | 
				
			||||||
 | 
					48.00,116.00,25.00,25.00,60.50,128.50
 | 
				
			||||||
 | 
					42.00,100.00,25.00,25.00,54.50,112.50
 | 
				
			||||||
 | 
					37.00,84.00,25.00,25.00,49.50,96.50
 | 
				
			||||||
 | 
					32.00,69.00,25.00,25.00,44.50,81.50
 | 
				
			||||||
 | 
					26.00,48.00,25.00,25.00,38.50,60.50
 | 
				
			||||||
 | 
					26.00,32.00,25.00,25.00,38.50,44.50
 | 
				
			||||||
 | 
					95.00,137.00,25.00,25.00,107.50,149.50
 | 
				
			||||||
 | 
					106.00,127.00,25.00,25.00,118.50,139.50
 | 
				
			||||||
 | 
					116.00,121.00,25.00,25.00,128.50,133.50
 | 
				
			||||||
 | 
					121.00,111.00,25.00,25.00,133.50,123.50
 | 
				
			||||||
 | 
					127.00,100.00,25.00,25.00,139.50,112.50
 | 
				
			||||||
 | 
					132.00,90.00,25.00,25.00,144.50,102.50
 | 
				
			||||||
 | 
					132.00,74.00,25.00,25.00,144.50,86.50
 | 
				
			||||||
 | 
					132.00,58.00,25.00,25.00,144.50,70.50
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 18 KiB  | 
| 
						 | 
					@ -0,0 +1,70 @@
 | 
				
			||||||
 | 
					84,0
 | 
				
			||||||
 | 
					x,y,dx,dy,x_c,y_c
 | 
				
			||||||
 | 
					74.00,92.00,22.00,22.00,85.00,103.00
 | 
				
			||||||
 | 
					69.00,92.00,22.00,22.00,80.00,103.00
 | 
				
			||||||
 | 
					64.00,92.00,22.00,22.00,75.00,103.00
 | 
				
			||||||
 | 
					78.00,92.00,22.00,22.00,89.00,103.00
 | 
				
			||||||
 | 
					83.00,92.00,22.00,22.00,94.00,103.00
 | 
				
			||||||
 | 
					74.00,83.00,22.00,22.00,85.00,94.00
 | 
				
			||||||
 | 
					74.00,78.00,22.00,22.00,85.00,89.00
 | 
				
			||||||
 | 
					74.00,69.00,22.00,22.00,85.00,80.00
 | 
				
			||||||
 | 
					74.00,60.00,22.00,22.00,85.00,71.00
 | 
				
			||||||
 | 
					60.00,60.00,22.00,22.00,71.00,71.00
 | 
				
			||||||
 | 
					60.00,60.00,22.00,22.00,71.00,71.00
 | 
				
			||||||
 | 
					55.00,60.00,22.00,22.00,66.00,71.00
 | 
				
			||||||
 | 
					55.00,55.00,22.00,22.00,66.00,66.00
 | 
				
			||||||
 | 
					51.00,55.00,22.00,22.00,62.00,66.00
 | 
				
			||||||
 | 
					46.00,55.00,22.00,22.00,57.00,66.00
 | 
				
			||||||
 | 
					37.00,51.00,22.00,22.00,48.00,62.00
 | 
				
			||||||
 | 
					41.00,46.00,22.00,22.00,52.00,57.00
 | 
				
			||||||
 | 
					46.00,41.00,22.00,22.00,57.00,52.00
 | 
				
			||||||
 | 
					55.00,46.00,22.00,22.00,66.00,57.00
 | 
				
			||||||
 | 
					64.00,51.00,22.00,22.00,75.00,62.00
 | 
				
			||||||
 | 
					87.00,64.00,22.00,22.00,98.00,75.00
 | 
				
			||||||
 | 
					92.00,64.00,22.00,22.00,103.00,75.00
 | 
				
			||||||
 | 
					96.00,64.00,22.00,22.00,107.00,75.00
 | 
				
			||||||
 | 
					92.00,64.00,22.00,22.00,103.00,75.00
 | 
				
			||||||
 | 
					92.00,64.00,22.00,22.00,103.00,75.00
 | 
				
			||||||
 | 
					96.00,64.00,22.00,22.00,107.00,75.00
 | 
				
			||||||
 | 
					106.00,55.00,22.00,22.00,117.00,66.00
 | 
				
			||||||
 | 
					101.00,51.00,22.00,22.00,112.00,62.00
 | 
				
			||||||
 | 
					92.00,51.00,22.00,22.00,103.00,62.00
 | 
				
			||||||
 | 
					87.00,51.00,22.00,22.00,98.00,62.00
 | 
				
			||||||
 | 
					83.00,51.00,22.00,22.00,94.00,62.00
 | 
				
			||||||
 | 
					74.00,96.00,22.00,22.00,85.00,107.00
 | 
				
			||||||
 | 
					69.00,96.00,22.00,22.00,80.00,107.00
 | 
				
			||||||
 | 
					60.00,101.00,22.00,22.00,71.00,112.00
 | 
				
			||||||
 | 
					55.00,110.00,22.00,22.00,66.00,121.00
 | 
				
			||||||
 | 
					55.00,110.00,22.00,22.00,66.00,121.00
 | 
				
			||||||
 | 
					69.00,101.00,22.00,22.00,80.00,112.00
 | 
				
			||||||
 | 
					74.00,101.00,22.00,22.00,85.00,112.00
 | 
				
			||||||
 | 
					83.00,92.00,22.00,22.00,94.00,103.00
 | 
				
			||||||
 | 
					87.00,96.00,22.00,22.00,98.00,107.00
 | 
				
			||||||
 | 
					87.00,101.00,22.00,22.00,98.00,112.00
 | 
				
			||||||
 | 
					87.00,101.00,22.00,22.00,98.00,112.00
 | 
				
			||||||
 | 
					83.00,96.00,22.00,22.00,94.00,107.00
 | 
				
			||||||
 | 
					83.00,110.00,22.00,22.00,94.00,121.00
 | 
				
			||||||
 | 
					78.00,106.00,22.00,22.00,89.00,117.00
 | 
				
			||||||
 | 
					78.00,110.00,22.00,22.00,89.00,121.00
 | 
				
			||||||
 | 
					64.00,106.00,22.00,22.00,75.00,117.00
 | 
				
			||||||
 | 
					55.00,110.00,22.00,22.00,66.00,121.00
 | 
				
			||||||
 | 
					55.00,101.00,22.00,22.00,66.00,112.00
 | 
				
			||||||
 | 
					51.00,101.00,22.00,22.00,62.00,112.00
 | 
				
			||||||
 | 
					64.00,110.00,22.00,22.00,75.00,121.00
 | 
				
			||||||
 | 
					69.00,133.00,22.00,22.00,80.00,144.00
 | 
				
			||||||
 | 
					55.00,129.00,22.00,22.00,66.00,140.00
 | 
				
			||||||
 | 
					46.00,119.00,22.00,22.00,57.00,130.00
 | 
				
			||||||
 | 
					37.00,110.00,22.00,22.00,48.00,121.00
 | 
				
			||||||
 | 
					28.00,106.00,22.00,22.00,39.00,117.00
 | 
				
			||||||
 | 
					23.00,96.00,22.00,22.00,34.00,107.00
 | 
				
			||||||
 | 
					23.00,87.00,22.00,22.00,34.00,98.00
 | 
				
			||||||
 | 
					18.00,74.00,22.00,22.00,29.00,85.00
 | 
				
			||||||
 | 
					18.00,60.00,22.00,22.00,29.00,71.00
 | 
				
			||||||
 | 
					78.00,129.00,22.00,22.00,89.00,140.00
 | 
				
			||||||
 | 
					87.00,124.00,22.00,22.00,98.00,135.00
 | 
				
			||||||
 | 
					101.00,115.00,22.00,22.00,112.00,126.00
 | 
				
			||||||
 | 
					101.00,101.00,22.00,22.00,112.00,112.00
 | 
				
			||||||
 | 
					106.00,92.00,22.00,22.00,117.00,103.00
 | 
				
			||||||
 | 
					110.00,83.00,22.00,22.00,121.00,94.00
 | 
				
			||||||
 | 
					115.00,69.00,22.00,22.00,126.00,80.00
 | 
				
			||||||
 | 
					115.00,55.00,22.00,22.00,126.00,66.00
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 16 KiB  | 
| 
						 | 
					@ -0,0 +1,70 @@
 | 
				
			||||||
 | 
					80,0
 | 
				
			||||||
 | 
					x,y,dx,dy,x_c,y_c
 | 
				
			||||||
 | 
					60.00,72.00,19.00,19.00,69.50,81.50
 | 
				
			||||||
 | 
					56.00,72.00,19.00,19.00,65.50,81.50
 | 
				
			||||||
 | 
					52.00,72.00,19.00,19.00,61.50,81.50
 | 
				
			||||||
 | 
					64.00,68.00,19.00,19.00,73.50,77.50
 | 
				
			||||||
 | 
					68.00,68.00,19.00,19.00,77.50,77.50
 | 
				
			||||||
 | 
					60.00,64.00,19.00,19.00,69.50,73.50
 | 
				
			||||||
 | 
					60.00,60.00,19.00,19.00,69.50,69.50
 | 
				
			||||||
 | 
					60.00,52.00,19.00,19.00,69.50,61.50
 | 
				
			||||||
 | 
					60.00,48.00,19.00,19.00,69.50,57.50
 | 
				
			||||||
 | 
					48.00,48.00,19.00,19.00,57.50,57.50
 | 
				
			||||||
 | 
					44.00,48.00,19.00,19.00,53.50,57.50
 | 
				
			||||||
 | 
					40.00,48.00,19.00,19.00,49.50,57.50
 | 
				
			||||||
 | 
					44.00,48.00,19.00,19.00,53.50,57.50
 | 
				
			||||||
 | 
					44.00,48.00,19.00,19.00,53.50,57.50
 | 
				
			||||||
 | 
					40.00,48.00,19.00,19.00,49.50,57.50
 | 
				
			||||||
 | 
					32.00,44.00,19.00,19.00,41.50,53.50
 | 
				
			||||||
 | 
					36.00,40.00,19.00,19.00,45.50,49.50
 | 
				
			||||||
 | 
					40.00,40.00,19.00,19.00,49.50,49.50
 | 
				
			||||||
 | 
					44.00,44.00,19.00,19.00,53.50,53.50
 | 
				
			||||||
 | 
					48.00,44.00,19.00,19.00,57.50,53.50
 | 
				
			||||||
 | 
					72.00,48.00,19.00,19.00,81.50,57.50
 | 
				
			||||||
 | 
					76.00,48.00,19.00,19.00,85.50,57.50
 | 
				
			||||||
 | 
					76.00,48.00,19.00,19.00,85.50,57.50
 | 
				
			||||||
 | 
					76.00,44.00,19.00,19.00,85.50,53.50
 | 
				
			||||||
 | 
					80.00,44.00,19.00,19.00,89.50,53.50
 | 
				
			||||||
 | 
					84.00,44.00,19.00,19.00,93.50,53.50
 | 
				
			||||||
 | 
					96.00,40.00,19.00,19.00,105.50,49.50
 | 
				
			||||||
 | 
					92.00,36.00,19.00,19.00,101.50,45.50
 | 
				
			||||||
 | 
					84.00,36.00,19.00,19.00,93.50,45.50
 | 
				
			||||||
 | 
					76.00,36.00,19.00,19.00,85.50,45.50
 | 
				
			||||||
 | 
					68.00,40.00,19.00,19.00,77.50,49.50
 | 
				
			||||||
 | 
					60.00,76.00,19.00,19.00,69.50,85.50
 | 
				
			||||||
 | 
					52.00,72.00,19.00,19.00,61.50,81.50
 | 
				
			||||||
 | 
					44.00,76.00,19.00,19.00,53.50,85.50
 | 
				
			||||||
 | 
					40.00,80.00,19.00,19.00,49.50,89.50
 | 
				
			||||||
 | 
					44.00,80.00,19.00,19.00,53.50,89.50
 | 
				
			||||||
 | 
					48.00,76.00,19.00,19.00,57.50,85.50
 | 
				
			||||||
 | 
					60.00,80.00,19.00,19.00,69.50,89.50
 | 
				
			||||||
 | 
					68.00,72.00,19.00,19.00,77.50,81.50
 | 
				
			||||||
 | 
					76.00,72.00,19.00,19.00,85.50,81.50
 | 
				
			||||||
 | 
					80.00,76.00,19.00,19.00,89.50,85.50
 | 
				
			||||||
 | 
					80.00,76.00,19.00,19.00,89.50,85.50
 | 
				
			||||||
 | 
					68.00,76.00,19.00,19.00,77.50,85.50
 | 
				
			||||||
 | 
					76.00,84.00,19.00,19.00,85.50,93.50
 | 
				
			||||||
 | 
					68.00,80.00,19.00,19.00,77.50,89.50
 | 
				
			||||||
 | 
					68.00,88.00,19.00,19.00,77.50,97.50
 | 
				
			||||||
 | 
					60.00,84.00,19.00,19.00,69.50,93.50
 | 
				
			||||||
 | 
					52.00,88.00,19.00,19.00,61.50,97.50
 | 
				
			||||||
 | 
					52.00,80.00,19.00,19.00,61.50,89.50
 | 
				
			||||||
 | 
					48.00,84.00,19.00,19.00,57.50,93.50
 | 
				
			||||||
 | 
					60.00,88.00,19.00,19.00,69.50,97.50
 | 
				
			||||||
 | 
					60.00,104.00,19.00,19.00,69.50,113.50
 | 
				
			||||||
 | 
					52.00,104.00,19.00,19.00,61.50,113.50
 | 
				
			||||||
 | 
					44.00,96.00,19.00,19.00,53.50,105.50
 | 
				
			||||||
 | 
					36.00,88.00,19.00,19.00,45.50,97.50
 | 
				
			||||||
 | 
					28.00,84.00,19.00,19.00,37.50,93.50
 | 
				
			||||||
 | 
					24.00,76.00,19.00,19.00,33.50,85.50
 | 
				
			||||||
 | 
					24.00,68.00,19.00,19.00,33.50,77.50
 | 
				
			||||||
 | 
					20.00,56.00,19.00,19.00,29.50,65.50
 | 
				
			||||||
 | 
					20.00,48.00,19.00,19.00,29.50,57.50
 | 
				
			||||||
 | 
					72.00,100.00,19.00,19.00,81.50,109.50
 | 
				
			||||||
 | 
					80.00,92.00,19.00,19.00,89.50,101.50
 | 
				
			||||||
 | 
					92.00,84.00,19.00,19.00,101.50,93.50
 | 
				
			||||||
 | 
					96.00,72.00,19.00,19.00,105.50,81.50
 | 
				
			||||||
 | 
					100.00,64.00,19.00,19.00,109.50,73.50
 | 
				
			||||||
 | 
					100.00,56.00,19.00,19.00,109.50,65.50
 | 
				
			||||||
 | 
					104.00,44.00,19.00,19.00,113.50,53.50
 | 
				
			||||||
 | 
					104.00,36.00,19.00,19.00,113.50,45.50
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 12 KiB  | 
| 
						 | 
					@ -0,0 +1,70 @@
 | 
				
			||||||
 | 
					110,0
 | 
				
			||||||
 | 
					x,y,dx,dy,x_c,y_c
 | 
				
			||||||
 | 
					60.00,74.00,22.00,22.00,71.00,85.00
 | 
				
			||||||
 | 
					55.00,74.00,22.00,22.00,66.00,85.00
 | 
				
			||||||
 | 
					51.00,74.00,22.00,22.00,62.00,85.00
 | 
				
			||||||
 | 
					64.00,74.00,22.00,22.00,75.00,85.00
 | 
				
			||||||
 | 
					69.00,74.00,22.00,22.00,80.00,85.00
 | 
				
			||||||
 | 
					60.00,64.00,22.00,22.00,71.00,75.00
 | 
				
			||||||
 | 
					60.00,60.00,22.00,22.00,71.00,71.00
 | 
				
			||||||
 | 
					60.00,51.00,22.00,22.00,71.00,62.00
 | 
				
			||||||
 | 
					60.00,46.00,22.00,22.00,71.00,57.00
 | 
				
			||||||
 | 
					46.00,46.00,22.00,22.00,57.00,57.00
 | 
				
			||||||
 | 
					41.00,46.00,22.00,22.00,52.00,57.00
 | 
				
			||||||
 | 
					41.00,46.00,22.00,22.00,52.00,57.00
 | 
				
			||||||
 | 
					41.00,41.00,22.00,22.00,52.00,52.00
 | 
				
			||||||
 | 
					41.00,41.00,22.00,22.00,52.00,52.00
 | 
				
			||||||
 | 
					37.00,46.00,22.00,22.00,48.00,57.00
 | 
				
			||||||
 | 
					23.00,41.00,22.00,22.00,34.00,52.00
 | 
				
			||||||
 | 
					28.00,37.00,22.00,22.00,39.00,48.00
 | 
				
			||||||
 | 
					32.00,32.00,22.00,22.00,43.00,43.00
 | 
				
			||||||
 | 
					41.00,37.00,22.00,22.00,52.00,48.00
 | 
				
			||||||
 | 
					51.00,41.00,22.00,22.00,62.00,52.00
 | 
				
			||||||
 | 
					69.00,46.00,22.00,22.00,80.00,57.00
 | 
				
			||||||
 | 
					74.00,46.00,22.00,22.00,85.00,57.00
 | 
				
			||||||
 | 
					78.00,46.00,22.00,22.00,89.00,57.00
 | 
				
			||||||
 | 
					74.00,46.00,22.00,22.00,85.00,57.00
 | 
				
			||||||
 | 
					78.00,46.00,22.00,22.00,89.00,57.00
 | 
				
			||||||
 | 
					83.00,46.00,22.00,22.00,94.00,57.00
 | 
				
			||||||
 | 
					92.00,37.00,22.00,22.00,103.00,48.00
 | 
				
			||||||
 | 
					87.00,37.00,22.00,22.00,98.00,48.00
 | 
				
			||||||
 | 
					78.00,37.00,22.00,22.00,89.00,48.00
 | 
				
			||||||
 | 
					74.00,37.00,22.00,22.00,85.00,48.00
 | 
				
			||||||
 | 
					64.00,37.00,22.00,22.00,75.00,48.00
 | 
				
			||||||
 | 
					60.00,78.00,22.00,22.00,71.00,89.00
 | 
				
			||||||
 | 
					55.00,74.00,22.00,22.00,66.00,85.00
 | 
				
			||||||
 | 
					46.00,74.00,22.00,22.00,57.00,85.00
 | 
				
			||||||
 | 
					41.00,78.00,22.00,22.00,52.00,89.00
 | 
				
			||||||
 | 
					46.00,78.00,22.00,22.00,57.00,89.00
 | 
				
			||||||
 | 
					51.00,78.00,22.00,22.00,62.00,89.00
 | 
				
			||||||
 | 
					60.00,83.00,22.00,22.00,71.00,94.00
 | 
				
			||||||
 | 
					69.00,74.00,22.00,22.00,80.00,85.00
 | 
				
			||||||
 | 
					74.00,78.00,22.00,22.00,85.00,89.00
 | 
				
			||||||
 | 
					78.00,78.00,22.00,22.00,89.00,89.00
 | 
				
			||||||
 | 
					78.00,78.00,22.00,22.00,89.00,89.00
 | 
				
			||||||
 | 
					74.00,78.00,22.00,22.00,85.00,89.00
 | 
				
			||||||
 | 
					74.00,87.00,22.00,22.00,85.00,98.00
 | 
				
			||||||
 | 
					64.00,83.00,22.00,22.00,75.00,94.00
 | 
				
			||||||
 | 
					64.00,92.00,22.00,22.00,75.00,103.00
 | 
				
			||||||
 | 
					55.00,87.00,22.00,22.00,66.00,98.00
 | 
				
			||||||
 | 
					46.00,92.00,22.00,22.00,57.00,103.00
 | 
				
			||||||
 | 
					46.00,83.00,22.00,22.00,57.00,94.00
 | 
				
			||||||
 | 
					41.00,83.00,22.00,22.00,52.00,94.00
 | 
				
			||||||
 | 
					55.00,92.00,22.00,22.00,66.00,103.00
 | 
				
			||||||
 | 
					60.00,115.00,22.00,22.00,71.00,126.00
 | 
				
			||||||
 | 
					51.00,110.00,22.00,22.00,62.00,121.00
 | 
				
			||||||
 | 
					41.00,101.00,22.00,22.00,52.00,112.00
 | 
				
			||||||
 | 
					32.00,92.00,22.00,22.00,43.00,103.00
 | 
				
			||||||
 | 
					28.00,83.00,22.00,22.00,39.00,94.00
 | 
				
			||||||
 | 
					23.00,74.00,22.00,22.00,34.00,85.00
 | 
				
			||||||
 | 
					18.00,64.00,22.00,22.00,29.00,75.00
 | 
				
			||||||
 | 
					14.00,51.00,22.00,22.00,25.00,62.00
 | 
				
			||||||
 | 
					14.00,37.00,22.00,22.00,25.00,48.00
 | 
				
			||||||
 | 
					69.00,115.00,22.00,22.00,80.00,126.00
 | 
				
			||||||
 | 
					78.00,110.00,22.00,22.00,89.00,121.00
 | 
				
			||||||
 | 
					87.00,101.00,22.00,22.00,98.00,112.00
 | 
				
			||||||
 | 
					92.00,87.00,22.00,22.00,103.00,98.00
 | 
				
			||||||
 | 
					96.00,74.00,22.00,22.00,107.00,85.00
 | 
				
			||||||
 | 
					101.00,64.00,22.00,22.00,112.00,75.00
 | 
				
			||||||
 | 
					106.00,51.00,22.00,22.00,117.00,62.00
 | 
				
			||||||
 | 
					106.00,41.00,22.00,22.00,117.00,52.00
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 12 KiB  | 
| 
						 | 
					@ -0,0 +1,70 @@
 | 
				
			||||||
 | 
					252,0
 | 
				
			||||||
 | 
					x,y,dx,dy,x_c,y_c
 | 
				
			||||||
 | 
					111.00,132.00,25.00,25.00,123.50,144.50
 | 
				
			||||||
 | 
					111.00,132.00,25.00,25.00,123.50,144.50
 | 
				
			||||||
 | 
					106.00,132.00,25.00,25.00,118.50,144.50
 | 
				
			||||||
 | 
					116.00,132.00,25.00,25.00,128.50,144.50
 | 
				
			||||||
 | 
					121.00,132.00,25.00,25.00,133.50,144.50
 | 
				
			||||||
 | 
					111.00,121.00,25.00,25.00,123.50,133.50
 | 
				
			||||||
 | 
					111.00,116.00,25.00,25.00,123.50,128.50
 | 
				
			||||||
 | 
					111.00,106.00,25.00,25.00,123.50,118.50
 | 
				
			||||||
 | 
					111.00,100.00,25.00,25.00,123.50,112.50
 | 
				
			||||||
 | 
					100.00,100.00,25.00,25.00,112.50,112.50
 | 
				
			||||||
 | 
					95.00,100.00,25.00,25.00,107.50,112.50
 | 
				
			||||||
 | 
					90.00,100.00,25.00,25.00,102.50,112.50
 | 
				
			||||||
 | 
					95.00,95.00,25.00,25.00,107.50,107.50
 | 
				
			||||||
 | 
					90.00,95.00,25.00,25.00,102.50,107.50
 | 
				
			||||||
 | 
					84.00,100.00,25.00,25.00,96.50,112.50
 | 
				
			||||||
 | 
					74.00,90.00,25.00,25.00,86.50,102.50
 | 
				
			||||||
 | 
					79.00,84.00,25.00,25.00,91.50,96.50
 | 
				
			||||||
 | 
					84.00,79.00,25.00,25.00,96.50,91.50
 | 
				
			||||||
 | 
					95.00,84.00,25.00,25.00,107.50,96.50
 | 
				
			||||||
 | 
					106.00,84.00,25.00,25.00,118.50,96.50
 | 
				
			||||||
 | 
					127.00,100.00,25.00,25.00,139.50,112.50
 | 
				
			||||||
 | 
					132.00,100.00,25.00,25.00,144.50,112.50
 | 
				
			||||||
 | 
					132.00,100.00,25.00,25.00,144.50,112.50
 | 
				
			||||||
 | 
					127.00,95.00,25.00,25.00,139.50,107.50
 | 
				
			||||||
 | 
					132.00,95.00,25.00,25.00,144.50,107.50
 | 
				
			||||||
 | 
					137.00,100.00,25.00,25.00,149.50,112.50
 | 
				
			||||||
 | 
					148.00,90.00,25.00,25.00,160.50,102.50
 | 
				
			||||||
 | 
					143.00,84.00,25.00,25.00,155.50,96.50
 | 
				
			||||||
 | 
					132.00,84.00,25.00,25.00,144.50,96.50
 | 
				
			||||||
 | 
					127.00,84.00,25.00,25.00,139.50,96.50
 | 
				
			||||||
 | 
					116.00,84.00,25.00,25.00,128.50,96.50
 | 
				
			||||||
 | 
					111.00,137.00,25.00,25.00,123.50,149.50
 | 
				
			||||||
 | 
					106.00,137.00,25.00,25.00,118.50,149.50
 | 
				
			||||||
 | 
					100.00,143.00,25.00,25.00,112.50,155.50
 | 
				
			||||||
 | 
					95.00,148.00,25.00,25.00,107.50,160.50
 | 
				
			||||||
 | 
					100.00,148.00,25.00,25.00,112.50,160.50
 | 
				
			||||||
 | 
					106.00,143.00,25.00,25.00,118.50,155.50
 | 
				
			||||||
 | 
					111.00,143.00,25.00,25.00,123.50,155.50
 | 
				
			||||||
 | 
					121.00,137.00,25.00,25.00,133.50,149.50
 | 
				
			||||||
 | 
					127.00,143.00,25.00,25.00,139.50,155.50
 | 
				
			||||||
 | 
					132.00,143.00,25.00,25.00,144.50,155.50
 | 
				
			||||||
 | 
					127.00,143.00,25.00,25.00,139.50,155.50
 | 
				
			||||||
 | 
					127.00,143.00,25.00,25.00,139.50,155.50
 | 
				
			||||||
 | 
					127.00,153.00,25.00,25.00,139.50,165.50
 | 
				
			||||||
 | 
					121.00,148.00,25.00,25.00,133.50,160.50
 | 
				
			||||||
 | 
					121.00,158.00,25.00,25.00,133.50,170.50
 | 
				
			||||||
 | 
					111.00,153.00,25.00,25.00,123.50,165.50
 | 
				
			||||||
 | 
					106.00,158.00,25.00,25.00,118.50,170.50
 | 
				
			||||||
 | 
					106.00,148.00,25.00,25.00,118.50,160.50
 | 
				
			||||||
 | 
					100.00,153.00,25.00,25.00,112.50,165.50
 | 
				
			||||||
 | 
					111.00,158.00,25.00,25.00,123.50,170.50
 | 
				
			||||||
 | 
					116.00,185.00,25.00,25.00,128.50,197.50
 | 
				
			||||||
 | 
					106.00,179.00,25.00,25.00,118.50,191.50
 | 
				
			||||||
 | 
					95.00,174.00,25.00,25.00,107.50,186.50
 | 
				
			||||||
 | 
					84.00,164.00,25.00,25.00,96.50,176.50
 | 
				
			||||||
 | 
					79.00,153.00,25.00,25.00,91.50,165.50
 | 
				
			||||||
 | 
					74.00,143.00,25.00,25.00,86.50,155.50
 | 
				
			||||||
 | 
					74.00,132.00,25.00,25.00,86.50,144.50
 | 
				
			||||||
 | 
					74.00,116.00,25.00,25.00,86.50,128.50
 | 
				
			||||||
 | 
					74.00,100.00,25.00,25.00,86.50,112.50
 | 
				
			||||||
 | 
					127.00,179.00,25.00,25.00,139.50,191.50
 | 
				
			||||||
 | 
					137.00,174.00,25.00,25.00,149.50,186.50
 | 
				
			||||||
 | 
					143.00,164.00,25.00,25.00,155.50,176.50
 | 
				
			||||||
 | 
					148.00,148.00,25.00,25.00,160.50,160.50
 | 
				
			||||||
 | 
					153.00,137.00,25.00,25.00,165.50,149.50
 | 
				
			||||||
 | 
					158.00,127.00,25.00,25.00,170.50,139.50
 | 
				
			||||||
 | 
					164.00,111.00,25.00,25.00,176.50,123.50
 | 
				
			||||||
 | 
					164.00,95.00,25.00,25.00,176.50,107.50
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 7.6 KiB  | 
| 
						 | 
					@ -0,0 +1,70 @@
 | 
				
			||||||
 | 
					126,0
 | 
				
			||||||
 | 
					x,y,dx,dy,x_c,y_c
 | 
				
			||||||
 | 
					112.00,132.00,19.00,19.00,121.50,141.50
 | 
				
			||||||
 | 
					108.00,132.00,19.00,19.00,117.50,141.50
 | 
				
			||||||
 | 
					108.00,132.00,19.00,19.00,117.50,141.50
 | 
				
			||||||
 | 
					116.00,132.00,19.00,19.00,125.50,141.50
 | 
				
			||||||
 | 
					116.00,132.00,19.00,19.00,125.50,141.50
 | 
				
			||||||
 | 
					112.00,124.00,19.00,19.00,121.50,133.50
 | 
				
			||||||
 | 
					112.00,116.00,19.00,19.00,121.50,125.50
 | 
				
			||||||
 | 
					112.00,108.00,19.00,19.00,121.50,117.50
 | 
				
			||||||
 | 
					112.00,100.00,19.00,19.00,121.50,109.50
 | 
				
			||||||
 | 
					100.00,100.00,19.00,19.00,109.50,109.50
 | 
				
			||||||
 | 
					96.00,100.00,19.00,19.00,105.50,109.50
 | 
				
			||||||
 | 
					92.00,100.00,19.00,19.00,101.50,109.50
 | 
				
			||||||
 | 
					96.00,96.00,19.00,19.00,105.50,105.50
 | 
				
			||||||
 | 
					92.00,96.00,19.00,19.00,101.50,105.50
 | 
				
			||||||
 | 
					88.00,96.00,19.00,19.00,97.50,105.50
 | 
				
			||||||
 | 
					80.00,88.00,19.00,19.00,89.50,97.50
 | 
				
			||||||
 | 
					84.00,84.00,19.00,19.00,93.50,93.50
 | 
				
			||||||
 | 
					92.00,84.00,19.00,19.00,101.50,93.50
 | 
				
			||||||
 | 
					96.00,88.00,19.00,19.00,105.50,97.50
 | 
				
			||||||
 | 
					104.00,88.00,19.00,19.00,113.50,97.50
 | 
				
			||||||
 | 
					124.00,104.00,19.00,19.00,133.50,113.50
 | 
				
			||||||
 | 
					132.00,104.00,19.00,19.00,141.50,113.50
 | 
				
			||||||
 | 
					136.00,104.00,19.00,19.00,145.50,113.50
 | 
				
			||||||
 | 
					128.00,104.00,19.00,19.00,137.50,113.50
 | 
				
			||||||
 | 
					132.00,104.00,19.00,19.00,141.50,113.50
 | 
				
			||||||
 | 
					136.00,104.00,19.00,19.00,145.50,113.50
 | 
				
			||||||
 | 
					148.00,96.00,19.00,19.00,157.50,105.50
 | 
				
			||||||
 | 
					144.00,92.00,19.00,19.00,153.50,101.50
 | 
				
			||||||
 | 
					136.00,92.00,19.00,19.00,145.50,101.50
 | 
				
			||||||
 | 
					128.00,92.00,19.00,19.00,137.50,101.50
 | 
				
			||||||
 | 
					120.00,92.00,19.00,19.00,129.50,101.50
 | 
				
			||||||
 | 
					112.00,136.00,19.00,19.00,121.50,145.50
 | 
				
			||||||
 | 
					108.00,136.00,19.00,19.00,117.50,145.50
 | 
				
			||||||
 | 
					100.00,136.00,19.00,19.00,109.50,145.50
 | 
				
			||||||
 | 
					96.00,140.00,19.00,19.00,105.50,149.50
 | 
				
			||||||
 | 
					96.00,140.00,19.00,19.00,105.50,149.50
 | 
				
			||||||
 | 
					108.00,140.00,19.00,19.00,117.50,149.50
 | 
				
			||||||
 | 
					112.00,140.00,19.00,19.00,121.50,149.50
 | 
				
			||||||
 | 
					116.00,136.00,19.00,19.00,125.50,145.50
 | 
				
			||||||
 | 
					120.00,140.00,19.00,19.00,129.50,149.50
 | 
				
			||||||
 | 
					124.00,140.00,19.00,19.00,133.50,149.50
 | 
				
			||||||
 | 
					120.00,140.00,19.00,19.00,129.50,149.50
 | 
				
			||||||
 | 
					120.00,140.00,19.00,19.00,129.50,149.50
 | 
				
			||||||
 | 
					120.00,148.00,19.00,19.00,129.50,157.50
 | 
				
			||||||
 | 
					116.00,144.00,19.00,19.00,125.50,153.50
 | 
				
			||||||
 | 
					116.00,152.00,19.00,19.00,125.50,161.50
 | 
				
			||||||
 | 
					108.00,148.00,19.00,19.00,117.50,157.50
 | 
				
			||||||
 | 
					100.00,152.00,19.00,19.00,109.50,161.50
 | 
				
			||||||
 | 
					100.00,144.00,19.00,19.00,109.50,153.50
 | 
				
			||||||
 | 
					96.00,148.00,19.00,19.00,105.50,157.50
 | 
				
			||||||
 | 
					108.00,152.00,19.00,19.00,117.50,161.50
 | 
				
			||||||
 | 
					112.00,172.00,19.00,19.00,121.50,181.50
 | 
				
			||||||
 | 
					100.00,168.00,19.00,19.00,109.50,177.50
 | 
				
			||||||
 | 
					92.00,164.00,19.00,19.00,101.50,173.50
 | 
				
			||||||
 | 
					84.00,156.00,19.00,19.00,93.50,165.50
 | 
				
			||||||
 | 
					80.00,148.00,19.00,19.00,89.50,157.50
 | 
				
			||||||
 | 
					76.00,136.00,19.00,19.00,85.50,145.50
 | 
				
			||||||
 | 
					76.00,124.00,19.00,19.00,85.50,133.50
 | 
				
			||||||
 | 
					72.00,108.00,19.00,19.00,81.50,117.50
 | 
				
			||||||
 | 
					72.00,96.00,19.00,19.00,81.50,105.50
 | 
				
			||||||
 | 
					120.00,168.00,19.00,19.00,129.50,177.50
 | 
				
			||||||
 | 
					128.00,160.00,19.00,19.00,137.50,169.50
 | 
				
			||||||
 | 
					136.00,152.00,19.00,19.00,145.50,161.50
 | 
				
			||||||
 | 
					140.00,136.00,19.00,19.00,149.50,145.50
 | 
				
			||||||
 | 
					144.00,128.00,19.00,19.00,153.50,137.50
 | 
				
			||||||
 | 
					148.00,120.00,19.00,19.00,157.50,129.50
 | 
				
			||||||
 | 
					156.00,108.00,19.00,19.00,165.50,117.50
 | 
				
			||||||
 | 
					156.00,96.00,19.00,19.00,165.50,105.50
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 14 KiB  | 
| 
		 After Width: | Height: | Size: 7.6 KiB  | 
| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					x,y,dx,dy,score,angle,type
 | 
				
			||||||
 | 
					61.0,62.0,132.0,132.0,344,0.0,haar
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 418 KiB  | 
| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					x,y,dx,dy,score,angle,type
 | 
				
			||||||
 | 
					327.0,101.0,121.0,121.0,154,0.0,lbp
 | 
				
			||||||
 | 
					237.0,106.0,113.0,113.0,139,0.0,lbp
 | 
				
			||||||
 | 
					164.0,49.0,91.0,91.0,49,0.0,lbp
 | 
				
			||||||
 | 
					434.0,86.0,94.0,94.0,95,0.0,lbp
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,70 @@
 | 
				
			||||||
 | 
					208,0
 | 
				
			||||||
 | 
					x,y,dx,dy,x_c,y_c
 | 
				
			||||||
 | 
					106.25,131.25,29.69,29.69,121.09,146.09
 | 
				
			||||||
 | 
					106.25,131.25,29.69,29.69,121.09,146.09
 | 
				
			||||||
 | 
					106.25,131.25,29.69,29.69,121.09,146.09
 | 
				
			||||||
 | 
					112.50,131.25,29.69,29.69,127.34,146.09
 | 
				
			||||||
 | 
					118.75,131.25,29.69,29.69,133.59,146.09
 | 
				
			||||||
 | 
					106.25,118.75,29.69,29.69,121.09,133.59
 | 
				
			||||||
 | 
					106.25,112.50,29.69,29.69,121.09,127.34
 | 
				
			||||||
 | 
					106.25,100.00,29.69,29.69,121.09,114.84
 | 
				
			||||||
 | 
					106.25,93.75,29.69,29.69,121.09,108.59
 | 
				
			||||||
 | 
					93.75,93.75,29.69,29.69,108.59,108.59
 | 
				
			||||||
 | 
					93.75,100.00,29.69,29.69,108.59,114.84
 | 
				
			||||||
 | 
					87.50,100.00,29.69,29.69,102.34,114.84
 | 
				
			||||||
 | 
					87.50,93.75,29.69,29.69,102.34,108.59
 | 
				
			||||||
 | 
					87.50,93.75,29.69,29.69,102.34,108.59
 | 
				
			||||||
 | 
					81.25,100.00,29.69,29.69,96.09,114.84
 | 
				
			||||||
 | 
					68.75,87.50,29.69,29.69,83.59,102.34
 | 
				
			||||||
 | 
					75.00,81.25,29.69,29.69,89.84,96.09
 | 
				
			||||||
 | 
					87.50,75.00,29.69,29.69,102.34,89.84
 | 
				
			||||||
 | 
					93.75,81.25,29.69,29.69,108.59,96.09
 | 
				
			||||||
 | 
					100.00,81.25,29.69,29.69,114.84,96.09
 | 
				
			||||||
 | 
					118.75,100.00,29.69,29.69,133.59,114.84
 | 
				
			||||||
 | 
					125.00,100.00,29.69,29.69,139.84,114.84
 | 
				
			||||||
 | 
					131.25,100.00,29.69,29.69,146.09,114.84
 | 
				
			||||||
 | 
					125.00,93.75,29.69,29.69,139.84,108.59
 | 
				
			||||||
 | 
					131.25,93.75,29.69,29.69,146.09,108.59
 | 
				
			||||||
 | 
					137.50,100.00,29.69,29.69,152.34,114.84
 | 
				
			||||||
 | 
					150.00,87.50,29.69,29.69,164.84,102.34
 | 
				
			||||||
 | 
					143.75,81.25,29.69,29.69,158.59,96.09
 | 
				
			||||||
 | 
					131.25,81.25,29.69,29.69,146.09,96.09
 | 
				
			||||||
 | 
					125.00,81.25,29.69,29.69,139.84,96.09
 | 
				
			||||||
 | 
					112.50,81.25,29.69,29.69,127.34,96.09
 | 
				
			||||||
 | 
					106.25,137.50,29.69,29.69,121.09,152.34
 | 
				
			||||||
 | 
					100.00,137.50,29.69,29.69,114.84,152.34
 | 
				
			||||||
 | 
					100.00,137.50,29.69,29.69,114.84,152.34
 | 
				
			||||||
 | 
					93.75,143.75,29.69,29.69,108.59,158.59
 | 
				
			||||||
 | 
					93.75,143.75,29.69,29.69,108.59,158.59
 | 
				
			||||||
 | 
					100.00,143.75,29.69,29.69,114.84,158.59
 | 
				
			||||||
 | 
					106.25,143.75,29.69,29.69,121.09,158.59
 | 
				
			||||||
 | 
					118.75,137.50,29.69,29.69,133.59,152.34
 | 
				
			||||||
 | 
					125.00,137.50,29.69,29.69,139.84,152.34
 | 
				
			||||||
 | 
					131.25,143.75,29.69,29.69,146.09,158.59
 | 
				
			||||||
 | 
					125.00,143.75,29.69,29.69,139.84,158.59
 | 
				
			||||||
 | 
					118.75,143.75,29.69,29.69,133.59,158.59
 | 
				
			||||||
 | 
					125.00,150.00,29.69,29.69,139.84,164.84
 | 
				
			||||||
 | 
					118.75,143.75,29.69,29.69,133.59,158.59
 | 
				
			||||||
 | 
					118.75,156.25,29.69,29.69,133.59,171.09
 | 
				
			||||||
 | 
					106.25,150.00,29.69,29.69,121.09,164.84
 | 
				
			||||||
 | 
					100.00,156.25,29.69,29.69,114.84,171.09
 | 
				
			||||||
 | 
					100.00,143.75,29.69,29.69,114.84,158.59
 | 
				
			||||||
 | 
					93.75,150.00,29.69,29.69,108.59,164.84
 | 
				
			||||||
 | 
					106.25,156.25,29.69,29.69,121.09,171.09
 | 
				
			||||||
 | 
					112.50,181.25,29.69,29.69,127.34,196.09
 | 
				
			||||||
 | 
					100.00,181.25,29.69,29.69,114.84,196.09
 | 
				
			||||||
 | 
					87.50,168.75,29.69,29.69,102.34,183.59
 | 
				
			||||||
 | 
					75.00,150.00,29.69,29.69,89.84,164.84
 | 
				
			||||||
 | 
					68.75,137.50,29.69,29.69,83.59,152.34
 | 
				
			||||||
 | 
					62.50,118.75,29.69,29.69,77.34,133.59
 | 
				
			||||||
 | 
					62.50,106.25,29.69,29.69,77.34,121.09
 | 
				
			||||||
 | 
					56.25,87.50,29.69,29.69,71.09,102.34
 | 
				
			||||||
 | 
					56.25,75.00,29.69,29.69,71.09,89.84
 | 
				
			||||||
 | 
					125.00,175.00,29.69,29.69,139.84,189.84
 | 
				
			||||||
 | 
					137.50,168.75,29.69,29.69,152.34,183.59
 | 
				
			||||||
 | 
					143.75,162.50,29.69,29.69,158.59,177.34
 | 
				
			||||||
 | 
					150.00,150.00,29.69,29.69,164.84,164.84
 | 
				
			||||||
 | 
					156.25,137.50,29.69,29.69,171.09,152.34
 | 
				
			||||||
 | 
					156.25,125.00,29.69,29.69,171.09,139.84
 | 
				
			||||||
 | 
					162.50,106.25,29.69,29.69,177.34,121.09
 | 
				
			||||||
 | 
					162.50,93.75,29.69,29.69,177.34,108.59
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 7.6 KiB  |