昨天我們已經成功取得攝像頭的影片並畫到canvas上了
今天我們要取出canvas的圖
經過一些改造後再次畫到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;
}