Handsfree.js — 一个通过计算机视觉集成手势,面部表情和各种姿势识别的前端库
当电视上出现上图这种科技大片的时候,有没有幻想过有一天可以实现上图的这种交互,当我打开Handsfree这个库的介绍页时,看到前端页面竟然能够识别人的手势,面部以及各种肢体动作,简直刷新了我对前端能力的认知。确信这种交互有一天成为可能。
计算机视觉加上AI这几年早已经不是什么新鲜的名词,随着GPU算力的不断提升,使得越来越多(小)的设备上实现人工智能成为可能。
国外的一个开发者制作了一个实现Handsfree各种效果的demo页面(https://handsfree.js.org/),并且制定了开发计划,详细的进度将在作者的twitter上实时公布。看下这个计划:
创建一个易于使用的通过面部,手,眼睛,姿势跟踪,声音和思想控制程序的库。
然后使用该库去创建自定义插件或者组件。
使用以上实现的这样一个仓库去帮助更多的人通过“用户脚本管理器”去实现浏览器插件。
然后培养实现一个围绕Handsfree的使用者和开发者的社区。
最后建立一个关于Handsfree的基金会促进实现更多优秀的创意。
下面看下Handsfree能够实现哪些有趣的功能:
触发交互事件
最近不久作者发布了名为Pincher的插件,可以实现24种以上的捏🤏的动作,包括三种状态,开始,保持,释放——分别用你的小拇指,无名指,中指,和食指去做捏的动作。它根据鼠标事件为模型模拟动作,然后通过类似于document.addEventListener() 去监听。
在浏览器上滚动网页
这是根据作者实现的一个浏览器插件去帮助实现的滚动网页,它使用名为MediaPipe Hands模型去跟踪手势,下面的Gif展示的就是使用pinchScroll插件实现的效果,只需要加入很少的代码实现这样一个自定义的功能
创建多种辅助技术
下面是作者最喜欢的一个功能,使用了名为“Face Pointer”的插件,可以通过面部移动屏幕上的点去实现点击和滚动网页。
控制桌面游戏
使用人的面部和手去模拟鼠标去玩“Into the Breach”这款游戏。所有的这些都是通过“Face Pointer”和“Hand Pointer”插件,集成到Robot.js中触发原生的鼠标事件来轻松实现的。
创建自己的游戏
不仅仅可以使用它玩游戏,还可以创建自定义交互的游戏
控制无人机和机器人
当然它不仅能控制浏览器端和桌面端的软件,通过web socket接口可以和任何与你的电脑建立连接的设备,就像和这个很普通的机器人进行模拟动作。
音乐和艺术创作
这里除了一些作者即将发布的想法奇特的应用可以帮助制作一些迷幻的音乐,还有更多的创意想法等着更多的开发者去实现。另外作者正在开发的WebXR DevTools Chrome 插件以帮助在没有XR设备的情况下去开发WebXR应用。
开发指南
以上已经介绍了一些Handsfree能实现的功能,接下来介绍如何去使用它。不要担心一开始会有所迷惑,它就是大概的介绍。稍后会有更多的简短聚焦的教程放出。你可以从作者的代码仓库中/boilerplate/cdn.html找到样例。
Handsfree开发上手
可以使用它的CDN资源快速开始,不需要任何的服务器,拷贝如下代码到html文件中执行:
<head>
<!-- Import helper classes and styles -->
<link
rel="stylesheet"
href="https://unpkg.com/[email protected]/build/lib/assets/handsfree.css" />
</head>
<body>
<!-- Import Handsfree.js in body (as it adds body classes) -->
<script src="https://unpkg.com/[email protected]/build/lib/handsfree.js"></script>
<script>
// Use the hand with defaults (and show the webcam with wireframes)
handsfree = new Handsfree({
showDebug: true,
hands: true
})
// Start webcam and tracking (personally, I always like to ask first)
handsfree.start()
</script>
</body>
也可以使用npm方式。其实默认情况下它仍然加载的是远程CDN资源,因为它的包体积太大了(超过10M),但是你也可以下载模型文件到自己的本地assets目录。
npm i handsfree
handsfree = new Handsfree({
showDebug: true,
// Use the hand model with custom config
hands: {
// Always make sure to enable them
enabled: true,
// Let's track up to 4 hands. It's best to be kind and ask permission first tho!
maxNumHands: 4,
}
})
// Start webcam and tracking (personally, I always like to ask first)
handsfree.start()
更多的配置参数列表详情请参见:
https://handsfree.js.org/ref/prop/config.html#the-full-list
数据处理
当然这个库不仅仅展示手的三维线框图这么简单,但是它实际上还不能做任何事情。除非通过两种方式配合Handsfree一起,作者常用的一种是使用handsfree.use(newPluginName, callback)生成插件,之所以叫它插件,因为当你运行handsfree.start()的时候它会挂载到主摄像头的循环队列里。它的回调在摄像头的每一帧里都将执行,并且收到计算机视觉模型里传出的所有的数据。以下是个简单的插件,仅仅通过console打印日志数据,姑且叫它logger。
// Let's use our hands again
handsfree = new Handsfree({showDebug: true, hands: true})
handsfree.start()
// Let's create a plugin called "logger" to console.log the data
handsfree.use('logger', (data) => {
// I like to always bail if there's no data,
// which might happen if you swap out hands for the face later on
if (!data.hands) return
// Log the data
console.log(data.hands)
// Do something if we are pinching with left [0] pinky [3]
if (data.hands.pinchState[0][3] === 'held') {
console.log('pinching with left pinky')
}
})
一旦你创建好了一个插件,它将在任何地方通过hansfree.plugin.pluginName调用到,而且拥有一些属性和方法。
handsfree.plugin.logger.enable()
handsfree.plugin.logger.disable()
// This is what the callback gets mapped to,
// and is what gets called on every frame that this plugin is enabled
handsfree.plugin.logger.onFrame
如果你需要更多高级的功能,那么你可以传入特定的hooks的对象,它将插件的不同的阶段执行,例如:
handsfree.use('advancedLogger', {
// True by default
enabled: true,
// A list of strings for tagging this plugin.
// Later you can bulk disable/enable these with: handsfree.enablePlugins(['tag1', 'tag2'])
tags: [],
// This special property can be adjusted later (or even before!) in various ways
config: {},
// Called immediately after the plugin is added, even if disabled
// The `this` context is the plugin itself: handsfree.plugin.advancedLogger
// If you need to create DOM elements or other setup, this is the method to do it in
onUse () {},
// Called when you .enable() this plugin
onEnabled () {},
// Called when you .disable() this plugin
onEnabled () {}
})
使用数据代替插件
有的时候你可能想用一个框架,一张图片,一个画布,或者一段视频来取代摄像头去跟踪里面的数据,或者在一个app内你很难调用到handsfree里面的东西。诸如此类的情况,你可以使用document的监听事件。
你可以在这些事件中通过ev.detail.data获取到相关的数据
// This will get called on every frame
document.addEventListener('handsfree-data', ev => console.log(ev.detail.data))
// Listen to when the thumb and index (0) are pinched on any hand
document.addEventListener('handsfree-finger-pinched-0')
// Listen to when the right (1) thumb and pinky (3) are pinched
document.addEventListener('handsfree-finger-pinched-1-3')
同时,你也可以通过handsfree实例直接访问data属性里面的数据
console.log(handsfree.data.hands)
更新模型和插件
Handsfree其实最强大之处在于可以随时更换模型和插件,当你的app中不同的路由呈现不同的handsfree使用场景的时候就显得很重要。它是通过handsfree.update(config)来达到目的的。作者在一个网页上在不重启摄像头的情况下呈现不同的demo,就是依靠这个实现的。
handsfree.use带有与实例化Hansfree时相同的config对象,但是它还做了另外几件事:
它不会合并更改,所以当你打开双手的情况下仅传递 handsfree.update({facemesh:true})参数,最终会导致两者都失败。
它会在任何需要的模型和依赖的地方自动加载loading。
赋予配置插件,关闭插件的能力
案例如下:
// Start with hands
const handsfree = new Handsfree({hands: true})
handsfree.start()
// Add facemesh
handsfree.update({facemesh: true})
// Replace both with pose
handsfree.update({
hands: false,
facemesh: false,
pose: true
})
// Use Weboji and enable the Face Pointer plugins
handsfree.update({
hands: false, facemesh: false, pose: false,
weboji: true,
plugin: {
// Enable some plugins
faceClick: true
faceScroll: true,
// Update the special .config properties of the plugins (this is so magical!)
facePointer: {
speed: {
x: 2,
y: 2
}
},
}
})
你也可以批量禁用插件和打开插件,甚至使用标签作为参数
// Disable and enable them all
handsfree.disablePlugins()
handsfree.enablePlugins()
// Disable all the "core" tagged plugins
handsfree.disablePlugins('core')
// Enable handsfree "browser" tagged plugins
handsfree.enablePlugins('browser')
这些插件下的配置参数都可以即时修改,通过config下面的属性名称进行访问:
// Change the Face Pointer speed
handsfree.plugin.facePointer.config.speed.x = 2
handsfree.plugin.facePointer.config.speed.y = 2
// Set the threshold for how much you have to smile to click (0 - 1)
handsfree.plugin.faceClick.config.morphs[0] = .25
元素类名
当页面状态发生变化的时候,body标签会自动加上class类名
body.handsfree-started
body.handsfree-loading
body.handsfree-model-weboji
body.handsfree-model-hands
Generic classes:
https://handsfree.js.org/ref/util/classes.html
Pinching states:
https://handsfree.js.org/ref/plugin/pinchers.html#classes
以上就是大概的介绍,为了防止你听的有点迷糊,作者新年的计划是每周至少一次针对每一个主题来出一份教程,作者将全职all in这个项目创造出更多的创意使用场景。支持作者可以加入https://github.com/sponsors/midiblocks。
该项目的核心AI技术用到了google的TensorFlow,还是很值得期待将来有更多的使用场景的。
译自:
https://dev.to/midiblocks/introducing-handsfree-js-integrate-hand-face-and-pose-gestures-to-your-frontend-4g3p