iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 20
1
Modern Web

寫JS30天系列 第 20

JS30 - 20 - Webcam Fun -part 2

  • 分享至 

  • xImage
  •  

昨天我們已經成功取得攝像頭的影片並畫到canvas上了
今天我們要取出canvas的圖
經過一些改造後再次畫到canvas上
我們今天除了影片中教學的「泛紅」、「色相分裂」、「過濾顏色」外
我們還新增了常用的「負片」、「灰階」

目標

  1. 取出canvas的圖
  2. 改造圖增加特效
  3. 放回canvas上

我們需要改造一下setInterval()的部分

return setInterval(() => {
        ctx.drawImage(video, 0, 0, width, height);
        
        //取出pixels(從canvas的0,0開始取,寬 = width, 高 = height)
        let pixels = ctx.getImageData(0, 0, width, height);
        //做效果
        pixels = redEffect(pixels);
        //放回(將pixels從0,0的位置開始放回)
        ctx.putImageData(pixels, 0, 0);
    }, 16);

如果這時候我們console.log(pixels)
會看到我們儲存像素點的地方是一個叫做Uint8ClampedArray的array

Uint8ClampedArray(1228800) [208, 192, 160, 255, 208, 192, 160, 255, 208, 192, 160, 255,...]

其內共有1百多萬個數值
其實它們是4個為一組
表示一個rgba的像素點要呈現的顏色
由左到右、由上到下排列

泛紅效果

泛紅效果就是要將紅色增加
其他兩種顏色下降

function redEffect(pixels) {
    for (let i = 0; i < pixels.data.length; i += 4) {
        pixels.data[i + 0] += 100;//red
        pixels.data[i + 1] -= 100;//green
        pixels.data[i + 2] *= 0.5;//blue
    }
    return pixels;
}

復古

復古風最重要的就是降低彩度
並增加紅色與黃色的比例

function oldStyle(pixels) {
    for (let i = 0; i < pixels.data.length; i += 4) {
        pixels.data[i + 0] += 150; //red
        pixels.data[i + 1] += 50; //green
        pixels.data[i + 2] += 50; //blue
        pixels.data[i + 3] *= 0.9; //alpha
    }
    return pixels;
}

色相分裂

只要將不同的顏色移動到不同的位置即可

function redSplit(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;
}

過濾顏色

JS30給我們的handler如下

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

過濾顏色就是
只要顏色落在指定區間
就讓他變透明(抽掉顏色)

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

負片

負片的效果就是把顏色變成互補色
那就是拿255去減掉原本取得的顏色

function negativeEffect(pixels) {
    for (let i = 0; i < pixels.data.length; i += 4){
        pixels.data[i + 0] = 255 - pixels.data[i + 0]; //red
        pixels.data[i + 1] = 255 - pixels.data[i + 1]; //green
        pixels.data[i + 2] = 255 - pixels.data[i + 2]; //blue
    }
    return pixels;
}

灰階

當三種顏色所包含的值相同時就會產生灰色
如果數值皆很低,則接近黑色
如果數值皆很高,則接近白色
因此,我們只要將三種顏色混合平均
再使三種顏色都等於平均值,即可解決

function grayscaleEffect(pixels) {
    for (let i = 0; i < pixels.data.length; i += 4) {
        let avg = (pixels.data[i + 0] + pixels.data[i + 1] + pixels.data[i + 2]) / 3;
        pixels.data[i + 0] = avg; //red
        pixels.data[i + 1] = avg; //green
        pixels.data[i + 2] = avg; //blue
    }
    return pixels;
}

Demo
完整程式碼


上一篇
JS30 - 19 - Webcam Fun - part 1
下一篇
JS30 - 21 - Native Speech Recognition
系列文
寫JS30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言