自IT Next,作者:Vincent Mühler,機器之心編譯,參與:Geek AI、張倩。
本文將為大家介紹一個建立在「tensorflow.js」內核上的 javascript API——「face-api.js」,它實現了三種卷積神經網絡架構,用于完成人臉檢測、識別和特征點檢測任務,可以在瀏覽器中進行人臉識別。
號外!號外!現在人們終于可以在瀏覽器中進行人臉識別了!本文將為大家介紹「face-api.js」,這是一個建立在「tensorflow.js」內核上的 javascript 模塊,它實現了三種卷積神經網絡(CNN)架構,用于完成人臉檢測、識別和特征點檢測任務。
像往常一樣,我們將查看一個簡單的代碼示例,這將使你能立即通過短短幾行代碼中的程序包開始了解這個 API。讓我們開始吧!
我們已經有了「face-recognition.js」,現在又來了另一個同樣的程序包?
如果你閱讀過本文作者另一篇關于「node.js」環境下進行人臉識別的文章《Node.js + face-recognition.js : Simple and Robust Face Recognition using Deep Learning》(Node.js + face-recognition.js:通過深度學習實現簡單而魯棒的人臉識別)(https://medium.com/@muehler.v/node-js-face-recognition-js-simple-and-robust-face-recognition-using-deep-learning-ea5ba8e852),你就會知道他在之前組裝過一個類似的程序包,例如「face-recgnition.js」,從而為「node.js」引入了人臉識別功能。
起初,作者并沒有預見到 JavaScript 社區對與人臉識別程序包的需求程度如此之高。對許多人而言,「face-recognition.js」似乎是一個不錯的、能夠免費試用的開源選項,它可以替代由微軟或亞馬遜等公司提供的付費人臉識別服務。但是作者曾多次被問道:是否有可能在瀏覽器中運行完整的人臉識別的工作流水線?
多虧了「tensorflow.js」,這種設想最終變為了現實!作者設法使用「tf.js
」內核實現了部分類似的工具,它們能得到和「face-recognition.js」幾乎相同的結果,但是作者是在瀏覽器中完成的這項工作!而且最棒的是,這套工具不需要建立任何的外部依賴,使用它非常方便。并且這套工具還能通過 GPU 進行加速,相關操作可以使用 WebGL 運行。
這足以讓我相信,JavaScript 社區需要這樣的一個為瀏覽器環境而編寫的程序包!可以設想一下你能通過它構建何種應用程序。
如何利用深度學習解決人臉識別問題
如果想要盡快開始實戰部分,那么你可以跳過這一章,直接跳到代碼分析部分去。但是為了更好地理解「face-api.js」中為了實現人臉識別所使用的方法,我強烈建議你順著這個章節閱讀下去,因為我常常被人們問到這個問題。
為簡單起見,我們實際想要實現的目標是在給定一張人臉的圖像時,識別出圖像中的人。為了實現這個目標,我們需要為每一個我們想要識別的人提供一張(或更多)他們的人臉圖像,并且給這些圖像打上人臉主人姓名的標簽作為參考數據。現在,我們將輸入圖像和參考數據進行對比,找到與輸入圖像最相似的參考圖像。如果有兩張圖像都與輸入足夠相似,那么我們輸出人名,否則輸出「unknown」(未知)。
聽起來確實是個好主意!然而,這個方案仍然存在兩個問題。首先,如果我們有一張顯示了多人的圖像,并且我們需要識別出其中所有的人,將會怎樣呢?其次,我們需要建立一種相似度度量手段,用來比較兩張人臉圖像。
人臉檢測
我們可以從人臉檢測技術中找到第一個問題的答案。簡單地說,我們將首先定位輸入圖像中的所有人臉。「face-api.js」針對人臉檢測工作實現了一個 SSD(Single Shot Multibox Detector)算法,它本質上是一個基于 MobileNetV1 的卷積神經網絡(CNN),在網絡的頂層加入了一些人臉邊框預測層。
該網絡將返回每張人臉的邊界框,并返回每個邊框相應的分數,即每個邊界框表示一張人臉的概率。這些分數被用于過濾邊界框,因為可能存在一張圖片并不包含任何一張人臉的情況。請注意,為了對邊界框進行檢索,即使圖像中僅僅只有一個人,也應該執行人臉檢測過程。
人臉特征點檢測及人臉對齊
在上文中,我們已經解決了第一個問題!然而,我想要指出的是,我們需要對齊邊界框,從而抽取出每個邊界框中的人臉居中的圖像,接著將其作為輸入傳給人臉識別網絡,因為這樣可以使人臉識別更加準確!
為了實現這個目標,「face-api.js」實現了一個簡單的卷積神經網絡(CNN),它將返回給定圖像的 68 個人臉特征點:
從特征點位置上看,邊界框可以將人臉居中。你可以從下圖中看到人臉檢測結果(左圖)與對齊后的人臉圖像(右圖)的對比:
人臉識別
現在,我們可以將提取出的對齊后的人臉圖像輸入到人臉識別網絡中,該網絡基于一個類似于 ResNet-34 的架構,基本上與 dlib(https://github.com/davisking/dlib/blob/master/examples/dnn_face_recognition_ex.cpp)中實現的架構一致。該網絡已經被訓練去學習出人臉特征到人臉描述符的映射(一個包含 128 個值的特征向量),這個過程通常也被稱為人臉嵌入。
現在讓我們回到最初對比兩張人臉圖像的問題:我們將使用每張抽取出的人臉圖像的人臉描述符,并且將它們與參考數據的人臉描述符進行對比。更確切地說,我們可以計算兩個人臉描述符之間的歐氏距離,并根據閾值判斷兩張人臉圖像是否相似(對于 150*150 的圖像來說,0.6 是一個很好的閾值)。使用歐氏距離的效果驚人的好,當然,你也可以選用任何一種分類器。下面的 gif 動圖可視化了通過歐氏距離比較兩張人臉圖像的過程:
至此,我們已經對人臉識別的理論有所了解。接下來讓我們開始編寫一個代碼示例。
是時候開始編程了!
在這個簡短的示例中,我們將看到如何一步步地運行人臉識別程序,識別出如下所示的輸入圖像中的多個人物:
導入腳本
首先,從 dist/face-api.js 獲得最新的版本(https://github.com/justadudewhohacks/face-api.js/tree/master/dist),或者從 dist/face-api.min.js 獲得縮減版,并且導入腳本:
<script src="face-api.js"></script>
如果你使用 npm 包管理工具,可以輸入如下指令:
npm i face-api.js
加載模型數據
你可以根據應用程序的要求加載你需要的特定模型。但是如果要運行一個完整的端到端的示例,我們還需要加載人臉檢測、人臉特征點檢測和人臉識別模型。相關的模型文件可以在代碼倉庫中找到,鏈接如下:https://github.com/justadudewhohacks/face-api.js/tree/master/weights。
其中,模型的權重已經被量化,文件大小相對于初始模型減小了 75%,使你的客戶端僅僅需要加載所需的最少的數據。此外,模型的權重被分到了最大為 4 MB 的數據塊中,使瀏覽器能夠緩存這些文件,這樣它們就只需要被加載一次。
模型文件可以直接作為你的 web 應用中的靜態資源被使用,或者你可以將它們存放在另外的主機上,通過指定的路徑或文件的 url 鏈接來加載。假如你將它們與你在 public/models 文件夾下的資產共同存放在一個 models 目錄中:
const MODEL_URL = '/models'
await faceapi.loadModels(MODEL_URL)
或者,如果你僅僅想要加載特定的模型:
const MODEL_URL = '/models'
await faceapi.loadFaceDetectionModel(MODEL_URL)
await faceapi.loadFaceLandmarkModel(MODEL_URL)
await faceapi.loadFaceRecognitionModel(MODEL_URL)
從輸入圖像中得到對所有人臉的完整描述
該神經網絡可以接收 HTML 圖像、畫布、視頻元素或張量(tensor)作為輸入。為了檢測出輸入圖像中分數(score)大于最小閾值(minScore)的人臉邊界框,我們可以使用下面的簡單操作:
const minConfidence = 0.8
const fullFaceDescriptions = await faceapi.allFaces(input, minConfidence)
一個完整的人臉描述符包含了檢測結果(邊界框+分數),人臉特征點以及計算出的描述符。正如你所看到的,「faceapi.allFaces」在底層完成了本文前面的章節所討論的所有工作。然而,你也可以手動地獲取人臉定位和特征點。如果這是你的目的,你可以參考 github repo 中的幾個示例。
請注意,邊界框和特征點的位置與原始圖像/媒體文件的尺寸有關。當顯示出的圖像尺寸與原始圖像的尺寸不相符時,你可以簡單地通過下面的方法重新調整它們的大小:
const resized = fullFaceDescriptions.map(fd => fd.forSize(width, height))
我們可以通過將邊界框在畫布上繪制出來對檢測結果進行可視化:
fullFaceDescription.forEach((fd, i) => {
faceapi.drawDetection(canvas, fd.detection, { withScore: true })
})
可以通過下面的方法將人臉特征點顯示出來:
fullFaceDescription.forEach((fd, i) => {
faceapi.drawLandmarks(canvas, fd.landmarks, { drawLines: true })
})
通常,我會在 img 元素的頂層覆蓋一個具有相同寬度和高度的絕對定位的畫布(想獲取更多信息,請參閱 github 上的示例)。
人臉識別
當我們知道了如何得到給定的圖像中所有人臉的位置和描述符后,我們將得到一些每張圖片顯示一個人的圖像,并且計算出它們的人臉描述符。這些描述符將作為我們的參考數據。
假設我們有一些可以用的示例圖片,我們首先從一個 url 鏈接處獲取圖片,然后使用「faceapi.bufferToImage」從它們的數據緩存中創建 HTML 圖像元素:
// fetch images from url as blobs
const blobs = await Promise.all(
['sheldon.png' 'raj.png', 'leonard.png', 'howard.png'].map(
uri => (await fetch(uri)).blob()
)
)
// convert blobs (buffers) to HTMLImage elements
const images = await Promise.all(blobs.map(
blob => await faceapi.bufferToImage(blob)
))
接下來,在每張圖像中,正如我們之前對輸入圖像所做的那樣,我們對人臉進行定位、計算人臉描述符:
const refDescriptions = await Promsie.all(images.map(
img => (await faceapi.allFaces(img))[0]
))
const refDescriptors = refDescriptions.map(fd => fd.descriptor)
現在,我們還需要做的就是遍歷我們輸入圖像的人臉描述符,并且找到參考數據中與輸入圖像距離最小的描述符:
const sortAsc = (a, b) => a - b
const labels = ['sheldon', 'raj', 'leonard', 'howard']
const results = fullFaceDescription.map((fd, i) => {
const bestMatch = refDescriptors.map(
refDesc => ({
label: labels[i],
distance: faceapi.euclideanDistance(fd.descriptor, refDesc)
})
).sort(sortAsc)[0]
return {
detection: fd.detection,
label: bestMatch.label,
distance: bestMatch.distance
}
})
正如前面提到的,我們在這里使用歐氏距離作為一種相似度度量,這樣做的效果非常好。我們在輸入圖像中檢測出的每一張人臉都是匹配程度最高的。
最后,我們可以將邊界框和它們的標簽一起繪制在畫布上,顯示檢測結果:
// 0.6 is a good distance threshold value to judge
// whether the descriptors match or not
const maxDistance = 0.6
results.forEach(result => {
faceapi.drawDetection(canvas, result.detection, { withScore: false })
const text = `${result.distance < maxDistance ? result.className : 'unkown'} (${result.distance})`
const { x, y, height: boxHeight } = detection.getBox()
faceapi.drawText(
canvas.getContext('2d'),
x,
y + boxHeight,
text
)
})
至此,我希望你對如何使用這個 API 有了一個初步的認識。同時,我也建議你看看文中給出的代碼倉庫中的其它示例。好好地把這個程序包玩個痛快吧!
錐漸變類似于徑向漸變。兩者都是圓形的,并且使用元素的中心作為顏色的源點。徑向漸變的顏色從圓的中心中心輻射,而圓錐漸變是圍繞一個中心點旋轉。
它們被稱為“圓錐形”,因為它們從上面看起來像的圓錐形。比如可以使用圓錐漸變制作餅圖或色輪。
語法如下:
background-image: conic-gradient([from angle] [at position,] color degree, color degree, ...);
參數說明:
值 | 描述 |
from angle | 可選。起始角度。默認值為 0deg |
at position | 可選。中心位置。默認居中。 |
color degree, ..., color degree | 角漸變斷點。該值包含一個顏色值,后跟一個可選的停止位置( 0 到 360 之間的度數或 0% 到 100% 之間的百分比)。 |
這是一個沒有設置任何屬性的,只聲明了2中顏色的漸變,如下示例:
.gradient {
background-image: conic-gradient(blue, red);
}
默認漸變的角度是0,正值時,會沿著順時針方向,負值相反方向,如下示例:
.gradient {
/* 一個旋轉 45 度的錐形漸變,從藍色漸變到紅色 */
background-image: conic-gradient(from 45deg, blue, red);
}
45度漸變
如下圖是-45deg時,漸變按逆時針方向。
-45度漸變
使用 at 語法,比如 at left ,at top left ,at 0 0(左上角)等設置中心點的位置,如下示例:
/* 一個藍紫色框:從藍色漸變到紅色,但只有右下象限可見,因為錐形漸變的中心位于左上角 */
conic-gradient(from 90deg at 0 0, blue, red);
at 0px 0px
此時,是否還是不太明白,我們可以繼續改變中心點的位置,比如設置中心點距離左邊50px 距離上邊50px,如下示例:
.gradient {
/* 錐形漸變的中心位于距離左上角各50px */
background:conic-gradient(from 90deg at 50px 50px, blue, red);
}
at 50px 50px
我們繼續,當中心點設置為 at left 什么效果?
background:conic-gradient(from 90deg at left, blue, red);
at left
是不是很有意思,你可以嘗試設置其它的值,看看中心點的位置是什么樣的。
和徑向漸變一樣,你可以設置顏色的結束位置,不同的地方是圓錐漸變的只能使用角度 deg、梯度 grad、弧度 rad和圈 turn這幾個單位。
如下示例:
/*使用角度*/
.gradient {
background:conic-gradient(red 0deg, orange 90deg, yellow 180deg, green 270deg, blue 360deg);
}
/*使用梯度grad*/
.gradient {
background:conic-gradient(red 40grad, 80grad, blue 360grad);
}
/*使用弧度 rad*/
.gradient {
background:conic-gradient(red .8rad, yellow .6rad, blue 1.3rad);
}
/*使用圈 turn*/
.gradient {
background:conic-gradient(#fff 0.09turn, #bbb 0.25turn, #666 0.6turn);
}
關于各種單位的具體介紹可以參考這里:https://developer.mozilla.org/zh-CN/docs/Web/CSS/gradient/conic-gradient
使用圓錐漸變可以制作一個色輪,如下示例:
html:
<div class="gradient"></div>
css:
div{
width:200px;
height:200px;
border-radius:200px;
}
.gradient {
/* 色輪 */
background: conic-gradient(
hsl(360, 100%, 50%),
hsl(315, 100%, 50%),
hsl(270, 100%, 50%),
hsl(225, 100%, 50%),
hsl(180, 100%, 50%),
hsl(135, 100%, 50%),
hsl(90, 100%, 50%),
hsl(45, 100%, 50%),
hsl(0, 100%, 50%)
);
}
效果:
色輪
通過上面的色輪示例,在結合之前學習線性或徑向漸變時改變顏色結束位置的知識,是否對如何制作一個餅圖有了思路。如下示例:
html:
<div class="gradient"></div>
css:
div{
width:200px;
height:200px;
border-radius:200px;
}
.gradient {
background:conic-gradient(
#ff00ff,
#ffff00 0deg 60deg,
#00ffff 60deg 120deg,
#66ff66 120deg 180deg,
#000066 180deg 240deg,
#ff0033 240deg 300deg,
#850244 300deg);
}
效果:
餅圖
到此已經介紹了圓錐漸變的使用方法,使用圓錐漸變可以制作很多特效,比如餅圖,非常實用,本篇作為入門介紹,難免有誤,不夠詳細,所以關于其詳細的內容原理可以參考下面的參考資料:
https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Images/Using_CSS_gradients
https://css-tricks.com/a-complete-guide-to-css-gradients/
下面將會繼續介紹其它漸變的使用方法,你可以關注我,繼續學習其它漸變知識。
其它相關文章:
本教程中,我們將學習如何創建一個簡單的React應用程序,并使用其他工具來幫助我們一路測試它。讓我們開始吧!
漸進式Web應用程序(PWA)迅速流行,因為基本上是基于性能的快速Web應用程序,可簡化移動應用程序體驗。PWA使用HTML,CSS和JavaScript等前端技術構建,以創造與本地移動應用程序相當的可用性和性能水平。它們具有響應能力,消耗更少的數據和存儲空間,并支持瀏覽器中的推送通知和離線使用。
構建一個漸進的網絡應用程序現在已經成為每個企業都希望遵循的網絡發展趨勢。Twitter和Flipboard等大型企業最近推出了漸進式網絡應用程序,為用戶提供移動體驗,而不需要他們實際安裝應用程序。在本文中,您將學習如何使用React構建漸進式Web應用程序。所以,讓我們開始吧。
首先,生成一個React應用程序create-react-app。為此,您需要運行以下命令:
現在安裝React Router:
您需要使用下面的代碼替換“src / App.js”內容以獲取帶有導航的基本模板。
import React, { Component } from 'react';
import { Router, browserHistory, Route, Link } from 'react-router';
import './App.css';
const NavBar = () => (
<div className="navbar">
<Link to="/">Feed</Link>
<Link to="/profile">Profile</Link>
</div>
);
const Template = ({ title }) => (
<div>
<NavBar />
<p className="page-info">
This is the {title} page.
</p>
</div>
);
const Feed = (props) => (
<Template title="Feed"/>
);
const Profile = (props) => (
<Template title="Profile"/>
);
class App extends Component {
render() {
return (
<Router history={browserHistory}>
<Route path="/" component={Feed}/>
<Route path="/profile" component={Profile}/>
</Router>
);
}
}
export default App;
現在,您必須更新默認樣式,方法是將下面的樣式替換為“src / App.css”,以使您的應用看起來干凈整潔。
.navbar {
background-color: #01C8E5;
text-align: center;
}
.navbar a {
display: inline-block;
padding: 10px;
color: #fff;
text-decoration: none;
}
.page-info {
text-align: center;
font-weight: bold;
}
然后運行 npm start 以在瀏覽器中測試該應用程序。這是一個有兩條路線的基本應用程序。您現在將其轉換為PWA。
Lighthouse是一款自動化的開源工具,用于根據PWA清單測試應用程序。它有助于對可訪問性,性能等進行審計。
用燈塔測試你的應用程序。點擊Chrome右上角的燈塔圖標,然后點擊“生成報告”按鈕。生成的報告將如下所示:
你將不得不修復所有失敗的審計。
服務工作者是連接應用程序和網絡的代理服務器。使用Service Worker,您將不得不攔截網絡請求并保存緩存的文件。這將使您的應用即使在網絡不可用的情況下也能正常工作。
在應用程序的公用文件夾中創建一個空白的worker.js文件,并將以下代碼添加到該文件中:
// Flag for enabling cache in production
var doCache = false;
var CACHE_NAME = 'pwa-app-cache';
// Delete old caches
self.addEventListener('activate', event => {
const currentCachelist = [CACHE_NAME];
event.waitUntil(
caches.keys()
.then(keyList =>
Promise.all(keyList.map(key => {
if (!currentCachelist.includes(key)) {
return caches.delete(key);
}
}))
)
);
});
// This triggers when user starts the app
self.addEventListener('install', function(event) {
if (doCache) {
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
fetch('asset-manifest.json')
.then(response => {
response.json();
})
.then(assets => {
// We will cache initial page and the main.js
// We could also cache assets like CSS and images
const urlsToCache = [
'/',
assets['main.js']
];
cache.addAll(urlsToCache);
})
})
);
}
});
// Here we intercept request and serve up the matching files
self.addEventListener('fetch', function(event) {
if (doCache) {
event.respondWith(
caches.match(event.request).then(function(response) {
return response || fetch(event.request);
})
);
}
});
現在檢查瀏覽器是否支持服務人員,然后注冊worker.js。為此,您需要將以下腳本添加到public / index.html文件中(請注意, shrink-to-fit=no 已從視口元標記中刪除)。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('worker.js').then(function(registration) {
console.log('Worker registration successful', registration.scope);
}, function(err) {
console.log('Worker registration failed', err);
}).catch(function(err) {
console.log(err);
});
});
} else {
console.log('Service Worker is not supported by browser.');
}
</script>
</body>
</html>
您必須重新啟動您的應用程序并重新加載瀏覽器,然后才能在開發人員控制臺中看到“工作人員注冊成功”消息?,F在重新生成Lighthouse報告。
你的應用會呈現一個空的根div,直到JavaScript加載并且React掛鉤了初始路由。您必須確保您的應用程序在沒有任何JS加載的情況下運行,并在React進場前顯示一些CSS和HTML。您更新的index.html將如下所示:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<title>React App</title>
<style type="text/css">
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
.navbar {
background-color: #01C8E5;
text-align: center;
}
.navbar a {
display: inline-block;
padding: 10px;
color: #fff;
text-decoration: none;
}
.page-info {
text-align: center;
font-weight: bold;
}
</style>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root">
<div>
<a href="/">Feed</a>
</div>
<p>
Loading an awesome app...
</p>
</div>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('worker.js').then(function(registration) {
console.log('Worker registration successful', registration.scope);
}, function(err) {
console.log('Worker registration failed', err);
}).catch(function(err) {
console.log(err);
});
});
} else {
console.log('Service Worker is not supported by browser.');
}
</script>
</body>
</html>
現在使用Lighthouse重新審核您的應用程序,您會注意到應用程序性能的提高。
您需要添加一個512x512圖標才能顯示在啟動畫面上。為此,您將不得不更新manifest.json并添加圖標t0公用文件夾。
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"start_url": "/",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
此外,使用以下元標記允許瀏覽器識別您的應用是PWA。
<! - 告訴瀏覽器它是一個PWA - >
< meta name = “mobile-web-app-capable” content = “yes” >
<! - 告訴iOS它是一個PWA - >
< meta name = “apple-mobile-web-app-capable” content = “yes” >
現在,只有HTTPS丟失,并且可以在部署應用程序后修復緩存。在worker.js文件中用'true'更新doCache標志。在Firebase控制臺中創建一個新項目并將其命名為“Pwa App”。然后在項目目錄中運行以下命令:
npm install -g firebase-tools
firebase login
firebase init
你的firebase.json應該是這樣的:
{
"hosting": {
"public": "build",
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}
初始化后,構建并部署您的應用程序。
在使用已部署的URL上的Lighthouse來審核應用后,您將看到以下結果。
最后,您已經使用React.js創建了您的第一個漸進式網絡應用程序!
*請認真填寫需求信息,我們會在24小時內與您取得聯系。