monochrome dithering in JavaScript (Bayer, Atkinson, Floyd–Steinberg)
我正在使用HTML5中的摄像头过滤器。对于老式Mac感觉,Atkinson抖动效果很好。
实时|代码
现在,我正在尝试为1989年Gameboy的感觉提供Bayer订购的抖动选项。
我仔细阅读了算法,但在将伪代码转换为JavaScript时遇到了麻烦:
1 2 3 4 5 | for each y for each x oldpixel := pixel[x][y] + threshold_map_4x4[x mod 4][y mod 4] newpixel := find_closest_palette_color(oldpixel) pixel[x][y] := newpixel |
在AS3,PHP或JS中是否有示例?您能否解释
(由Meemoo Gameboy GIFerizer制造)
弄清楚了。在Wikipedia中,它表示"例如,在单色渲染中,如果像素的值(缩放到0-9范围内)小于矩阵相应单元格中的像素数,则将该像素绘制为黑色,否则将其绘制为黑色白色。"在js中,通过平均当前像素(0-255)和地图值(15-240)并将其与阈值(通常为129)进行比较,我获得了良好的效果:
1 2 | var map = (imageData.data[currentPixel] + bayerThresholdMap[x%4][y%4]) / 2; imageData.data[currentPixel] = (map < threshold) ? 0 : 255; |
此处是我使用不同算法的整个单色函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | var bayerThresholdMap = [ [ 15, 135, 45, 165 ], [ 195, 75, 225, 105 ], [ 60, 180, 30, 150 ], [ 240, 120, 210, 90 ] ]; var lumR = []; var lumG = []; var lumB = []; for (var i=0; i<256; i++) { lumR[i] = i*0.299; lumG[i] = i*0.587; lumB[i] = i*0.114; } function monochrome(imageData, threshold, type){ var imageDataLength = imageData.data.length; // Greyscale luminance (sets r pixels to luminance of rgb) for (var i = 0; i <= imageDataLength; i += 4) { imageData.data[i] = Math.floor(lumR[imageData.data[i]] + lumG[imageData.data[i+1]] + lumB[imageData.data[i+2]]); } var w = imageData.width; var newPixel, err; for (var currentPixel = 0; currentPixel <= imageDataLength; currentPixel+=4) { if (type ==="none") { // No dithering imageData.data[currentPixel] = imageData.data[currentPixel] < threshold ? 0 : 255; } else if (type ==="bayer") { // 4x4 Bayer ordered dithering algorithm var x = currentPixel/4 % w; var y = Math.floor(currentPixel/4 / w); var map = Math.floor( (imageData.data[currentPixel] + bayerThresholdMap[x%4][y%4]) / 2 ); imageData.data[currentPixel] = (map < threshold) ? 0 : 255; } else if (type ==="floydsteinberg") { // Floyda€"Steinberg dithering algorithm newPixel = imageData.data[currentPixel] < 129 ? 0 : 255; err = Math.floor((imageData.data[currentPixel] - newPixel) / 16); imageData.data[currentPixel] = newPixel; imageData.data[currentPixel + 4 ] += err*7; imageData.data[currentPixel + 4*w - 4 ] += err*3; imageData.data[currentPixel + 4*w ] += err*5; imageData.data[currentPixel + 4*w + 4 ] += err*1; } else { // Bill Atkinson's dithering algorithm newPixel = imageData.data[currentPixel] < threshold ? 0 : 255; err = Math.floor((imageData.data[currentPixel] - newPixel) / 8); imageData.data[currentPixel] = newPixel; imageData.data[currentPixel + 4 ] += err; imageData.data[currentPixel + 8 ] += err; imageData.data[currentPixel + 4*w - 4 ] += err; imageData.data[currentPixel + 4*w ] += err; imageData.data[currentPixel + 4*w + 4 ] += err; imageData.data[currentPixel + 8*w ] += err; } // Set g and b pixels equal to r imageData.data[currentPixel + 1] = imageData.data[currentPixel + 2] = imageData.data[currentPixel]; } return imageData; } |
我很欣赏优化提示。
这是我所有的单色抖动功能,可用作网络工作者:https://github.com/meemoo/meemooapp/blob/master/src/nodes/image-monochrome-worker.js
我将其作为调试代码进行操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | var canvas = document.createElement('canvas'); var ctx = canvas.getContext('2d'); var img = new Image(); img.src ="http://i.stack.imgur.com/tHDY8.png"; img.onload = function() { canvas.width = this.width; canvas.height = this.height; ctx.drawImage( this, 0, 0, this.width, this.height ); var imageData = ctx.getImageData( 0, 0, this.width, this.height); var depth = 32; // Matrix var threshold_map_4x4 = [ [ 1, 9, 3, 11 ], [ 13, 5, 15, 7 ], [ 4, 12, 2, 10 ], [ 16, 8, 14, 6 ] ]; // imageData var width = imageData.width; var height = imageData.height; var pixel = imageData.data; var x, y, a, b; // filter for ( x=0; x<width; x++ ) { for ( y=0; y<height; y++ ) { a = ( x * height + y ) * 4; b = threshold_map_4x4[ x%4 ][ y%4 ]; pixel[ a + 0 ] = ( (pixel[ a + 0 ]+ b) / depth | 0 ) * depth; pixel[ a + 1 ] = ( (pixel[ a + 1 ]+ b) / depth | 0 ) * depth; pixel[ a + 2 ] = ( (pixel[ a + 2 ]+ b) / depth | 0 ) * depth; //pixel[ a + 3 ] = ( (pixel[ a + 3 ]+ b) / depth | 3 ) * depth; } } ctx.putImageData( imageData, 0, 0); }; document.body.appendChild(canvas); |
似乎工作正常,您可以更改depth变量以更改后代化。