This is a quick sample using F# and Accord.NET to do face detection. The method uses the provided Haar-like feature detection. The results aren’t particularly good, but for little effort it’s an ok start. At a minimum, it does reasonably well at detecting potential regions of interest. For the test images, the best improvements were found when constraining the min/max range based on the known sizes of faces in the pictures.
Using Paket, here is a sample paket.dependencies file.
open System open Accord open Accord.Imaging open Accord.Vision open Accord.Vision.Detection open System.Drawing open System.Drawing.Imaging open System.IO open Accord.MachineLearning
let imageRoot = Path.GetFullPath(Path.Combine(__SOURCE_DIRECTORY__, "../data/haar_face/")) let resultsRoot = Path.GetFullPath(Path.Combine(__SOURCE_DIRECTORY__, "../data/haar_face/results/"))
Two cascades are provided, FaceHaarCascade() and NoseHaarCascade(). Custom ones can be created, but for an initial test, one of the provided cascades is good enough. The minSize and maxSize values are hardcoded hacks to match the expected face sizes in the test images. They are used to define the minimum and maximum window size to consider as the algorithm scans the image.
1 2 3 4
let cascade = Cascades.FaceHaarCascade() //let cascade = Cascades.NoseHaarCascade() let minSize = 200 let maxSize = 2000
This is a function that will add a bounding box to the bitmap with the specified line color and width.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// Draw bound-box on Bitmap // TopLeft: (x1, y1) // BottomRight: (x2, y2) let drawRectangle (bitmap:Bitmap) (x1:int) (y1:int) (x2:int) (y2:int) (lineWidth:int) (lineColor:Color) = [x1..x2] |> List.iter (fun x -> [0..lineWidth] |> List.iter (fun i -> bitmap.SetPixel(x, y1 + i, lineColor) bitmap.SetPixel(x, y2 - i, lineColor)))
[y1..y2] |> List.iter (fun y -> [0..lineWidth] |> List.iter (fun i -> bitmap.SetPixel(x1 + i, y, lineColor) bitmap.SetPixel(x2 - i, y, lineColor)))
This is the core component of interest. It loads a bitmap, runs the object detector, draws bounding boxes around the detected locations, then saves a “result” image.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
let processImage (cascade:HaarCascade) (minSize:int) (maxSize:int) (resultsDir:string) (imageName:string) = let resultImageName = Path.Combine(resultsDir, Path.GetFileName(imageName)) let bitmap = new Bitmap(imageName)
let haar = HaarObjectDetector(cascade, minSize) haar.MaxSize <- new Size(new Point(new Size(maxSize, maxSize))) let faceFinder = haar.ProcessFrame(bitmap)