本文可視為《用HTML5實現Face Service》的進階,在Face Service的基礎上,我們將使用純Javascript來實現如下的功能:
從本文的內容中,你將意識到,Javascript能做的,能實現的,遠遠比你想象的多。
示範
一、實現
1、Face Service
Face.com有包括檢測、識別在內的多個API介面,根據《用HTML5實現Face Service》一文,我們已經可以實現圖片上傳,並得到檢測的結果,結果如下:
返回的參數
返回參數的詳細解釋參見http://developers.face.com/docs/api/return-values/,其中tags為多張照片的識別結果,每一個結果包括了耳朵、眼睛、嘴、鼻的中心位置,以及年齡、性別、是否佩戴眼鏡、情緒、是否在笑等多種資訊。
上傳圖片並請求介面的代碼如下。
function buildRequest(imgSrc) {<br /> document.getElementById("detect").disabled = true;<br /> document.getElementById("beauty").disabled = true;</p><p> var canvas = document.getElementById('canvas'); <br /> var ctx = canvas.getContext('2d'); </p><p> var imgObj = new Image(); <br /> imgObj.src = imgSrc; <br /> canvas.width = imgObj.width; <br /> canvas.height = imgObj.height; <br /> ctx.drawImage(imgObj, 0, 0); <br /> var imgPixels = ctx.getImageData(0, 0, canvas.width, canvas.height); </p><p> document.getElementById("bigImg").style.width = imgObj.width;</p><p> var data = canvas.toDataURL('image/jpeg', 1.0); <br /> newblob = dataURItoBlob(data); </p><p> var formdata = new FormData(); <br /> formdata.append("api_key", "your key"); <br /> formdata.append("api_secret", "your secret"); <br /> formdata.append("filename","avatar.jpg"); </p><p> formdata.append("file",newblob); </p><p> $.ajax({ <br /> url: 'http://api.face.com/faces/detect.json?attributes=age_est,gender,mood,smiling,glasses', <br /> data: formdata, <br /> cache: false, <br /> contentType: false, <br /> processData: false, <br /> dataType:"json", <br /> type: 'POST', <br /> success: function (data) { <br /> handleResult(data.photos[0]); <br /> }<br /> }); <br />}
人臉標註
我們將根據Face Service的結果對五官和面部進行標註。標註的方式有兩種,一種是基於Canvas的繪圖,一種是傳統DIV方式標註。下面我們採用第二種方式,原理是在各個點上畫一個3*3的DIV,DIV的背景色為紅色,採用絕對位置。介面返回的五官數值為寬高所在點的百分比值,所以需要先進行換算。
標註五官的方法如下:
function drawFacial(data) {<br /> var width = data.width;<br /> var height = data.height;</p><p> var points = ["eye_left", "eye_right", "mouth_left", "mouth_center", "mouth_right", "nose", "ear_left", "ear_right"];</p><p> for(var i = 0; i < points.length; i++) {<br /> var div = document.createElement('div');<br /> div.style.width = "3px";<br /> div.style.height = "3px";<br /> div.style.backgroundColor = "red";<br /> div.style.position = "absolute";<br /> div.className = "facePoint";</p><p> var values = data.tags[0][points[i]];</p><p> if(values != null) {<br /> var left_x = values.x;<br /> div.style.left = left_x * width / 100 - 1 + "px";<br /> var left_y = values.y;<br /> div.style.top = left_y * height / 100 - 1 + "px";</p><p> document.body.appendChild(div);<br /> }<br /> }<br />}
標註人臉地區的方法如下:
function drawFace(data) {<br />var width = data.width;<br />var height = data.height;</p><p>var faceWidth = data.tags[0].width * width / 100;<br />var faceHeight = data.tags[0].height * height / 100;<br />var faceCenter = data.tags[0].center;</p><p>var div = document.createElement('div');<br />div.style.width = faceWidth + "px";<br />div.style.height = faceHeight + "px";<br />div.style.borderColor = "red";<br />div.style.borderWidth = "1px";<br />div.style.borderStyle = "dotted";<br />div.style.position = "absolute";<br />div.className = "faceBox";</p><p>div.style.left = faceCenter.x * width / 100 - faceWidth / 2 - 1 + "px";<br />div.style.top = faceCenter.y * height / 100 - faceHeight / 2 - 1 + "px";</p><p>document.body.appendChild(div);<br />}
從結果來看,Face.com的檢測結果非常精準。
人臉美容
對人臉我們採用兩種效果疊加進行美容,分別是增加亮度和模糊度,這樣可以讓人臉看起來更白,皮膚更好。
1、 增加亮度
增加亮度其實非常簡單,只需要在像素點的RGB通道上增加一個固定的值。代碼如下:
var imgPixels = ctx.getImageData(faceLeft, faceTop, faceWidth, faceHeight); </p><p>var data = imgPixels.data;<br />adjustment = 5;</p><p>for (var i = 0; i < data.length; i += 4) {<br />data[i] += adjustment;<br />data[i+1] += adjustment;<br />data[i+2] += adjustment;<br />}
2、 添加模糊效果
模糊效果較為複雜,平常我們常用到的包括均值模糊和高斯模糊。在HTML5Rocks上有一篇很棒的文章《IMAGE FILTERSWITH CANVAS》,裡面有各種影像處理效果,我們可以直接應用裡面的模糊效果(也即3*3的均值模糊)。
原圖
銳利化
索貝爾濾鏡
代碼如下:
var Filters = {};</p><p>Filters.convolute = function(pixels, weights, opaque) {<br /> var side = Math.round(Math.sqrt(weights.length));<br /> var halfSide = Math.floor(side/2);<br /> var src = pixels.data;<br /> var sw = pixels.width;<br /> var sh = pixels.height;<br /> // pad output by the convolution matrix<br /> var w = sw;<br /> var h = sh;<br /> var output = Filters.createImageData(w, h);<br /> var dst = output.data;<br /> // go through the destination image pixels<br /> var alphaFac = opaque ? 1 : 0;<br /> for (var y=0; y<h; y++) {<br /> for (var x=0; x<w; x++) {<br /> var sy = y;<br /> var sx = x;<br /> var dstOff = (y*w+x)*4;<br /> // calculate the weighed sum of the source image pixels that<br /> // fall under the convolution matrix<br /> var r=0, g=0, b=0, a=0;<br /> for (var cy=0; cy<side; cy++) {<br /> for (var cx=0; cx<side; cx++) {<br /> var scy = sy + cy - halfSide;<br /> var scx = sx + cx - halfSide;<br /> if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {<br /> var srcOff = (scy*sw+scx)*4;<br /> var wt = weights[cy*side+cx];<br /> r += src[srcOff] * wt;<br /> g += src[srcOff+1] * wt;<br /> b += src[srcOff+2] * wt;<br /> a += src[srcOff+3] * wt;<br /> }<br /> }<br /> }<br /> dst[dstOff] = r;<br /> dst[dstOff+1] = g;<br /> dst[dstOff+2] = b;<br /> dst[dstOff+3] = a + alphaFac*(255-a);<br /> }<br /> }<br /> return output;<br />};</p><p>Filters.tmpCanvas = document.createElement('canvas');<br />Filters.tmpCtx = Filters.tmpCanvas.getContext('2d');</p><p>Filters.createImageData = function(w,h) {<br /> return this.tmpCtx.createImageData(w,h);<br />};
最後對Canvas裡的映像疊加兩種效果的代碼如下:
var width = data.width;<br />var height = data.height;</p><p>var faceWidth = data.tags[0].width * width / 100;<br />var faceHeight = data.tags[0].height * height / 100;<br />var faceCenter = data.tags[0].center;</p><p>var faceLeft = faceCenter.x * width / 100 - faceWidth / 2;<br />var faceTop = faceCenter.y * height / 100 - faceHeight / 2;</p><p>var canvas = document.getElementById('canvas');<br />var ctx = canvas.getContext('2d');<br />var imgPixels = ctx.getImageData(faceLeft, faceTop, faceWidth, faceHeight); </p><p>var data = imgPixels.data;<br />adjustment = 5;</p><p>for (var i = 0; i < data.length; i += 4) {<br />data[i] += adjustment;<br />data[i+1] += adjustment;<br />data[i+2] += adjustment;<br />}<br />imgPixels = Filters.convolute(imgPixels, [ 1/9, 1/9, 1/9,<br />1/9, 1/9, 1/9,<br />1/9, 1/9, 1/9 ], 1);</p><p>ctx.putImageData(imgPixels, faceLeft, faceTop, 1, 1, faceWidth - 3, faceHeight - 3);
這樣,我們完成了所有的代碼。是不是相當的有成就感?看起來我們完成了相當新穎而酷炫的工作。
二、思考
1、 美容濾鏡是否有其他實現的方式?
之前我介紹過CSS3濾鏡,它有虛化和亮度調節兩個方法可以方便直接的為圖片添加濾鏡效果,可以在《遇見CSS3濾鏡》這篇文章裡瞭解到。
2、我們還可以實現哪些美容效果?
1)去紅眼
檢測到眼睛位置後,我們可以採用一定範圍內的濾鏡,把這個範圍內的紅色轉換為黑色,從而實現去紅眼效果。
2)眼睛放大
檢測到眼睛位置後,我們還可以採用一定的演算法,實現眼睛放大的效果。要體驗這個效果,可以下載百度魔圖用戶端體驗。
百度魔圖-強大的圖片處理用戶端
3)背景虛化
背景虛化效果基本和我們文中提到的效果是相反的方式。
3、 文中的效果是否還可以最佳化?
例如是否可以把文中美容的地區將正方形變為橢圓形,更為貼近人臉的形狀?其實並不難,讀者有興趣可深入思考和實現。
4、 除了美容,我們還能完成什麼樣的功能?
Face.com提供了三個功能示範:人臉Tag、情景照片、人臉尋找。對於應用開發人員來說,從來不缺乏創意,例如Face.com圖片檢測返回的參數裡包括圖片旋轉角度,我們可以利用這個參數實現圖片批量自動旋轉的功能(我最近在整理旅遊的照片時就深深的理解了這個功能的便利性和必要性)。或者基於微博的圖片愛情速配App?我期待更有創意和使用價值idea的出現。
擴充閱讀
- 《遇見CSS3濾鏡》
- 《如何使用HTML5實現拍照上傳應用》
- 《用HTML5建立超酷映像灰階漸層效果》
本文來自蔣宇捷的部落格,轉載請註明。