本文转载自bloghtml
转载请注明出处前端
黑白照片的时代虽然已通过去,但如今看到之前的照片,是否是有一种回到过去的感受,很cool有木有~
看完这篇文章,就能够把彩色照片变成各类各样的黑白的照片啦。git
本文完整的在线例子图片灰度算法例子,例子的图片有点多,可能有些慢。程序员
例子的源码位于blog/demo里github
原色是指不能透过其余颜色的混合调配而得出的“基本色”。通常来讲叠加型的三原色是红色、绿色、蓝色,以不一样比例将原色混合,能够产生出其余的新颜色。这套原色系统常被称为“RGB色彩空间”,亦即由红(R)绿(G)蓝(B)所组合出的色彩系统。算法
当这三种原色以等比例叠加在一块儿时,会变成灰色;若将此三原色的强度均调至最大而且等量重叠时,则会呈现白色。灰度就是没有色彩,RGB色彩份量所有相等。canvas
算法不区分语言,这里之前端举例。可使用canvas
取得图片某个区域的像素数据数组
//伪代码 var img = new Image(); img.src = 'xxx.jpg'; var myCanvas = document.querySelector(canvasId); var canvasCtx = myCanvas.getContext("2d"); canvasCtx.drawImage(img, 0, 0, img.width, img.height); //图片的像素数据 var data = canvasCtx.getImageData(0, 0, img.width, img.height);
使用getImageData()
返回一个ImageData对象,此对象有个data
属性就是咱们要的数据了,数据是以Uint8ClampedArray 描述的一个一维数组,包含以 RGBA 顺序的数据,数据使用 0 至 255(包含)的整数表示。 因此,一个像素会有4个数据(RGBA),RGB是红绿蓝,A指的是透明度。优化
举个例子:本文720480的水果图片,一共有720 480 = 259200像素,每一个像素又有4个数据,因此数据数组的总长度为259200 * 4 = 1036800。ui
能够看到图片的数据很长,若是一次性处理不少图片的时候,计算量至关可观,因此例子中会使用worker,把繁重的计算任务交给后台线程。
取得每个像素的red,green,blue值。
使用灰度算法,算出一个灰度值。
用这个灰度值代替像素原始的red,green,blue值。
好比咱们的灰度算法是:
Gray = (Red + Green + Blue) / 3
计算过程:
//伪代码 for(var Pixel in Image){ var Red = Image[Pixel].Red var Green = Image[Pixel].Green var Blue = Image[Pixel].Blue var Gray = (Red + Green + Blue) / 3 Image[Pixel].Red = Gray Image[Pixel].Green = Gray Image[Pixel].Blue = Gray }
很简单对吧。
不少好吃的鲜艳水果,可是它们立刻要变灰了!!
这是最多见的灰度算法,简单暴力,把它放到第一位。公式是:
Gray = (Red + Green + Blue) / 3
这个算法能够生成不错灰度值,由于公式简单,因此易于维护和优化。然而它也不是没有缺点,由于简单快速,从人眼的感知角度看,图片的灰度阴影和亮度方面作的还不够好。因此,咱们须要更复杂的运算。
算法1与算法2生成的图片彷佛没太大差异,因此增长一个例子,将图片上半部分用算法1,下半部分用算法2。
仔细看的话,中间有一根黑线。上半部分(算法1)比下半部分(算法2)更苍白一些。若是仍是看不出来,注意最右边的柠檬,算法1的柠檬反光更强烈,算法2的柠檬更柔和。
第二种算法考虑到了人眼对不一样光感知程度不一样。人的眼睛内有几种辨别颜色的锥形感光细胞,分别对黄绿色、绿色和蓝紫色的光最敏感。虽然眼球中的椎状细胞并不是对红、绿、蓝三色的感觉度最强,可是由肉眼的椎状细胞所能感觉的光的带宽很大,红、绿、蓝也可以独立刺激这三种颜色的受光体。
人类对红绿蓝三色的感知程度依次是: 绿>红>蓝,因此平均算法从这个角度看是不科学的。应该按照人类对光的感知程度为每一个颜色设定一个权重,它们的之间的地位不该该是平等的。
一个图像处理通用的公式是:
Gray = (Red * 0.3 + Green * 0.59 + Blue * 0.11)
能够看到,每一个颜色的系数相差很大。
如今对图像灰度处理的最佳公式还存在争议,有一些相似的公式:
Gray = (Red * 0.2126 + Green * 0.7152 + Blue * 0.0722)
or
Gray = (Red * 0.299 + Green * 0.587 + Blue * 0.114)
它们只是在系数上存在一些误差,大致的比值差很少。
在说这个算法以前,先说说RGB,大多数程序员都使用RGB模型,每一种颜色均可以由红绿蓝组成,RGB对计算机来讲能够很好的描述颜色,但对于人类而言就很难理解了。若是升国旗的时候说,“五星红旗多么RGB(255, 0, 42)”,可能会被暴打一顿。但我说鲜红的五星红旗,老师可能会点头称赞。
因此为了更通俗易懂,有时咱们选择HLS模型描述颜色,这三个字母分别表示Hue(色调)、Saturation(饱和度)、Lightness(亮度)。色调,取值为:0 - 360,0(或360)表示红色,120表示绿色,240表示蓝色,也可取其余数值来指定颜色。饱和度,取值为:0.0% - 100.0%,它一般指颜色的鲜艳程度。亮度,取值为:0.0% - 100.0%,黑色的亮度为0。
去饱和的过程就是把RGB转换为HLS,而后将饱和度设为0。所以,咱们须要取一种颜色,转换它为最不饱和的值。这个数学公式比本文介绍的更复杂,这里提供一个简单的公式,一个像素能够被去饱和经过计算RGB中的最大值和最小值的中间值:
Gray = ( Math.max(Red, Green, Blue) + Math.min(Red, Green, Blue) ) / 2
去饱和后,图片立体感减弱,可是更柔和。对比算法2,能够很明显的看出差别,从效果上看,可能大多数人都喜欢算法2,算法3是目前为止,处理的图片立体感最弱,最黑暗的。
分解算法能够认为是去饱和更简单一种的方式。分解是基于每个像素的,只取RGB的最大值或者最小值。
最大值分解:
Gray = Math.max(Red, Green, Blue)
最小值分解:
Gray = Math.min(Red, Green, Blue)
正如上面展示的,最大值分解提供了更明亮的图,而最小值分解提供了更黑暗的图。
图片变灰更快捷的方法,这个方法不用作任何计算,取一个通道的值直接做为灰度值。
Gray = Red
or
Gray = Green
or
Gray = Blue
无论相不相信,大多数数码相机都用这个算法生成灰度图片。很难预测这种转换的结果,因此这种算法多用于艺术效果。
这是到目前为止最有趣的算法,容许用户提供一个灰色阴影值,值的范围在2-256。2的结果是一张全白的图片,256的结果和算法1同样。
该算法经过选择阴影值来工做,它的公式有点复杂
ConversionFactor = 255 / (NumberOfShades - 1) AverageValue = (Red + Green + Blue) / 3 Gray = Math.round((AverageValue / ConversionFactor) + 0.5) * ConversionFactor
NumberOfShades 的范围在2-256。
从技术上说,任何灰度算法均可以计算AverageValue,它仅仅提供一个初始灰度的估计值。
“+ 0.5” 是一个可选参数,用于模拟四舍五入。
这是一篇颇有趣的文章,不只仅是介绍灰度算法,对了解图片的处理过程也颇有帮助。