Skip to content

Instantly share code, notes, and snippets.

@cowboy
Last active October 18, 2021 01:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cowboy/275360358a2c25fbeafa770ad0efcd25 to your computer and use it in GitHub Desktop.
Save cowboy/275360358a2c25fbeafa770ad0efcd25 to your computer and use it in GitHub Desktop.
Hand tracking detection in OBS Studio
<!DOCTYPE html>
<html lang="en">
<head>
<script>
// ===========================================================================
// Hand detection in OBS Studio
// by "Cowboy" Ben Alman - 2021
// https://gist.github.com/cowboy/275360358a2c25fbeafa770ad0efcd25
// ===========================================================================
//
// The images in the "images" div should be written on a 1s (or so) timer by
// this plugin: https://github.com/synap5e/obs-screenshot-plugin
//
// Save this HTML file in the same folder as the images and add it to OBS as a
// browser source. Give it permissions if you want it to control OBS. Add an
// <img> in the "images" div for each image you want to process. The image id
// will be passed to the "notify" function when a hand has been detected in an
// image for longer than CONTINUOUS_DETECT_THRESHOLD.
//
// Customize the "notify" function using the APIs listed here:
// https://github.com/obsproject/obs-browser#control-obs
//
// Note that the sources need to be visible for the filter to render the images,
// but you can put those sources plus a browser source with this HTML file in it
// in a scene, then put that scene in all your other scenes (behind the other
// sources, so it stays hidden).
const CONTINUOUS_DETECT_THRESHOLD = 5000
const IMAGE_RELOAD_AND_DETECT_INTERVAL = 500
// See "Sample predictions result" at:
// https://victordibia.com/handtrack.js/#/docs
const IS_VALID_PREDICTION = (p) => p.label === 'open' && p.score > 0.7
// Actually do something with the obs API. All getter methods
// are promisifed (see code below)
// https://github.com/obsproject/obs-browser#control-obs
const HAND_DETECT_EXCEEDS_THRESHOLD = async (id) => {
console.log('image id', id)
const { name } = await obs.getCurrentScene()
console.log('current scene', name)
}
</script>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.jsdelivr.net/npm/handtrackjs@latest/dist/handtrack.min.js"></script>
<title>Hand detection in OBS Studio</title>
<style>
html,
body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
background: #000;
}
#images {
display: flex;
flex-wrap: wrap;
}
#images > * {
display: block;
margin: 0;
padding: 0;
width: 50%; /* works well for <= 4 images, adjust if needed */
}
</style>
</head>
<body>
<div id="images">
<img id="keyboards" src="./keyboards-cam.png" />
<img id="modular" src="./modular-cam.png" />
</div>
<script>
// Promisified obsstudio interface:
// https://github.com/obsproject/obs-browser#control-obs
const obs = {
...obsstudio,
...Object.entries(obsstudio)
.filter(([name]) => /^get/.test(name))
.reduce(
(acc, [name, fn]) => ({
...acc,
[name]: () => new Promise((resolve) => fn(resolve)),
}),
{}
),
}
let model
handTrack.load().then((_model) => {
model = _model
})
const images = [...document.images]
const reloadAndTestImages = async () => {
await Promise.all(
images.map((image) => {
// Force image to reload
image.src = image.src.replace(/\?.*$|$/, `?${Date.now()}`)
// console.log(image.src)
return new Promise((resolve) => {
image.onload = resolve
})
})
)
if (!model) {
return
}
for (let image of images) {
const predictions = await model.detect(image)
const valid = predictions.filter(IS_VALID_PREDICTION)
if (valid.length > 0) {
console.log(image.id, ...valid.map((p) => [p.label, p.score]))
detect(image.id)
}
}
}
setInterval(reloadAndTestImages, IMAGE_RELOAD_AND_DETECT_INTERVAL)
let lastNotifiedId = null
let lastDetectedId = null
let lastDetectTime = 0
let continuousDetectStart = Infinity
const detect = (id) => {
const now = Date.now()
// A new id was been detected or too much time has elapsed since
// the last detect. Detection is no longer continuous.
if (
id !== lastDetectedId ||
now - lastDetectTime > IMAGE_RELOAD_AND_DETECT_INTERVAL * 3
) {
continuousDetectStart = now
}
// Id hasn't been notified, and detection has been continuous
// for longer than the threshold
else if (
id !== lastNotifiedId &&
now - continuousDetectStart > CONTINUOUS_DETECT_THRESHOLD
) {
lastNotifiedId = id
HAND_DETECT_EXCEEDS_THRESHOLD(id)
}
lastDetectedId = id
lastDetectTime = now
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment