Vite + Vue3!使用人臉識別技術製作專屬拜年表情包

語言: CN / TW / HK

theme: juejin highlight: an-old-hope


PK創意鬧新春,我正在參加「春節創意投稿大賽」,詳情請看:春節創意投稿大賽

前言

春節即將到來,過年的時候要給親戚朋友拜年,疫情期間不允許聚集走動,所以通過手機拜年發紅包就變成了過年的日常。說一句“新年快樂,恭喜發財”難以表達我們的祝福,所以很多人都會發一些可愛有趣的表情包。為了表達自己的真切祝福,我就想用人臉識別技術替換表情包頭像,合成一張新的表情包。以下是效果圖,是不是還有點意思!線上體驗地址和原始碼地址在文章結尾處。

54A2E637-1E06-4C50-BB91-46429CC46868.gif

face-api.js

雖然人臉識別技術已經出現很久,但是我一直沒有接觸過,所以做這個東西之前我去查找了一些技術方案。作為前端開發人員,我只會寫一點簡單的後端,所以我希望可以儘可能的使用前端的技術去做。後面找到了 face-api.js 這個庫。看了提供的功能。可以滿足我的需求。於是下載官方demo學習。克隆下來專案後,我們進入example\examples-browser目錄,執行yarn 或者 npm install安裝依賴包,安裝完成後執行npm tun start啟動專案,注意這裡要使用本機IP加埠號在瀏覽器訪問,不能使用localhost。在這些例子中我們找到faceExtraction這個例子就是我們想要的人臉識別並提取。接下來就是看例子學習就行。

微信截圖_20220112145902.png

實現上傳圖片獲取人臉圖

例子中用的是jquery + node的方式來做的,我自己覺定使用了vite + cue3 + TS來構建專案,開發環境都沒有什麼問題,但是在打包的時候報錯了,看到一些關於webgl的錯誤,因為暫時不知道怎麼解決,所以就捨棄了TS。

微信截圖_20220112151845.png

建立好專案後新增一個上傳圖片的控制元件,可以使用UI元件庫的,也可以自己寫。根據例子我們需要獲取到圖片的file物件。實際上圖片沒有上傳到伺服器,所以只要在change事件內獲取即可。通過faceapi.bufferToImage(imgFile)方法獲取到Image物件。 js // * 上傳圖片 const handleChange = async (file) => { const imgFile = file.file; isUpload.value = true; // 上傳loading const img = await faceapi.bufferToImage(imgFile); imgUrl.value = img.src; isUpload.value = false; updateResults(); }; 接下來就可以呼叫人臉識別提取的api獲取到人臉。在獲取人臉影象之前要先載入人臉識別模型。根據使用的模型返回對應的模型物件。 ```js import * as faceapi from 'face-api.js'; const SSD_MOBILENETV1 = 'ssd_mobilenetv1' // SSD 行動網路檢測模型 const TINY_FACE_DETECTOR = 'tiny_face_detector' // 微型人臉檢測器模型

let selectedFaceDetector = SSD_MOBILENETV1

export const getCurrentFaceDetectionNet = () => { if (selectedFaceDetector === SSD_MOBILENETV1) { return faceapi.nets.ssdMobilenetv1 } if (selectedFaceDetector === TINY_FACE_DETECTOR) { return faceapi.nets.tinyFaceDetector } }

export const isFaceDetectionModelLoaded = () => { console.log(getCurrentFaceDetectionNet()); return !!getCurrentFaceDetectionNet().params }

` 根據官方的例子使用的是ssd_mobilenetv1``這個模型,在npm上面有具體的說明。

微信截圖_20220112153244.png

所以在專案載入的時候我們要載入人臉識別模型,根據isFaceDetectionModelLoaded()這個方法的返回值判斷是否已經載入完成。人臉模型檔案放在publish即可。

js onMounted(() => { // ! 初始化,載入人臉識別模型 if (!isFaceDetectionModelLoaded()) { getCurrentFaceDetectionNet().load(); } }); 模型載入完成後就可以獲取人臉圖了。獲取到的faceImages是一個數組,裡面是的元素是canvas物件。 ```js // * 獲取人臉頭像 const updateResults = async () => { const isFace = isFaceDetectionModelLoaded(); console.log(isFace); if (!isFace) { return; } const inputImgEl = document.getElementById('inputImg'); const options = getFaceDetectorOptions(); const detections = await faceapi.detectAllFaces(inputImgEl, options); // * 得到人臉資料(陣列) const faceImages = await faceapi.extractFaces(inputImgEl, detections);

if (faceImages.length > 0) { displayExtractedFaces(faceImages); } else { message.warning('識別不到人臉'); facesContainer.innerHTML = ''; mergeUrl.value = ''; } }; 獲取到資料後就可以渲染人臉到頁面。因為是canvas標籤,所以不能使用v-html來渲染,所以這裡使用dom元素append的方法來新增在頁面。js // * 渲染獲取的人臉圖片 const displayExtractedFaces = (faceImages) => { const facesContainer = document.getElementById('facesContainer'); facesContainer.innerHTML = ''; faceImages.forEach((canvas) => facesContainer.append(canvas)); convertCanvasToImage(faceImages[0]); }; ```

使用人臉圖和背景圖合成圖片

兩張圖片合起來變成一張圖片有很多種方案,我這裡用的是把圖片放在畫布上面的方式。首先要新建一張畫布。把背景圖放上去。這裡要注意先放背景圖,再放人臉圖,因為人臉圖比較小,先放會被背景圖覆蓋遮住。

```js // * 建立畫布 const canvas = document.createElement('canvas');

// * 建立背景圖 const bgImg = new Image(); bgImg.src = bg; bgImg.crossOrigin = 'Anonymous';

bgImg.onload = () => { const width = bgImg.width; const height = bgImg.height; // * 按照背景圖的寬高設定畫布寬高 canvas.width = width; canvas.height = height; // ! 把圖片放入畫布中, 先放背景圖,再放人臉,防止覆蓋 canvas.getContext('2d').drawImage(bgImg, 0, 0, width, height); } 接下來放人臉圖,因為人臉圖是canvas物件,所以需要先轉成圖片的base64格式的url。js const image = new Image(); const url = canvas.toDataURL('image/png'); image.src = url; ``` 轉成Image物件後就可以使用canvas.getContext('2d').drawImage()方法把人臉圖放到畫布,調整一下位置和大小就可以遮住背景圖的頭部。

微信截圖_20220112172058.png

實現圖片下載

圖片已經合成在畫布上面,我們又知道畫布怎麼轉成url地址,所以實現下載就很簡單了。

js const downloadImg = () => { console.log(downloadUrl.value); if (!downloadUrl.value) { message.warning('沒有圖片可以下載!'); return; } const url = downloadUrl.value; //* 獲取圖片地址 const a = document.createElement('a'); //* 建立一個a節點插入的document const event = new MouseEvent('click'); //* 模擬滑鼠click點選事件 a.download = 'happy-newyear'; //* 設定a節點的download屬性值 a.href = url; //* 將圖片的src賦值給a節點的href a.dispatchEvent(event); //* 觸發滑鼠點選事件 }; 到此,人臉摳圖合成新的表情包就實現了。發給你的朋友親戚要紅包吧!

專案改進

現在的專案只能上傳單張圖片,並且模板單一。後續希望新增以下功能。有好的提議也可以在評論區提出。當然有興趣的朋友可以一起加入開發。

  • 支援上傳多張圖片,獲取所有圖片後合成全家福。

  • 支援背景自定義上傳。

  • 支援匯出gif格式圖片。

總結

這個小專案雖然簡單,但是從技術選型到完成專案還是花了不少的時間。人臉識別技術現在已經很成熟了,在我們的生活中都有應用到。也是我們需要學習的技術。通過這個專案我也算是入了門,雖然是站在巨人的肩膀上做的,但是也獲得了經驗,拓寬了視野。

原始碼和體驗地址

因為人臉識別模型檔案比較大,並且伺服器效能一般,所以人臉識別模型下載的很慢,出現頁面後需要等待一段時間才能使用人臉識別功能。線上體驗地址

原始碼地址

zan.png