Instantly share code, notes, and snippets.

What would you like to do?
import cv2, glob, math, time
import numpy as np
# This function is used to sort a list made of dictionnaries by a value in the dictionnaries
def get_color(item):
return item["average_color"]
# These variables are for use with OpenCV2 only
cascPath = "haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(cascPath)
# Size of the side of the square of each face in pixels
face_size = 40
# The folder where the images are
imagesList = glob.glob("data/*.jpg")
# The ratio of the final image. Using decimal separators ensures that Python treats the values as floats, not integers
ratio = 16.0/9.0
# Initial values for counters we use for debugging puroposes
no_face_found = 0
could_not_paste = 0
# Relative size of the width of the image to the width of the infobox
infobox_relative_w = 3
# This list will contain all faces
faces_list = []
# Go through all images in the folder
for imagePath in imagesList:
imageName = imagePath.split("/")[1].replace(".jpg", "")
image = cv2.imread(imagePath)
# Converts to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Detect faces in the image
faces = faceCascade.detectMultiScale(
minSize=(30, 30),
flags =
# OpenCV found just 1 face
# We ignore all images with 0 or 2 or more faces
if (len(faces) == 1):
for (x, y, w, h) in faces:
# Here, there's a small trick. We crop the images twice. We do a very drastic crop
# first, to capture the face without any hair (black hair will skew the
# analysis). A second crop on the original image is done, just to make a square image.
# First crop
# The offset is the amount by which to crop, set to one third of the image's width
offset = int(float(w) / 3)
# Crop a tiny square
crop_img = image[y + offset:y+w-offset, x+offset:x+w-offset]
# Computes the average color based on the average value of each channel
r, g, b, a = cv2.mean(crop_img)
average = r + g + b
# Second crop
# Crop the image to display with a smaller offset
offset = int(float(w) / 10)
img = image[y + offset:y+w-offset, x+offset:x+w-offset]
# Resize the image to the size we decided above
img = cv2.resize(img, (face_size, face_size))
# Adds the image and the average color to the list of faces
faces_list.append({"image": img, "average_color": average})
# If no face was found, outputs an error message
no_face_found += 1
print "No face found for image: "+imageName
# Order the list of faces by average_color
faces_list = sorted(faces_list, key=get_color)
# Computes the width and length of the collage, so that the ratio we set above is respected
num_faces = len(faces_list)
num_rows = round(math.sqrt(num_faces * (1/ratio))) + 1
num_cols = round(ratio * num_rows)
# The size of the final image
w = int(num_cols * face_size)
h = int(num_rows * face_size)
# Creates the final, blank image
collage = np.zeros((h,w,3), np.uint8)
# Opens the infobox file
infobox = cv2.imread("infobox.png")
# Computes the size of the infox, which is one fifth of the total image
infobox_w = int(face_size * round(num_cols / infobox_relative_w))
infobox_h = int(infobox_w * (float(infobox.shape[0])/float(infobox.shape[1])))
# Resizes the infobox
infobox = cv2.resize(infobox, (infobox_w, infobox_h))
# Pastes the infobox in place, at the top right
collage[0:infobox_h, w-infobox_w:w] = infobox
# Makes a collage of all faces, starting from the bottom right
col_count = num_cols - 1
row_count = num_rows - 1
for face in faces_list:
# Pastes the individual face in the large collage
y = row_count * face_size
x = col_count * face_size
if (row_count < 0):
# No more space left
collage[y:y+face_size, x:x+face_size] = face["image"]
# Changes colum
col_count -= 1
# if the end of line is reached, go up
if col_count == -1:
# If the infobox is reached, go all the way left
if (y < infobox_h):
col_count = num_cols - round(num_cols / infobox_relative_w) - 1
col_count = num_cols - 1
row_count -= 1
# Saves the final collage
cv2.imwrite("data/final/"+ str(time.time()) +".jpg", collage)
# Outputs the number of faces for debuging purposes
print "total faces: ", len(faces_list), " total not found:", no_face_found, "could no paste: ", could_not_paste
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment