canvas-修改图片亮度
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
}