19.基本图像效果
修改像素的颜色值并不意味着必须从零开始创建整个图像,已经存在的图像也是可以修改的。有一个例子就是基本照片处理——通过修改图像中的像素来修改它的显示效果。这种效果在画布中实现是很简单的,特别是现在你已经掌握了像素的操作方法。
反转颜色
这个效果将反转图像的颜色值,这会使它看起来有些奇怪(找不到更适合的词来形容)。基本方法就是用255减去像素现在的颜色值(150),所得的就是反转后的颜色(255-150=105)。让我们尝试一些不同的操作,然后看看最后的代码,这里并没有新知识。
var image = new Image(); image.src = "example.jpg"; $(image).load(function() { context.drawImage(image, 0, 0, 1024, 683, 0, 0, 500, 500); var imageData = context.getImageData(0, 0, canvas.width(), canvas.height()); var pixels = imageData.data; var numPixels = pixels.length; context.clearRect(0, 0, canvas.width(), canvas.height()); for (var i = 0; i < numPixels; i++) { pixels[i*4] = 255-pixels[i*4]; // Red pixels[i*4+1] = 255-pixels[i*4+1]; // Green pixels[i*4+2] = 255-pixels[i*4+2]; // Blue }; context.putImageData(imageData, 0, 0); });
前面几行代码创建了一个新的Image对象,然后加载上一节使用的示例图像。等到图像加载完成之后,将图像绘制到画布上,并将包含所有像素的ImageData对象保存到一个变量中。在保存了像素之后,清除画布,开始循环处理原始图像中所有的像素。
在循环中,我们使用与上一节中第一个例子(红色正方形)相同的方法来防问像素,并用255减去当前值。不需要对阿尔法值进行任何处理,因为我们要保留原始图像中的这个值。
最后是将像素绘制回画布,这样就得到一个反转颜色的图像(参见图1)。
灰度
另一个有趣的效果是灰度,这也许是更有用的一种效果。将彩色图像变为灰色(有时候也称为黑白色,但是这种说法并不准确),除了访问和修改颜色值,实现代码与反转颜色例子中的代码完全相同。
for (var i = 0; i < numPixels; i++) { var average = (pixels[i*4]+pixels[i*4+1]+pixels[i*4+2])/3; pixels[i*4] = average; // Red pixels[i*4+1] = average; // Green pixels[i*4+2] = average; // Blue };
将彩色转换为灰度要求计算出现有颜色值的平均值,即将它们加在一起然后除以颜色个数。这个平均颜色将作为三种颜色(红、绿和蓝)的值。其结果是将每一种颇色转换为灰度(参见图2)。
像素化
你是否曾经看到过新闻或文件中人物脸孔被像素化的情况?这是一种强大的特效,它可以将图像变得不可识别,但并不真正删除整个部分。实际上重新在画布上创建会相对简单一些,只需要将图像按栅格分割,或者对每个片段的颜色取平均值,或者选取每个片段的颜色。我们将使用的代码与上一节马赛克的例子很相似。
var image = new Image(); image.src = "example.jpg"; $(image).load(function() { context.drawImage(image, 0, 0, 1024, 683, 0, 0, 500, 500); var imageData = context.getImageData(0, 0, canvas.width(), canvas.height()); var pixels = imageData.data; context.clearRect(0, 0, canvas.width(), canvas.height()); var numTileRows = 20; var numTileCols = 20; var tileWidth = imageData.width/numTileCols; var tileHeight = imageData.height/numTileRows; for (var r = 0; r < numTileRows; r++) { }; });
循环之前的代码都是一样的。访问图像,等待图像加载,将它绘制到画布中,保存ImageData对象,从画布清除该图像,然后给分割的图像赋值确定块(片段)的数量和尺寸。
这两个循环的工作方式与马赛克的例子是一样的:第一个循环处理每一行块,第二个循环则处理当前行中的每一个块。而新的代码位于循环中,访问颜色值和创建像素化效果。
这里将使用第二种方法来获取像素化效果的颜色值,为每一个块选择一种颜色。最简单的方法是使用块的中心位置像素,将以下代码添加到第二个循环中,就可以得到这个信息:
var x = (c*tileWidth)+(tileWidth/2); var y = (r*tileHeight)+(tileHeight/2); var pos = (Math.floor(y)*(imageData.width*4))+(Math.floor(x)*4);
前两行将得到当前块中心像素从0开始表示的(x,y)坐标。这个计算方法与马赛克例子非常相似,先找到块边缘的(x,y)坐标位置,然后加上一半宽度或高度,从而确定中心。然后将(x,y)坐标传入标准公式,这样就得到CanvasPixelArray中该像素的索引值.但你可能注意到了,(x,y)坐标值在Math对象的floor方法中进行了取整处理。其原因是,除(x,y)是整数,否则这个返回的索引将是错误的,所以我们使用floor方法将值取整为下一个最小整数(侧如,3.567取整后变成3)。
最后,我们得到了访问颜色值和绘制像素化效果所需要的全部信息。将下面的代码插入到变量pos的声明语句之后。
var red = pixels[pos]; var green = pixels[pos+1]; var blue = pixels[pos+2]; context.fillStyle = "rgb("+red+", "+green+", "+blue+")"; context.fillRect(x-(tileWidth/2), y-(tileHeight/2), tileWidth, tileHeight);
这里没有新代码,它只是访问红色、绿色和蓝色值,然后使用这些值来设置fillStyle。最后一步是在块的位置上绘制一个正方形,它是使用所访问的颜色填充的。结果是将示例图像变成一个独特的像素化效果(参见图3)。
我们可以进一步将正方形修改为圆形(参见图4)。
context.beginPath(); context.arc(x, y, tileWidth/2, 0, Math.PI*2, false); context.closePath(); context.fill();
现在效果更酷了!
文件下载(已下载 2677 次)发布时间:2013/2/3 下午8:54:31 阅读次数:7130