Що є правильним способом затиснути шум від гусениць?


9

При зменшенні глибини кольору і затуманенні з 2-бітовим шумом (з n =] 0,5,1,5 [і вихід = підлога (вхід * (2 ^ біт-1) + n)), кінці діапазону значень (входи 0,0 і 1,0 ) галасливі. Хочеться, щоб вони були суцільним кольором.

Приклад: https://www.shadertoy.com/view/llsfz4

Градієнт шуму (вище - скріншот шадертої, що зображає градієнт і обидва кінці, які мають бути суцільно білими та чорними відповідно, але натомість шумно)

Звичайно, проблему можна вирішити, просто стиснувши діапазон значень, щоб кінці завжди були округлені до одиничних значень. Однак це відчуває злом, і мені цікаво, чи є спосіб реалізувати це "правильно"?


Чомусь shadertoy не працював у моєму браузері. Чи можете ви опублікувати / кілька простих зображень, щоб продемонструвати, що ви маєте на увазі?
Саймон Ф

1
Чи не слід це більше нагадувати n =] - 1, 1 [?
JarkkoL

@JarkkoL Ну формула для перетворення плаваючої точки в ціле число - це вихід = floor (вхід * intmax + n), де n = 0,5 без шуму, тому що ви хочете (наприклад)> = 0,5 округлити, але <0,5 вниз. Ось чому шум "зосереджений" на рівні 0,5.
hotmultimedia

@SimonF додав зображення shadertoy
hotmultimedia

1
Здається, ви обрізали висновок, а не округлили його (як це роблять GPU) - округлення, принаймні, ви отримаєте належні білі кольори : shadertoy.com/view/MlsfD7 (image: i.stack.imgur.com/kxQWl.png )
Mikkel Gjoel

Відповіді:


8

TL; DR: 2 * 1LSB трикутне-pdf відмирання в обрізних краях на 0 і 1 за рахунок затискання. Рішення полягає в тому, щоб лерпувати до 1-бітної рівномірної дистрибуції в цих крайових шарах.

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

Перш за все, ось проблема, яку ми розглядаємо, під час кількісного визначення сигналу на 3 біти з тригранним двостороннім розширенням у форматі pdf:

виходи - по суті те, що показала гаряча мультимедіа.

Зростаючий контраст, ефект, описаний у запитанні, стає очевидним: Вихід не має середнього до чорного / білого в крайових шафах (а насправді виходить за межі 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) );

Який фіксує крайові шафи, зберігаючи трикутну обмацування недоторканою для решти діапазону:

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

Тож, щоб не відповісти на ваше запитання: я не знаю, чи є більш математично обґрунтоване рішення, і я однаково прагну знати, що зробили Masters of Past :) До того часу, принаймні, у нас є цей жахливий хак, щоб підтримувати наш код.

EDIT
Я, мабуть, повинен висвітлити попереджувальну пропозицію, подану в запитанні, щодо простого стиснення сигналу. Оскільки середнє значення не є лінійним у крайових шарах, просто стискання вхідного сигналу не дає ідеального результату - хоча це фіксує кінцеві точки: введіть тут опис зображення

Список літератури


Дивно, що лерп по краях дає ідеальний вигляд. Я б очікував хоча б невеликого відхилення: Р
Алан Вулф

Так, я також був позитивно здивований :) Я вважаю, що це працює, тому що ми лінійно зменшуємо величину дифференціалу, з тією ж швидкістю сигнал зменшується. Тож хоча б масштаб збігається ... але я згоден, що цікаво, що безпосередньо змішування дистрибутивів не має негативних побічних ефектів.
Mikkel Gjoel

@MikkelGjoel На жаль, ваша думка неправильна через помилку у вашому коді. Ви повторно використовували той самий RNG для обох, dithertriа dithernormне незалежних. Після того, як ви пропрацюєте всю математику та скасуєте всі умови, ви виявите, що ви зовсім не лергуєте! Натомість код діє як жорстке відсічення v < 0.5 / depth || v > 1 - 0.5/depth, миттєво перемикаючись на рівномірний розподіл там. Не те, що це забирає від вас приємне дихання, це просто непотрібно. Виправити помилку насправді погано, у вас виявиться гірша туга. Просто використовуйте жорстке обрізання.
orlp

Покопившись ще глибше, я знайшов ще одну проблему у вашій шадертої, де ви не робите гамма-коригування під час усереднення зразків (ви середні в просторі sRGB, який не є лінійним). Якщо ви правильно поводитеся з гаммою, ми виявимо, що, на жаль, ми ще цього не зробили. Ми повинні сформувати свій шум для вирішення гамма-корекції. Ось shadertoy, що відображає проблему: 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- content / uploads / 2016/03 / GdcVdrLottes.pdf


(Вибачте, що я потрапив випадково, а термін редагування минув). У прикладі цього прикладу є одна з тих ситуацій, коли це мало б значення: надання зображень із сірою шкалою з плаваючою точкою на 3-бітному дисплеї. Тут інтенсивність сильно змінюється, просто змінюючи LSB. Я намагаюся зрозуміти, чи є який-небудь "правильний спосіб" відобразити кінцеві значення в суцільні кольори, наприклад, стискаючи діапазон значень і насичуючи кінцеві значення. І що таке математичне пояснення? У прикладі формули вхідне значення 1,0 не дає виходу, який в середньому дорівнює 7, і саме це мене турбує.
hotmultimedia

1

Я спростив ідею Міккела Джойола про те, щоб збити трикутний шум до простої функції, яка потребує лише одного дзвінка RNG. Я позбавив усіх непотрібних шматочків, тому це повинно бути досить читабельним і зрозумілим, що відбувається:

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

Щодо ідеї та контексту, я посилаюсь на відповідь Міккеля Джойола.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.