你笑你就输了 使用 Javascript AI 你笑你就输了 安装配置启动

2025-06-09

你笑你就输了,使用 Javascript AI

你笑你就输了

安装

配置

发射

这篇文章最初发表在我的个人博客上。

TLDR

我做了一个网页应用,它能通过人工智能摄像头监控你的笑容。我会给你看搞笑视频,如果你笑了你就输了!它非常有趣,使用起来感觉很棒,而且它是开源的,而且只使用了网页技术!

花 5 分钟时间开怀大笑。

如果完成了,你肯定至少看过一两个视频笑过。一定笑过!
否则,要么你太强势,要么你没有灵魂。

想添加一个搞笑视频吗?发现了 bug 了吗?缺少了什么功能?这个项目是开源的,欢迎你参与我的合并请求审批非常容易!

如果您想知道我为什么以及如何构建这个应用程序,您将在本文的其余部分找到答案!

这个想法

正如我之前所说,这段时间过得相当糟糕。结果,就像所有有点抑郁的人一样,我漫不经心地在 YouTube 上闲逛。我正在寻找一些有趣的内容来改变我的想法。

就在那时,我(又一次)看到了那些著名的“你笑你就输”视频。原理很简单:你把人们放在搞笑视频前,如果他们笑了,你就输了。

为什么不在浏览器中做同样的事情,用人工智能监控用户的笑容呢?

我已经拥有了所需的一切。视频将来自 YouTube,因此无需托管、管理流媒体或管理播放器。它将是一个静态网站,以简化应用的托管。最重要的是,我已经知道如何检测面部笑容了。

我给自己两天时间编写所有代码,托管项目,将您正在阅读的文章翻译成两种语言,并将代码开源到我的 GitHub 上。好了,开始吧。

微笑检测

所以,不管你信不信,这绝对是迄今为止最简单、最快捷的部分。原因有几个。

  • 第一个原因:如今,通过人工智能模型进行表情检测非常容易。任何人都可以做到或设置它。
  • 第二个原因:我在之前的项目中已经做过了!

记得吗?我之前用 GIF 做那些无聊的事情的时候也用过同样的东西。

因此,如果您想了解这部分具体是如何工作的,我建议您阅读专门的文章

简而言之,我使用face-api 库来管理整个复杂的部分。使用网络摄像头时,我会在启动应用程序时加载模型。之后我只需要使用高级 face-api API。我每秒检查两次用户是否在微笑。

/**
 * Load models from faceapi
 * @async
 */
async function loadModels() {
    await faceapi.nets.tinyFaceDetector.loadFromUri("https://www.smile-lose.com/models")
    await faceapi.nets.faceExpressionNet.loadFromUri("https://www.smile-lose.com/models")
}

/**
 * Setup the webcam stream for the user.
 * On success, the stream of the webcam is set to the source of the HTML5 tag.
 * On error, the error is logged and the process continue.
 */
function setupWebcam() {
    navigator.mediaDevices
        .getUserMedia({ video: true, audio: false })
        .then(stream => {
            webcam.srcObject = stream
            if (isFirstRound) startFirstRound()
        })
        .catch(() => {
            document.getElementById("smileStatus").textContent = "camera not found"
            isUsingCamera = false
            if (isFirstRound) startFirstRound()
        })
}

/**
 * Determine if the user is smiling or not by getting the most likely current expression 
 * using the facepi detection object. Build a array to iterate on each possibility and 
 * pick the most likely.
 * @param {Object} expressions object of expressions
 * @return {Boolean}
 */
function isSmiling(expressions) {
    // filtering false positive
    const maxValue = Math.max(
        ...Object.values(expressions).filter(value => value <= 1)
    )

    const expressionsKeys = Object.keys(expressions)
    const mostLikely = expressionsKeys.filter(
        expression => expressions[expression] === maxValue
    )

    if (mostLikely[0] && mostLikely[0] == 'happy')
        return true

    return false
}

/**
 * Set an refresh interval where the faceapi will scan the face of the subject
 * and return an object of the most likely expressions.
 * Use this detection data to pick an expression and spread background gifs on divs.
 * @async
 */
async function refreshState() {
    setInterval(async() => {
        const detections = await faceapi
            .detectAllFaces(webcam, new faceapi.TinyFaceDetectorOptions())
            .withFaceExpressions()

        if (detections && detections[0] && detections[0].expressions) {
            isUsingCamera = true

            if (isSmiling(detections[0].expressions)) {
                currentSmileStatus = true
                document.getElementById("smileStatus").textContent = "YOU SMILE !"
            } else {
                document.getElementById("smileStatus").textContent = "not smiling"
            }
        }
    }, 400)
}
Enter fullscreen mode Exit fullscreen mode

您将在 GitHub 中找到该项目的所有源代码!

视频管理

如前所述,我无法管理视频的托管或流媒体播放。我希望托管和使用这个项目的成本接近于零。这是一个静态网站,这在这方面会很有帮助。感谢 S3 + Cloudflare 🙂

所以我想用 YouTube 播放器、YouTube 视频和 YouTube API。谢谢 YouTube。问题是我想继续用我自己的网站。所以我必须使用 YouTube 播放器的嵌入版本。

不用担心,YouTube 为嵌入播放器提供了专用 API

我以前从未使用过 YouTube API,但我必须说它非常容易理解和使用。

/**
 * Setup the youtube player using the official API
 */
function setupYoutubePlayer() {
    player = new YT.Player('player', {
        height: '100%',
        width: '100%',
        videoId: 'ewjkzE6X3BM',
        playerVars: {
            'controls': 0,
            'rel': 0,
            'showinfo': 0,
            'modestbranding': 1,
            'iv_load_policy': 3,
            'disablekb': 1
        },
        events: { 'onStateChange': onPlayerStateChange }
    })
}

/**
 * We want to show the intermissions when a video is over.
 * Listening to the event onPlayerStateChange of the youtube api.
 */
function onPlayerStateChange(event) {
    // 0 means the video is over
    if (event.data === 0) {
        player.stopVideo()
        showIntermission()
    }
}

/**
 * Entrypoint. This should be use once.
 */
function startFirstRound() {
    isFirstRound = false
    currentSmileStatus = false

    document.getElementById("loading").style.display = 'none'
    document.getElementById('intermission').className = 'fadeOut'

    player.playVideo()
}

/**
 * Showing the next video to the user.
 * This should be only trigger but the click on next video.
 */
function showNextVideo(event) {
    event.preventDefault()

    document.getElementById('loading').style.display = 'block'
    document.getElementById('result').style.display = 'none'

    if (listOfVideoIds.length) {
        const nextVideoId = extractRandomAvailableVideoId()
        player.loadVideoById({ videoId: nextVideoId })
        player.playVideo()

        setTimeout(() => {
            currentSmileStatus = false
            document.getElementById('intermission').className = 'fadeOut'
        }, 1000)
    } else {
        showCredit()
    }
}
Enter fullscreen mode Exit fullscreen mode

最后,我在应用程序最开始声明的一个简单的字符串数组(YouTube 视频 ID)中管理视频。每次用户点击观看其他视频时,我都会随机选择一个。然后,我会从数组中移除该 ID,并将其作为嵌入 YouTube 播放器的来源插入。简单!

待办事项

我很快就完成了。
结果,这个应用程序缺少了很多东西。
你想帮忙吗?

这里需要添加很多东西:

  • 分数管理:2020 年 3 月 11 日 -由此 PR 完成
  • 管理其他嵌入式播放器(dailymotion、vimeo、twitch)
  • 跳过按钮可以作弊并转到下一个视频:2020 年 3 月 11 日 -由此 PR 完成
  • 对微笑检测的管理不太严格(在计算真正的微笑之前要经过几次微笑)
  • 检测到用户不再处于摄像头的视野范围内(非常容易做到)
  • 隐藏部分视频结尾处的 YouTube 卡片显示

如果您对此列表中的某些内容感兴趣并且不惧怕 Javascript:您可以在这里找到 GitHub

你笑你就输了

打开相机,我们给你看搞笑视频。如果你笑了,你就输了!

https://www.smile-lose.com/

安装

npm install

配置

SSL

为了加载模型并使相机在本地工作,您需要安装自签名证书。

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365

完整解释在这里:https://hackernoon.com/set-up-ssl-in-nodejs-and-express-using-openssl-f2529eab5bb

当询问密码时,只需输入:default

正在加载模型 URL

在本地,您需要调整 URL 的配置以使模型正常加载:请参阅libs/app.js中的loadModels 函数

发射

开发

npm run-script start-dev

产品

npm start

再说一次,我很容易获得 PR 批准,所以不要犹豫。

结语

挑战结束。我笑得很开心,感觉很棒。希望你也一样。在这漫长的一天里,我能做的就只有这么多了。下周一见!

鏂囩珷鏉ユ簮锛�https://dev.to/jesuisundev/you-smile-you-lose-using-javascript-ai-2m5a
PREV
理解高阶函数
NEXT
5 分钟内理解 GraphQL