https://www.makingsoftware.com/chapters/color-spaces-models-and-gamuts
Нашел классную статью про цвета. Ну и в принципе, судя по сайту, у автора большие планы
Нашел классную статью про цвета. Ну и в принципе, судя по сайту, у автора большие планы
🔥4❤3
Так, от краски уже болит голова, да и кончилась она, возвращаюсь к искажениям.
Как я уже говорил, следующим этапом за геометрией идет PSF (Point Spread Function), который, хоть и отражает несколько разных искажений
- дефокус,
- продольная цветовая абберация,
- скос линзы,
- размытие к краю,
- кома,
- астигматизм,
- жировые полосы/царапины,
сводится к свертке с ядром, отражающим все перечисленные эффекты - разбить на части не получится правильным образом.
Тем не менее, я буду рассказывать про каждый по отдельности, а в конце соберу в один эффект.
Как я уже говорил, следующим этапом за геометрией идет PSF (Point Spread Function), который, хоть и отражает несколько разных искажений
- дефокус,
- продольная цветовая абберация,
- скос линзы,
- размытие к краю,
- кома,
- астигматизм,
- жировые полосы/царапины,
сводится к свертке с ядром, отражающим все перечисленные эффекты - разбить на части не получится правильным образом.
Тем не менее, я буду рассказывать про каждый по отдельности, а в конце соберу в один эффект.
❤4
Небольшое отступление:
Есть два эффекта, которые применить к обычному видео проблематично (возможно, нейронка поможет):
1 - Глубина резкости (если есть карта глубин для каждого кадра, то, в принципе, возможно)
2 - Искажения проекции на разных фокусных расстояния
Так как их нормальным путем реализовать не получится, то и рассказывать я о них не буду, но если интересна физика/геометрия процесса - напишите - распишу на пальцах
Есть два эффекта, которые применить к обычному видео проблематично (возможно, нейронка поможет):
1 - Глубина резкости (если есть карта глубин для каждого кадра, то, в принципе, возможно)
2 - Искажения проекции на разных фокусных расстояния
Так как их нормальным путем реализовать не получится, то и рассказывать я о них не буду, но если интересна физика/геометрия процесса - напишите - распишу на пальцах
❤5
Media is too big
VIEW IN TELEGRAM
Дефокус
Размытие объекта на картинке из-за непопадания его проекции в фокус.
В идеале - именно объекта, а не всей картинке - так будет физически вернее. Но для этого нам нужна карта глубин, коей как правило, нет, поэтому будем делать просто размытие всей картинки.
Размытие объекта на картинке из-за непопадания его проекции в фокус.
В идеале - именно объекта, а не всей картинке - так будет физически вернее. Но для этого нам нужна карта глубин, коей как правило, нет, поэтому будем делать просто размытие всей картинки.
❤3🔥1
Media is too big
VIEW IN TELEGRAM
Принцип:
Когда объект уходит из фокуса, то лучи от него не сходятся в точку на плоскости сенсора, где они должны быть, а в круг, что приводит к размытию картинки.
Причем в зависимости от расстояния до плоскости резкости размытие должно быть больше (но мы упростим и будем делать везде одинаковое).
То есть радиус диска ядра свертки зависит от диафрагмы, расстояния до плоскости резкости и от шага пикселя/зерна на сенсоре
Когда объект уходит из фокуса, то лучи от него не сходятся в точку на плоскости сенсора, где они должны быть, а в круг, что приводит к размытию картинки.
Причем в зависимости от расстояния до плоскости резкости размытие должно быть больше (но мы упростим и будем делать везде одинаковое).
То есть радиус диска ядра свертки зависит от диафрагмы, расстояния до плоскости резкости и от шага пикселя/зерна на сенсоре
❤2🔥2
This media is not supported in your browser
VIEW IN TELEGRAM
Алгоритм:
1. Можно пойти простым путем и взять MPSImageGaussianBlur - это даже будет выглядеть правдоподобно на малых значениях размытия, но неестественно на больших. Зато будет очень быстро
2. Можно пойти сложным путем и гонять свертку диском. Честно это делать супер дорого, поэтому вместо этого ограничиваются псевдослучайными равномерными сэмплами по нему (иначе вам придется гонять террабайты в секунду - видюха будет не рада такому). Зато у вас будет +/- красивое боке, кастомные формы диафрагмы. Ну и остальные эффекты требуют кастомной свертки
На видео сравнение, как выглядит гаусс и диск (разница, думаю, очевидна)
1. Можно пойти простым путем и взять MPSImageGaussianBlur - это даже будет выглядеть правдоподобно на малых значениях размытия, но неестественно на больших. Зато будет очень быстро
2. Можно пойти сложным путем и гонять свертку диском. Честно это делать супер дорого, поэтому вместо этого ограничиваются псевдослучайными равномерными сэмплами по нему (иначе вам придется гонять террабайты в секунду - видюха будет не рада такому). Зато у вас будет +/- красивое боке, кастомные формы диафрагмы. Ну и остальные эффекты требуют кастомной свертки
На видео сравнение, как выглядит гаусс и диск (разница, думаю, очевидна)
🔥4❤1
Media is too big
VIEW IN TELEGRAM
Сечение пучка - боке принимает форму диафргамы.
Принцип
По сути ядро свертки - это проекция формы диафрагмы, соответственно, в зависимотсти от числа и формы лепестков вы будете получать разные фигуры на боке. Плюс чем сильнее закрыта диафргма, тем четче эта форма будет. Правда при этом у вас вырастет глубина резкости, но мы ее не имитируем.
Принцип
По сути ядро свертки - это проекция формы диафрагмы, соответственно, в зависимотсти от числа и формы лепестков вы будете получать разные фигуры на боке. Плюс чем сильнее закрыта диафргма, тем четче эта форма будет. Правда при этом у вас вырастет глубина резкости, но мы ее не имитируем.
❤3
Алгоритм
Тут по сути мы модифицируем форму ядра свретки, отбрасывая точки за пределами формы чем-нибудь типа
Что можно еще "поломать":
- сделать скругление маски через SDF например
- сделать искажение формы, например неоднородным масштабом и поворотом
- исказить один из секторов лепестков
- сделать любую форму в принципе
Тут по сути мы модифицируем форму ядра свретки, отбрасывая точки за пределами формы чем-нибудь типа
// Regular n-gon aperture test (inscribed in unit circle). blades<3 → circle (always inside).
inline bool insideAperture(float2 d, int blades) {
if (blades < 3) return true; // circle
float N = float(blades);
// polar angle
float a = atan2(d.y, d.x);
// sector size and half-sector
float sector = 2.0f * M_PI_F / N;
float halfSec = 0.5f * sector;
// wrap angle to [0, sector)
float m = fmod(a + M_PI_F, sector);
if (m < 0.0f) m += sector;
// offset to nearest edge normal (in [-half, half])
float delta = m - halfSec;
// allowed radius along this direction for polygon with circumradius=1
float rAllowed = cos(M_PI_F / N) / max(cos(delta), 1e-4f);
float r = length(d);
return r <= rAllowed;
}
Что можно еще "поломать":
- сделать скругление маски через SDF например
- сделать искажение формы, например неоднородным масштабом и поворотом
- исказить один из секторов лепестков
- сделать любую форму в принципе
🔥3
Media is too big
VIEW IN TELEGRAM
Вот эти вот звездочки - это дифракция
Принцип:
Возникает она тем сильнее, чем сильнее у нас закрыта диафрагма. Число лучей зависит от числа лепестков, а вид - от их формы.
Как видно из названия, основано искажение на дифракции: в нашем случае - огибании грани лепестка, что даёт вдоль её нормали усиления, формируя направленные лучи.
Для круга симметрия полная - направленных пиков нет, только свечение или кольца (их в примере нет, но, думаю, вы их видели на фото и глазами: когда на фонари таращились).
Принцип:
Возникает она тем сильнее, чем сильнее у нас закрыта диафрагма. Число лучей зависит от числа лепестков, а вид - от их формы.
Как видно из названия, основано искажение на дифракции: в нашем случае - огибании грани лепестка, что даёт вдоль её нормали усиления, формируя направленные лучи.
Для круга симметрия полная - направленных пиков нет, только свечение или кольца (их в примере нет, но, думаю, вы их видели на фото и глазами: когда на фонари таращились).
❤5
Алгоритм:
Тут мы не трогаем свертку, а делаем блюр вдоль каждого луча (перпендикулярно лепестку). При этом каждую прочитанную точку мы проверяем на яркость и, если она недостаточная, игнорируем ее вклад. Таким образом "звездочки" будут только от якрих источников света.
Можно в принципе сделать нелинейную функцию, чтобы ближе к источнику света луч звездочки был более ярким, чем по мере удаления.
Дополнительно можете сделать еще небольшой блюр перпендикулярно направлению лучей звезды.
Проблема этого эффекта в том, что, если его считать честно, он выходит довольно тяжелым, поэтому можно делать в низком качестве на первью и честно пересчитывать для экспорта.
Кстати, пришла идея как можно ускорить, сделав в несколько проходов, но потребуется сильно больше памяти:
1 - Вытаскиваем в одтельную текстуру меньшего размера (не более чем в два раза) яркие пиксели - сразу уменьшаем нагрузку на пересчет яркости
2 - Делаем несколько копий, повернутых в нужных направлениях
3 - По каждой пробегаем одномерным ядром (вертикальным или горизонтальным)
4 - Поворачиваем обратно
5 - Компилируем поверх исходной картинки или в еще одну и ее подблюриваем гаусом, а потом накладываем на исходную
Не факт, что это лучшее решение, но что пришло в голову быстро
Тут мы не трогаем свертку, а делаем блюр вдоль каждого луча (перпендикулярно лепестку). При этом каждую прочитанную точку мы проверяем на яркость и, если она недостаточная, игнорируем ее вклад. Таким образом "звездочки" будут только от якрих источников света.
Можно в принципе сделать нелинейную функцию, чтобы ближе к источнику света луч звездочки был более ярким, чем по мере удаления.
Дополнительно можете сделать еще небольшой блюр перпендикулярно направлению лучей звезды.
Проблема этого эффекта в том, что, если его считать честно, он выходит довольно тяжелым, поэтому можно делать в низком качестве на первью и честно пересчитывать для экспорта.
Кстати, пришла идея как можно ускорить, сделав в несколько проходов, но потребуется сильно больше памяти:
1 - Вытаскиваем в одтельную текстуру меньшего размера (не более чем в два раза) яркие пиксели - сразу уменьшаем нагрузку на пересчет яркости
2 - Делаем несколько копий, повернутых в нужных направлениях
3 - По каждой пробегаем одномерным ядром (вертикальным или горизонтальным)
4 - Поворачиваем обратно
5 - Компилируем поверх исходной картинки или в еще одну и ее подблюриваем гаусом, а потом накладываем на исходную
Не факт, что это лучшее решение, но что пришло в голову быстро
🔥4
This media is not supported in your browser
VIEW IN TELEGRAM
Астигматизм
Немного отличается от того, что у вас, но суть +/- та же - изображение смазывается.
Есть два вида:
1 - тангенциальный (вдоль радиуса)
2 - радиальный (он же саггитальный) - вдоль окружности
Принцип:
Как мы уже знаем, идеально все работает только вдоль оптической оси, вне ее лучи фокусируются как правило вне соответствующей оси (если провести от исходной точки через центр условной линзы) из-за чего объекты так или иначе искажаются (вспоминаем геометрические искажения) и становятся смазанными.
Можно было бы копнуть глубже и рассказать про S и T фокусы, но я тут про общие принципы и реализацию рассказываю, а не школьный курс физики)))
Немного отличается от того, что у вас, но суть +/- та же - изображение смазывается.
Есть два вида:
1 - тангенциальный (вдоль радиуса)
2 - радиальный (он же саггитальный) - вдоль окружности
Принцип:
Как мы уже знаем, идеально все работает только вдоль оптической оси, вне ее лучи фокусируются как правило вне соответствующей оси (если провести от исходной точки через центр условной линзы) из-за чего объекты так или иначе искажаются (вспоминаем геометрические искажения) и становятся смазанными.
Можно было бы копнуть глубже и рассказать про S и T фокусы, но я тут про общие принципы и реализацию рассказываю, а не школьный курс физики)))
❤4
This media is not supported in your browser
VIEW IN TELEGRAM
Алгоритм
Cчитаем параметры пространства, и когда считаем точки для свертки - применяем его
Cчитаем параметры пространства, и когда считаем точки для свертки - применяем его
struct AstigCtx {
float2 e_r;
float2 e_t;
float s_r;
float s_t;
};
struct Parameters {
float2 invSize;
float2 opticalCenter;
float astigK;
};
inline AstigCtx astigSetup(float2 uv, Parameters params) {
AstigCtx C;
float ar = params.invSize.y / params.invSize.x;
float2 df = uv - params.opticalCenter;
float2 dfIso= float2(df.x * ar, df.y);
float rhoN = clamp(length(dfIso) * 2.0f, 0.0f, 1.0f);
float2 rIso = (length(dfIso) > 1e-6f) ? normalize(dfIso) : float2(1.0f, 0.0f);
C.e_r = normalize(float2(rIso.x / ar, rIso.y));
C.e_t = float2(-C.e_r.y, C.e_r.x);
float mag = fabs(params.astigK) * (rhoN * rhoN);
float a = 1.0f + mag;
C.s_r = (params.astigK >= 0.0f) ? (1.0f / a) : a;
C.s_t = (params.astigK >= 0.0f) ? a : (1.0f / a);
return C;
}
inline float2 astigApply(float2 d, AstigCtx C) {
float dr = dot(d, C.e_r);
float dt = dot(d, C.e_t);
return dr * C.s_r * C.e_r + dt * C.s_t * C.e_t;
}
❤3👏1
Из искажений входной оптики осталось всего ничего:
- кома
- продольная цветовая абберация - засветы - виньетка на сенсоре Потом пойдет интереснее. Пока можете выбрать, что интереснее:
- кома
- продольная цветовая абберация - засветы - виньетка на сенсоре Потом пойдет интереснее. Пока можете выбрать, что интереснее:
Anonymous Poll
60%
пленочная камера
30%
ТВ трубка
10%
ПЗС матрица
🔥2
This media is not supported in your browser
VIEW IN TELEGRAM
Кома
Смещение пучка из центра пятна боке - получаются свеого рода кометы.
Смещение пучка из центра пятна боке - получаются свеого рода кометы.
👍1
Алгоритм
1. Как и с астигматизмом, сначала рассчитываем параметры радиально-тангенциального поля в точке (можно расширить функцию для астигаматизма
2. Для каждой точки свертки пересчитываем, куда она смещается
1. Как и с астигматизмом, сначала рассчитываем параметры радиально-тангенциального поля в точке (можно расширить функцию для астигаматизма
2. Для каждой точки свертки пересчитываем, куда она смещается
struct AstigCtx {
float2 e_r; // radial unit in uv metric
float2 e_t; // tangential unit (perp)
float s_r; // radial scale
float s_t; // tangential scale
float rhoN; // normalized field height (0 center .. 1 edge)
};
inline AstigCtx astigSetup(float2 uv, constant Parameters& P) {
AstigCtx C;
float ar = P.invSize.y / P.invSize.x; // width/height
float2 oc = P.opticalCenter;
float2 df = uv - oc;
float2 dfIso = float2(df.x * ar, df.y);
float rhoN = clamp(length(dfIso) * 2.0f, 0.0f, 1.0f);
float2 rIso = (length(dfIso) > 1e-6f) ? normalize(dfIso) : float2(1.0f, 0.0f);
float2 e_r = normalize(float2(rIso.x / ar, rIso.y));
float2 e_t = float2(-e_r.y, e_r.x);
float mag = fabs(P.astigK) * (rhoN * rhoN);
float a = 1.0f + mag;
float s_r = (P.astigK >= 0.0f) ? (1.0f / a) : a;
float s_t = (P.astigK >= 0.0f) ? a : (1.0f / a);
C.e_r = e_r;
C.e_t = e_t;
C.s_r = s_r;
C.s_t = s_t;
C.rhoN = rhoN;
return C;
}
inline float2 comaApply(float2 d, AstigCtx C, float comaK) {
if (fabs(comaK) < 1e-6f) return d;
// Project onto radial/tangential axes
float dr = dot(d, C.e_r);
float dt = dot(d, C.e_t);
// Magnitude grows ~ rho^3 across the field (third-order behavior)
float mag = comaK * (C.rhoN * C.rhoN * C.rhoN);
// Quadratic skew in pupil coordinates → "comet" shape in image PSF
float2 delta = ( (dr*dr - 0.5f*dt*dt) * C.e_r
+ (dr*dt) * C.e_t );
return d + mag * delta;
}
👍2
Вообще, как видите, сверточные эффекты тоже опираются на линейную оптику, но, если для идеального фокуса мы можем использовать исключительно пересчет координат, то для объектов вне фокуса необходимо применять свертку и тут все крутится вокруг ядра этой свертки (ее формы, центра и тд).
👍3