iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 20
0

[程式碼&DEMO] [HackMD完整筆記]

目標


驅動webcam來紀錄影像資訊,再用cavas達成拍照與濾鏡效果。

步驟流程


預設的配置:

  • video: 原始鏡頭位置。
  • canvas: 擷取鏡頭的內容渲染在畫布上,及特效呈現之處。
  • strip: 產生圖檔的位置。
  • snap: 點擊照相時產生的音效。

STEP0.建立一個localhost服務器

STEP1 取得影像

function getVideo() {
   // 取得視訊裝置,回傳Promise狀態
  navigator.mediaDevices.getUserMedia({ video: true, audio: false })
  
    //允許就把回傳的MediaStream寫進html的video tag中進行播放
    .then(localMediaStream => {
      console.log(localMediaStream);   
      video.srcObject = localMediaStream;
      video.play();
    })
    
    //失敗就印出錯誤結果
    .catch(err => {
      console.error(`OH NO!!!`, err);
    });
}

navigator.mediaDevices.getUserMedia()取得攝影鏡頭的權限。
返回一個promise對象。

  • 點選允許=>回傳一個resolved狀態。回呼函數為一個MediaStream物件。
  • 點選拒絕=>回傳一個Reject狀態。回呼一個PermissionDeniedError函數。

STEP2 擷取視訊影像放到canvas中

function paintToCanvas() {
  //設定畫布大小
  const width = video.videoWidth;
  const height = video.videoHeight;
  canvas.width = width;
  canvas.height = height;
  /// 用setInterval來不斷地取得目前影像資訊
  return setInterval(() => {
    //將畫面擷取下來
    ctx.drawImage(video, 0, 0, width, height);
    //獲取canvas區域內的像素數據存進pixels變數中
    let pixels = ctx.getImageData(0, 0, width, height);
    //呼叫rgbSplit()把rgb像素分離
    pixels = rgbSplit(pixels);
    //將ImageData的數據繪製到圖像上
    ctx.putImageData(pixels, 0, 0);
  }, 16);
}

STEP3 製作拍照功能

function takePhoto() {
  // 拍照的音效
  snap.currentTime = 0;//音效從第0秒開始播放
  snap.play();

  // 用.toDataURL把canvas內容轉成base64的圖檔資訊
  const data = canvas.toDataURL('image/jpeg');
  //建立一個新的<a>元素
  const link = document.createElement('a');
  //設置連結位置為轉圖檔後的base64位置
  link.href = data;
  // 設置連結屬性為下載
  link.setAttribute('download', 'handsome');
  // 新增一個預覽圖
  link.innerHTML = `<img src="${data}" alt="Handsome Man" />`;
  //在圖片區插入新圖片(在第一筆的位置)
  strip.insertBefore(link, strip.firstChild);
}

STEP4 製作濾鏡效果

  • 紅色濾鏡效果
function redEffect(pixels) {
//用迴圈將取回的所有像素資料全部跑一次,四個一組(r,g,b,alpha)
  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;
}

把紅的項目加深處理,藍綠的項目-50及減半。

  • 色彩分離效果
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 + 500] = pixels.data[i + 1]; // GREEN
    pixels.data[i - 550] = pixels.data[i + 2]; // Blue
  }
  return pixels;
}
  • 綠色濾鏡效果
function greenScreen(pixels) {
  const levels = {};
  
  document.querySelectorAll('.rgb input').forEach((input) => {
    levels[input.name] = input.value;
  });

  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;
}

學習筆記


navigator.mediaDevices.getUserMedia()

語法:
var promise = navigator.mediaDevices.getUserMedia(constraints);

window.URL.createObjectURL(localMediaStream)

建立一個帶有URL的DOMString,代表參數值中傳入的物件。此URL的生命週期與創造它的window一樣。

strip.insertBefore(l,s)


上一篇
[JS30]DAY18 : Adding Up Times with Reduce
下一篇
[JS30]DAY20 : Speech Detection
系列文
JS煉金術:Javascript30+聲光玩轉的Drum Pads30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言