canvas操作-修改圖片亮度
圖片亮度的概念
我們一般對圖片的概念就是又很多畫素點構成的一幅圖片,一個畫素點由RGBA
四個值表示。
- R:紅色
- G:綠色
- B:藍色
- A:透明度
不過RGBA
並不能直觀的表現出畫素點的亮度,它比較適合機器理解,給一個rgba
的畫素我們可以猜出它是偏什麼顏色的,不過卻不能理解它的亮度值。所以rgba
並不是一個很好作為圖片處理的格式,這裡我們引入HSV(HSB)
它的組成為:
- H:色調代表的是顏色,在這個模型中,色調是從0度到360度。
- S:飽和度表示顏色空間中的灰色範圍。它的範圍從0到100%(0~1)。當值為0時,顏色為灰色,當值為1時,顏色為原色。一個比較淡的顏色是由於飽和度較低,也就是顏色包含更多的灰色。
- V(B):值是顏色的亮度,並隨顏色飽和度而變化。它的範圍從0到100%,值為0時,顏色空間將全部變黑。隨著數值的增加,色彩空間亮度增加並顯示出各種顏色。
用下面這個圓柱體來表示 HSV 顏色空間,圓柱體的橫截面可以看做是一個極座標系 ,H 用極座標的極角表示,S 用極座標的極軸長度表示,V 用圓柱中軸的高度表示。
這裡色調各個角度表示的顏色如下:
角度 | 顏色 |
---|---|
0-60 | 紅色 |
60-120 | 黃色 |
120-180 | 綠色 |
180-240 | 青色 |
240-300 | 藍色 |
300-360 | 紅紫色 |
下面用ps截圖舉一個例子:
下圖的取到的點對應的HSV
是(16,71,97),而RGB
是(248,120,73)這裡通過HSV
可以看出亮度是97,非常直觀。
而下面這張圖是通過濾鏡給圖片增加了一個50%透明黑色遮罩,就是降低了50%的亮度。通過HSV
可以看出亮度為49。所以我們可以通過rgba和hsv之間的轉換來調整圖片的亮度。
調整圖片亮度的方案
- 通過
rgba
和hsv
之間的轉換 - 通過在原圖片的上層蓋上一層具有一定透明度的黑色遮罩
這裡方案2實施起來比較簡單,不過在處理具備透明度的png
圖片時,會導致透明部分也被遮罩導致變得非透明。所以方案1會比較通用。
實現方案一
從RGB到HSV的轉換
轉換的公式
設 (r, g, b)分別是一個顏色的紅、綠和藍座標,它們的值是在0到1之間的實數。設max等價於r, g和b中的最大者。設min等於這些值中的最小者。要找到在HSL空間中的 (h, s, l)值,這裡的h ∈ [0, 360)度是角度的色相角,而s, l ∈ [0,1]是飽和度和亮度,計算為:
h的值通常規範化到位於0到360°之間。而h = 0用於max = min的(定義為灰色)時候而不是留下h未定義。
HSL和HSV有同樣的色相定義,但是其他分量不同。HSV顏色的s和v的值定義如下:
javascript實現程式碼
// arr: rgb陣列
function rgb2hsv (arr) {
let rr;
let gg;
let bb;
let r = arr[0] / 255;
let g = arr[1] / 255;
let b = arr[2] / 255;
let h;
let s;
let v = Math.max(r, g, b);
let diff = v - Math.min(r, g, b);
let diffc = function (c) {
return (v - c) / 6 / diff + 1 / 2;
};
if (diff === 0) {
h = s = 0;
} else {
s = diff / v;
rr = diffc(r);
gg = diffc(g);
bb = diffc(b);
if (r === v) {
h = bb - gg;
} else if (g === v) {
h = (1 / 3) + rr - bb;
} else if (b === v) {
h = (2 / 3) + gg - rr;
}
if (h < 0) {
h += 1;
} else if (h > 1) {
h -= 1;
}
}
return [Math.round(h * 360), Math.round(s * 100), Math.round(v * 100)]
}
從HSV到RGB的轉換
轉換的公式
給定在HSV中 (h, s, v)值定義的一個顏色,帶有如上的變化於0到360之間的h,和分別表示飽和度和明度的變化於0到1之間的s和v,在RGB空間中對應的 (r, g, b)三原色可以計算為(R,G,B變化於0到1之間):
javascript實現程式碼
function hsv2rgb (hsv) {
let _l = hsv[0];
let _m = hsv[1];
let _n = hsv[2];
let newR;
let newG;
let newB;
if (_m === 0) {
_l = _m = _n = Math.round(255 * _n / 100);
newR = _l;
newG = _m;
newB = _n;
} else {
_m = _m / 100;
_n = _n / 100;
let p = Math.floor(_l / 60) % 6;
let f = _l / 60 - p;
let a = _n * (1 - _m);
let b = _n * (1 - _m * f);
let c = _n * (1 - _m * (1 - f));
switch (p) {
case 0:
newR = _n; newG = c; newB = a;
break;
case 1:
newR = b; newG = _n; newB = a;
break;
case 2:
newR = a; newG = _n; newB = c;
break;
case 3:
newR = a; newG = b; newB = _n;
break;
case 4:
newR = c; newG = a; newB = _n;
break;
case 5:
newR = _n; newG = a; newB = b;
break;
}
newR = Math.round(255 * newR);
newG = Math.round(255 * newG);
newB = Math.round(255 * newB);
}
return [newR, newG, newB]
}
實現亮度的調整
下面的程式碼是實現降低圖片50%亮度的實現
<body>
<img src="./pic.jpg" id="pic">
<canvas id="canvas" width="200" height="273"></canvas>
</body>
const pic = document.querySelector('#pic')
const canvas = document.querySelector('#canvas')
const ctx = canvas.getContext('2d')
ctx.drawImage(pic, 0, 0, 200, 273);
const imgData = ctx.getImageData(0, 0, 200, 273)
// 降低50%的亮度
ctx.putImageData(changeLuminance(imgData, -0.5), 0, 0)
// 修改圖片亮度, imgdata為從canvas獲取到的rgba陣列,value為需要增加或減少的亮度值(0~1)
function changeLuminance (imgdata, value) {
const data = imgdata.data
for (let i = 0; i < data.length; i+=4) {
const hsv = rgb2hsv([data[i], data[i + 1], data[i + 2]])
hsv[2] *= (1 + value)
const rgb = hsv2rgb([...hsv])
data[i] = rgb[0];
data[i + 1] = rgb[1];
data[i + 2] = rgb[2];
}
return imgdata
}