Javascript_30_19

Derek·2021년 1월 5일
3

javascript_30

목록 보기
20/31
post-thumbnail

안녕하세요!

Derek 입니다 :)

너무 추워요. 방금 집들어오다가 안경에 김이 안사라져서 개짜증나네요.

마스크..! 이놈의 마스크 너무 화나는 밤이에요!
오늘은 Day 19 project 포스팅을 하려고 해요. 벌써 19번째..!

dog 기쁩니다 :)

이번 포스팅은 꽤나 길지만.. 재밌는 글이 될거에요! 긴거 개싫음 개귀찮




19. Webcam Fun

목표

먼저 양해의 말씀 전해드립니다. 보여드려야해서 어쩔수 없이 이 지경까지 이르렀네요.

webcam 을 작동시킨다. 작동되는 비디오를 canvas 로 가져와서, 사진을 찍어 저장가능하게 하며, 특수효과도 가능하게 한다.

나름.. 규모가 컸던 과제였어요. 허나 이게 유용할 것 같진 않고.. 일단 정리만 해두면 언젠가 사용하겠지, 라는 의미에서 즐겁게 해봤습니다.


Derek 과 Wes Bos 구현코드

const getVideo = () => {
    navigator.mediaDevices.getUserMedia({ video: true, audio: false })
    .then(localMediaStream => {
        video.srcObject = localMediaStream;
        video.play();
    })
    .catch(err => {
        console.error(`OH NO!!!`, err);
      });
}

const paintToCanvas = () => {
    const width = video.videoWidth;
    const height = video.videoHeight;

    canvas.width = width;
    canvas.height = height;

    setInterval( () => {
        ctx.drawImage(video, 0, 0, width, height)

        // take the pixels out, 
        let pixels = ctx.getImageData(0, 0, width, height);
        
        // and then mess with them,
        // pixels = redEffect(pixels);
        // pixels = RGBSplit(pixels);
        // ctx.globalAlpha = 0.8;

        // and then finally put them back in to ctx.
        ctx.putImageData(pixels, 0, 0);
    }, 16)
}

const takePhoto = () => {
    snap.currentTime = 0 ;
    snap.play();

    const data = canvas.toDataURL("image/jpeg");
    const link = document.createElement("a");
    link.href = data;
    link.setAttribute("download", "Derek");
    link.innerHTML = `<img src = "${data}" alt = Derek />`;
    strip.insertBefore(link, strip.firstChild);
}

getVideo();

video.addEventListener("canplay", paintToCanvas);

크게 3가지 함수가 돌아갑니다. 특수효과를 주는 함수는 따로 마지막에 정리할게요 :)

하나하나 함수 보겠습니다.


1) video 재생함수

const getVideo = () => {
    navigator.mediaDevices.getUserMedia({ video: true, audio: false })
    .then(localMediaStream => {
        video.srcObject = localMediaStream;
        video.play();
    })
    .catch(err => {
        console.error(`OH NO!!!`, err);
      });
}

getVideo();

말 그대로, 웹캠으로 비디오를 켜서 재생하는 함수입니다.

navigator.mediaDevices.getUserMedia({ video: true, audio: false })

이 친구를 수행하면 미디어 입력장치를 허용하게 됩니다. MDN 사이트 설명에 보시면 알 수 있어요.
여기서는 promise 를 직접 사용하여 스트림을 사용했습니다.

사실 정확한 원리는 잘 이해가 가진 않지만, 정해 놓은 틀처럼, 웹캠을 사용하고 싶으면 위와 같이 작성하면 쉽게 될 것 같습니다. 🤗

2) Canvas 로 옮기기

재생하는 비디오를 Canvas 로 옮기는 작업이에요! 08번 project에서 다루었던 내용을 참고하시면 더욱 좋을 것 같습니다 :)

const paintToCanvas = () => {
    const width = video.videoWidth;
    const height = video.videoHeight;

    canvas.width = width;
    canvas.height = height;

    setInterval( () => {
        ctx.drawImage(video, 0, 0, width, height)

        // take the pixels out, 
        let pixels = ctx.getImageData(0, 0, width, height);
        
        // and then mess with them,
        // pixels = redEffect(pixels);
        // pixels = RGBSplit(pixels);
        // ctx.globalAlpha = 0.8;
        // pixels = greenScreen(pixels);

        // and then finally put them back in to ctx.
        ctx.putImageData(pixels, 0, 0);
    }, 16)
}

paintToCanvas 함수는 재생되는 비디오를 캔버스에서 재생이 되도록 하는 함수에요! 다시 한번 눈갱 죄송합니다.

위 움짤을 보시면 우측 상단에 getVideo 함수가 실행된 것이고, 큰 화면은 getVideo 함수를 canvas 로 가져온 것 입니다.

그렇게 하기 위해서, 일단 너비와 높이를 맞춰주었어요. (4줄)

그러고 나서 익명 함수를 16 밀리초마다 실행을 시키는데, video 재생되는 것을 이미지로 따서, 설정한 캔버스에 아주 짧은 시간마다 보여주는 모습입니다. 즉, 빠르게 스샷을 찍어서 canvas 에 보여주니 비디오가 재생되는 것 처럼 보이는 것이죠!

setInterval( () => {
        ctx.drawImage(video, 0, 0, width, height)

        // take the pixels out, 
        let pixels = ctx.getImageData(0, 0, width, height);
        
        // and then mess with them,
        // pixels = redEffect(pixels);
        // pixels = RGBSplit(pixels);
        // ctx.globalAlpha = 0.8;
        // pixels = greenScreen(pixels);

        // and then finally put them back in to ctx.
        ctx.putImageData(pixels, 0, 0);
    }, 16)

이 함수가 그 부분입니다. drawImage 함수로 이미지를 만들고, 보여주는 함수에요.

중간부터 pixels는 필터를 주기 위한 작업이라, 추후에 말씀드리겠습니다.

3) takePhoto 함수

다음은 사진을 찍는 함수입니다!

const takePhoto = () => {
    snap.currentTime = 0 ;
    snap.play();

    const data = canvas.toDataURL("image/jpeg");
    const link = document.createElement("a");
    link.href = data;
    link.setAttribute("download", "Derek");
    link.innerHTML = `<img src = "${data}" alt = Derek />`;
    strip.insertBefore(link, strip.firstChild);
}

아 참고로, snap 은, audio 태그입니다. 사진찍기 버튼 누를때마다 찰칵 소리가 나도록 했어요.

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

snapconst snap = document.querySelector('.snap') 로 조정했습니다.

1번째 플젝을 보시면 audio 재생에 대해서 자세히 다루고 있어요! 참고 부탁드립니다. :)

앞에서 다뤘듯이, canvas 에 순간순간 사진을 빠르게 보여주니 비디오처럼 보이는 것을 활용합니다.

해당 버튼을 누르면, 사진을 data 로 가져와요!

그래서 단순히 a 태크를 활용해서, setAttribute 함수를 사용하고, innerHTML 와 백틱을 사용해서 HTML 에 추가하여, 보여주는 모습입니다.

4) filter 기능

다음은 간단하게 언급만 하려는 필터 기능입니다!

const redEffect = (pixels) => {
    for(let i = 0 ; i < pixels.data.length; i += 4) {
        pixels.data[i] = pixels.data[i] + 70; 
        pixels.data[i + 1] = pixels.data[i + 1] - 50;
        pixels.data[i + 2] = pixels.data[i + 2] * 0.5; 
    }
    return pixels;
}

const RGBSplit = (pixels) => {
    for(let i = 0 ; i < pixels.data.length; i += 4) {
        pixels.data[i - 150] = pixels.data[i + 0]; // RED
        pixels.data[i + 500] = pixels.data[i + 1]; // GREEN
        pixels.data[i - 550] = pixels.data[i + 2]; // Blue
    }
    return pixels;
}

이렇게 3개의 필터를 넣을 수가 있어요. 이 함수들은 상단에 paintToCanvas 함수에 주석처리 된 곳을 하나씩만 풀어서 적용할 수 있습니다.

간단히 설명하자면, pixels.dataRGBA 값으로, pixels.data[i] 하나하나가 R 값, G 값, B 값, A 값입니다. A 값은 색의 표현이 아니기 때문에 redEffect 함수와 RGBSplit 함수에서는 3개 픽셀만 다루는 모습입니다.

모든 픽셀에 대해서 for문 을 돌며 각각의 픽셀을 R 값은 R 값 끼리 조정하고, G 값은 G 값 끼리만 조정하고.. 그런 로직을 따라갑니다.

  • RedEffect 함수 : 단순히 각각 픽셀을 조정합니다.
  • RGBSplit 함수 : 3개의 픽셀값을 다른 곳으로 이동시켜, 3개로 분리되게 보입니다. (하단 참조)


    예를 들어 RGBSplit 함수를 적용시켰을때는,

    이런식으로.. 필터를 줄 수가 있어요!



이렇게 웹캠을 사용해보는 과제도 정리해보았습니다!

이런 내용은 굳이 암기하여 숙지 할 필요는 없어보여요. 단순히 필요할때만 구글링해서 찾아보는게 나을 것 같습니다 :) 저는 정리를 목적으로 포스팅 해보았어요!

틀린내용이나 수정할 내용이 있다면 언제든지 피드백 부탁드립니다!

감사합니다!🤗

profile
Whereof one cannot speak, thereof one must be silent.

2개의 댓글

comment-user-thumbnail
2021년 1월 5일

옷의 RGB 컬러가 인상적이네요.
코드 잘보고갑니다

1개의 답글