The project I’m currently working on requires cropping of hundreds of portraits from the First World War archives at the Imperial War Museum.
Running a batch script on a directory of images is straight forward except my script is pretty dumb and tries to do a centre crop to create a square image. Unfortunately some of these images are not suitable for centre cropping:
Some of these portraits are quite long in height so a centre crop often results in the decapitation of the subject!
The logical thing to do here is to have your script first detect where the face is and then make a more intelligent crop to ensure the face remains in the new image. But surely face recognition requires super computers and several PhDs? Yes, it does. But we don’t really care who the subject is, we just need to know where the face is (or at least something that looks like a face). What we need is face detection, not recognition.
I was surprised to come across this little beauty: OpenCV, an open library for vision processing and luckily there’s a nice Python binding for it.
I tried out a sample from Robert Martin McGuire’s blog and was amazed at how simple and effective it was.
Robert’s script spits out two coordinates from the image that places a rectangle of where the face is. If your image has more than one person in it (or things that look like faces – more on that later) it will return two sets for each face.
Here’s the same image after running it through our face detection script:
Perfect! now we can adjust our cropping script to ensure that the face is within the bounds.
I tried this using really high resolution images and the script detected several faces in the image where there was only one. The problem is that if you have a lot of detail in your image like background artifacts and smudges there is likely to be some pattern that matches those of a face. For best results you may want to work with smaller images.
You can get this script from Robert’s site but here it is for all you lazy people. Make sure you’ve installed all necessary libraries. On Debian/Ubuntu you should be able to use this:
$ sudo apt-get install python-opencv libcv-dev python-imaging
Test out the script like this:
$python thescript.py original.jpg output.jpg
If you get errors chances are it’s not finding the XML files. I had to copy these manually to get it to work. Note: this script doesn’t do any cropping, it just shows you where the face is and you will need to do the cropping yourself with some trial and error.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
import os import sys from opencv.cv import * from opencv.highgui import * import Image, ImageDraw def print_rectangle(x1,y1,x2,y2): #function to modify the img im = Image.open(sys.argv) draw = ImageDraw.Draw(im) draw.rectangle([x1,y1,x2,y2]) im.save(sys.argv) def detectObjects(image): """Converts an image to grayscale and prints the locations of any faces found""" grayscale = cvCreateImage(cvSize(image.width, image.height), 8, 1) cvCvtColor(image, grayscale, CV_BGR2GRAY) storage = cvCreateMemStorage(0) cvClearMemStorage(storage) cvEqualizeHist(grayscale, grayscale) cascade = cvLoadHaarClassifierCascade( '/usr/share/opencv/haarcascade/haarcascade_frontalface_alt.xml', cvSize(1, 1)) faces = cvHaarDetectObjects(grayscale, cascade, storage, 1.2, 2, CV_HAAR_DO_CANNY_PRUNING, cvSize(50, 50)) if faces.total > 0: for f in faces: x1,y1,x2,y2=f.x,f.y,f.x+f.width,f.y+f.height print("[(%d,%d) -> (%d,%d)]" % (f.x, f.y, f.x + f.width, f.y + f.height)) print_rectangle(x1,y1,x2,y2) #call to a python pil def main(): image = cvLoadImage(sys.argv); detectObjects(image) if __name__ == "__main__": main()