前言

你是否曾经注意到一些网站在控制台打印出来的一些有趣的菜单。但是又觉得手动撸一个很麻烦?你有没有看过无人机矩阵的精彩表演?你有见过黑白木刻风的艺术照片吗?

img

今天,基于这些启发,我尝试借助canvas,来实现一些有趣的图片点阵化转化。话不多说,直接开始。

一、绘图

1.canvas绘图的第一步,当然是绑定canvas标签

// html中
<canvas id="myCanvas2" width="500" style="border:1px solid #c3c3c3;"></canvas>
<img id="img2" src="../assets/99.jpg" style="opacity:0">

// js中
var canvas2= document.getElementById('myCanvas2');// 获取canvas对象
var ctx = canvas2.getContext('2d');// 创建context对象
let img = document.getElementById('img2')// 获取图片对象

2.这里创建context对象需要在浏览器加载完毕后执行,别忘了将代码放在window.onload中

3.接着,需要根据照片的长宽对canvas的长宽进行调整(这里我固定了图片和canvas容器的宽度为500px,然后计算高度)

let side = img.width / 500// 固定的宽度值
let _y = img.height / side// 得到响应高度
document.getElementById('myCanvas2').height = _y

4.然后,就是把图片绘制到canvas中(这里规定了图片对象,定点坐标和绘制图案的长宽)

ctx.drawImage(img,0,0,500,_y)

img

 (绘制完成)

 二、像素点获取

1.有了图片,我们就可以开始对每个像素点进行分析了。这里我们要用到的是canvas context对象的getImageData来获取像素点信息,分别可以定义的参数为(左上角x轴,左上角y轴,所需获取的x轴像素,所需获取的y轴像素)。

2.这里关于getImageData方法有一个需要注意的点,如果我们是引入本地图片后使用getImageData,会出现跨域现象。网传的火狐浏览器下不会出现该问题(亲测无效)。无奈改为以vue-cli来构建整个demo,然后从node服务器获取图片资源。

let x = canvas2.width
let y = canvas2.height
for(let i=0;i<y;i+=1){
   for(let j=0;j<x;j+=1){
       let b = ctx.getImageData(j, i, 1, 1).data
       console.log(b)
   }
}

img

 (打印得到的像素点集合)

3.由于一张图片的像素点实在太多,我们可以选择只遍历十分之一甚至百分之一的像素点来获取一部分的点信息。

 三、像素点信息初步处理

1.我们的最终目的,是为了把图片转换成点阵。所以,我们需要采用某种方式判断当前的点是否打点。这里我们利用rgb颜色值的特点,采用一条公式#R* 0.299+#G*0.587+#B* 0.114的值来判断颜色的深浅。这个值是在0~255之间,越靠近255,说明当前点的颜色越浅。

这里,我暂时以225作为临界值,如果值超过225则为浅色打空心点,如果值低于225则为深色打实心点。

同时,我暂时设置每十个点获取一次,这样不会因为图片过大导致点阵过大。

for(let i=0;i<y;i+=10){
   for(let j=0;j<x;j+=10){
      let b = ctx.getImageData(j, i, 1, 1).data
      if(b[0]* 0.299+b[1]*0.587+b[2]* 0.114>=225){
          // 浅色
          this.ca2+=''
      }
      else{
          // 深色
          this.ca2+=''
      }
   }
   this.ca2+='<br/>'
}

接着,就是初步的效果啦。

img

 emm,脸出来了,但是其他细节似乎不太行。

四、像素点进一步处理

为什么上面的效果不太理想呢,这里我分析可能有多个原因。

  • 首先,我们对于像素颜色的深浅判断仅仅是使用r、g、b三个颜色值和固定参数的相加,这并不准确。这种操作有可能导致深红深蓝深绿或者浅红浅蓝浅绿最后得出了相同的参数值。

  • 其次,刚刚我们写死了判断深浅的的关键数值为225,这并不能适用于每一张图片,因为有的图深色像素多,有的图深色像素少。

  • 同时,刚刚我们也写死了像素获取的精度为十分之一,这也导致了每张图的处理效果有一定差异;

  • 最后,本来我们这里就只设置了黑白两色,对于细节的表示肯定是有所欠缺。

基于上面的点,我们做一些优化。

五、图片灰度

  1. 图片灰度,解决无法分辨深红深蓝深绿的问题,我们可以先把图片转化为黑白,这样,判断颜色深浅变得更加纯粹,只是判断颜色的黑白程度。

  2. 一个灰度像素的颜色值特点是r、g、b三个颜色值相同(即红青蓝三色等比混合后为黑灰)。所以我们只需要对每个像素的明度进行计算得到灰度值gray,然后把gray赋给r、g、b,便得到了灰黑色。灰度的计算公式有多种,我们这里采用平均灰度法得到灰度值。

let x = canvas2.width
let y = canvas2.height
let pixels = ctx.getImageData(0,0,x,y); 
let pixeldata = pixels.data; 
for(var i=0;i<pixeldata.length;i+=4){
   let gray =parseInt( pixels.data[i]*0.3 + pixels.data[i+1] *0.59 + pixels.data[i+2]*0.11);             
   pixels.data[i] = gray; 
   pixels.data[i+1] = gray; 
   pixels.data[i+2] = gray; 
   pixels.data[i+3] = 255; 
}
ctx.putImageData(pixels,0,0);

3.黑白图完成。

img

六、调整容差和像素获取精度

1.前面提到,我们写死225作为判断深浅的值。这里其实就是对黑色的选择容差。

什么是容差?容差,指的是在选取颜色时所设置的选取范围,容差越大,选取的范围也越大。

img

 (容差的大小会直接影响像素判断的效果)

2.所以,这里我们将容差调整为可设置项。这样便可以根据需要调整像素判断标准了。

3.同时我们也设置一个精度变量。通过设置精度变量,调整选取像素的密度,来动态设置点阵的大小规模。

img

4.看看不同精度和容差的效果

img img

七、增加颜色

在此基础上,如果我们增加几个颜色范围,使效果更加细致

img

(使用emoji表情里面的月食表情🌕🌖🌗🌘🌑描述像素点)

img

(使用▂▃▅▇)

是不是还不错。