如何使用面部運動與排版交互

已發表: 2022-03-10
快速總結↬在本文中,我們將介紹如何結合機器學習、可變字體和 CSS 網格來創建響應接近度、傾斜度和用戶面部數量的佈局。

網頁設計師一直在尋找新的方法來改進頁面內容的呈現。 有時,這可能會導致巧妙的解決方案或與通常遠離設計領域的技術進行交互。 在本文中,我們將把排版與人工智能聯繫起來,使用機器學習來檢測用戶面部的接近程度等事物,以提高文本的易讀性。

我們將嘗試如何使用 Tensorflow 進行人臉識別,以便從相機中提取一些信息,例如屏幕與用戶面部之間的距離或閱讀頁面的人數。 然後,我們將這些數據傳遞給 CSS 以適應排版和調整頁面佈局。

什麼是張量流?

Tensorflow 是谷歌機器學習的開源平台。 機器學習是計算機科學的一個領域,它研究學習從圖像、音軌、時間序列、自然文本和一般數據中識別複雜關係和重複模式的算法。 這些算法生成數學模型(也稱為訓練模型),這是一種可用於根據輸入數據做出決策的模式。 如果你想討論這個話題,Charlie Gerard 在 Smashing Mag 上為前端開發人員寫了一篇關於 ML 的文章。

Tensorflow 為 AI 開發人員、數據科學家、數學家提供了很多工具,但如果數據分析不是您的日常麵包,請不要驚慌! 好消息是,您無需成為專家即可使用它,只要您使用預構建模型,就像我們將要使用的那樣。

TensorFlow 模型可通過其 JavaScript SDK 在 Web 上使用。

設置

為了開始使用人臉識別算法,我們需要遵循幾個步驟:

  • 加載 TensorFlow SDK。
  • 加載包含數學模型的 Facemesh 庫。
  • 訪問用戶的相機並將其流式傳輸到 HTML 視頻元素。 Facemesh 將分析來自視頻標籤的幀以檢測人臉的存在。

在這個項目中,我們將通過 CDN 使用 Tensorflow,但如果您更喜歡 bundler 方式,它也可以在 NPM 上使用:

 <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-core"></script> <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-converter"></script> <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-webgl"></script>

Tensorflow 本身並不能做到這一點,因此我們需要添加 Facemesh,這是一個構建在 ML 框架之上的庫,並提供了一個已經訓練好的人臉識別模型:

 <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/facemesh"></script>

下一步是設置 Facemesh 庫以加載經過訓練的模型並定義將從視頻流中評估人臉數據的函數:

 // create and place the video const video = document.createElement('video'); document.body.appendChild(video); // setup facemesh const model = await facemesh.load({ backend: 'wasm', maxFaces: 1, }); async function detectFaces() { const faces = await model.estimateFaces(video); console.log(faces); // recursively detect faces requestAnimationFrame(detectFaces); }

現在我們準備好使用視頻標籤詢問用戶訪問其相機流的權限:

 // enable autoplay video.setAttribute('autoplay', ''); video.setAttribute('muted', ''); video.setAttribute('playsinline', ''); // start face detection when ready video.addEventListener('canplaythrough', detectFaces); // stream the camera video.srcObject = await navigator.mediaDevices.getUserMedia({ audio: false, video: { facingMode: 'user', }, }); // let's go! video.play();

navigator.mediaDevices.getUserMedia 方法將提示權限並將開始將相機流式傳輸到視頻元素中。 一旦被接受,相機將開始流式傳輸到視頻標籤,而瀏覽器控制台將記錄 Facemesh 檢測到的面部信息。

請注意,相機權限需要安全的 https 連接或 localhost:您不能簡單地打開 index.html 文件。 如果您不確定如何為 Node 設置本地服務器 checkout http-server 或遵循 Python 指南或 PHP 指南。

跳躍後更多! 繼續往下看↓

案例 1. 使用智能手機攝像頭調整排版

我們使用智能手機在任何地方瀏覽網絡。 不久前,有一段時間,我們常常乘坐擁擠的火車或公共汽車,因為沒有空間,我們將智能手機放在離眼睛很近的地方。 在我們一天中的許多時刻和地點,我們經常改變智能手機的位置和傾斜度,即使我們正在觀看同一個網站。 眼睛和智能手機之間的距離會影響我們的閱讀能力。 評估該距離,我們可以調整微排版,以優化字形以實現更近或更遠的閱讀。

人臉檢測當然也意味著眼睛位置檢測。 我們可以使用 Facemesh 提供的數據來計算我們的臉相對於相機拍攝的整張照片的大小。 我們可以假設我們的臉越大,我們離屏幕越近。 我們可以設置一個從 0(一隻手臂相距很遠——臉部大約佔據相機的一半)到 1(粘在屏幕上)的比例,並通過分段檢測當前值:

 async function detectFaces() { const faces = await model.estimateFaces(video); if (faces.length === 0) { // is somebody out there? return requestAnimationFrame(detectFaces); } const [face] = faces; // extract face surface corners let { bottomRight, topLeft} = face.boundingBox; // calculate face surface size let width = bottomRight[0] - topLeft[0]; let height = bottomRight[1] - topLeft[1]; let videoWidth = video.videoWidth; let videoHeight = video.videoHeight; let adjustWidth = videoWidth / 2; let adjustHeight = videoHeight / 2; // detect the ratio between face and full camera picture let widthRatio = Math.max(Math.min((width - adjustWidth) / (videoWidth - adjustWidth), 1), 0); let heightRatio = Math.max(Math.min((height - adjustHeight) / (videoHeight - adjustHeight), 1), 0); let ratio = Math.max(widthRatio, heightRatio); // recursively detect faces requestAnimationFrame(detectFaces); }
用戶面部的分段表示被放置在智能手機的框架內,以指示面部佔據了屏幕的多少區域。
Facemesh 檢測到的兩個特徵示例,例如眼睛、鼻子和嘴巴的位置和傾斜度。 我們可以使用點之間的區域來計算與智能手機攝像頭的距離。 (大預覽)

現在我們已經計算了ratio ,是時候讓一些神奇的事情發生了,將值傳遞給樣式表:

 document.documentElement.style.setProperty('--user-distance', ratio);

有了這個值和一點計算,我們可以輕鬆地對字體粗細、大小甚至樣式進行細微的更改,但我們可以做得更好。 使用可變字體,一種具有參數化形狀和字形空間的字體,我們可以通過更新其光學尺寸變化來調整每個字形的感知。

由於每種可變字體都使用自己的光學尺寸值比例,因此我們需要將比率值與該比例相關聯。 此外,我們可能只想在可用光學尺寸的子集之間移動,以便僅提供少量增強。

 .main-text { --min-opsz: 10; --max-opsz: 15; --opsz: calc(var(--min-opsz) + (var(--user-distance) * (var(--max-opsz) - var(--min-opsz)))); ... font-family: 'Amstelvar', serif; font-variation-settings: 'opsz' var(--opsz); }

你可以在這裡看到它。 請注意,此示例僅演示該技術的工作原理。 為了真正提供更好的閱讀體驗,用戶的眼睛應該幾乎察覺不到排版的變化。 在這裡,我們利用了字形形狀,但使用顏色來增加或減少對比度只是另一個很好的嘗試解決方案。 另一個實驗是檢測人臉的角度以計算閱讀的角度,修改上升、下降和字母的高度:

請參閱 Edoardo Cavazza 的鋼筆 [Facemesh 和上升器/下降器](https://codepen.io/smashingmag/pen/oNxrYop)。

請參閱 Edoardo Cavazza 的 Pen Facemesh 和上升器/下降器。

案例#2:當觀看人數發生變化時調整佈局

在第二種情況下,我們將根據觀看屏幕的人數來更改佈局。 我們可以想像在高中課堂的背景下,交互式白板上顯示的一篇文章。 這種情況與不推薦使用的投影媒體查詢檢測到的情況完全不同,因為如果觀看的學生人數少於或多於 10 人,我們希望調整頁面佈局。當教室裡只有幾個學生時,他們可以安全地接近白板,但如果整個教室都在場,可能空間不夠,我們需要更改佈局以顯示更少(和更大)的東西。

我們只需要對前面的腳本進行一些更改,以便正確檢測在白板上觀看的人臉數量。 首先,我們需要指示 Facemesh 檢測多個人臉:

 const model = await facemesh.load({ backend: 'wasm', maxFaces: 30, });

然後,我們必須將該數字傳遞給樣式表:

 async function detectFaces() { const faces = await model.estimateFaces(video); document.documentElement.style.setProperty('--watching', faces.length); // recursively detect faces requestAnimationFrame(detectFace); }

同樣,我們可以使用該值來簡單地增加字體大小,但我們的目標是提供完全不同的佈局。 CSS 網格佈局可以幫助我們完成這項任務。 此投影文檔是一個長表單,旁邊包含相關圖像:

 <section> <article> <h1>...</h1> <h2>...</h2> <p>...</p> </article> <aside> <img src="..." alt="..." /> </aside> </section>

這是它的默認佈局:

 section { display: grid; grid-template-columns: repeat(12, 1fr); grid-column-gap: 1em; width: 120ch; max-width: 100%; padding: 1em; } section article { grid-column: 1 / -5; } section aside { grid-column: 7 / -1; }
左圖:當觀看人數超過 10 人時,我們使用所有可用列優先考慮正文,並將圖像移到正文之後。右圖:使用 12 列的頁面佈局,帶有活動的 Firefox 網格檢查器指南。所有 12 列用於正文,4 列用於其後的圖像。
:頁面的默認佈局將 12 列中的 8 列用於長格式文本,其餘 4 列用於圖像。 :當觀看人數超過 10 人時,我們會使用所有可用列優先顯示主要文本,並將圖像移動到文本之後。 (大預覽)

當多人觀看時,我們需要對長篇閱讀上下文給予特權,給主欄更多的空間,增加它的字體大小,並去除乾擾元素。 為此,我們增加了跨欄的數量,將主文本下方的旁邊移動。

 :root { --watching: 10; } section { /** The maximum number of people watching for the default layout */ --switch: 10; /** The default number of columns for the text */ --text: 8; /** The default number of columns for the aside */ --aside: 4; grid-template-columns: repeat(calc(var(--text) + var(--aside)), 1fr); } section article { /** * Kinda magic calculation. * When the number of people watching is lower than --switch, it returns -2 * When the number of people watching is greater than --switch, it returns -1 * We are going to use this number for negative span calculation */ --layout: calc(min(2, (max(var(--switch), var(--watching)) - var(--switch) + 1)) - 3); /** * Calculate the position of the end column. * When --layout is -1, the calculation just returns -1 * When --layout is -2, the calculation is lower than -1 */ --layout-span: calc((var(--aside) * var(--layout)) + var(--aside) - 1); /** * Calculate the maximum index of the last column (the one "before" the aside) */ --max-span: calc(-1 * var(--aside) - 1); /** * get the max between --layout-span and the latest column index. * -1 means full width * --max-span means default layout */ --span: max(var(--max-span), var(--span)); grid-column-start: 1; grid-column-end: var(--span); }
  • 你可以在這裡看到它 →

反之亦然,當一小群學生在黑板附近體驗文字時,我們可以提供更多細節,例如媒體文件和交互動作觸發器。

超越人臉識別

我們面臨的案例 () 只是我們如何將人臉識別技術用於佈局或印刷範圍的兩個示例。 Tensorflow 提供了其他模型和庫,可以將相機流轉換為我們頁面的變量。 此外,我們不應忘記,在我們的智能手機中,我們可以使用傳感器 API 來利用許多其他傳感器:GPS、加速度計、環境光等。

由於情緒影響我們閱讀、學習和搜索信息的方式,通過機器學習,我們還可以分析用戶的表情,根據用戶的精神從最小佈局切換到詳細佈局。

多年來,我們一直習慣於使用 CSS 媒體查詢進行響應式網頁設計。 然而,視口的大小只是用戶體驗的變量之一。 最近,一種旨在尊重用戶偏好的新型媒體查詢登陸瀏覽器,例如prefers-color-schemeprefers-reduced-motion 。 這為設計人員和開發人員提供了一種在網頁設計實踐中向前邁進的方法,允許網頁適應整個環境而不僅僅是用戶的設備。 在大數據時代,我們有機會超越響應式和自適應設計。 我們的網頁終於可以“離開屏幕”,成為用戶全球體驗的一部分。 交互設計將涉及所有這些可能性,因此在接下來的幾年中繼續嘗試技術和網頁設計之間可能的組合將是至關重要的。