Блендинг изображений
Определение: |
Гармонизация изображений (англ. image harmonization) — метод, позволяющий наложить часть одного изображения поверх другого таким образом, чтобы композиция изображений выглядела естественно, без швов на границах вставки и с соответсвующими цветами и текстурами [1]. |
- картинка (можно вставить картинку с дайвером, если сможем её обработать гармонизатором)*
Определение: |
Блендинг изображений (англ. image blending) — метод, позволяющий вставить часть одного изображения в другое таким образом, чтобы композиция изображений выглядела естественно, без швов на границах вставки и соответсвующими цветами и текстурами. В отличие от гармонизации, блендинг сам определяет какие пиксели фонового изображения нужно заменить.[1] |
- картинка с дайвером (там видно, что пузырики с фонового изображения остались поверх дайвера)*
Содержание
Блендинг Пуассона
todo Note: Блендинг Пуассона на самом деле является гармонизацией, так как требует маску заменяемых пикселей. Почему-то в статьях его называют блендингом (Poisson blending), хотя оригинальная статья называлась Poisson Image Editing[2]
Простая вставка одного изображения поверх другого нередко влечет заметный перепад яркости на границе вставки (рис. $1.1$). Метод Пуассона заключается в сглаживании этого перепада (рис. $1.2$) с целью сделать дефект менее заметным, используя градиент вставляемого изображения и значения пикселей фонового изображения на границе вставки.
Замечание: Для RGB изображений задача минимизации решается для каждого цветового канала отдельно.
Давайте обозначим за $S$ изображение, которое служит фоном, а за $I$ — изображение, вставляемое поверх $S$. Область вставки будем задавать двоичной маской $M$, содержащей единицы в области наложения. Например:
Фоновое изображение $S$ |
Накладываемое изображение $I$ |
Маска $M$ |
---|---|---|
Пусть $p$ — координаты пикселя двухмерного изображения (т.е. $(x, y)$). За $Img_p$ обозначим значение пикселя с координатами $p$ изображения $Img$. Пусть $\Omega$ — множество координат $p$, что $M_p = 1$. Тогда $\partial \Omega$ — координаты границы вставляемой области.
Пусть $N_p$ — множество соседей $p$ (максимум четыре пикселя, имеющих общую границу с $p$, т.е. пиксели со следующими координатами: $(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)$). Для всех пар $(p, q)$ таких, что $q \in N_p$, введем $v_{pq} = I_p - I_q$
Обозначим результат блендинга за $O$. Так как мы хотим сделать результат бесшовным, пиксели $O_p$, где $p \in \partial\Omega$, сделаем равными $S_p$. Для $p, q$ из внутренней части $\Omega$ постараемся найти такие значения, чтобы их разность была близка к $v_{pq}$. Для этого решим задачу минимизации:
$\underset{f_p,\; p \in \Omega}{\mathrm{min}}\; \underset{p, q \in \Omega}{\sum}\; (O_p - O_q - v_{pq})^2$, где $O_p = S_p$ для $p \in \partial \Omega$
Заметим, что функция, которую мы хотим минимизировать, квадратична относительно переменных $O_p, p \in \Omega$. Для решения задачи минимизации вычислим частные производные по этим переменным и найдем значения переменных, при которых частные производные будут равны нулю.
Для $p \in \Omega$:
$\frac{\partial{\underset{p, q \in \Omega}{\sum}\; (O_p - O_q - v_{pq})^2}}{\partial O_p} = \underset{q \in N_p}{\sum} 2 (O_p - O_q - v_{pq}) - \underset{q \in N_p}{\sum} 2 (O_q - O_p - v_{qp}) = 2 \underset{q \in N_p}{\sum} 2 (O_p - O_q - v_{pq})$.
Приравнивая к нулю, получаем: $|N_p| O_p - \underset{q \in N_p}{\sum} O_q = \underset{q \in N_p}{\sum} v_{pq}$.
Для точек, граничащих с $\partial \Omega$: $\;|N_p| O_p - \underset{q \in N_p \cap \Omega}{\sum} O_q = \underset{q \in N_p \cap \partial \Omega}{\sum} S_q + \underset{q \in N_p}{\sum} v_{pq}$.
Решаем систему уравнений и получаем значения $O_p$ для пикселей $p$ из внутренности $\Omega$. Для решения систем такого вида могут быть использованы итеративные алгоритмы Gauss-Seidel и V-cycle multigrid[2].
Mетод Пуассона сдвигает цвета накладываемого изображения, сохраняя свойства градиента (то если $I_{p1}$ был меньше $I_{p2}$, то после преобразования $I_{p1}$ не станет больше $I_{p2}$), однако значение само градиета может получиться другим.[3]
Трансфер стиля
Прежде чем переходить к гармонизации картин, рассмотрим задачу трансфера стиля с изображения $S$ на изображение $I$. Для этого используются выходы скрытых слоёв свёрточной нейронной сети VGG-19[4].
Основная идея генерации изображения — решение оптимизационной задачи $\mathcal{L}(O, I, S) \xrightarrow[O]{} min$, где $O$ — итоговое изображение, $\mathcal{L}(O, I, S)$ — функция потерь. Такую задачу можно решать градиентным спуском в пространстве изображений используя метод обратного распространения ошибки.
Определение: |
Пусть $F^l\left[I\right] \in \mathcal{R}^{N_l \times M_l}$ — выход $l$-го слоя сети на изображении $I$. Представим его как матрицу $N_l \times M_l$,
где $N_l$ — количество фильтров в $l$-ом слое, $M_l$ — количество признаков (высота, умноженная на ширину). Тогда $F^l_{ij}\left[I\right]$ — $j$-ый признак $i$-го фильтра в $l$-ом слое. |
Определение: |
Матрица Грама (англ. Gram matrix) — матрица попарных скалярных произведений. В нашем случае матрица отражает корреляцию между выходами фильтров. $G^l\left[I\right] \in \mathcal{R}^{N_l \times N_l} = F^l\left[I\right]F^l\left[I\right]^T$. |
Image Style Transfer Using Convolutional Neural Networks[5].
Определение: |
$\mathcal{L}^{\alpha}_{content}(I, O) = \displaystyle\sum_l \frac{\alpha_l}{2 N_l M_l}\displaystyle\sum_{i, j} (F^l_{ij}\left[I\right] - F^l_{ij}\left[O\right])^2$ — функция потерь содержания, где $\alpha_l$ — вклад $l$-го слоя в функцию потерь[6]. |
Определение: |
$\mathcal{L}^{\beta}_{style}(I, O) = \displaystyle\sum_l \frac{\beta_l}{2N_l^2} \displaystyle\sum_{i, j} (G^l_{ij}\left[I\right] - G^l_{ij}\left[O\right])^2$ — функция потерь стиля, где $\beta_l$ — вклад $l$-го слоя в функцию потерь[6]. |
Итоговой функцией потерь будет $\mathcal{L}_{Gatys} = \mathcal{L}_{content}(I, O) + w_{style}\mathcal{L}_{style}(I, O)$[6]. Вес $w_{style}$, векторы $\alpha$ и $\beta$ являются, в некотором смысле, гиперпараметрами алгоритма, которые нужно подбирать.
Авторы статьи показывают, что в качестве начальной инициализации можно брать изображение $I$, изображение $S$ или белый шум — алгоритм даёт похожие результаты в этих случаях.
Histogram Loss
Авторы другой статьи[7] показывают, что результаты, полученные с помощью $\mathcal{L}_{Gatys}$ нестабильны и предложили другую функцию потерь, основанную на сопоставлении гистограмм.
Определение: |
Сопоставление гистограмм (англ. Histogram matching) — метод обработки изображения, после которого гистограмма изображения совпадает с целевой гистограммой[8]. |
Определение: |
Пусть $R = histmatch(S, O)$ — отображение пикселей такое, что гистограмма $S$ совпадает с гистограммой $R(O)$. |
Определение: |
$\mathcal{L}^{\gamma}_{hist}(O, S) = \displaystyle\sum_l \gamma_l \displaystyle\sum_{i, j} (F^l_{ij}\left[O\right] - R(F^l_{ij}\left[O\right]))^2$ — функция потерь гистограмм, где $\gamma_l$ — вклад $l$-го слоя в функцию потерь |
Total variation loss
Также добавим ещё одну функцию потерь, которая должна делать картинку более гладкой[9][10].
Определение: |
$\mathcal{L}_{tv}(O) = \displaystyle\sum_{i, j} (O^l_{i, j} - O^l_{i-1, j}))^2 + (O^l_{i, j} - O^l_{i, j-1}))^2$ — общая вариационная потеря (англ. Total variation loss). |
Глубокая гармонизация картин[11]
Для того чтобы вставить изображение в картину или рисунок нужно не только сделать бесшовный переход и изменить цвета, но ещё и изменить текстуру вставляемого изображения, например сымитировать мазки кистью. Используем для этого комбинацию подходов из других статей[5][10][7].
Алгоритм будет состоять из двух проходов. Первый проход будет делать грубую гармонизацию, а второй — более тщательную. Отличаться они будут стилевым маппингом и функциями потерь.
Определение: |
Стилевым маппингом мы будем называть отображение $P : \mathcal{R}^{N_l \times M_l} \rightarrow \mathcal{R}^{N_l \times M_l}$, которое некоторым образом переставляет столбцы матрицы (не обязательно обратимо, то есть столбцы могут теряться и копироваться). Более формально, пусть $p(j)$ — новая позиция столбца $j$, тогда $P(Q)_{i, p(j)} = Q_{ij}$. |
Один проход будет состоять из 3 частей:
- Входное $I$ и стилевое $S$ изображения подаются на вход нейронной сети VGG-19, так мы получаем $F^l_{ij}\left[I\right]$ и $F^l_{ij}\left[S\right]$.
- Для каждого слоя $l$ некоторым алгоритмом cтроится стилевой маппинг $P_l$, который сопоставляет столбцам из $F_l[I]$ столбцы из $F_l[S]$.
- Изображение $O$ восстанавливается градиентным спуском по пространству изображений, используя некоторую функцию потерь.
fun $SinglePassHarmonization$( $I$, // Входное изображение $M$, // Маска $S$, // Стилевое изображение $\pi$, // Алгоритм построения стилевого маппинга $\mathcal{L}$ // Функция потерь ): // Строим матрицы $F[I]$ и $F[S]$ с помощью свёрточной сети VGG-19 $F[I] \leftarrow ComputeNeuralActivations(I)$ $F[S] \leftarrow ComputeNeuralActivations(S)$ // Строим стилевой маппинг $P \leftarrow \pi(F[I], M, F[S])$ // Градиентным спуском ищем изображение $O$, которое минимизирует $\mathcal{L}$ $O \leftarrow Reconstruct(I, M, S, P, \mathcal{L})$ return $O$
Первый проход
Определение: |
Вектор активации (англ. activation vector) — это вектор значений функции активации для каждого фильтра свёрточного слоя. Заметим, что столбцы $F_l$ являются векторами активации. |
Определение: |
Патчем (англ. patch) для столбца $j$ будем называть тензор $3 \times 3 \times N_l$, который состоит из соседних векторов активации в тензоре свёрточного слоя, с центром в столбце $j$ |
Первый проход делает грубую гармонизацию, но при этом он хорошо работает с любыми стилями. Здесь используется алгоритм IndependentMapping
для построения стилевого маппинга. Этот алгоритм для каждого столбца $j$ в $F_l[I]$ ищет столбец $p(j)$ в $F_l[S]$, такой что евклидово расстояние между патчем $F_l[I]$ с центром $j$ и патчем $F_l[S]$ с центром $p(j)$ минимально (метод ближайшего соседа).
fun $IndependentMapping$( $F[I]$, // Выходы слоёв после входного изображения $Mask$, // Маска $F[S]$ // Выходы слоёв после стилевого изображения ): for $l \in [1 : L]$: // L = количество слоёв сети for $j \in [1 : M_l]$: if $j \in Resize(Mask, l)$: // рассматриваем патчи только внутри маски, которую нужно масштабировать в соответсвии с размером слоя $l$ $P_l(j) \leftarrow NearestNeighborIndex(F[I], j, F[S])$ return $P$
В первом проходе используется модифицированная функция потерь $\mathcal{L}_{Gatys}$, с тем лишь отличием, что к $F_l[S]$ применяется стилевой маппинг $P_l$.
Определение: |
$\mathcal{L}_1(I, S, O, P) = \mathcal{L}^{\alpha}_{content}(I, O) + w_{style}\mathcal{L}^{\beta}_{style}(S, O, P)$, где $w_{style}$ — вес стилевой функции потерь |
- TODO Figure 2b, 5c ()
Второй проход
Второй проход делает более качественную гармонизацию после первого прохода. Здесь мы будем использовать более сложный алгоритм ConsistentMapping
построения стилевого маппинга и более сложную функцию потерь. Суть этого алгоритма в том, чтобы найти стилевой мапинг на некотором слое $l_{ref}$ и перенести этот маппинг на остальные слои. Также, мы будем предпочитать маппинги, в которых смежные патчи в $F_l[S]$ остаются смежными после мапинга, чтобы обеспечить пространсвенную согласованность (видимо таким образом мы хотим переносить сложные текстуры более качественно, например мазки кистью).
fun ConsistentMapping( $F[I]$, // Выходы слоёв после входного изображения $Mask$, // Маска $F[S]$ // Выходы слоёв после стилевого изображения ): // Сначала посчитаем маппинг как в IndependentMapping только для слоя $l_{ref}$ for $j \in [1 : M_{l_{ref}}]$: if $j \in Resize(Mask, l_{ref})$: $P_0(j) \leftarrow NearestNeighborIndex(F[I], j, F[S])$ // Далее обеспечиваем пространсвенную согласованность for $j \in [1 : M_{l_{ref}}]$: if $j \in Resize(Mask, l_{ref})$: $q \leftarrow P_0(j)$ // Инициализируем множество кандидатов на новый маппинг $CSet \leftarrow \{q\}$ // Перебираем все смежные патчи for $o \in {N, NE, E, SE, S, SW, W, NW}$: // Добавляем в кандидаты патч, сосед которого является маппингом для нашего соседа в соответсвующем направлении $CSet \leftarrow CSet \cup \{P_0(j + o) - o\}$ // Среди всех кандидатов выбираем тот, который ближе всего к маппингам наших соседей $P_{l_{ref}}(j) \leftarrow argmin_{c \in CSet}\displaystyle\sum_o \|(F_{l_{ref}}[S]_c - F_{l_{ref}}[S]_{P_0(j + o)}\|^2$ // Теперь нужно перенести маппинг для $l_{ref}$ на остальные слои for $l \in [1 : L] \setminus \{l_{ref}\}$: for $j \in [1 : M_l]$: if $j \in Resize(Mask, l)$: // Вычисляем позицию $j'$ на слое $l_{ref}$ соответствующую позиции $j$ на слое $l$ $j' \leftarrow ChangeResolution(l, l_{ref}, j)$ // Берём маппинг для позиции $j'$ $q \leftarrow P_{l_{ref}}(j')$ // Переносим позицию $q$ обратно на слой $l$ $P_l(j) \leftarrow ChangeResolution(l_{ref}, l, q)$ return P
При вычислении стилевого маппинга появляется очень много дублирующихся векторов, что даёт не очень хорошие результаты. Поэтому при вычислении матрицы Грама выкинем повторяющиеся векторы. Назовём эту функцию потерь $\mathcal{L}_{s1}$.
Определение: |
$\mathcal{L}_2(I, S, O, P) = \mathcal{L}^{\alpha}_{content}(I, O) + w_{style}\mathcal{L}^{\beta}_{s1}(S, O, P) + w_{hist}\mathcal{L}^{\gamma}_{hist}(S, O) + w_{tv}\mathcal{L}_{tv}(O)$, где $w_{style}$, $w_{hist}$, $w_{tv}$ — веса соответсвующих функций потерь |
Итоговый алгоритм
Теперь осталось запустить две стадии
fun $Harmonization$( $I$, // Входное изображение $Mask$, // Маска $S$ // Стилевое изображение ): // Грубый проход алгоритма. Каждый слой рассматривается отдельно при построении стилевого маппинга. $I' \leftarrow SinglePassHarmonization(I, Mask, S, IndependentMapping, \mathcal{L}_1)$ // Улучшение результата. Стилевой маппинг строится консистентно для всех слоёв. $O \leftarrow SinglePassHarmonization(I', Mask, S, ConsistentMapping, \mathcal{L}_2)$ return $O$
Постобработка
Описанный алгоритм даёт хорошие результаты в целом, но при ближайшем рассмотрении могут быть артефакты. Поэтому сделаем двухступенчатую постобработку (подробное описание есть в оригинальной статье[11]):
- Переведём изображение в CIELAB и применим Guided filter для a и b каналов.
- С помощью алгоритма PatchMatch[13] и того же Guided filter делаем так чтобы все патчи выходного изображения присутсвовали в стилевом (чтобы не было новых объектов или структур)
Подбор гиперпараметров
Возьмём $l_{ref}$ = conv4_1. Выберем следующие веса для слоёв:
Параметр | conv1_1 | conv2_1 | conv3_1 | conv4_1 | conv5_1 |
---|---|---|---|---|---|
$\alpha$ | $0$ | $0$ | $0$ | $1$ | $0$ |
$\beta$ | $0$ | $0$ | $1/3$ | $1/3$ | $1/3$ |
Параметр | conv1_1 | conv2_1 | conv3_1 | conv4_1 | conv5_1 |
---|---|---|---|---|---|
$\alpha$ | $0$ | $0$ | $0$ | $1$ | $0$ |
$\beta$ | $1/4$ | $1/4$ | $1/4$ | $1/4$ | $0$ |
$\gamma$ | $1/2$ | $0$ | $0$ | $1/2$ | $0$ |
Введём гиперпараметр $\tau$ и возьмём $w_{style} = w_{hist} = \tau$, $w_{tv} = \tau\frac{10}{1 + \exp(10^4 * noise(S) - 25)}$, где $noise(S) = med_{i,j}\left\{(O^l_{i, j} - O^l_{i-1, j}))^2 + (O^l_{i, j} - O^l_{i, j-1}))^2\right\}$[14]
Для того чтобы подбирать $\tau$ авторы статьи использовали классификатор стилей изображений. Они взяли VGG-19, обучили её классифицировать 18 различных стилей. Эти стили были разделены на 3 категории с разными $\tau$. Используя Softmax можно интерполировать необходимый $\tau$ по следующей таблице:
Категория стиля | Примеры стилей | $\tau$ |
---|---|---|
Слабый | Барокко, Высокое Возрождение | $1$ |
Средний | Абстрактное Искусство, Постимпрессионизм | $5$ |
Сильный | Кубизм, Экспрессионизм | $10$ |
Примеры
Исходное изображение | Простая вставка | Результат | Постобработка |
---|---|---|---|
Глубокий блендинг
kekkekek
todo: че зачем
Алгоритм глубокого блендинга состоит из двух этапов. На первом этапе на стилевое изображения $S$ бесшовно накладывается входное изображение $I$, получается подготовительное блендинг-изображение $B$. На втором этапе $B$ модифицируется таким образом, чтобы результат по стилю был похож на $S$.
Примеры входных данных:
Стилевое изображение $S$ |
|||
---|---|---|---|
Накладываемое изображение $I$ |
Первый этап
Построение подготовительного изображения начинается с белого шума. Для подсчета $\mathcal{L}_{style}$ и $\mathcal{L}_{content}$ авторами статьи[1] использовалась сеть VGG-19[4], обученная на ImageNet[15].
Для удобства введем вспомогательную функцию применения маски:
// Вставка части изображения $I$, определенной маской $M$, // на изображение $S$ fun $ApplyMask(M, I, S)$: // За $\odot$ обозначим покомпонентное произведение return $I \odot M + S \odot (1 - M)$
Алгоритм первого этапа:
fun $SeamlessBlending$( $I$, // Входное изображение $M$, // Маска $S$ // Стилевое изображение ): // Инициализируем первое приближение белым шумом $Z \leftarrow RandomNoise() $ $B \leftarrow ApplyMask(M, Z, S)$ // Определим суммарную функцию потерь с весами слагаемых $w$ $\mathcal{L}_{total}(Z) \leftarrow w_{grad}\mathcal{L}_{grad}(I, S, B) + w_{cont}\mathcal{L}_{cont}(I, Z) + w_{style}\mathcal{L}_{style}(S, B) + w_{tv}\mathcal{L}_{tv}(B) + w_{hist}\mathcal{L}_{hist}(S, B)$ // С помощью алгоритма L-BFGS ищем изображение $Z$, которое минимизирует $\mathcal{L}_{total}$ $Z \leftarrow Reconstruct(\mathcal{L}_{total}, Z)$ return $ApplyMask(M, Z, S)$
Второй этап
fun $StyleRefinement$( $B$, // Подготовительное блендинг-изображение, результат первого этапа $M$, // Маска $S$ // Стилевое изображение ): $O \leftarrow B$ // Определим суммарную функцию потерь с весами слагаемых $w$ $\mathcal{L}_{total}(Z) \leftarrow w_{cont}\mathcal{L}_{cont}(B, O) + w_{style}\mathcal{L}_{style}(S, O) + w_{tv}\mathcal{L}_{tv}(O) + w_{hist}\mathcal{L}_{hist}(S, O)$ // С помощью алгоритма L-BFGS ищем изображение $O$, которое минимизирует $\mathcal{L}_{total}$ $O \leftarrow Reconstruct(\mathcal{L}_{total}, O)$ return $O$
Примеры
Ссылки
Примечания
- ↑ 1,0 1,1 1,2 Deep Image Blending Lingzhi Zhang, Tarmily Wen, Jianbo Shi (2020)
- ↑ 2,0 2,1 Poisson Image Editing Patrick Perez, Michel Gangnet, Andrew Blake (2003)
- ↑ https://erkaman.github.io/posts/poisson_blending.html Poisson blending для самых маленьких
- ↑ 4,0 4,1 Very Deep Convolutional Networks for Large-Scale Image Recognition Karen Simonyan, Andrew Zisserman (2014)
- ↑ 5,0 5,1 Image Style Transfer Using Convolutional Neural Networks Leon A. Gatys, Alexander S. Ecker, Matthias Bethge (2016)
- ↑ 6,0 6,1 6,2 Здесь используется определение функции потерь, которое отличается от статьи Гатиса, но используется в таком виде в статье про гармонизацию.
- ↑ 7,0 7,1 Stable and Controllable Neural Texture Synthesis and Style Transfer Using Histogram Losses Eric Risser, Pierre Wilmot, Connelly Barnes (2017)
- ↑ https://en.wikipedia.org/wiki/Histogram_matching
- ↑ Understanding Deep Image Representations by Inverting Them Aravindh Mahendran, Andrea Vedaldi (2015)
- ↑ 10,0 10,1 Perceptual Losses for Real-Time Style Transfer and Super-Resolution Justin Johnson, Alexandre Alahi, Li Fei-Fei (2016)
- ↑ 11,0 11,1 11,2 https://arxiv.org/pdf/1804.03189.pdf Fujun Luan, Sylvain Paris, Eli Shechtman, Kavita Bala (2018)
- ↑ Visual Attribute Transfer through Deep Image Analogy Jing Liao, Yuan Yao, Lu Yuan, Gang Hua, Sing Bing Kang (2017)
- ↑ https://www.researchgate.net/profile/Eli_Shechtman/publication/220184392_PatchMatch_A_Randomized_Correspondence_Algorithm_for_Structural_Image_Editing/links/02e7e520897b12bf0f000000.pdf Connelly Barnes, Eli Shechtman, Adam Finkelstein, Dan B Goldman (2009)
- ↑ [https://github.com/luanfujun/deep-painterly-harmonization/blob/a33a9a70366b6baff1cc0291f857b5895b271fc1/neural_paint.lua#L470 код функции $noise$
- ↑ https://image-net.org/papers/imagenet_cvpr09.pdf J. Deng, W. Dong, R. Socher, L.-J. Li, K. Li, and L. FeiFei. Imagenet: A large-scale hierarchical image database