Какой правильный способ ограничения шума дизеринга?

9

При уменьшении глубины цвета и размывании с помощью 2-битного шума (с n =] 0,5,1,5 [и output = floor (input * (2 ^ bits-1) + n)), заканчивается диапазон значений (входные значения 0,0 и 1,0 ) шумные. Было бы желательно, чтобы они были однотонными.

Пример: https://www.shadertoy.com/view/llsfz4

Градиент шума (выше скриншот с шейдертой, изображающий градиент и оба конца, которые должны быть сплошным белым и черным соответственно, но вместо этого шумные)

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

hotmultimedia
источник
По какой-то причине шейдертой не запустился мой браузер. Не могли бы вы опубликовать / несколько простых изображений, чтобы продемонстрировать, что вы имеете в виду?
Симон Ф
1
Разве это не должно быть больше похоже на n =] - 1, 1 [?
JarkkoL
@JarkkoL Итак, формула для преобразования числа с плавающей запятой в целое - это output = floor (input * intmax + n), где n = 0,5 без шума, потому что вы хотите (например)> = 0,5 округлить в большую сторону, но <0,5 вниз. Вот почему шум «центрируется» на 0,5.
hotmultimedia
@SimonF добавил образ шадертой
hotmultimedia
1
Кажется, вы урезали вывод, а не округлили его (как это делают графические процессоры) - вместо этого округлите, по крайней мере, вы получите правильные белые цвета : shadertoy.com/view/MlsfD7 (image: i.stack.imgur.com/kxQWl.png )
Миккель Гьоэль

Ответы:

8

TL; DR: 2 * 1LSB. Треугольные-pdf разрывы дизеринга в краевых корпусах при 0 и 1 из-за зажима. Решение состоит в том, чтобы перейти к 1-битному равномерному колебанию в этих случаях.

Я добавляю второй ответ, видя, что это оказалось немного сложнее, чем я первоначально думал. Похоже, что эта проблема была "TODO: нужно зажимать?" в моем коде с тех пор, как я перешел с нормализованного на треугольное сглаживание ... в 2012 году. Приятно наконец посмотреть на него :) Полный код для решения / изображений, использованных в этом посте: https://www.shadertoy.com/view/llXfzS

Прежде всего, вот проблема, которую мы рассматриваем при квантовании сигнала на 3 бита с дизерингом 2 * 1LSB triangular-pdf:

выходы - По сути, что показали hotmultimedia.

При увеличении контраста эффект, описанный в вопросе, становится очевидным: выходной сигнал не усредняется по черно-белому в краевых корпусах (и на самом деле перед этим он значительно превышает 0/1).

введите описание изображения здесь

Глядя на график, вы получите немного больше информации:

введите описание изображения здесь (серые линии обозначают 0/1, также серым цветом обозначен сигнал, который мы пытаемся вывести, желтая линия - среднее значение сглаженного / квантованного выхода, красная - ошибка (среднее значение сигнала)).

Интересно, что не только средний выходной сигнал не равен 0/1 на границах, он также не является линейным (вероятно, из-за треугольного pdf шума). Глядя на нижний предел, становится понятным, почему выходной сигнал расходится: поскольку сигнал со сглаживанием начинает включать отрицательные значения, фиксация на выходе изменяет значение нижних сглаженных участков вывода (то есть отрицательных значений), тем самым увеличивая значение среднего. Иллюстрация выглядит по порядку (равномерное, симметричное смещение 2LSB, среднее значение все еще в желтом цвете):

введите описание изображения здесь

Теперь, если мы просто используем нормализованный дизеринг 1LSB, в краях-случаях вообще нет проблем, но тогда, конечно, мы теряем хорошие свойства треугольного дизеринга (см., Например, эту презентацию ).

введите описание изображения здесь

Таким образом, (прагматичное, эмпирическое) решение (хак) должно вернуться к [-0,5; 0,5 [равномерное сглаживание для граничного случая:

float dithertri = (rnd.x + rnd.y - 1.0); //note: symmetric, triangular dither, [-1;1[
float dithernorm = rnd.x - 0.5; //note: symmetric, uniform dither [-0.5;0.5[

float sizt_lo = clamp( v/(0.5/7.0), 0.0, 1.0 );
float sizt_hi = 1.0 - clamp( (v-6.5/7.0)/(1.0-6.5/7.0), 0.0, 1.0 );

dither = lerp( dithernorm, dithertri, min(sizt_lo, sizt_hi) );

Который фиксирует кромки, сохраняя треугольное сглаживание без изменений для оставшегося диапазона:

введите описание изображения здесь

Поэтому, чтобы не отвечать на ваш вопрос: я не знаю, существует ли более математически надежное решение, и в равной степени стремлюсь узнать, что сделали Мастера прошлого :) До тех пор, по крайней мере, у нас был этот ужасный хак, чтобы поддерживать наш код в работе.

РЕДАКТИРОВАТЬ
Я, вероятно, должен охватить предложение обхода, данное в Вопросе, о простом сжатии сигнала. Поскольку среднее значение не является линейным в крайних случаях, простое сжатие входного сигнала не дает идеального результата - хотя оно фиксирует конечные точки: введите описание изображения здесь

Ссылки

Миккель Гьоэль
источник
Удивительно, что поводок по краям дает идеальный результат. Я бы ожидал хотя бы небольшого отклонения: P
Алан Вулф
Да, я тоже был приятно удивлен :) Я считаю, что это работает, потому что мы линейно уменьшаем величину дизеринга, с той же скоростью, с которой сигнал уменьшается. Так что, по крайней мере, масштаб соответствует ... но я согласен, что интересно, что прямое смешивание распределений, по-видимому, не имеет отрицательных побочных эффектов.
Миккель Гьоэль
@MikkelGjoel К сожалению, ваше мнение неверно из-за ошибки в вашем коде. Вы повторно использовали один и тот же ГСЧ для обоих dithertriи dithernormвместо независимого. После того, как вы проработаете всю математику и отмените все условия, вы обнаружите, что совсем не рыдаете! Вместо этого код действует как жесткое ограничение v < 0.5 / depth || v > 1 - 0.5/depth, мгновенно переключаясь на равномерное распределение. Не то, чтобы это убирало от приятного смешения, которое у вас есть, это просто излишне сложно. Исправление ошибки на самом деле плохо, в результате вы получите худший дизеринг. Просто используйте жесткий отсечение.
orlp
После копания еще глубже я обнаружил еще одну проблему в вашей шейдертой, когда вы не выполняете гамма-коррекцию при усреднении сэмплов (вы усредняете в пространстве sRGB, которое не является линейным). Если вы правильно обработаете гамму, мы обнаружим, что, к сожалению, мы еще не закончили. Мы должны сформировать наш шум, чтобы справиться с гамма-коррекцией. Вот шадертой, отображающей проблему: shadertoy.com/view/3tf3Dn . Я перепробовал множество вещей и не смог заставить его работать, поэтому я разместил вопрос здесь: computergraphics.stackexchange.com/questions/8793/… .
orlp
3

Я не уверен, что смогу полностью ответить на ваш вопрос, но я добавлю некоторые мысли, и, возможно, мы сможем прийти к ответу вместе :)

Во-первых, основа вопроса для меня немного неясна: почему вы считаете желательным иметь чистый черный / белый, когда в любом другом цвете есть шум? Идеальный результат после сглаживания - ваш оригинальный сигнал с абсолютно равномерным шумом. Если черный и белый различны, ваш шум становится зависимым от сигнала (хотя это может быть хорошо, так как это происходит, когда цвета в любом случае ограничены).

Тем не менее, существуют ситуации, когда наличие шума в белом или черном цветах действительно представляет проблему (мне неизвестны случаи использования, в которых требуется, чтобы и черный, и белый одновременно были «чистыми»): при рендеринге аддитивно смешанной частицы в виде квадрата с текстура, вы не хотите, чтобы шум был добавлен во весь квад, так как это будет видно и за пределами текстуры. Одним из решений является смещение шума, а не добавление [-0,5; 1,5 [вы добавляете [-2,0; 0,0 [(т.е. вычитаете 2 бита шума). Это довольно эмпирическое решение, но я не знаю более правильного подхода. Размышляя об этом, вы, вероятно, также захотите повысить свой сигнал, чтобы компенсировать потерянную интенсивность ...

В некотором роде Тимоти Лоттес выступил на GDC с докладом о формировании шума в тех частях спектра, где он больше всего необходим, и о снижении шума на ярком конце спектра: http://32ipi028l5q82yhj72224m8j-wpengine.netdna-ssl.com/wp- содержание / добавления / 2016/03 / GdcVdrLottes.pdf

Миккель Гьоэль
источник
(извините, я случайно нажал ввод, и срок редактирования истек). Вариант использования в примере - это одна из тех ситуаций, где это имеет значение: рендеринг изображения в градациях серого с плавающей точкой на 3-битном устройстве отображения. Здесь интенсивность сильно меняется, просто меняя LSB. Я пытаюсь понять, существует ли какой-либо "правильный способ", чтобы конечные значения отображались в сплошные цвета, например, сжатие диапазона значений и насыщение конечных значений. И каково математическое объяснение этого? В формуле примера входное значение 1,0 не дает выходных данных, в среднем равных 7, и это меня беспокоит.
hotmultimedia
1

Я упростил идею Миккеля Гьоэля о сглаживании треугольным шумом до простой функции, которая требует только одного вызова ГСЧ. Я убрал все ненужные биты, чтобы они были достаточно понятны и понятны:

// Dithers and quantizes color value c in [0, 1] to the given color depth.
// It's expected that rng contains a uniform random variable on [0, 1].
uint dither_quantize(float c, uint depth, float rng) {
    float cmax = float(depth) - 1.0;
    float ci = c * cmax;

    float d;
    if (ci < 0.5 || ci >= cmax - 0.5) {
        // Uniform distribution on [-0.5, 0.5] for edges.
        d = rng - 0.5;
    } else {
        // Symmetric triangular distribution on [-1, 1].
        d = (rng < 0.5) ? sqrt(2.0 * rng) - 1.0 : 1.0 - sqrt(2.0 - 2.0*rng);
    }

    return uint(clamp(ci + d + 0.5, 0.0, cmax));
}

Для идеи и контекста я отошлю вас к ответу Миккель Гьоэль.

orlp
источник