Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at the New York ColdFusion User Group (Sep. 2008) with:

Selecting My SOTR Caffeinated Raffle Winner Using ColdFusion And Face.com's Facial Detection API

By Ben Nadel on
Tags: ColdFusion

A couple of days before Scotch On The Rocks (SOTR - Europe's premier ColdFusion conference), I Tweeted that anyone who brought be a sugar-free energy drink would get entered into a raffle. I know it's taken me a few weeks to get around to it; but, Darren Walker, Gareth Sykes, Paul Klinkenberg, Tom Chiverton (and Rachael), and Tyler Schofield were awesome enough to supply me with enough caffeine to cope with the 5-hour timezone shift and lack of sleep.

 
 
 
 
 
 
For funzies - at #SOTR - anyone who brings me a *sugar-free* energy drink gets entered in a raffle :) I will be needing caffeine! 
 
 
 

Since I had my picture taken with each one of them, I thought a fun way to go about selecting a winner would be through some sort of Facial Recognition algorithm. At first, I was going to try the Faint project (as described by Todd Sharp). But, as it turns out, Faint only works on Windows machines thanks to some of the linked libraries that it uses.

After a bit of Googling, however, I discovered Face.com. Face.com provides a free developer API for facial recognition and organization of people within photos. But, more than just facial detection, it helps you match faces across photos and then organize them into some sort of contact database (at least from what I gather) that will help you automatically tag photos going forward.

Face.com appears to have a really robust feature set; but for our purposes, we'll only need the raw face detection API. The API can be accessed using CFHTTP in which one photo can be posted at a time. For each photo that is posted, Face.com returns a collection of "Tags" - each of which represents a facial detection within the given photo.

 
 
 
 
 
 
Face.com facial detection API response. 
 
 
 

For each tag, Face.com reports the boundaries of the face, facial landmarks (ie. eyes, nose, mouth, ears), and rotational position of the face (which I assume is what Yaw, Roll, and Pitch represent). It also tells you, with a given degree of confidence, if the visual representation is, in fact, a face, whether or not it's smiling, wearing glasses, and what gender it is. To select my Caffeinated Comrade winner, I figured I could use Face.com to select the photo that contains the most "confident" face. That is, the photo in which the API is most sure about: Face, Gender, Smiling, and Eye Glasses.

  • <!---
  • Give this page some extra timeout wiggle room since we'll be
  • uploading and then detail with image manipulation APIs.
  • --->
  • <cfsetting requesttimeout="#(60 * 2)#" />
  •  
  •  
  • <!--- Define the path to our images directory. --->
  • <cfset imageDirectory = expandPath( "./images/" ) />
  •  
  • <!---
  • Define the image to upload for face-detection. These are
  • the awesome ColdFusion developers who hooked me up with
  • some caffeinated drinks when I was at Scotch on the Rocks.
  • --->
  • <cfset imageFiles = [
  • "darren_walker.jpg",
  • "gareth_sykes.jpg",
  • "paul_klinkenberg.jpg",
  • "tom_chiverton.jpg",
  • "tyler_schofield.jpg"
  • ] />
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <!---
  • Create an array to hold detection results. Face.com can only
  • accept one image at a time, so we'll have too loop over the
  • photos and post each individually, storing the results in turn.
  • --->
  • <cfset results = [] />
  •  
  • <!---
  • Post these photos to Face.com. Each photo will be analyzed
  • for faces; each face will be returned as a "Tag" with
  • positional information as well as confidence as to whether
  • or not the face was an actual face.
  • --->
  • <cfloop
  • index="imageFile"
  • array="#imageFiles#">
  •  
  •  
  • <!---
  • Post the photo to the Face.com API.
  •  
  • NOTE: By using a ".json" as the target web service file
  • extension, we will be getting a JSON response.
  • --->
  • <cfhttp
  • result="detectionRequest"
  • method="post"
  • url="http://api.face.com/faces/detect.json">
  •  
  • <!--- Post credentials. --->
  • <cfhttpparam
  • type="url"
  • name="api_key"
  • value="#request.faceAPIKey#"
  • />
  •  
  • <cfhttpparam
  • type="url"
  • name="api_secret"
  • value="#request.faceAPISecret#"
  • />
  •  
  • <!---
  • Set the matching strength. We'll use Normal. An
  • "Agressive" search might turn up more faces; but for
  • our pursoses, that shouldn't be necessary (the photos
  • are very clear).
  • --->
  • <cfhttpparam
  • type="url"
  • name="detector"
  • value="Normal"
  • />
  •  
  • <!--- Post the file (as "filename"). --->
  • <cfhttpparam
  • type="file"
  • name="filename"
  • file="#imageDirectory##imageFile#"
  • mimetype="images/jpeg"
  • />
  •  
  • </cfhttp>
  •  
  •  
  • <!---
  • Deserialize the facial detection response and save the
  • data structure for evaluation.
  • --->
  • <cfset arrayAppend(
  • results,
  • deserializeJSON( detectionRequest.fileContent )
  • ) />
  •  
  • </cfloop>
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <!---
  • At this point, we have passed each image to the Face.com and
  • should have detection information. Now, we are going to take
  • those results and see if we can highlight the faces in our
  • images.
  • --->
  •  
  •  
  • <!--- Create an array to hold our ColdFusion images objects. --->
  • <cfset images = [] />
  •  
  • <!---
  • Each of the tags within our images (as returned from Face.com)
  • provides information about the face, gender, eye glasses, ears,
  • and mouth. And each of these is given a confidence. To figure
  • out the WINNER, we are going to which photo contains the face
  • that Face.com gives the most confident detection.
  • --->
  • <cfset entries = queryNew(
  • "url, confidence",
  • "cf_sql_varchar, cf_sql_integer"
  • ) />
  •  
  •  
  • <!--- Loop over the results to help create the images. --->
  • <cfloop
  • index="resultIndex"
  • from="1"
  • to="#arrayLen( results )#"
  • step="1">
  •  
  •  
  • <!--- Get a reference to this result. --->
  • <cfset result = results[ resultIndex ].photos[ 1 ] />
  •  
  • <!--- Create a ColdFusion image for this result. --->
  • <cfset image = imageNew( "./images/#imageFiles[ resultIndex ]#" ) />
  •  
  • <!---
  • Get the image height and width (we can get this from the
  • image, but since the result provides it already for their
  • calculations, just grab it).
  • --->
  • <cfset imageWidth = result.width />
  • <cfset imageHeight = result.height />
  •  
  • <!---
  • Now, loop over the "Tags". Each tag represents a facial
  • detection within the current photo.
  • --->
  • <cfloop
  • index="tag"
  • array="#result.tags#">
  •  
  • <!---
  • Get the tag dimensions. While we have defined the image
  • dimensions above as pixels, everything about the tags is
  • expressed as percentage (to allow for arbitrary image
  • scaling).
  • --->
  • <cfset tagWidth = (tag.width / 100 * imageWidth) />
  • <cfset tagHeight = (tag.height / 100 * imageHeight) />
  •  
  • <!---
  • The coordinates of the tag are expressed as X/Y at the
  • center of the tag.
  • --->
  • <cfset tagCenterX = (tag.center.x / 100 * imageWidth) />
  • <cfset tagCenterY = (tag.center.y / 100 * imageHeight) />
  •  
  • <!---
  • Before we draw the box, let's define the line properties
  • of the rectangle.
  • --->
  • <cfset strokeOptions = {
  • width = 3,
  • endcaps = "round"
  • } />
  •  
  • <!--- Set the line drawing properties. --->
  • <cfset ImageSetDrawingStroke( image, strokeOptions ) />
  •  
  • <!--- Draw the box around the current face (Tag). --->
  • <cfset imageDrawRect(
  • image,
  • (tagCenterX - (tagWidth / 2)),
  • (tagCenterY - (tagHeight / 2)),
  • tagWidth,
  • tagHeight
  • ) />
  •  
  •  
  • <!--- Each tag is going to be an entry in our query. --->
  • <cfset queryAddRow( entries ) />
  •  
  • <!--- Set the query values. --->
  • <cfset entries[ "url" ][ entries.recordCount ] = javaCast(
  • "string",
  • results[ resultIndex ].photos[ 1 ].url
  • ) />
  •  
  • <cfset entries[ "confidence" ][ entries.recordCount ] = javaCast(
  • "int",
  • (
  • tag.attributes.face.confidence +
  • tag.attributes.gender.confidence +
  • tag.attributes.glasses.confidence +
  • tag.attributes.smiling.confidence
  • )
  • ) />
  •  
  • </cfloop>
  •  
  •  
  • <!---
  • Now that all of the tags have been applied to the image,
  • store the upadated ColdFusion image object for later use.
  • --->
  • <cfset arrayAppend( images, image ) />
  •  
  •  
  • </cfloop>
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <h1>
  • Scotch On The Rocks Caffeinated Comrades
  • </h1>
  •  
  •  
  • <cfloop
  • index="image"
  • array="#images#">
  •  
  • <p>
  • <!--- Draw the image to the browser. --->
  • <cfimage
  • action="writeToBrowser"
  • source="#image#"
  • width="505"
  • />
  • </p>
  •  
  • </cfloop>
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <!---
  • Now that we have output all the images and facial detection
  • boxes, it's time to see which face Face.com was most generally
  • confident about.
  •  
  • Re-order to the entries query by confidence.
  • --->
  • <cfquery name="entries" dbtype="query">
  • SELECT
  • *
  • FROM
  • entries
  • ORDER BY
  • confidence DESC
  • </cfquery>
  •  
  • <h2>
  • And The Winner Is....
  • </h2>
  •  
  • <p>
  • <!--- Output the winner. --->
  • <cfimage
  • action="writeToBrowser"
  • source="#entries.url#"
  • width="505"
  • />
  • </p>

When we run the above code, we get the following page output:

 
 
 
 
 
 
SOTR Caffeinated Comrades raffler winner selection using Face.com facial detection API. 
 
 
 

Congratulations to Tom Chiverton and Rachael!

And, on a personal note, an extra special thanks to Tom and Rachael who are totally awesome and let me tag along Saturday after the SOTR conference to explore Edinburgh and The Whiskey Experience. Oh, and they also got me my first-ever Irn-Bru which, from what I can tell, is basically liquified Gummi Bears.

 
 
 
 
 
 
IrnBru - pronounced, urn-brew. 
 
 
 

It's pronounced, earn-br'!




Reader Comments

Liquified Gummi Bears, LOL. Quite a good description actually :)

I've always pronounced it Iron Brew, and a few copy cat companies have labelled it that as well. Helps the non-Scotch tongued out there I'm sure ;).

Glad you enjoyed it and the trip though Ben; it's one of our national treasures :)

James
( +20yrs as a Scottish legal alien )

Reply to this Comment

@James,

I started off pronouncing it "iron brew", but was quickly corrected by the "locals" that I was adding far too many syllables :)

It seems that a lot of English-to-Scottish translation is performed by simply removing parts of words :P

Reply to this Comment

Absolutely and it gets a LOT worse the further north you go :). I swear some of the locals here don't even speak the same language as me!!

They would have probably given you a different answer / pronunciation for it as well, lol.

Reply to this Comment

Liquified gummi bears? Okay, that's going to be easier to digest (these gummi bears sure are tough to chew, aren't these?).

And . . . how does Face.com determine whether a person is smiling or not?

Reply to this Comment

@Lola,

I have no idea how Face.com works - photo analysis is magic as far as I'm concerned :)

Reply to this Comment

Now that was an awesome way to do a raffle! I enjoyed reading it, but will be feeling sad and disappointed all night now :-P
See you next year probably!

Reply to this Comment

@Paul,

Thanks - AND, thanks for the drink! Always great to see you (and congratulations again on the beautiful baby!). See you next year.

Reply to this Comment

Nice post Ben and awesome way to pick a winner! I have to question the results though, as I believe that the confidence rating is based on all the faces detected in the photo! (kidding)

I was great to catch up with you again, I'm just sad that I didn't get a new picture at all this year :)

Reply to this Comment

@John,

Always good to catch up - sorry we didn't get to talk more. The conference is getting so big these days! I'll definitely see you next year. My goal: be able to talk to you about ORM by next year :D

Reply to this Comment

Post A Comment

?
You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.