монохромный дизеринг в JavaScript (Байер, Аткинсон, Флойд-Стейнберг)

Я играю с фильтрами веб-камеры в HTML5. У меня есть дизер Аткинсона, отлично работающий для этого старого -школьное ощущение Мака.

meemoo cam to dither
Текущий | Код

Теперь я пытаюсь сделать вариант сглаживания по заказу Байера для ощущения Gameboy 1989 года.

Я читал об алгоритме, но у меня возникли проблемы с преобразованием этого псевдокода в JavaScript:

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? Не могли бы вы объяснить, что происходит с threshold_map_4x4[x mod 4][y mod 4]?


gameboy style gif(создано с помощью Meemoo Gameboy GIFerizer)

Догадаться. В Википедии написано: «Например, при монохромном рендеринге, если значение пикселя (в диапазоне от 0 до 9) меньше, чем число в соответствующей ячейке матрицы, нарисуйте этот пиксель черным цветом, в противном случае нарисуйте его белым. ." В js я получил хорошие результаты, усредняя текущий пиксель (0-255) и значение карты (15-240) и сравнивая его с порогом (обычно 129):

var map = (imageData.data[currentPixel] + bayerThresholdMap[x%4][y%4]) / 2;
imageData.data[currentPixel] = (map < threshold) ? 0 : 255;

Вот вся моя монохромная функция с разными алгоритмами:

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") {
      // Floyd–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;
}

Буду признателен за советы по оптимизации.


person forresto    schedule 14.09.2012    source источник


Ответы (2)


Вот все мои функции монохромного дизеринга, которые можно использовать в качестве веб-работника: https://github.com/meemoo/meemooapp/blob/master/src/nodes/image-monochrome-worker.js

Демонстрация в реальном времени с веб-камерой: http://meemoo.org/iframework/#gist/3721129

person forresto    schedule 03.05.2013
comment
@ 500-InternalServerError :) исправлено. - person forresto; 14.01.2020

Я делаю это как код отладки:

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);

И, кажется, работает нормально, вы можете изменить переменную глубины, чтобы изменить постеризацию.

person Vincent Thibault    schedule 14.09.2012
comment
Поскольку он монохромный, вы можете изменить его на: pixel[ a ] = pixel[ a + 1 ] = pixel[ a + 2 ] = ( (pixel[ a ]+ b) / depth | 0 ) * depth; - person forresto; 14.09.2012
comment
Проверьте консоль, у меня проблема с междоменным изображением (я использую старый браузер...). Итак, я перехожу к URL-адресу изображения, открываю консоль и копирую и вставляю код, и он работает нормально (tof.canardpc.com/view/f47567dc-d06b-4fea-8b9f-ac7c23e9ceb8.jpg) или, может быть, вам нужен другой результат? - person Vincent Thibault; 17.09.2012
comment
Да, я хотел монохромный вывод. Я поместил свои алгоритмы в исходный вопрос и сделал это демо: meemoo.org/iframework/#gist /3721129 - person forresto; 17.09.2012