Skip to content

Instantly share code, notes, and snippets.

@j0hn
Created January 7, 2013 19:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save j0hn/4477557 to your computer and use it in GitHub Desktop.
Save j0hn/4477557 to your computer and use it in GitHub Desktop.
Face classification using SimpleAI
#!/usr/bin/env python
# coding: utf-8
import os
import math
import random
from PIL import Image
from simpleai.machine_learning import ClassificationProblem, is_attribute, NaiveBayes, precision, KNearestNeighbors
BASE_PATH = os.path.dirname(os.path.abspath(__file__))
CORPUS_PATH = os.path.join(BASE_PATH, "corpus", "faces")
class FaceImage(object):
"""
Example of the face image corpus.
Attributes:
* userid: the user id of the person in the image
* pose: the head position of the person.
possible values: straight, left, right, up
* expression: the facial expression of the person.
possible values: neutral, happy, sad, angry.
* eyes: is the eye state of the person.
possible values: open, sunglasses.
* scale: the scale of the image.
possible values: 1, 2, and 4.
1 indicates a full-resolution image (128 columns by 120 rows);
2 indicates a half-resolution image (64 by 60);
4 indicates a quarter-resolution image (32 by 30).
"""
def __init__(self, filepath):
self.image = Image.open(filepath)
self.width, self.height = self.image.size
self.pixels = self.image.load()
# filename format:
# <userid> <pose> <expression> <eyes> <scale>.bmp
filename = filepath.rsplit("/", 1)[-1]
filename = filename[:-4] # Remove the .bmp part
fields = filename.split("_")
if len(fields) == 4:
fields.append("1") # Add scale to the ones without
self.userid, self.pose, self.expression, self.eyes, self.scale = fields
def __len__(self):
return self.width * self.height
def __iter__(self):
"""
Iterates over every pixel in the image and returns
a 3-uple with (x, y, intensity) of the pixel.
"""
for x in xrange(self.width):
for y in xrange(self.height):
red, green, blue = self.pixels[(x, y)]
assert red == green == blue
yield x, y, red
class FaceClassification(ClassificationProblem):
@is_attribute
def black_center_of_mass_x(self, image):
return self.black_center_of_mass(image)[0]
@is_attribute
def black_center_of_mass_y(self, image):
return self.black_center_of_mass(image)[1]
def black_center_of_mass(self, image):
mean = self.image_mean_level(image)
coords = self.center_of_mass(image, lambda x, y, color: color < mean)
return coords
return self.discretize(coords)
@is_attribute
def white_center_of_mass_x(self, image):
return self.white_center_of_mass(image)[0]
@is_attribute
def white_center_of_mass_y(self, image):
return self.white_center_of_mass(image)[1]
def white_center_of_mass(self, image):
mean = self.image_mean_level(image)
coords = self.center_of_mass(image, lambda x, y, color: color >= mean)
return coords
return self.discretize(coords)
def discretize(self, coords):
bot, top, N = 0.20, 0.8, 10
x, y = coords
# x = min(x, top)
# x = max(x, bot) - bot
# y = min(y, top)
# y = max(y, bot) - bot
# print coords, x, y
return int(N * x), int(N * y)
def center_of_mass(self, image, selector):
sx = 0
sy = 0
n = 0
for x, y, color in image:
if not selector(x, y, color):
continue
sx += x
sy += y
n += 1
cx, cy = sx / n, sy / n
# cx = cx / image.width
# cy = cy / image.height
return cx, cy
def image_mean_level(self, image):
mean = getattr(image, "_mean_level", None)
if mean is None:
mean = sum(pixel[2] for pixel in image) / len(image)
image._mean_level = mean
return mean
def distance(self, image_a, image_b):
be = self.euclidean(self.black_center_of_mass(image_a),
self.black_center_of_mass(image_b))
we = self.euclidean(self.white_center_of_mass(image_a),
self.white_center_of_mass(image_b))
return be + we
def euclidean(self, coords_a, coords_b):
ax, ay = coords_a
bx, by = coords_b
return math.sqrt((ax - bx) ** 2 + (ay - by) ** 2)
def target(self, image):
return image.eyes
class OnlineFacesCorpus(object):
def __init__(self, corpus_folder, accept=None):
self.corpus_folder = corpus_folder
self.accept = accept
def __iter__(self):
i = 0
for folder in os.listdir(self.corpus_folder):
folder_path = os.path.join(self.corpus_folder, folder)
if not os.path.isdir(folder_path):
continue
for filename in os.listdir(folder_path):
filepath = os.path.join(folder_path, filename)
if os.path.isdir(filepath):
continue
if filepath.endswith(".bmp"):
if self.accept(i):
yield FaceImage(filepath)
i += 1
if i % 10 == 0:
print "read {} examples".format(i)
def main():
testindexes = set(random.sample(xrange(1800), 3))
test = OnlineFacesCorpus(CORPUS_PATH, accept=lambda i: i in testindexes)
corpus = OnlineFacesCorpus(CORPUS_PATH, accept=lambda i: i not in testindexes)
problem = FaceClassification()
# i = 0
# for image in corpus:
# print [a(image) for a in problem.attributes]
# i += 1
# if i == 10:
# break
# return
classifier = KNearestNeighbors(corpus, problem)
p = precision(classifier, problem.target, test)
print "Precision = {}".format(p)
if __name__ == "__main__":
main()
#!/usr/bin/env python
# coding: utf-8
from faces import *
path = "/some/path/"
img = FaceImage(path)
prob = FaceClassification()
mean = prob.image_mean_level(img)
mean = mean * 2
new = img.image.copy()
pix = new.load()
for x in xrange(img.width):
for y in xrange(img.height):
if pix[(x, y)][0] > mean:
pix[(x, y)] = (255, 255, 255)
else:
pix[(x, y)] = (0, 0, 0)
wx, wy = prob.white_center_of_mass(img)
bx, by = prob.black_center_of_mass(img)
pix[(wx, wy)] = (255, 0, 0)
pix[(bx, by)] = (0, 0, 255)
new.save("out.bmp", "BMP")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment