Это правильная реализация упорядоченного дизеринга? - PullRequest
0 голосов
/ 25 января 2019

Меня интересует дизеринг, точнее заказанный дизеринг.Я потратил много часов на исследования и эксперименты.И надеюсь, что после всех усилий мой код работает как надо.

Я собираюсь предоставить свой код, который выполняет дизеринг, и некоторые примеры.

Мои вопросы

  • Как это работает?
  • Мой код правильный?
  • Могут ли быть какие-то упрощения?

Вот псевдокод алгоритма дизеринга из этой статьи Википедии, на которой основан мой кодна.

Pseudo code

И вот переменные объяснения:

M (i, j) - карта порогов на i -я строка и j -й столбец, c ′ - преобразованный цвет, а r - величина разброса в цветовом пространстве.,Предполагая палитру RGB с равномерно выбранными цветами, в которых каждая цветовая координата представлена ​​октетом, обычно можно выбрать: enter image description here


Теперь вот моя реализация (в псевдокоде):

ditherMatrix = 4x4; // By 4x4 I mean the normal 4x4 dither matrix from the wikipedia article
ditherMatrixSize = 4;
offset = (ditherMatrixSize * (ditherMatrixSize / 2) - 0.5); 
/* In the wiki article it uses 0.5 as an offset, 
   but I have to do this instead. 
   Is this correct? Atleast it works. */

allowedChanelValues = 1; // example: 2 = (0 or 128 or 255 red, 0 or 128 or 255 green, 0 or 128 or 255 blue)
r = 255 / allowedChanelValues / (ditherMatrixSize * ditherMatrixSize);

// Applying the dither
transfromedColor.r = 
    currentPixel.r + r * ((ditherMatrix[x % ditherMatrixSize, y % ditherMatrixSize]) - offset);
transfromedColor.g = 
    currentPixel.g + r * ((ditherMatrix[x % ditherMatrixSize, y % ditherMatrixSize]) - offset);
transfromedColor.g = 
    currentPixel.g + r * ((ditherMatrix[x % ditherMatrixSize, y % ditherMatrixSize]) - offset);

// Quantizing, see https://youtu.be/0L2n8Tg2FwI 6:40 and 9:58 for explanation
transfromedColor.r = Round(allowedChanelValues * oldR / 255) * (255 / allowedChanelValues);
transfromedColor.g = Round(allowedChanelValues * oldG / 255) * (255 / allowedChanelValues);
transfromedColor.b = Round(allowedChanelValues * oldB / 255) * (255 / allowedChanelValues);

ВАЖНО: Теперь этот код можно оптимизировать многими способами, но это не такмое намерение (пока), я просто хочу, чтобы оно работало правильно, а затем я посмотрю на то, что можно оптимизировать.

ПОБОЧНОЕ ПРИМЕЧАНИЕ: Когда я только начинал, я жестко программировалзначения, например, вместо ditherMatrixSize * (ditherMatrixSize / 2) - 0,5, я жестко закодировал 7.5.Но после попытки использовать другие матрицы дизеринга, я просто использовал этот код для получения значения вместо его жесткого кодирования.


Вот несколько примеров

enter image description here

(Верх - Размытый, Нижний - Оригинал. Матрица: 4x4 , а значение параметра,4ChanelValues ​​равно 1 )


enter image description here

(слева - размытое, справа - оригинальное. Матрица имеет размер 4x4 , а значение,4ChanelValues ​​равно 1 )


enter image description here

(Верх - Разбитый, Нижний - Оригинал. Матрица: 4x4 , а значение параметраchannelValues ​​составляет 2 ) Источник


enter image description here

(верх - размытый, низ - оригинал. Матрица: 4x4 , и этораз я использовал предопределенную палитру с 16 цветами allowChanelValues ​​ cuberoot 16 - 2.519 ) Source

И вот как это выглядит без палитры и с разрешениемdChanelValues: 2 , матрица: 4x4

enter image description here

Здесь я чувствую, что что-то не так, может бытьэто правильно и выглядит странно из-за слишком небольшого значения allowChanelValues.

И вот как это выглядит, если я изменяю allowChanelValues ​​на 4

enter image description here


Я говорил ранее о том, возможно ли упростить код.

Я нашел этот псевдокод здесь

  Threshold = COLOR(256/4, 256/4, 256/4); /* Estimated precision of the palette */
  For each pixel, Input, in the original picture:
    Factor  = ThresholdMatrix[xcoordinate % X][ycoordinate % Y];
    Attempt = Input + Factor * Threshold
    Color = FindClosestColorFrom(Palette, Attempt)
    Draw pixel using Color

Чтобы реализовать это, я должен нормализовать матрицу, чтобы она варьировалась от 0 до 1. И вот как я ее реализую:

factor = 1;
offset = (ditherMatrixSize * (ditherMatrixSize / 2) - 0.5d) / (ditherMatrixSize * ditherMatrixSize); 
// This time the offset is a bit different because I normalized the matrix
r = 256 / factor; // r is the same as the Threshold

transformedPixelColor.R = 
   currentPixel.R + r * (ditherMatrix[(x) % ditherMatrixSize, (y) % ditherMatrixSize] - offset)
transformedPixelColor.G = 
   currentPixel.G + r * (ditherMatrix[(x) % ditherMatrixSize, (y) % ditherMatrixSize] - offset)
transformedPixelColor.B = 
   currentPixel.B + r * (ditherMatrix[(x) % ditherMatrixSize, (y) % ditherMatrixSize] - offset)

Это работает так же, как и другая.Я даже нашел третий способ дизеринга, но он более сложный и дизеринг точно так же, как эти два, поэтому я не буду его освещать.

Так что вы думаете?Алгоритм работает корректно?

Заранее спасибо!

...