[JavaScript30] ๐Ÿ“น 19. Unreal Webcam Fun

์กฐ์ค€ํ˜•ยท2021๋…„ 7์›” 14์ผ
0

JavaScript30

๋ชฉ๋ก ๋ณด๊ธฐ
19/30

๐Ÿ“น 19. Unreal Webcam Fun

HTML Javascript๋ฅผ ์ด์šฉํ•ด์„œ ์›น์บ ์„ ์‚ฌ์šฉํ•˜๊ณ , ๋‹ค์–‘ํ•œ ํšจ๊ณผ ์ ์šฉํ•ด๋ณด๊ธฐ.

์ดˆ๊ธฐ์ฝ”๋“œ

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>Get User Media Code Along!</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>

  <div class="photobooth">
    <div class="controls">
      <button onClick="takePhoto()">Take Photo</button>
		<div class="rgb">
        <label for="rmin">Red Min:</label>
        <input type="range" min=0 max=255 name="rmin">
        <label for="rmax">Red Max:</label>
        <input type="range" min=0 max=255 name="rmax">

        <br>

        <label for="gmin">Green Min:</label>
        <input type="range" min=0 max=255 name="gmin">
        <label for="gmax">Green Max:</label>
        <input type="range" min=0 max=255 name="gmax">

        <br>

        <label for="bmin">Blue Min:</label>
        <input type="range" min=0 max=255 name="bmin">
        <label for="bmax">Blue Max:</label>
        <input type="range" min=0 max=255 name="bmax">
      </div>
    </div>

    <canvas class="photo"></canvas>
    <video class="player"></video>
    <div class="strip"></div>
  </div>

  <audio class="snap" src="./snap.mp3" hidden></audio>

  <script src="scripts.js"></script>

</body>
</html>
const video = document.querySelector('.player');
const canvas = document.querySelector('.photo');
const ctx = canvas.getContext('2d');
const strip = document.querySelector('.strip');
const snap = document.querySelector('.snap');

์ดˆ๊ธฐ ํ™”๋ฉด

๐ŸŒ ์ƒˆ๋กœ ์•Œ๊ฒŒ ๋œ ๊ฒƒ

๐Ÿ‘‰ window.URL.createObjectURL ๋น„์ถ”์ฒœ

์˜์ƒ๋Œ€๋กœ ๋”ฐ๋ผ ์‹คํ–‰ ํ–ˆ๋‹ค๊ฐ€ ๊ณ„์† ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ด FINISHED ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ณด๋‹ˆ chrome๊ณผ fireFox์—์„œ ๋น„์ถ”์ฒœํ•œ๋‹ค๊ณ  ์ ํ˜€์žˆ์—‡๋‹ค.

MDN์˜ createObjectURL์„ ๋“ค์–ด๊ฐ€ ๋ณด๋‹ˆ ๋ฏธ๋””์–ด ์ŠคํŠธ๋ฆผ์— ๊ฐœ์ฒดURL ์‚ฌ์šฉ ๋ถ€๋ถ„์—์„œ

๋ฏธ๋””์–ด ์†Œ์Šค ์‚ฌ์–‘์˜ ์ด์ „ ๋ฒ„์ „์—์„œ๋Š” <video>์š”์†Œ์— ์ŠคํŠธ๋ฆผ์„ ์ฒจ๋ถ€ ํ•˜๋ ค๋ฉด MediaStream์š”์†Œ์— ์ŠคํŠธ๋ฆผ์„ ์ฒจ๋ถ€ ํ•˜๋ ค๋ฉด MediaStream์š”์†Œ์— ์ŠคํŠธ๋ฆผ์„ ์ฒจ๋ถ€ ํ•˜๋ ค๋ฉด MediaStream์š”์†Œ์— ์ŠคํŠธ๋ฆผ์„ ์ฒจ๋ถ€ ํ•˜๋ ค๋ฉด MediaStream. ์ด๊ฒƒ์€ ๋” ์ด์ƒ ํ•„์š”ํ•˜์ง€ ์•Š์œผ๋ฉฐ ๋ธŒ๋ผ์šฐ์ €๋Š”์ด๋ฅผ์œ„ํ•œ ์ง€์›์„ ์ œ๊ฑฐํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Important: If you still have code that relies on createObjectURL() to attach streams to media elements, 
you need to update your code to set srcObject to the MediaStream directly.

๋ผ๊ณ  ์ ํžŒ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

video.srcObject = localMediaStream;

๋กœ ๋ณ€๊ฒฝํ•˜๋ฉด ๋™์ž‘ํ•˜๊ฒŒ ๋œ๋‹ค.

์ฐธ๊ณ  :

https://developer.mozilla. org/en-US/docs/Web/API/URL/createObjectURL

https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/srcObject

๐Ÿ‘‰ favicon.ico 404 Error

takePhoto()๋ถ€๋ถ„์„ ๋”ฐ๋ผํ•˜๋‹ค ๋ณด๋ฉด ์•„๋ ˆ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค.

GET http://localhost:3000/favicon.ico 404 (Not Found) error

favicon.ico๋Š” ๋ธŒ๋ผ์šฐ์ € ํƒญ๋ถ€๋ถ„์—์„œ ํƒ€์ดํ‹€ ๋งจ ์™ผ์ชฝ์— ์•„์ด์ฝ˜์„ ๋งํ•œ๋‹ค.

์•„์ด์ฝ˜์ด ์—†๊ฑฐ๋‚˜ ๊ฒฝ๋กœ๊ฐ€ ๋งž์ง€์•Š์•„ ๋ฐœ์ƒํ•˜๋Š” ์˜ค๋ฅ˜๋ผ๊ณ  ํ•œ๋‹ค.

ํ•ด๊ฒฐ๋ฐฉ๋ฒ• :

html headํƒœ๊ทธ์—์„œ link๋ฅผ ์ถ”๊ฐ€ํ•ด์คŒ.

<html> 
    <head> 
        <link rel="shortcut icon" href="#"> 
    </head> 
</html>

์ฐธ๊ณ  :

https://cheonfamily.tistory.com/7 [์ฒœ๋ฆฌ๊ธธ๋„ ํ•œ ๊ฑธ์Œ๋ถ€ํ„ฐ]

๐Ÿ‘‰ download์†์„ฑ

๋‹ค์šด๋กœ๋“œ ์—ฐ๊ฒฐ ์‹œ download attribute ์‚ฌ์šฉ.
๋ธŒ๋ผ์šฐ์ €์—์„œ ์—ด์ง€ ์•ˆํ˜น ๋‹ค์šด๋กœ๋“œํ•  ๋ฆฌ์†Œ์Šค์— ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒฝ์šฐ ๋‹ค์šด๋กœ๋“œ ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ธฐ๋ณธ ์ €์žฅ ํŒŒ์ผ ์ด๋ฆ„์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค.

[EX] Firefox 39์˜ Windows ๋ฒ„์ „์— ๋Œ€ํ•œ ๋‹ค์šด๋กœ๋“œ ๋งํฌ๊ฐ€ ์žˆ๋Š” ์˜ˆ

<a href="https://download.mozilla.org/?product=firefox-39.0-SSL&os=win&lang=en-US"
   download="firefox-39-installer.exe">
  Download Firefox 39 for Windows
</a>

์ฐธ๊ณ  :

https://developer.mozilla.org/ko/docs/Learn/HTML/Introduction_to_HTML/Creating_hyperlinks

๐ŸŒ ๊ณผ์ •

๐Ÿ‘‰ 0. ๋ณ€์ˆ˜ ์„ค์ • (์ดˆ๊ธฐ์ฝ”๋“œ)

const video = document.querySelector('.player');
const canvas = document.querySelector('.photo');
const ctx = canvas.getContext('2d');
const strip = document.querySelector('.strip');
const snap = document.querySelector('.snap');

video : ์šฐ์ธก์ƒ๋‹จ์— ๊ทธ๋ ค์งˆ video์˜์—ญ

canvas : ํ™”๋ฉด ์•„๋ž˜์— video์˜์—ญ์„ ๋ณต์‚ฌํ•ด ์ถœ๋ ฅํ•  ๋ถ€๋ถ„.

ctx : canvas๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋ถ€๋ถ„.

strip : ์บก์ณ ํ›„ ์‚ฌ์ง„ ๋ถ€๋ถ„์ด ๋‚˜ํƒ€๋‚  ์˜์—ญ

snap : ์‚ฌ์ง„ ์ฐ์„ ๋•Œ ์ฐฐ์นต ์†Œ๋ฆฌ๋‚˜๊ฒŒ ์žฌ์ƒ.

๐Ÿ‘‰ 1. NPM ์„ค์น˜ ๋ฐ ์‹œ์ž‘.

npm install
npm start

์‹œ์ž‘์€ index.html์ด ์‹œ์ž‘๋จ.

๐Ÿ‘‰ 2. getVideo()

function getVideo() {    
  navigator.mediaDevices.getUserMedia({ video: true, audio: false })        
    .then(localMediaStream => {            
    console.log(localMediaStream);
    // ํฌ๋กฌ,firefox์—์„œ ๋น„์ถ”์ฒœ            
    // video.src = window.URL.createObjectURL(localMediaStream);
    video.srcObject = localMediaStream;            
    video.play();        
  }).catch(error => console.error(`OH NO!!! ` ,error));

}

getVideoํ•จ์ˆ˜์— ์‹ค์‹œ๊ฐ„์œผ๋กœ ์›น์บ ์˜ ํ•˜๋ฉด์ด ๋‚˜์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ๋‚ด์šฉ์„ ์ž‘์„ฑ.
์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ OH NO!!์™€ ์—๋Ÿฌ๋ฉ”์„ธ์ง€๊ฐ€ ์ฝ˜์†”์— ์ฐํž˜.

getMedia์‹œ ์ฝ˜์†” ์ถœ๋ ฅ๊ฒฐ๊ณผ.

๐Ÿ‘‰ 3. paintToCanvas()

function paintToCanvas() {    
  const width = video.videoWidth;    
  const height = video.videoHeight;    
  // console.log(width, height);    
  canvas.width = width;    
  canvas.height = height;    
  return setInterval(() => {        
    ctx.drawImage(video, 0, 0, width, height)        
    // take the pixels out        
    let pixels = ctx.getImageData(0, 0, width, height);        
    // mess with them - ํšจ๊ณผ ๋ถ€๋ถ„.        
    // pixels = redEffect(pixels);        
    // pixels = rgbSplit(pixels);        
    // ctx.globalAlpha = 0.1;        
    // pixels = greenScreen(pixels);        
    // console.log(pixels);        
    // put them bakc        
    ctx.putImageData(pixels, 0, 0);    
  }, 16);
}

์บ”๋ฒ„์Šค์˜ ์˜์—ญ์„ ์ง€์ •ํ•˜๊ณ , video๋ฅผ ๊ทธ๋ฆฐ๋‹ค.

๐Ÿ‘‰ 4. takePhoto()

์‚ฌ์ง„์„ ์ฐ๋Š” ๋ฉ”์„œ๋“œ

function takePhoto() {    
  // played the sound    
  snap.currentTime = 0;    
  snap.play();    
  // take the date out of the canvas    
  const data = canvas.toDataURL('image/jpeg');    
  const link = document.createElement('a');    
  link.href = data;    
  link.setAttribute('download', 'handsome');    
  // link.textContent = 'Download Image';    
  link.innerHTML = `<img src="${data}" alt="Handsome Man" />`;
  strip.insertBefore(link, strip.firstChild);    
  console.log(data);
}

snap์„ ์žฌ์ƒ์‹œํ‚ค๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ ์‚ฌ์ง„์†Œ๋ฆฌ๊ฐ€ ์žฌ์ƒ๋œ๋‹ค.

download์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์บก์ณ ์‹œ ์ด๋ฏธ์ง€๋ฅผ ์ €์žฅํ•จ.

์ƒ์„ฑ๋œ ๋งํฌ ์ด๋ฏธ์ง€๋“ค์„ ์•„๋ž˜ strip์˜์—ญ์— ์ถ”๊ฐ€ํ•œ๋‹ค.

๐Ÿ‘‰ 5. ๋‹ค์–‘ํ•œ ํšจ๊ณผ๋“ค

  • redEffect()
function redEffect(pixels) {    
  for (let i = 0; i < pixels.data.length; i += 4) {        
    pixels.data[i + 0] = pixels.data[i + 0] + 200; // RED        
    pixels.data[i + 1] = pixels.data[i + 1] - 50; // GREEN
    pixels.data[i + 2] = pixels.data[i + 2] * 0.5; // BLUE    
  }    
  return pixels;
}

pixels์˜ data์— red๋ถ€๋ถ„์˜ ๊ฐ’์„ ๋ถ‰๊ฒŒ ๋งŒ๋“ค์–ด ๋ถ‰์€ ์Šคํฌ๋ฆฐ์ด ๋‚˜ํƒ€๋‚˜๊ฒŒ ๋งŒ๋“ฌ.

  • rgbSplit()

์ƒ‰์ƒ๋ณ„๋กœ ๋‚˜๋ˆ ์ ธ์„œ ๋ณด์ด๊ฒŒ ํ•จ.

function rgbSplit(pixels) {    
  for (let i = 0; i < pixels.data.length; i += 4) {       
    pixels.data[i - 150] = pixels.data[i + 0]; // RED        
    pixels.data[i + 100] = pixels.data[i + 1]; // GREEN        
    pixels.data[i + 150] = pixels.data[i + 2]; // BLUE    
  }    
  return pixels;
}

  • greenScreen()
function greenScreen(pixels) {    
  const levels = {};    
  document.querySelectorAll('.rgb input').forEach((input) => {
    levels[input.name] = input.value;    
  });    
  // console.log(levels);    
  for (i = 0; i < pixels.data.length; i = i + 4) {        
    red = pixels.data[i + 0];        
    green = pixels.data[i + 1];        
    blue = pixels.data[i + 2];        
    alpha = pixels.data[i + 3];            
    if (red >= levels.rmin && green >= levels.gmin && blue >= levels.bmin 
        && red <= levels.rmax && green <= levels.gmax && blue <= levels.bmax) {        // take it out!        
      pixels.data[i + 3] = 0;        
    }    
  }        
  return pixels;
}

rgb ๊ฐ’์˜ ์ œํ•œ์„ ๋‘ .

  • globalAlpha

context์— globalAlpha๊ฐ’์„ ์ฃผ์–ด ์ž”์ƒํšจ๊ณผ๊ฐ€ ๋‚˜ํƒ€๋‚˜๊ฒŒ๋จ.

return setInterval(() => {        
  ctx.drawImage(video, 0, 0, width, height)        
  // take the pixels out        
  let pixels = ctx.getImageData(0, 0, width, height);        
  // mess with them        
  // pixels = redEffect(pixels);        
  // pixels = rgbSplit(pixels);        
  ctx.globalAlpha = 0.1;        
  pixels = greenScreen(pixels);        
  // console.log(pixels);        
  // put them bakc        
  ctx.putImageData(pixels, 0, 0);    
}, 16);
  • ํšจ๊ณผ์ ์šฉ

paintToCanvas()์—์„œ setInterval๋ถ€๋ถ„์—์„œ ์บ”๋ฒ„์Šค๋ฅผ ๊ณ„์†๊ทธ๋ฆฌ๋Š”๋ฐ
์ด ๋•Œ pixels์— ํšจ๊ณผ๋ฅผ ์ง€์ •ํ•ด์ค€๋‹ค.

return setInterval(() => {        
  ctx.drawImage(video, 0, 0, width, height)        
  // take the pixels out        
  let pixels = ctx.getImageData(0, 0, width, height);        
  // mess with them        
  // pixels = redEffect(pixels);        
  // pixels = rgbSplit(pixels);        
  // ctx.globalAlpha = 0.1;        
  pixels = greenScreen(pixels);        
  // console.log(pixels);        
  // put them bakc        
  ctx.putImageData(pixels, 0, 0);    
}, 16);

๐Ÿ‘‰ 6.์ด๋ฒคํŠธ ์‹คํ–‰.

getVideo();video.addEventListener('canplay', paintToCanvas);

โ— ์šฉ๋Ÿ‰๋ฌธ์ œ๋กœ ์ธํ•ด GitHub์—์„œ ๊ณผ์ •๋ณ„ ์‹คํ–‰ํ™”๋ฉด์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
https://github.com/JuneHyung/JavaScript30/blob/master/19%20-%20Webcam%20Fun/README.md

profile
๊นƒํ—ˆ๋ธŒ : github.com/JuneHyung

0๊ฐœ์˜ ๋Œ“๊ธ€