Skip to content

sewonjun/happyThings_FE

Repository files navigation

Happy Things Logo

๊ฐ์ • ์ถ”๋ก ์„ ํ†ตํ•ด ํ–‰๋ณตํ•œ ์–ผ๊ตด์ด ๋ณด์ด๋ฉด ์‚ฌ์ง„์„ ์ฐ์–ด์ฃผ๋Š” AI ๊ธฐ๋ฐ˜ ๊ฐ์ • ์ถ”๋ก  ์›น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ Happy Things์ž…๋‹ˆ๋‹ค

๐Ÿ“’ Table of Contents

Preview

  • ์‹ค์‹œ๊ฐ„ ๋น„๋””์˜ค ์ŠคํŠธ๋ฆฌ๋ฐ์œผ๋กœ ๊ฐ์ • ์ถ”๋ก ์˜ ๊ฒฐ๊ณผ๋ฅผ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ–‰๋ณตํ•œ ํ‘œ์ •์ด ์บก์ณ๋˜์–ด ์‚ฌ์ง„์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์‚ฌ์ง„์— ์งง์€ ๋ฉ”๋ชจ๋ฅผ ์ ์–ด png๋กœ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Introduction

Happy Things๋Š” ์•ˆ๋ฉด ์ธ์‹์„ ํ†ตํ•ด ์‚ฌ๋žŒ์˜ ๊ฐ์ •์„ ์ถ”๋ก ํ•˜์—ฌ, ๊ทธ ์ˆœ๊ฐ„์„ ์‚ฌ์ง„์œผ๋กœ ์ œ๊ณตํ•ด์ฃผ๋Š” ์›น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ž…๋‹ˆ๋‹ค. ์ž์ฒด ๊ฐ์ • ์ถ”๋ก  ๋ชจ๋ธ์„ ๊ฐœ๋ฐœํ•˜์—ฌ, ๋ชจ๋ธ์ด ํ–‰๋ณตํ•œ ํ‘œ์ •์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์บก์ณํ•˜์—ฌ, ์‚ฌ์ง„์œผ๋กœ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ œ๊ณต๋ฐ›์€ ์‚ฌ์ง„์€ ๋‹ค์šด๋กœ๋“œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

Challenges

ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ์–ด๋ ค์› ๋˜ ์ฑŒ๋ฆฐ์ง€๋Š” ํฌ๊ฒŒ 3๊ฐ€์ง€๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

  1. ์ž์ฒด ๊ฐ์ • ์ถ”๋ก  ๋ชจ๋ธ์„ ์–ด๋–ป๊ฒŒ ๋งŒ๋“ค๊ฒƒ์ธ๊ฐ€?
  2. ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ ํ™˜๊ฒฝ์—์„œ 3๊ฐ€์ง€ ์ž‘์—…์„ ์–ด๋–ป๊ฒŒ ๋ณ‘๋ ฌ์ ์œผ๋กœ ์‹คํ–‰์‹œํ‚ฌ ๊ฒƒ์ธ๊ฐ€?
  3. ํฌ๋กœ์Šค ๋ธŒ๋ผ์šฐ์ง•์„ ์œ„ํ•œ safari ๋ฌธ์ œ ํ•ด๊ฒฐ ์—ฌ์ •

1. ์ž์ฒด ๊ฐ์ • ์ถ”๋ก  ๋ชจ๋ธ์„ ์–ด๋–ป๊ฒŒ ๋งŒ๋“ค๊ฒƒ์ธ๊ฐ€?

1-1. ๊ฐ์ • ์ถ”๋ก  ๋ชจ๋ธ ๊ฐœ๋ฐœ ๊ณผ์ •

๋จธ์‹ ๋Ÿฌ๋‹์„ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” 3๊ฐ€์ง€ ํฐ ๋‹จ๊ณ„๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๊ณผ๊ฑฐ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘
  • ๋ฐ์ดํ„ฐ ์ „์ฒ˜๋ฆฌ
  • ๋ฐ์ดํ„ฐ๋กœ ๋ชจ๋ธ์„ ํ•™์Šต

๊ฐ€์žฅ ๋จผ์ € ๊ฐ์ •๋ณ„๋กœ ๊ตฌ๋ณ„๋˜์–ด ์žˆ๋Š” ๊ณผ๊ฑฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์–ผ๊ตด์ด 48x48ํ”ฝ์…€ ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ์ด๋ฏธ์ง€๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์–ด ์žˆ๋Š” ๋ฐ์ดํ„ฐ์…‹ fer2013์„ ์ด์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘์„ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

.jpg๋กœ ๋˜์–ด ์žˆ๋Š” ๋ฐ์ดํ„ฐ์…‹์„ ๋ชจ๋ธ์— ํ•™์Šต ์‹œํ‚ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ฐ์ดํ„ฐ ์ „์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ์ง€ ์–ผ๊ตด ์ธ์‹์„ ํ†ตํ•ด ์•ˆ๋ฉด ๊ทผ์œก์„ ์ˆ˜์น˜๋กœ ์ธก์ •ํ•  ์ˆ˜ ์žˆ๋Š” MediaPipe์˜ Face-landmark-detection ๋ชจ๋ธ์„ ํ†ตํ•ด 3D๋กœ ์ธ์‹ํ•œ ์•ˆ๋ฉด ๊ทผ์œก์˜ ์›€์ง์ž„ ์ •๋„๋ฅผ ์ˆ˜์น˜ํ™” ์‹œํ‚ฌ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

[
    {
        "index": 25,
        "score": 0.845664918422699,
        "categoryName": "jawOpen",
        "displayName": ""
    },
    {
        "index": 4,
        "score": 0.48291221261024475,
        "categoryName": "browOuterUpLeft",
        "displayName": ""
    },
    {
        "index": 18,
        "score": 0.31452351808547974,
        "categoryName": "eyeLookUpRight",
        "displayName": ""
    },
    {
        "index": 17,
        "score": 0.29944589734077454,
        "categoryName": "eyeLookUpLeft",
        "displayName": ""
    },

    //...์ค‘๋žต
]
Mediapipe์—์„œ ์ œ๊ณตํ•˜๋Š” ์ด๋ฏธ์ง€ ์•ˆ๋ฉด์ธ์‹์„ ํ†ตํ•œ 52๊ฐœ์˜ ์•ˆ๋ฉด ๊ทผ์œก ๊ฐ’ ์˜ˆ์‹œ
์ „์ฒ˜๋ฆฌ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ง€๋„ ํ•™์Šต์œผ๋กœ ๋”ฅ๋จธ์‹  ๋Ÿฌ๋‹์„ ์‹œํ‚ค๊ธฐ ์œ„ํ•ด์„œ๋Š” label์„ ๋ถ™์—ฌ์ค˜์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. label์€ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๊ฐ€ ํ–‰๋ณตํ•œ ์ด๋ฏธ์ง€์ธ์ง€, ํ–‰๋ณตํ•˜์ง€ ์•Š์€ ์ด๋ฏธ์ง€์ธ์ง€ ๋ชจ๋ธ ํ•™์Šต์‹œ ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์žˆ๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
์ง€๋„ ํ•™์Šต์ด๋ž€? ์ž…๋ ฅ๊ณผ ํƒ€๊นƒ์„ ์ „๋‹ฌํ•˜์—ฌ ๋ชจ๋ธ์„ ํ›ˆ๋ จํ•œ ๋‹ค์Œ ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ์˜ˆ์ธกํ•˜๋Š” ๋ฐ ํ™œ์šฉํ•œ๋‹ค. k-์ตœ๊ทผ์ ‘ ์ด์›ƒ์ด ์ง€๋„ ํ•™์Šต ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‹ค.
//ํ–‰๋ณตํ•œ ์ด๋ฏธ์ง€ ๋ผ๋ฒจ๋ง =  1
1,
0.04288517311215401,0.09924527257680893,0.0024865891318768263,7.495935392398678e-7,0.0000015387073517558747,0.2613745927810669,0.1912066638469696,0.4886472225189209,0.254801481962204,0.004974926356226206,0.003475902369245887,0.011440315283834934,0.6184675693511963,0.6213760375976562

//ํ–‰๋ณตํ•˜์ง€ ์•Š์€ ์ด๋ฏธ์ง€ ๋ผ๋ฒจ๋ง = 0
0,
0.0005167833296582103,0.00033727806294336915,0.47885823249816895,4.2532121824478963e-7,2.177084184040723e-7,0.008147920481860638,0.02306659333407879,0.038605958223342896,0.045772310346364975,0.031849455088377,0.04954182356595993,0.005695571657270193,0.0000036304211334936554,0.0000025047413600987056

์œ„์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ ์˜ˆ์‹œ์ฒ˜๋Ÿผ ํ–‰๋ณตํ•œ ์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ์ธ ๊ฒฝ์šฐ 1์„ ๋ผ๋ฒจ๋ง์œผ๋กœ ๋ถ™์ด๊ณ , ํ–‰๋ณตํ•˜์ง€ ์•Š์€ ์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ์ธ ๊ฒฝ์šฐ 0์œผ๋กœ ๋ฐ์ดํ„ฐ ๋ผ๋ฒจ๋ง์„ ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.


์ „์ฒ˜๋ฆฌ๋œ ๋ฐ์ดํ„ฐ๋“ค์„ ํ•™์Šต ์‹œํ‚ฌ ๊ณผ์ •์—์„œ tensorflow๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋ธ์— ํ•™์Šต ์‹œํ‚ค๊ธฐ ์œ„ํ•ด์„œ๋Š” tensor๋ผ๋Š” ๋ฐ์ดํ„ฐ ํฌ๋ฉง์œผ๋กœ ๋ชจ๋ธ์„ ํ•™์Šต ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜์„ ์ œ๊ณตํ•ด์ฃผ๋Š” Danfojs๋ฅผ ํ†ตํ•ด .csvํŒŒ์ผ์„ tensor ๋ฐ์ดํ„ฐ ํฌ๋ฉง์œผ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋ชจ๋ธ์„ ํ•™์Šต ์‹œํ‚ฌ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ ํ•™์Šต ๊ฒฐ๊ณผ, ์•ฝ 80% ํ•™์Šต ์ •ํ™•๋„๋ฅผ ๊ฐ€์ง„ ๊ฐ์ • ์ถ”๋ก  ๋ชจ๋ธ์„ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

แ„†แ…ฉแ„ƒแ…ฆแ†ฏ_แ„’แ…กแ†จแ„‰แ…ณแ†ธแ„€แ…งแ†ฏแ„€แ…ช

1-2. ๊ฐ์ • ์ถ”๋ก  ๋ชจ๋ธ ์ถ”๊ฐ€ ํ•™์Šต ๊ณผ์ •

2. ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ ํ™˜๊ฒฝ์—์„œ ๋ณ‘๋ ฌ์ ์œผ๋กœ 3๊ฐ€์ง€ ์ž‘์—…์„ ์–ด๋–ป๊ฒŒ ํ•  ๊ฒƒ์ธ๊ฐ€?

์‹ค์‹œ๊ฐ„์œผ๋กœ ์–ผ๊ตด์ด ์ŠคํŠธ๋ฆฌ๋ฐ ๋˜๋Š” ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰๋˜์–ด์•ผ ํ•  3๊ฐ€์ง€ ์ž‘์—…์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

  • Mediapipe๋ฅผ ํ†ตํ•œ 3D ์–ผ๊ตด ์ธ์‹ ๋ฐ ์–ผ๊ตด ์ธ์‹ ๋งˆ์Šคํฌ ์”Œ์šฐ๊ธฐ
  • ๊ฐ์ • ์ถ”๋ก  ๋ชจ๋ธ์„ ํ†ตํ•œ ๊ฐ์ • ์ถ”๋ก 
  • ๊ฐ์ • ์ถ”๋ก ์‹œ, ํ–‰๋ณตํ•œ ์–ผ๊ตด์ผ ์‹œ ์‚ฌ์šฉ์ž ์–ผ๊ตด ์บก์ณ

์ด ์„ธ๊ฐ€์ง€ ์ž‘์—…์„ ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ ํ™˜๊ฒฝ์—์„œ ๋ณ‘๋ ฌ์ ์œผ๋กœ ์‹คํ–‰ํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ณ‘๋ ฌ์ ์œผ๋กœ ์„ธ๊ฐ€์ง€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ชจ๋“  ์ž‘์—…์ด ํ•˜๋‚˜์˜ ํ”„๋ ˆ์ž„ ์•ˆ์—์„œ ๋‹ค ๋๋‚˜์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ํ˜„๋Œ€ ๋””์Šคํ”Œ๋ ˆ์ด๋Š” ์ดˆ๋‹น 60ํ”„๋ ˆ์ž„์œผ๋กœ ๊ฐฑ์‹ ๋˜๋ฏ€๋กœ, ๋Œ€๋žต 16ms ์•ˆ์— ์„ธ๊ฐ€์ง€ ์ž‘์—…์ด ๋ชจ๋‘ ์‹คํ–‰๋˜์–ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ํ•˜๋‚˜์˜ ํ”„๋ ˆ์ž„ ์•ˆ์—์„œ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ ์‹คํ–‰ ์‹œ๊ฐ„ ์ตœ์ ํ™”๋ฅผ ์ค‘์ ์œผ๋กœ ๋‘๊ณ  ์ž‘์—…ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

2.1 ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ Œ๋”๋ง์„ ์œ„ํ•œ ๋ฐฉ๋ฒ•: requestAnimationFrame vs setTimeout

์‹ค์‹œ๊ฐ„์œผ๋กœ ์„ธ ๊ฐ€์ง€ ์ž‘์—…์„ ๋™์‹œ์— ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ requestAnimationFrame, setTimeout ๋‘๊ฐ€์ง€๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
rAF๋ฅผ ์‚ฌ์šฉํ–ˆ์„๋•Œ, ์•ฝ 16.7ms๋กœ ํ”„๋ ˆ์ž„์ด ๊ทœ์น™์ ์œผ๋กœ ๋ Œ๋”๋ง ๋˜๊ณ  ์žˆ๊ณ , setTimeout์„ ์‹คํ–‰ํ–ˆ์„๋•Œ๋Š” ์•ฝ 16~50ms๋กœ ๋ถˆ๊ทœ์น™์ ์œผ๋กœ ํ”„๋ ˆ์ž„ ๋ Œ๋”๋ง์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

requestAnimationFrame
  async function predictWebcam() {
    //...
    let startTimeMs = performance.now();
    const results = await faceLandmarker.detectForVideo(video, startTimeMs);
    //...
    if (!lastTime.current || currentTime - lastTime.current >= delay) {
      lastTime.current = currentTime;
      //...
      if (webcamRunning) {
      //requestAnimationFrame ์‚ฌ์šฉ
      //๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋ฆฌํŽ˜์ธํŠธ๋  ์ค€๋น„๊ฐ€ ๋๋‚˜๋ฉด ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์‹คํ–‰
        const animationFrameId = window.requestAnimationFrame(predictWebcam);
        setAnimationId(animationFrameId);
      }
    } else {
      if (webcamRunning) {
        const animationFrameId = window.requestAnimationFrame(predictWebcam);
        setAnimationId(animationFrameId);
      }
    }
setTimeout
async function predictWebcam() {
  //...
  let startTimeMs = performance.now();
  const results = await faceLandmarker.detectForVideo(video, startTimeMs);
  //...
  if (!lastTime.current || currentTime - lastTime.current >= delay) {
    lastTime.current = currentTime;
    //...
    if (webcamRunning) {
      //setTimeout์„ ์ด์šฉ
      // 16ms ์ง€์—ฐ ํ›„ ์‹คํ–‰
      const timeoutId = setTimeout(predictWebcam, 16);
      setAnimationId(timeoutId);
    }
  } else {
    if (webcamRunning) {
      const timeoutId = setTimeout(predictWebcam, 16);
      setAnimationId(timeoutId);
    }
  }
}

requestAnimationFrame์„ ์‚ฌ์šฉํ–ˆ์„๋•Œ, ํ”„๋ ˆ์ž„ ๋ Œ๋”๋ง ์ฃผ๊ธฐ
image

setTimeout์„ ์‚ฌ์šฉํ–ˆ์„๋•Œ, ํ”„๋ ˆ์ž„ ๋ Œ๋”๋ง ์ฃผ๊ธฐ
image

์ด๋Š” requestAnimationFrame๊ณผ setTimeout์˜ ๋™์ž‘ ์ฐจ์ด์—์„œ ์ผ์–ด๋‚˜๋Š” ํ˜„์ƒ์ด๋ผ๋Š” ์‚ฌ์‹ค์„ ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋ฐฉ๋ฒ• ํŠน์ง•
requestAnimationFrame - ๋‹ค์–‘ํ•œ ๋””๋ฐ”์ด์Šค์—์„œ ํ•ด๋‹น ๋ธŒ๋ผ์šฐ์ €์˜ ๋ฆฌํŽ˜์ธํŠธ ์ฃผ๊ธฐ์— ๋งž๊ฒŒ ์ฝœ๋ฐฑ์„ ์˜ˆ์•ฝํ•œ๋‹ค.
- ์‚ฌ์šฉ์ž์˜ ๋””๋ฐ”์ด์Šค ์„ฑ๋Šฅ๊ณผ ๋ฐฐํ„ฐ๋ฆฌ ์ˆ˜๋ช…์„ ๊ณ ๋ คํ•œ๋‹ค
- ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋น„ํ™œ์„ฑํ™” ๋˜๋ฉด ์ฝœ๋ฐฑ์ด ์ค‘๋‹จ๋œ๋‹ค. (๋ฆฌ์†Œ์Šค ์ ˆ์•ฝ)
setTimeout - ์ •ํ•ด์ง„ ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ์œผ๋กœ ์‹คํ–‰. => ๋ธŒ๋ผ์šฐ์ € ๋ Œ๋”๋ง ์ฃผ๊ธฐ์™€ ๋™๊ธฐํ™” ๋˜์ง€ ์•Š๋Š”๋‹ค.
- ๋ธŒ๋ผ์šฐ์ € ํ™œ์„ฑ ์—ฌ๋ถ€์™€ ์ƒ๊ด€์—†์ด ์ง€์ •๋œ ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ๋Œ€๋กœ ์‹คํ–‰์ด ๋œ๋‹ค. (๋ฆฌ์†Œ์Šค ๋‚ญ๋น„)
- ๋‹ค๋ฅธ ์ž‘์—… ์ˆ˜ํ–‰์œผ๋กœ ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์‹คํ–‰์ด ์ง€์—ฐ๋˜๋ฉด, ํ”„๋ ˆ์ž„ ๋“œ๋ž์ด ์ผ์–ด๋‚˜๊ณ , ์ด๋Š” ํ™”๋ฉด ๋ฒ„๋ฒ…๊ฑฐ๋ฆผ์„ ์ดˆ๋ž˜ํ•œ๋‹ค.

requestAnimationFrame์€ ๋ธŒ๋ผ์šฐ์ € ํ”„๋ ˆ์ž„ ๋ Œ๋”๋ง ์ฃผ๊ธฐ์— ๋งž์ถฐ ์ฝœ๋ฐฑ์„ ์˜ˆ์•ฝํ•˜๊ณ , ๋ฆฌ์†Œ์Šค ์ ˆ์•ฝ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌด์—‡๋ณด๋‹ค ๋‹ค์–‘ํ•œ ๋””๋ฐ”์ด์Šค์—์„œ ์ž‘๋™์ด ๊ฐ€๋Šฅํ•ด์•ผ ํ•˜๋Š” ํ”„๋กœ์ ํŠธ ํŠน์„ฑ์ƒ, ๋ธŒ๋ผ์šฐ์ €์— ๋งž์ถฐ ์ฝœ๋ฐฑ์„ ์˜ˆ์•ฝํ•œ๋‹ค๋Š” ์ ์—์„œ requestAnimationFrame์ด setTimeout๋ณด๋‹ค ์ด ํ”„๋กœ์ ํŠธ์— ์ ํ•ฉํ•˜๋‹ค๊ณ  ํŒ๋‹จํ•˜์˜€์Šต๋‹ˆ๋‹ค.

2.2 timestamp๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋ ˆ์ž„ ์บก์ณํ•˜๊ธฐ

requestAnimationFrame์„ ํ†ตํ•ด ์–ผ๊ตด ์ธ์‹ ๋ฐ ๊ฐ์ • ์ถ”๋ก ์„ ๋ธŒ๋ผ์šฐ์ €์˜ ํ”„๋ ˆ์ž„์— ๋งž์ถฐ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์‚ฌ์šฉ์ž ์–ผ๊ตด์„ ์บก์ณํ•ด์ฃผ๋Š”๋ฐ์„œ ๋‹ค๋ฅธ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ํ”„๋ ˆ์ž„์—์„œ ์บก์ณ๋ฅผ ์ง„ํ–‰ํ•˜๋‹ˆ ํ”„๋ ˆ์ž„ ๋ Œ๋”๋ง ์ฃผ๊ธฐ๊ฐ€ 15-20ms์—์„œ 15-33ms๋กœ ๊ธธ์–ด์กŒ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ํ”„๋ ˆ์ž„ ๋ Œ๋”๋ง ์ฃผ๊ธฐ์˜ ๋ถˆ๊ทœ์น™์„ฑ์„ ๊ฐ€์ ธ์˜ค๊ณ , ๊ฒฐ๊ตญ ์–ผ๊ตด ์ธ์‹์ด ๋ถ€๋“œ๋Ÿฝ์ง€ ์•Š๊ฒŒ ์ด๋ฃจ์–ด์ง€๋Š” ๊ฒฐ๊ณผ๋ฅผ ์ดˆ๋ž˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฐ ์ƒํ™ฉ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์ผ์ •ํ•œ ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ์„ ๋‘๊ณ  ์บก์ณ๋ฅผ ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๊ณ ๋ฏผํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์ผ์ • ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ์„ ๋‘๊ธฐ ์œ„ํ•ด์„œ requestAnimationFrame์—์„œ๋Š” performance.now๋ฅผ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค. performance.now()๋Š” ์ด์ „ ํ”„๋ ˆ์ž„๊ณผ ํ˜„์žฌ ํ”„๋ ˆ์ž„์˜ ์‹œ๊ฐ„์ฐจ๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ๊ธฐ๋กํ•˜์—ฌ ์ผ์ • ์‹œ๊ฐ„์ด ์ง€๋‚œํ›„์—, capture ๋กœ์ง์ด ์ž‘๋™ํ•˜๋„๋ก timestamp๋ฅผ ๋„์ž…ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

const currentTime = performance.now();
const delay = 500;

//currentTime - lastTime์ด delay๋งŒํผ ๋˜์—ˆ์„๋•Œ, ์บก์ณ ์ง„ํ–‰
if (!lastTime.current || currentTime - lastTime.current >= delay) {
  lastTime.current = currentTime;

  // ์บก์ณ ๋กœ์ง
  if (webcamRunning) {
    const animationFrameId = window.requestAnimationFrame(predictWebcam);
    setAnimationId(animationFrameId);
  }
} else {
  if (webcamRunning) {
    const animationFrameId = window.requestAnimationFrame(predictWebcam);
    setAnimationId(animationFrameId);
  }
}

Note

ํ”„๋ ˆ์ž„ ํƒญ์— ๋…ธ๋ž€์ƒ‰ ๋น—๊ธˆ์ด ์ณ์ €์žˆ๋Š” ์˜์—ญ์€ ํ”„๋ ˆ์ž„์ด ๊ทธ๋ ค์ง€์ง€ ์•Š์•˜๋‹ค๋Š” ํ‘œ์‹œ์ด๋‹ค.

timestamp๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„์„œ ํ”„๋ ˆ์ž„ ๋“œ๋ž์ด ์ผ์–ด๋‚จ
image

timestamp๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋ ˆ์ž„ ๋“œ๋ž์ด ์ƒ๊ธฐ์ง€ ์•Š์Œ
image

3. ํฌ๋กœ์Šค ๋ธŒ๋ผ์šฐ์ง•์„ ์œ„ํ•œ safari ๋ฌธ์ œ ํ•ด๊ฒฐ ์—ฌ์ •

์นด๋ฉ”๋ผ ๊ถŒํ•œ์„ ์–ป๊ธฐ ์œ„ํ•ด MediaDevices.getUserMedia()๋ฅผ ํ†ตํ•ด ๋ฏธ๋””์–ด ์ž…๋ ฅ ์‚ฌ์šฉ ํ—ˆ๊ฐ€๋ฅผ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ Chrome์ด๋‚˜ MicorSoft Edge์—์„œ๋Š” ์นด๋ฉ”๋ผ ๊ถŒํ•œ ์š”์ฒญ์„ ํ•  ์ˆ˜ ์žˆ์—ˆ์ง€๋งŒ, safari์—์„œ๋Š” ์˜ค๋ฅ˜๊ฐ€ ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค

3-1. safari์—์„œ์˜ ์นด๋ฉ”๋ผ ์ ‘๊ทผ ๊ถŒํ•œ ์˜ค๋ฅ˜

image

๋‹ค๋ฅธ ๋ธŒ๋ผ์šฐ์ €์™€๋Š” ๋‹ค๋ฅด๊ฒŒ safari๋Š” MediaDevices.getUserMedia()๋ฅผ undefined๋ฅผ ๋ฐ˜ํ™˜ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ด๋Š” MediaDevices.getUserMedia()๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ํ•˜๋Š” ์ •์ฑ… ๋•Œ๋ฌธ์ธ๊ฒƒ์„ ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. HTTPS, file:/// URL ์ฒด๊ณ„๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๋“œ๋œ ํŽ˜์ด์ง€ ๋˜๋Š” ๋กœ์ปฌ ํ˜ธ์ŠคํŠธ์—์„œ ๋กœ๋“œ๋œ ํŽ˜์ด์ง€๊ฐ€ ์•„๋‹ˆ๋ฉด MediaDevices.getUserMedia()๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ”„๋กœ๋ฏธ์Šค๋ฅผ ๊ฑฐ๋ถ€ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ safari์—์„œ๋Š” localhost๋„ ์˜ˆ์™ธ๊ฐ€ ์•„๋‹ˆ์˜€์Šต๋‹ˆ๋‹ค. safari์—์„œ๋Š” ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋„ HTTPS๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด MediaDevices.getUserMedia()๋ฅผ ๋ฐ˜ํ™˜ ๋ฐ›์„ ์ˆ˜ ์—†๊ธฐ์— ๋กœ์ปฌ์—์„œ HTTPS๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” @vitejs/plugin-basic-ssl๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๊ฒฐํ•˜์˜€์Šต๋‹ˆ๋‹ค.

3-2. safari์—์„œ์˜ ๋น„๋””์˜ค ์†์„ฑ ์˜ค๋ฅ˜

๋ชจ๋ฐ”์ผ ํ™˜๊ฒฝ์—์„œ video๊ฐ€ ์ „์ฒด ํ™”๋ฉด์œผ๋กœ ์ปค์ง€๋ฉด์„œ ์•ˆ๋ฉด ์ธ์‹ ๋งˆ์Šคํฌ๊ฐ€ ์”Œ์›Œ์ง€์ง€ ์•Š๋Š” ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค. ์ „์ฒด ํ™”๋ฉด์—์„œ ๋‚˜์˜ค๋ฉด ๋ผ์ด๋ธŒ ์ŠคํŠธ๋ฆฌ๋ฐ์ด ๋” ์ด์ƒ ๋Œ์•„๊ฐ€์ง€ ์•Š๊ณ , ์•ˆ๋ฉด ์ธ์‹ ๋งˆ์Šคํฌ๊ฐ€ ๋ฉˆ์ถฐ์žˆ๋Š” ๋ชจ์Šต์„ ๋ณผ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์ด๋Š” ios์˜ ๋ฌธ์ œ๋ผ๋Š” ์‚ฌ์‹ค์„ ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. webkit์—์„œ ๋ฐœํ‘œํ•œ New <video> Policies for iOS์„ ํ†ตํ•ด, ๊ณผ๊ฑฐ์˜ ios์—์„œ์˜ video ํƒœ๊ทธ์— ๋Œ€ํ•œ ๋ณ€ํ™”๋ฅผ ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ๋ธ”๋กœ๊ทธ ๊ธ€์—์„œ video ํƒœ๊ทธ์˜ ์žฌ์ƒ์„ ์œ„ํ•ด์„œ๋Š” ์‚ฌ์šฉ์ž ์ œ์Šค์ฒ˜, ์ฆ‰ eventListener์˜ ๋™์ž‘์ด ํ•„์š”ํ•˜๋‹ค๊ณ  ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ New <video> Policies for iOS์—์„œ ๋ช…์‹œํ•˜๊ธธ ios10 ์ดํ›„๋กœ ๋ฌด์Œ video ์š”์†Œ์— ๋Œ€ํ•œ ์œ ์ € ์ œ์Šค์ฒ˜ ์š”๊ตฌ ์‚ฌํ•ญ์ด ์™„ํ™”๋˜์—ˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ, ๋ฌด์Œ video์˜ ๊ฒฝ์šฐ autoplay๋ฅผ ํ—ˆ์šฉํ•˜๋„๋ก ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ฒฐ๊ตญ video ํƒœ๊ทธ์— playsinline ์†์„ฑ ์ถ”๊ฐ€๋ฅผ ํ†ตํ•ด ์ธ๋ผ์ธ์œผ๋กœ ์žฌ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์žฌ์ƒ์ด ์‹œ์ž‘๋  ๋•Œ ์ž๋™์œผ๋กœ ์ „์ฒดํ™”๋ฉด ๋ชจ๋“œ๋กœ ๋“ค์–ด๊ฐ€๋Š” ํ˜„์ƒ์„ ํ•ด๊ฒฐํ•˜์˜€์Šต๋‹ˆ๋‹ค.

Schedule

ํ”„๋กœ์ ํŠธ ๊ธฐ๊ฐ„: 2023.08.07 ~ 2023.09.07 /

1์ฃผ์ฐจ ์ผ์ •
  • ์•„์ด๋””์–ด ์ˆ˜์ง‘
  • ๋ ˆํผ๋Ÿฐ์Šค ์ˆ˜์ง‘
  • Figma๋ฅผ ์‚ฌ์šฉํ•œ Mockup ์ œ์ž‘
  • Notion์„ ์‚ฌ์šฉํ•œ ์นธ๋ฐ˜ ์ž‘์„ฑ

  • 2์ฃผ์ฐจ ~ 3์ฃผ์ฐจ ์ผ์ •
  • husky, eslint, lint-staged๋ฅผ ์‚ฌ์šฉํ•œ ์ดˆ๊ธฐ ์„ค์ •
  • ํ”„๋ก ํŠธ์—”๋“œ, ๋ฐฑ์—”๋“œ ๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ ์ดˆ๊ธฐ ์„ค์ •
  • Mediapipe Facelandmark detection ์ ์šฉ์‹œํ‚ค๊ธฐ
  • ๋ฐฑ์—”๋“œ ๊ธฐ๋Šฅ ๊ตฌํ˜„
  • ํ”„๋ก ํŠธ์—”๋“œ ๊ธฐ๋Šฅ ๊ตฌํ˜„
  • ๋ฐœํ‘œ ์ค€๋น„

  • Tech Stacks

    Frontend


    Backend



    About

    No description, website, or topics provided.

    Resources

    Stars

    Watchers

    Forks

    Releases

    No releases published

    Packages

    No packages published