Creates output generation function

Finds and saves center of faces
Uses most probable face as input

Video generation to be implemented
Picture cropping to be implemented,
V0 should be finished !


Signed-off-by: julien Lengrand-Lambert <julien@lengrand.fr>
This commit is contained in:
julien Lengrand-Lambert
2012-04-03 14:03:38 +02:00
parent 8a1366c3e9
commit 1491b8d8f9
5 changed files with 254 additions and 20 deletions

40
FaceParams.py Normal file
View File

@@ -0,0 +1,40 @@
'''
Created on 30 mars 2012
@author: jll
'''
import cv
import os
class FaceParams(object):
'''
Simple class used to store parameters used for Face detection
'''
def __init__(self, xml_folder, i_scale=2, h_scale=1.2, h_flags=0, mn=2):
'''
Constructor
'''
# Setting up some default parameters for Face Detection
self.face_cascade = cv.Load(os.path.join(xml_folder, "haarcascade_frontalface_alt.xml"))
self.eye_cascade = cv.Load(os.path.join(xml_folder, "haarcascade_eye.xml"))
# To be defined more precisely
self.min_size = (20,20)
self.image_scale = i_scale
self.haar_scale = h_scale
self.min_neighbors = mn
self.haar_flags = h_flags
def __str__(self):
"""
More convenient print method
"""
print "---------"
print "Selected parameters for Face Detection:"
print "Selected cascade for Face detection : %s" % ("haarcascade_frontalface_alt")
print "Minimum Size (x, y): %d" % (self.min_size[0], self.min_size[1])
print "Image scaling: %d, %d)" % (self.image_scale)
print "Haar scaling: %f" % (self.haar_scale)
print "Number of Haar flags: %d" % (self.haar_flags)
print "Minimum number of neighbors: %d" % (self.min_neighbors)
print "---------"

174
Guy.py
View File

@@ -10,32 +10,91 @@ import os # needed only for main. Shall be removed
class Guy(object):
'''
Represents the user on the people at a fixed time.
All datas found for this time may be found here.
All data found for this time may be found here.
'''
def __init__(self, image, image_id):
'''
Constructor
'''
self.in_image = image # input image
self.in_x = None
self.in_y = None
self.in_channels = self.in_image.nChannels
self.in_channels = image.nChannels
self.name = image_id # Name of the picture used as input
self.out_im = None
self.in_image = None # input image
self.faces = [] # List of faces detected for this input
# TODO: should eyes be tied to a precise face ?
self.eyes = [] # List of eyes detected for this input
self.eyes = [] # List of eyes detected for this input
# Some operations on variables
(self.in_x, self.in_y) = cv.GetSize(self.in_image) # image size in x, y
(self.in_x, self.in_y) = cv.GetSize(image) # image size in x, y
# Creation of the output image
# Two variables used to define the new center of interest of the image
# they are defined as the middle of input image at first
self.x_center = self.in_x / 2
self.y_center = self.in_y / 2
# Creation of the images
self.in_image = cv.CreateImage((self.in_x, self.in_y),cv.IPL_DEPTH_8U, self.in_channels)
cv.Copy(image, self.in_image)
self.out_im = cv.CreateImage((self.in_x, self.in_y),cv.IPL_DEPTH_8U, self.in_channels)
cv.Zero(self.out_im) # put everything to 0
def search_face(self, face_params):
"""
Search on the picture for a face.
Populates faces
"""
# Allocate the temporary images
gray = cv.CreateImage((self.in_x, self.in_y),
cv.IPL_DEPTH_8U,
1)
smallImage = cv.CreateImage((cv.Round(self.in_x / face_params.image_scale),
cv.Round (self.in_y / face_params.image_scale)),
cv.IPL_DEPTH_8U ,
1)
# Converts color input image to grayscale
cv.CvtColor(self.in_image, gray, cv.CV_BGR2GRAY)
# Scales input image for faster processing
cv.Resize(gray, smallImage, cv.CV_INTER_LINEAR)
# Equalizes the histogram
cv.EqualizeHist(smallImage, smallImage)
# Detect the faces
self.faces = cv.HaarDetectObjects(smallImage,
face_params.face_cascade,
cv.CreateMemStorage(0),
face_params.haar_scale,
face_params.min_neighbors,
face_params.haar_flags,
face_params.min_size)
# sorting faces to keep only the most probable one
self.sort_faces()
self.update_center() # finds centre of face in image
def sort_faces(self):
"""
sort faces by probability, most probable one first
"""
if self.has_face() : # needed ?
self.faces.sort(key= lambda prob : prob[1], reverse=True)
def update_center(self):
"""
Using sorted faces, defines the new center of interest of the output image
"""
if self.has_face():
((x, y, w, h), n) = self.faces[0]
self.x_center = x + w / 2
self.y_center = y + h / 2
def in_display(self, time):
"""
Displays the input image, for time ms.
@@ -43,7 +102,8 @@ class Guy(object):
"""
cv.NamedWindow(self.name)
cv.ShowImage(self.name, self.in_image)
cv.WaitKey(time)
cv.WaitKey(time)
cv.DestroyWindow(self.name)
def out_display(self, time):
"""
@@ -55,7 +115,105 @@ class Guy(object):
cv.NamedWindow(win_name)
cv.ShowImage(win_name, self.in_image)
cv.WaitKey(time)
cv.DestroyWindow(win_name)
def save_result(self, face_params, out_folder, ext, debug=False):
"""
Saves output image to the given format (given in extension)
"""
# FIXME : face_params to be removed !
# TODO : should be refactored into a create_output(debug function)
if (debug and self.has_face()):
# Adds a rectangle around the output, and a point in the center
((x, y, w, h), n) = self.faces[0]
# the input to cv.HaarDetectObjects was resized, so scale the
# bounding box of each face and convert it to two CvPoints
pt1 = (int(x * face_params.image_scale),
int(y * face_params.image_scale))
pt2 = (int((x + w) * face_params.image_scale),
int((y + h) * face_params.image_scale))
cv.Rectangle(self.out_im,
pt1,
pt2,
cv.RGB(255, 0, 0),
3, 8, 0)# surrounds face
# Adds point in the center
pt3 = (int(self.x_center * face_params.image_scale),
int(self.y_center * face_params.image_scale))
cv.Line(self.out_im,
pt3,
pt3,
cv.RGB(0, 255, 0),
3, 8, 0)
# check that format is a string ? ?
file_name = self.name + "." + ext
out_name = os.path.join(out_folder, file_name)
print "Saving %s" %(out_name)
cv.SaveImage(out_name, self.out_im)
def show_debug(self, face_params, time):
"""
Function that may be used after having populated faces.
Displays each guy, with a red square around detected face,
for time ms.
TODO: green square around most probable face?
See the doc !
"""
# Instead of doing it for all, do it only for the biggest one
if self.has_face() :
debug_image = cv.CreateImage((self.in_x, self.in_y),cv.IPL_DEPTH_8U, self.in_channels)
cv.Copy(self.in_image, debug_image)
# some nice drawings
((x, y, w, h), n) = self.faces[0]
# the input to cv.HaarDetectObjects was resized, so scale the
# bounding box of each face and convert it to two CvPoints
pt1 = (int(x * face_params.image_scale),
int(y * face_params.image_scale))
pt2 = (int((x + w) * face_params.image_scale),
int((y + h) * face_params.image_scale))
cv.Rectangle(debug_image,
pt1,
pt2,
cv.RGB(255, 0, 0),
3, 8, 0)# surrounds face
# Adds point in the center
pt3 = (int(self.x_center * face_params.image_scale),
int(self.y_center * face_params.image_scale))
cv.Line(debug_image,
pt3,
pt3,
cv.RGB(0, 255, 0),
3, 8, 0)
win_name = self.name + " - debug"
cv.NamedWindow(win_name, cv.CV_WINDOW_NORMAL)
cv.ResizeWindow(win_name, 640, 480)
cv.ShowImage(win_name, debug_image)
cv.WaitKey(time)
cv.DestroyWindow(win_name)
else :
print "Warning! No face found for %s" %(self.name)
def num_faces(self):
"""
Returns the number of faces found for this guy
"""
return len(self.faces)
def has_face(self):
"""
Returns True if at least one face has been found
"""
return (len(self.faces) > 0)
if __name__ == "__main__":
# quick and dirty tests
name = "input/search.jpg"

View File

@@ -6,6 +6,7 @@ Created on 27 mars 2012
import cv
import os
import Guy
from FaceParams import FaceParams
class FaceMovie(object):
'''
@@ -13,8 +14,6 @@ class FaceMovie(object):
Contains the core image processing functions.
Supports the communication layer with the end user interface.
'''
def __init__(self, in_folder, out_folder, param_folder):
'''
Constructor
@@ -26,15 +25,7 @@ class FaceMovie(object):
self.guys = [] # List of pictures in source folder
# Setting up some default parameters for Face Detection
self.face_cascade = cv.Load(os.path.join(param_folder, "haarcascade_frontalface_alt.xml"))
self.eye_cascade = cv.Load(os.path.join(param_folder, "haarcascade_eye.xml"))
# To be defined more precisely
self.min_size = (20,20)
self.image_scale = 2
self.haar_scale = 1.2
self.min_neighbors = 2
self.haar_flags = 0
self.face_params = FaceParams(self.params_source)
def list_guys(self):
"""
@@ -58,14 +49,59 @@ class FaceMovie(object):
# populating guys
self.guys.append(a_guy)
def search_faces(self):
"""
Searches for all faces in the guys we have
Results to be stored directly in guys
"""
for a_guy in self.guys:
a_guy.search_face(self.face_params)
if a_guy.has_face(): # face(s) have been found
print "%d faces found for %s" % (a_guy.num_faces(), a_guy.name)
# Informative functions
def number_guys(self):
"""
Simply returns the number of guys in the current to-be movie
"""
return len(self.guys)
def show_faces(self, time=1000, mode="debug"):
"""
Show all faces that have been found for the guys.
The time for which each image will be diplayed can be chosen.
Several modes can be chosen to adapt the result.
"""
# TODO: Where can I find the lower function?
if mode == "debug" :
for a_guy in self.guys:
a_guy.show_debug(self.face_params, time)
else:
print "Warning : only supported mode is debug"
def save_faces(self, out_folder, format="png", debug=True):
"""
Save all faces into out_folder, in the given format
Debug is used to draw rectangles around found faces
"""
for a_guy in self.guys:
a_guy.save_result(self.face_params,
out_folder,
format,
debug)
if __name__ == "__main__":
# quick and dirty tests
root_fo = "C:\Users\jll\perso\FaceMovie"
in_fo = os.path.join(root_fo, "input\Axel")
in_fo = os.path.join(root_fo, "input\Axel_tsts")
#in_fo = os.path.join(root_fo, "input\Axel")
out_fo = os.path.join(root_fo, "output")
par_fo = os.path.join(root_fo, "haarcascades")
my_movie = FaceMovie(in_fo, out_fo, par_fo)
my_movie.list_guys()
my_movie.search_faces()
#my_movie.show_faces(2000, "debug")
my_movie.save_faces("output", debug=True)
print "Done !"

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB