2016 年快结束了,最后一天总要留下点什么。

前段时间迷上像素画,折腾了好一段时间,后来思考了下,能不能用程序或者有什么图像处理软件能把一张图片直接转成像素风格?
于是先谷歌一下,在豆瓣发现了这个很棒的小站,教你画像素画,其中有朋友分享一个PS处理的方法:位图快速转像素,这个方法处理对比度强的图片效果是不错的,于是我按照同样的思路,尝试用Canvas来完成同样的效果,纯粹只是不想打开PS。

看最终效果 or 不想折腾PS的 快戳这里:图像转换像素图

思路

  1. 将一张图片缩小到一定的百分比,比如25%,这时候截图,图片将丢失一些像素信息,对的就是要这种效果。
  2. 使用PS的阈值功能,将此时的图片处理,在设置合适的阈值下,让图片达到最佳显示效果。
  3. 最后将图片放大至看到像素点,就能看到我们要的像素化效果。

那么,用Canvas怎么实现呢?

首先应该理解阈值的概念,阈值可以理解为临界值,大于临界值呈现一种状态,小于临界值又呈现另一种状态。PS中阈值可以将图片变成黑白图像,阈值的范围是0~255,假设阈值是192,则PS会将亮度小于192的像素点转成黑色,将亮度大于192的转成白色。

getImageData

CanvasImageData对象可以得到图像的所有信息,imageData.data是一个保存着图片像素信息的数组,数组中每个值的范围是0~255,每四个值表示一个像素点的颜色信息,格式是这样的:imageData.data = [像素点1的R, 像素点1的G, 像素点1的B, 像素点1的A, 像素点2的R, 像素点2的G, 像素点2的B, 像素点2的A, ... , 像素点n的A]

var pixel = ctx.getImageData(x, y, width, height);

putImageData

imageData.data数组做修改后,可以通过putImageData写入修改后的像素数据。

ctx.putImageData(imageData, x, y);

实现

缩小图像

var scale = 0.25;
ctx.drawImage(image, 0, 0, image.width * scale, image.height * scale);

灰度化并阈值处理

为了实现最终效果,我这里是将图片的所有像素信息先灰度化,替代亮度信息,然后每个像素点的灰度值与设定的阈值相比较,大于阈值的显示为白色,小于阈值的显示为黑色。

// 灰度值的计算公式,由rgb值计算
var gray = 0.299 * r + 0.587 * g + 0.114 * b;

阈值处理的方法实现:

/**
 * [convert description]
 * @param  {[type]} ctx       [description]
 * @param  {[type]} imageData [description]
 * @param  {[type]} threshold [阈值]
 * @return {[type]}           [description]
 */
function convert(ctx, imageData, threshold) {
    var data = imageData.data;
    for (var i = 0; i < data.length; i += 4) {
        // 灰度计算公式
        var gray = 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 *data[i + 2];
        var color = gray >= threshold ? 255 : 0;
        var alpha = data[i + 3];
        data[i]     = color;                         // red
        data[i + 1] = color;                         // green
        data[i + 2] = color;                         // blue
        data[i + 3] = alpha >= threshold ? 255 : 0;  // alpha, 去掉png图透明
    }
    ctx.putImageData(imageData, 0, 0);
}

将图像还原大小

ctx.drawImage(image, 0, 0, image.width / scale, image.height / scale);

反锯齿处理

小图经过放大默认是模糊的,将imageSmoothingEnabled置为false才能清晰显示像素点。

ctx.imageSmoothingEnabled = false;
ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;

完整的代码已经上传至 github(https://github.com/chuiliu/the-pixel-art

来,看效果(左图为原图):


对于对比度强的图片,不进行阈值处理也可以有不错的效果

最终效果还是可以,但是这个方法本身存在一定局限性,对于对比度不高的图片效果很差,几乎不适用。所以玩的时候是比较挑图片的。

在线版仅支持高版本浏览器,欢迎试玩,快戳这里