There are quite a few interesting APIs evolving in the "modern web", but not all of them are going to be things you would use in most projects. I've been very public about my feelings concerning canvas for example. Great for games and charting - but not much else. That doesn't make it a bad feature. It just makes it one I won't use terribly often. Whenever I read about some new cool feature being developed, my mind starts trying to figure out what they could be used for in a practical sense. Obviously what's practical to you may not be practical to me, but figuring out how I would actually use a feature is part of how I learn it.
One such feature is getUserMedia (W3C Spec). This is a JavaScript API that gives you access to (with permission) the user's web cam and microphone. getUserMedia is currently supported in Opera and Chrome (I believe it is in version 18 now, but you may need to grab Canary. You also need to enable it. Instructions on that here.) Once you get past actually enabling it, the API is rather simple. Here's a quick request for access:
The first argument to getUserMedia is the type. According to the spec, this is supposed to be an object where you enable audio, video, or both, like so: {audio:true, video:true}. However in my testing, passing a string, "video", worked fine. The demo you will be seeing is based on another demo so that line possibly came from an earlier build that still works with Chrome. The second and third arguments are your success and failure callbacks respectively.
You can see in the gist where the success handler assigns the video stream to an HTML5 video tag. What's cool then is that once you have that running you can use the Canvas API to take pictures. For a demo of this, check out Greg Miernicki's demo:
If this demo doesn't work for you - then stop - and try following the instructions again to enable support. (Although I plan on sharing a few screen shots so if you just want to keep reading, that's fine too.)
Based on Greg's demo, it occurred to me that there is something cool we can do with pictures of our web cams. (Cue the dirty jokes.) I remembered that Face.com had a very cool API for parsing pictures for faces. (I blogged a ColdFusion example back in November.) I wondered then if we could combine Greg's demo with the Face.com API to do some basic facial recognition.
Turns out there are a few significant issues with this. First - while Face.com has a nice REST API, how would we use it from a JavaScript application? Secondly - Face.com requires you to either upload a picture or give it a URL. I know I could send a canvas picture to a server and have my backend upload it to Face.com, but is there a way to bypass the server and send the picture right to the API?
The first issue actually turned out to be a non-issue. Face.com implements CORS (Cross-Origin Resource Sharing). CORS basically allows a server to expose itself to Ajax calls from documents on other domains. It's a great feature and I hope more services enable it.
The more complex issue then was taking the canvas data and sending it to Face.com. How can I fake a file upload? Turns out there's another cool new trick - FormData. Fellow ColdFusion blogger Sagar Ganatra has an excellent blog entry on the topic. Here's how I used it:
Let's look at this line by line. First off - I need to get the binary data from the canvas object. There's a few ways of doing this, but I wanted a Binary Blob specifically. Notice the dataURIToBlob method. This comes from a StackOverflow post I found a few weeks back.
I create a new FormData object and then simply begin setting my values. You can see I pass in a few API requirements but the crucial parts are the filename and file object itself.
Below that you can see the simple jQuery Ajax call. Face.com has a variety of options, but I basically just asked it to return an estimated age, gender, mood, and whether or not the person was smiling and wearing glasses. That's it. I get a nice JSON packet back and format it.
Now obviously no API is perfect. I've had different levels of results from using the API. Sometimes it's pretty damn accurate and sometimes it isn't. Overall though it's pretty cool. Here are some scary pictures of yours truly testing it out.
Ok, ready to test it yourself? Just click the demo button below. For the entire source, just view source! This is 100% client-side code.
For another look at getUserMedia, check out these examples:
- It's Curtains for Marital Strife Thanks to getUserMedia
- Testing WebRTC on Chrome
- Bleeding Edge HTML5, WebRTC & Device Access
- Capturing Audio & Video in HTML5
Edit on May 23: Chrome recently modified the getUserMedia API to match the spec (I believe) which requires you to pass an object of media you want, so instead of "video", I used {video:true}.
Archived Comments
I'm sueing! Your demo says that I'm 47 while yours only says 22.
I built it to reduce my age. ;)
Never a bad idea to compliment your maker.
I've heard of women faking their age, but web programmers?
Actually, I would have put 19 myself :>)
Another great example of this from Daniel Goodwin. He actually adds training support to his demo.
http://vimeo.com/26850592
Demo is not working in Chrome 21. I think they have changed the API to accept a map instead of a comma separated list i.e. {video: true} instead of 'video'
Yep, I saw that recently when I tested my code. I will update the blog entry today. Thanks!
Post + demo updated. Thanks Sagar.
I should now post the other one I did. I was going to show it at cfObjective but never got around to it.
I tweaked your code to send the picture to a CF page that saves it to the server and sends back the file name.
Thanks for the blog post!
https://github.com/rchadgra...
Glad to help - but please note that this API is going away. :( There are two new APIs out there that I know of. (Don't have em handy right now though.)
I don't have the URLs, but they are ReKognition and Lambda.
I thought i would ditch the function dataURItoBlob() in the JS and use binaryDecode() in CF. I get "The input and output encodings are not same".
I am taking the raw data from canvas.toDataURL() and sending it to my CF page.
Any ideas to try to get CF to Decode this base64 data?
This is beginning of the data being sent:
image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQA
Did you try toBinary?
ToBinary() results in "The parameter 1 of function ToBinary, which is now data:image/jpeg;base64,/9j/4AAQSk... must be a Base-64 encoded string."
cfset image = BinaryDecode(tobase64(file), "Base64") AND
cfset image = tobase64(file)
does not throw an error, but the JPG that is written by CFFile action=write is corrupt.
Can you shoot me (via email) a file with JUST the data?
So it looks like base64 sometimes includes clues in front of it - like
data:image/jpeg;base64,
I'm assuming that is part of the spec, but CF doesn't like it when using toBinary. I believe - stress believe - you are safe to just remove it:
contents = fileRead("/Users/ray/Downloads/test.txt");
contents = replace(contents, "data:image/jpeg;base64,","");
b = toBinary(contents);
This worked for me. Well, it didn't throw an error. I didn't try displaying it. ;)
Ok, I did, and it worked.
<cfimage action="writetobrowser" source="#b#">
Yay! That fixed it.
Good thing you did not view the image. It is my messy office and ugly mug.
Thanks Ray!
I've tried your demo,but it doesn't work, it keeps saying: "working hard for the money.." is it my pc or the code? because I would really love to try it out!
Face.com shut down their API.
Although that may not be it. What error do you see in the console?
It is BlobBuilder - that isn't supported anymore. I'll try to get a fix later today.
So I fixed the BlobBuilder thing (it is easy to work around), but the API still won't respond, so I'm pretty sure that the API itself is dead. There are alternatives out there though.
Btw, an alternative: http://www.raymondcamden.co...
Well, that's a shame. But thanks for the extra info!
http://www.seeingmachines.c... is this a possibility?
Well, did you look at the site and try to figure it out? :) That link appears to be for Windows code. If you don't see a REST or web service option, then you are out of luck.
Hi,
can you b able to suggest how u have made this web api
I didn't make the API.