什么是颜色矩阵?

矩阵(Matrix)是一个按照长方阵列排列的复数或实数集合,而颜色矩阵就是将颜色rgba值通过矩阵计算来进行系列的处理。例如:


       | a b c d e |            | R |
 A = | f g h i   j |       B = | G |
       | k l m n o |           | B |
       | p q r s t  |           | A |
                                  | 1 |


A是需要对图片颜色值进行处理的5x4变换矩阵,B是原RGBA的颜色值(因为A是5x4的矩阵,所以B最后添加1来计算A的偏移值),而AxB(矩阵相乘)会得到处理后的1x4 rgba色值矩阵C。

玩过ps的都应该知道图片调整里有个玩法叫通道混合,通过改变不同通道中的值来调整图片的风格,而颜色矩阵计算的原理与其相似,下面我们简单介绍一下变换矩阵A中各值的含义:

  • 第一行为红色通道的值。
  • 第二行为绿色通道的值。
  • 第三行为蓝色通道的值。
  • 第四行为透明度的调整值。
  • 每行元素从左至右分别表示R G B A和偏移值。

一些基本的计算矩阵介绍

在SVG中使用颜色矩阵

SVG有提供支持颜色变换矩阵的<feColorMatrix>标签,只需将变换矩阵A放放入values属性中即可,例如图片去色:

<svg width="1024" height="640" viewBox="0 0 1024 640" id="fecolor-test" preserveAspectRatio="xMidYMid slice">
    <defs>
        <filter id="fecolor-filter">
            <feColorMatrix type="matrix" values=
"0.3086 0.6094 0.0820 0 0
 0.3086 0.6094 0.0820 0 0
 0.3086 0.6094 0.0820 0 0
 0 0 0 1 0" />
        </filter>
    </defs>
    <image width="1024" height="640" filter="url(#fecolor-filter)" href="https://static.pexels.com/photos/7976/pexels-photo.jpg" />
</svg>
 <image  width="1024" height="640" src="https://static.pexels.com/photos/7976/pexels-photo.jpg" />

在Canvas中使用颜色矩阵

canvas对图片数据也有相当强大的处理功能,能够从像素级别操作位图,当然[lte ie8]不支持。
需要了解的函数有三个:

  • ctx.createImageData(width,height);  // 用于创建ImageData对象

  • ctx.getImageData(x,y,width,height);  // 用于从canvas中获取ImageData对象

  • ctx.putImageData(imagedata, x, y, dx, dy, width, height);  // 用于将ImagaData对象的数据填写到 canvas中,起到覆盖canvas中原图像的作用,可以只输入前三个参数。参数分别是:用于提供填充图像数据的imagedata对象,imagedata对象左上角相对于canvas左上角的坐标x,y,在canvas上用来填充imagedata区域的左上角相对imagedata对象左上角的坐标x,y(相对于canvas左上角),填充区域的长度和宽度。

我们将图片绘制到canvas中,然后通过ctx.getImageData获取图片颜色信息

// 写一个简单的矩阵乘法
function matrixMath(a, b) {
  return a.map(x => x.reduce((ix, _, i) => {
    if (b[0][i]) {
      ix.push(b.reduce((iy, cy, y) => {
        iy += cy[i] * x[y];
        return iy;
      }, 0));
    }
    return ix;
  }, []));
}

// 褪色矩阵
const matrixA = [
   [0.3086, 0.6094, 0.0820, 0, 0],
  [0.3086, 0.6094, 0.0820, 0, 0],
  [0.3086, 0.6094, 0.0820, 0, 0],
  [0, 0, 0, 1, 0]
]

// 读取ImageDatas
const ImageData = ctx.getImageData(0,0,canvas.width,canvas.height) 
const colorBuffer = ImageData.data

// imageDatas为图片每个像素的RGBA值按顺序排列的Uint8ClampedArray数组。
for( let i = 0; i - colorBuffer.length; i += 4){
        let matrixB = [
             [colorBuffer[i]], 
             [colorBuffer[i + 1]], 
             [colorBuffer[i + 2]],
             [colorBuffer[i + 3]],
             [1]
        ]

     let [r, g, b, a] = matrixMath(matrixA, matrixB)

     colorBuffer[i] = r[0]
     colorBuffer[i + 1] = g[0]
     colorBuffer[i + 2] = b[0]
     colorBuffer[i + 3] = a[0]
}
ctx.putImageData(ImageData, 0, 0)

效果展示

null