Коррекция нелинейных геометрических искажений

В Photoshop CC есть такой очень важный и полезный инструмент, называется Free Transform (и еще несколько вариантов на туже тему). Очень нужная штука, но работает почти никогда.

Снимок экрана 2016-06-21 в 19.50.38Вот если загрузить PNG или вектор — то запросто. А обычный JPEG вызывает у редактора отторжение. И пока не создашь выделение или слой с выделением — меню трансформации остается неактивным. А еще мне порядком, поднадоела вот эта тестовая картинка (та что за меню торчит), точнее не сама картинка, а то как камера iPhone с её широко-угольным объективом портит пропорции лица. Конечно, в PS есть Lens Correction, исправляющий дисторсии и прочие недостатки оптических систем (хотя на картинке не недостаток, а фича), но во-первых: он тормозной, во-вторых: не всегда хочется исправить просто «бочку» или «подушку».

Попробуем устранить это недоразумение и потренируемся сегодня на кроликах Bezier-кривых — позволяющих подкрутить ручками практических любые, самые запущенные нелинейные дефекты геометрии. Или наоборот: накрутить самые безумные искажения в «художественных» целях.

В результате эксзерцисов получим утилиту, позволяющую загружать в окно редактора файл изображения, издеваться над ним, удалять лишнее, и сохранять исправленную до нужной кондиции фоточку в  свой фотоальбом.

Приложение можно скачать. Или получить исходный код и продолжить эксперименты самостоятельно.

Про нелинейность

Есть только одна линейная система, и два миллиарда нелинейных. В прошлый раз, мы, можно сказать, рассмотрели все многообразие первого класса задач. Но в компьютерной графике так сложилось, что для решения большинства практических проблем, из всего второго миллиарда нелинейных функций, выбрали полиномы Бернштейна. Выбрали относительно недавно, всего лишь середине прошлого века. Но вы эту историю и без меня неплохо знаете, а то и более детально, и стало быть, в курсе причин чем так они интересны. Хотя бы ровно вот этим, эта история достойна практического воплощения в Metal-ле.

Кривые Безье в фрагментном шейдере

В случае линейной трансформации нам было необходимо всего два треугольника или 6 вершин для реализации графического шейдера. По 6ти вершинам мы делали линейные преобразования в один проход и собственно все. В случае с рекурсивно вычисляемым полиномом, мы не можем ограничится только 6 вершинами и по идее должны вычислить все возможные триангуляции поверхности фото-пластины. Что, очевидно, весьма не дешево с точки зрения вычислительных ресурсов. Для таких случаев, современные API работы с карточками предоставляют возможность генерации в GPU дополняющих многоугольников на лету. К сожалению, Metal, в текущей, версии не поддерживает тесселяцию. К счастью, Apple уже объявила о реализации доступа к тесселяторам в следующих версиях iOS и macOS. И даже выкатила пример. Однако, мы не будем ждать, а обойдемся решением в частном виде: в нашем случае поддержка 3D-поверхностей Безье ни к чему, можно обойтись и 2D, а для этого достаточно реализовать пересчет координат не на уровне вершинного шейдера, а во фрагментном — прямо по поверхности текстуры, хотя понимаю, с тесселяторм было бы несколько удобнее и практичнее.

Запишем в качестве шпаргалки главное выражение тысячелетия (принесшее некоторым его юзерам миллиарды денег, между прочим, только одни True Type шрифты чего стоят, например):

{Q(u)=\begin{bmatrix}u^3&u^2&u&1\end{bmatrix}B\begin{bmatrix}P_0\\P_1\\P_2\\P_3\end{bmatrix}}

Или в виде поверхности для кубического полинома по сетке из 16ти точек (4×4):

{Q(u,v)=\begin{bmatrix}u^3&u^2&u&1  \end{bmatrix}B\begin{bmatrix}P(u,v)\end{bmatrix}B\begin{bmatrix}v^3\\v^2\\v\\1\end{bmatrix}} , где:

{\begin{bmatrix}P(u,v)\end{bmatrix}} — тензор контрольных точек поверхности,

{B = \begin{bmatrix}-1 & 3 & -3 & 1\\3 & -6 & 3 & 0\\-3 & 3 & 0 & 0\\1 & 0 & 0 & 0\\\end{bmatrix}}  — базисная матрица Безье.

Чтобы было удобно записывать тензорные произведения в шейдерах MSL создадим структуру описывающую модель тензора контрольных точек поверхности Безье и оператор произведения:

typedef struct {
float2 vectors[4][4];
} IMPFloat2x4x4

inline const float4x4 operator*(IMPFloat2x4x4 const tensor, float4 const vector) {
float4 v[4] = {float4(0),float4(0),float4(0),float4(0)};
for (int j=0; j<4; j++){
for (int i=0; i<4; i++){
v[j] += float4(tensor.vectors[i][j],0,0)*vector[i];
}
}
return float4x4(v[0],v[1],v[2],v[3]);
}

/// @brief Деформация поверхностью Безье на плоскости
///
fragment float4 fragment_bezierWarpTransformation(
IMPVertexOut in [[stage_in]],
// исходная текстура
texture2d<float, access::sample> inTexture [[texture(0)]],
// контрольные точки
const device IMPFloat2x4x4 &surface [[ buffer(0) ]],
// цвет фона
const device float4 &color [[ buffer(1) ]])
{

constexpr sampler s(address::clamp_to_zero, filter::linear, coord::normalized);
float3 p = float3(in.texcoord.xy,0);

//
// Финальная трансформация через эквивалетное тензорное произведение
//
// Q(u,v) = (u^3,u^2,u,1)B[P]B(v^3,v^2,v,1)
// где [P] - тензор контрольных точек
//
float4x4 B = {
{-1, 3, -3, 1},
{ 3, -6, 3, 0},
{-3, 3, 0, 0},
{ 1, 0, 0, 0}
};

float4 BU = float4(pow(p.x,3), pow(p.x,2), p.x, 1) * B;
float4 BV = B * float4(pow(p.y,3), pow(p.y,2), p.y, 1);

float4 position = surface*BV*BU;

float4 inColor = inTexture.sample(s,position.xy);

if (inColor.a==0) {
inColor = color;
}

return inColor;
}

Хостовую обертку для исполнения шейдера можно почитать в первоисточнике, проекта ImageMetalling. Реализацию UI там-же.

Выводы

Вглядитесь в лицо не одухотворенное тензорной алгеброй. А затем посмотрите на лицо ей облагороженное: оно стало похоже на лицо здорового человека, а не откормленного самодовольного мещанина, использующего фотокамеру iPhone не по назначению. Мы в очередной раз сделали мир лучше и его лица чище. Инджой! И да, Сергей Натанович Бернштейн  — великий.

Bezier-Warp-Filter


Авторы блога не преследуют целей быть предельно корректным, но если заметили явную ашипку, если написали явную глупость, если что-то не понятно, есть идеи улучшения: комментируйте или пишите на: imagemetalling [*] gmail.com.

Реклама

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s