前面给大家带来了操作像素的API,此时此刻,我觉得应该配以小实例来进行进一步的说明和演示,以便给大家带来更宽广的视野和灵感,你们看了我的那么多的文章,应该是懂我的风格,废话不多说,进入正题:
这次给大家带来的是图片的马赛克效果,这种效果你们应该很熟悉了,比如看到一些较污的新闻,关键的地方总是有这万恶的马赛克挡着,咱们今天就来剖析一下这马赛克到底是撒东西,是一个什么原理:
大家看看这张图,这就是马赛克的效果,整个图就是一颗颗很大的像素点构成的,当然这里说的像素点指的不是一像素,而是一个像素点可能是由几个像素或者是十几个像素构成的,像素点越大,图像就约模糊,像素点越小,图片越清晰,什么,你对上图无感?好吧,我们看一下现实生活中你见过的马赛克:
看,左边的大美女,打上马赛克,立马变神秘了,那马赛克纠结是一个什么原理呢?
对比上图的左右两个脸,右边的脸虽然看不清,但还是能看清轮廓的,而且右边的马赛克部分像素点非常大,而左边的呢,几乎看不到像素点,那么左边的是不是就不是由像素点构成的呢?我们把图放大n倍来看看:
上图是截图的马赛克图片的一部分,放大后可以清除的看到,图像是由一个个像素点构成的,可以发现,马赛克的像素颗粒也是由许多的像素点构成的,只是这些颗粒的颜色是一个的,缩小就感觉是一个大的像素颗粒,而偏不的则是一个个小的像素点,各自颜色不一样,才形象非常细腻的图像,由此可见,要想做马赛克效果,只需要将一个区域内的像素的的颜色用里面一个随机的颜色替代,就会使得图像模糊,这样缩小了看就是马赛克效果了,而且这个区域也大,图像越模糊,越小,则图像越清晰!
知道了马赛克的原理,那么我们就可以通过操作像素来制作马赛克效果了,怎么做呢?比如将一整张图做成马赛克效果,那么,如果我们将这张图等分成n份,且每一份随机在这个区域里找一个像素点的颜色,然后将这个区域的颜色都设为这个颜色,全部操作完,马赛克就做完了,哈哈,挺抽象的,那老规矩,跟着我一步一步的做吧:
首先我们在画布上引进来一张图,然后我们建一个函数,用于绘制马赛克(其目的我再说一般,一是图片必须加装完成,才能进行像素操作,二是结构更清晰,便于理解)
var aImg = new Image(); aImg.src = '1.jpg'; aImg.onload = function(){ draw(this); } //此函数用于后面的像素操作 function draw(obj){ ctx.drawImage(obj,0,0,400,400); }
我们将画布的大小设为图片的2倍,右边用于显示马赛克的图
下面就该获取像素了,不清楚的同学可以先去看看前文的API的第5篇,工欲善其事必先利其器
function draw(obj){ ctx.drawImage(obj,0,0,400,400); var oImg = ctx.getImageData(0,0,400,400); var w = oImg.width; var h = oImg.height; ctx.putImageData(oImg,w,0); }
在右侧把图片用像素操作复制一个放到右边
一模一样双胞胎,哈哈,接下来就是醉关键的地方了,就是如何将一个区域的像素点用一个颜色替代,我们画一个图来示意一下:
看看上图,图画的很粗糙哈(俗话说图糙理不糙),比如说图片是100*100像素的图,那么原来是由1*1像素的点构成的,现在我们等分成10份,一份的像素点就是10*10,那么这份的像素点在里面随机设置一个颜色,然后同理操作所有的,就成了一个只显示10分之一图像的马赛克了
这里引用我们原来写的获取某一点的颜色的函数和设置某一点的颜色的函数,可以作为通用方法:
function getXY(obj,x,y){ var w = obj.width; var h = obj.height; var d = obj.data; var color = []; color[0] = obj.data[4*(y*w+x)]; color[1] = obj.data[4*(y*w+x)+1]; color[2] = obj.data[4*(y*w+x)+2]; color[3] = obj.data[4*(y*w+x)+3]; return color; } function setXY(obj,x,y,color){ var w = obj.width; var h = obj.height; var d = obj.data; obj.data[4*(y*w+x)] = color[0]; obj.data[4*(y*w+x)+1] = color[1]; obj.data[4*(y*w+x)+2] = color[2]; obj.data[4*(y*w+x)+3] = color[3]; }
接下来就是操作像素了,我们在一个新的ImageData上操作
//创建一个新的ImageData对象 var newImg = ctx.createImageData(obj.width,obj.height); //马赛克的程度,数字越大越模糊 var num = 5; //等分画布 var stepW = w/num; var stepH = h/num; //这里是循环画布的像素点 for(var i=0;i<stepH;i++){ for(var j=0;j<stepW;j++){ //获取一个小方格的随机颜色,这是小方格的随机位置获取的 var color = getXY(oImg,j*num+Math.floor(Math.random()*num),i*num+Math.floor(Math.random()*num)); //这里是循环小方格的像素点, for(var k=0;k<num;k++){ for(var l=0;l<num;l++){ //设置小方格的颜色 setXY(newImg,j*num+l,i*num+k,color); } } } }
整理所有的代码就是:
var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var aImg = new Image(); aImg.src = '1.jpg'; aImg.onload = function(){ draw(this); } function draw(obj){ ctx.drawImage(obj,0,0,400,400); var oImg = ctx.getImageData(0,0,400,400); var w = oImg.width; var h = oImg.height; //创建一个新的ImageData对象 var newImg = ctx.createImageData(obj.width,obj.height); //马赛克的程度,数字越大越模糊 var num = 5; //等分画布 var stepW = w/num; var stepH = h/num; //这里是循环画布的像素点 for(var i=0;i<stepH;i++){ for(var j=0;j<stepW;j++){ //获取一个小方格的随机颜色,这是小方格的随机位置获取的 var color = getXY(oImg,j*num+Math.floor(Math.random()*num),i*num+Math.floor(Math.random()*num)); //这里是循环小方格的像素点, for(var k=0;k<num;k++){ for(var l=0;l<num;l++){ //设置小方格的颜色 setXY(newImg,j*num+l,i*num+k,color); } } } } ctx.putImageData(newImg,w,0); } function getXY(obj,x,y){ var w = obj.width; var h = obj.height; var d = obj.data; var color = []; color[0] = obj.data[4*(y*w+x)]; color[1] = obj.data[4*(y*w+x)+1]; color[2] = obj.data[4*(y*w+x)+2]; color[3] = obj.data[4*(y*w+x)+3]; return color; } function setXY(obj,x,y,color){ var w = obj.width; var h = obj.height; var d = obj.data; obj.data[4*(y*w+x)] = color[0]; obj.data[4*(y*w+x)+1] = color[1]; obj.data[4*(y*w+x)+2] = color[2]; obj.data[4*(y*w+x)+3] = color[3]; }
得到的效果:
为了显得好看一点,我们只是取了一个比较小的值,现在五分之一的像素点,如果是十分之一,得到是就是这样的,值在num的地方修改:
具体效果看这里——canvas马赛克效果
这是整张图做成马赛克,能不能跟上面的美女一样,做成局部的马赛克呢?其实也很简单,我们起初获取的是整张图的像素,现在我们只获取局部我们想要的部分的像素就可以了:
var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var aImg = new Image(); aImg.src = '1.jpg'; aImg.onload = function(){ draw(this); } function draw(obj){ //绘制第一张图 ctx.drawImage(obj,0,0,400,400); //绘制第二张图,用作对比 ctx.drawImage(obj,400,0,400,400); //获取坐边图像的局部坐标的部分像素(左图位置在258,为方便演示,这里设成和马赛克显示位置一样的坐标) var oImg = ctx.getImageData(650,60,50,50); var w = oImg.width; var h = oImg.height; //马赛克的程度,数字越大越模糊 var num = 10; //等分画布 var stepW = w/num; var stepH = h/num; //这里是循环画布的像素点 for(var i=0;i<stepH;i++){ for(var j=0;j<stepW;j++){ //获取一个小方格的随机颜色,这是小方格的随机位置获取的 var color = getXY(oImg,j*num+Math.floor(Math.random()*num),i*num+Math.floor(Math.random()*num)); //这里是循环小方格的像素点, for(var k=0;k<num;k++){ for(var l=0;l<num;l++){ //设置小方格的颜色 setXY(oImg,j*num+l,i*num+k,color); } } } } ctx.putImageData(oImg,650,60); } function getXY(obj,x,y){ var w = obj.width; var h = obj.height; var d = obj.data; var color = []; color[0] = obj.data[4*(y*w+x)]; color[1] = obj.data[4*(y*w+x)+1]; color[2] = obj.data[4*(y*w+x)+2]; color[3] = obj.data[4*(y*w+x)+3]; return color; } function setXY(obj,x,y,color){ var w = obj.width; var h = obj.height; var d = obj.data; obj.data[4*(y*w+x)] = color[0]; obj.data[4*(y*w+x)+1] = color[1]; obj.data[4*(y*w+x)+2] = color[2]; obj.data[4*(y*w+x)+3] = color[3]; }
是不是跟刚开始的那个美女很像了,可能有的同学注意到,我有时候创建了一个新的ImageData对象,有的时候没有,那到底什么时候需要创建新的ImageData对象,什么时候不需要呢?原则上说,如果你操作的像素不在原图上,则需要新创建一个ImageData对象,但是也不是绝对的,你在原图上操作像素,也是可以创建一个新的ImageData对象的,具体还是看需要!
上面的局部马赛克效果看这里—— canvas图片局部马赛克效果
今天就讲到这里,后面要是有时间,在给几个例子给大家学习一下!