咱們通常對圖片的概念就是又不少像素點構成的一幅圖片,一個像素點由RGBA
四個值表示。javascript
不過RGBA
並不能直觀的表現出像素點的亮度,它比較適合機器理解,給一個rgba
的像素咱們能夠猜出它是偏什麼顏色的,不過卻不能理解它的亮度值。因此rgba
並非一個很好做爲圖片處理的格式,這裏咱們引入HSV(HSB)
它的組成爲:html
用下面這個圓柱體來表示 HSV 顏色空間,圓柱體的橫截面能夠看作是一個極座標系 ,H 用極座標的極角表示,S 用極座標的極軸長度表示,V 用圓柱中軸的高度表示。java
這裏色調各個角度表示的顏色以下:canvas
角度 | 顏色 |
---|---|
0-60 | 紅色 |
60-120 | 黃色 |
120-180 | 綠色 |
180-240 | 青色 |
240-300 | 藍色 |
300-360 | 紅紫色 |
下圖的取到的點對應的HSV
是(16,71,97),而RGB
是(248,120,73)這裏經過HSV
能夠看出亮度是97,很是直觀。數組
而下面這張圖是經過濾鏡給圖片增長了一個50%透明黑色遮罩,就是下降了50%的亮度。經過HSV
能夠看出亮度爲49。因此咱們能夠經過rgba和hsv之間的轉換來調整圖片的亮度。3d
rgba
和hsv
之間的轉換這裏方案2實施起來比較簡單,不過在處理具有透明度的png
圖片時,會致使透明部分也被遮罩致使變得非透明。因此方案1會比較通用。code
設 (r, g, b)分別是一個顏色的紅、綠和藍座標,它們的值是在0到1之間的實數。設max等價於r, g和b中的最大者。設min等於這些值中的最小者。要找到在HSL空間中的 (h, s, l)值,這裏的h ∈ [0, 360)度是角度的色相角,而s, l ∈ [0,1]是飽和度和亮度,計算爲:htm
h的值一般規範化到位於0到360°之間。而h = 0用於max = min的(定義爲灰色)時候而不是留下h未定義。blog
HSL和HSV有一樣的色相定義,可是其餘份量不一樣。HSV顏色的s和v的值定義以下:圖片
// 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中 (h, s, v)值定義的一個顏色,帶有如上的變化於0到360之間的h,和分別表示飽和度和明度的變化於0到1之間的s和v,在RGB空間中對應的 (r, g, b)三原色能夠計算爲(R,G,B變化於0到1之間):
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 }