咱们通常对图片的概念就是又不少像素点构成的一幅图片,一个像素点由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 }