Блендинг изображений — различия между версиями
Wafemand (обсуждение | вклад) м (→Итоговый алгоритм) |
Wafemand (обсуждение | вклад) (перенос таблицы) |
||
Строка 239: | Строка 239: | ||
'''return''' $O$ | '''return''' $O$ | ||
</font> | </font> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
===Постобработка=== | ===Постобработка=== | ||
Строка 356: | Строка 334: | ||
| $10$ | | $10$ | ||
|} | |} | ||
+ | |||
+ | ===Примеры=== | ||
+ | |||
+ | {| | ||
+ | ! Исходное изображение | ||
+ | ! Простая вставка | ||
+ | ! Результат | ||
+ | ! Постобработка | ||
+ | |- | ||
+ | | [[Файл:5_target.jpg|300px]] | ||
+ | | [[Файл:5_naive.jpg|300px]] | ||
+ | | [[Файл:5_final_res.png|300px]] | ||
+ | | [[Файл:5_final_res2.png|300px]] | ||
+ | |- | ||
+ | | [[Файл:6_target.jpg|300px]] | ||
+ | | [[Файл:6_naive.jpg|300px]] | ||
+ | | [[Файл:6_final_res.png|300px]] | ||
+ | | [[Файл:6_final_res2.png|300px]] | ||
+ | |- | ||
+ | | [[Файл:10_target.jpg|300px]] | ||
+ | | [[Файл:10_naive.jpg|300px]] | ||
+ | | [[Файл:10_final_res.png|300px]] | ||
+ | | [[Файл:10_final_res2.png|300px]] | ||
+ | |} | ||
+ | |||
==Глубокий блендинг== | ==Глубокий блендинг== |
Версия 23:13, 7 января 2021
Определение: |
Гармонизация изображений (англ. image harmonization) — метод, позволяющий наложить часть одного изображения поверх другого таким образом, чтобы композиция изображений выглядела естественно, без швов на границах вставки и с соответсвующими цветами и текстурами [1]. |
- картинка (можно вставить картинку с дайвером, если сможем её обработать гармонизатором)*
Определение: |
Блендинг изображений (англ. image blending) — метод, позволяющий вставить часть одного изображения в другое таким образом, чтобы композиция изображений выглядела естественно, без швов на границах вставки и соответсвующими цветами и текстурами. В отличие от гармонизации, блендинг сам определяет какие пиксели фонового изображения нужно заменить.[1] |
- картинка с дайвером (там видно, что пузырики с фонового изображения остались поверх дайвера)*
Содержание
Блендинг Пуассона
todo Note: Блендинг Пуассона на самом деле является гармонизацией, так как требует маску заменяемых пикселей. Почему-то в статьях его называют блендингом (Poisson blending), хотя оригинальная статья называлась Poisson Image Editing[2]
Простая вставка одного изображения поверх другого нередко влечет заметный перепад яркости на границе вставки. Метод Пуассона заключается в сглаживании этого перепада с целью сделать дефект менее заметным, используя градиент вставляемого изображения и значения пикселей фонового изображения на границе вставки.
todo Note: Для RGB изображений задача минимизации решается для каждого цветового канала отдельно.
Давайте обозначим за $A$ изображение, которое служит фоном, а за $B$ — изображение, вставляемое поверх $A$.
Пусть $p$ — координаты пикселя двухмерного изображения (т.е. $(x, y)$). $A_p$ — значения пикселя фонового изображение, $B_p$ — значение пикселя вставляемого изображения. Пусть $\Omega$ — множество координат $p$, на которых определено вставляемое изображение $B$. $\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} = B_p - B_q$
Обозначим результат блендинга за $O$. Для того чтобы найти значение пикселей в месте наложения $B$, решаем задачу минимизации:
$\underset{f_p,\; p \in \Omega}{\mathrm{min}}\; \underset{p, q \in \Omega}{\sum}\; (O_p - O_q - v_{pq})^2$, где $O_p = A_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} A_q + \underset{q \in N_p}{\sum} v_{pq}$.
Решаем систему уравнений и получаем значения $O_p$ для $p \in \Omega$.
todo: Поскольку система уравнений sparse symmetric positive-defined, можно использовать следующие итеративные алгоритмы: Gauss-Seidel, V-cycle multigrid.
Заметим, что метод Пуассона сдвигает цвета накладываемого изображения и сохраняет свойства градиента (прям всегда? нужно подумоть), туду
Poisson blending для самых маленьких
https://erkaman.github.io/posts/poisson_blending.html
Another thing that we wish to remark is that even though poisson blending shifts the color of the source image, it still preserves the features of it. In the original source image, f4 is smaller than f3, f5 is greater than f4, and so on, and this also applies to our recovered image. This information was encoded by the gradients of the source image. However, it is also important to realize that poisson blending does not exactly preserve the gradients. In the recovered image, the gradient f3,4 assumes the value 7−4=3, but it was 26−22=4 in the original source image. In the previous section, the gradients of the recovered image were identical to the gradients of the original image. But with poisson blending, the gradients of a completely different image are pasted into another image, and the result of this is that the solver is not always able to recover an image whose gradients exactly match the specified gradients. But the solver tries to find an image whose gradients match as close as possible, and in practice, poisson blending yields good results, which we shall show examples of in the following section.
Трансфер стиля
Прежде чем переходить к гармонизации картин, рассмотрим задачу трансфера стиля с изображения $S$ на изображение $I$. Для этого используются выходы скрытых слоёв свёрточной нейронной сети VGG-19[3].
Основная идея генерации изображения — решение оптимизационной задачи $\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[4].
Определение: |
$\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$-го слоя в функцию потерь[5]. |
Определение: |
$\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$-го слоя в функцию потерь[5]. |
Итоговой функцией потерь будет $\mathcal{L}_{Gatys} = \mathcal{L}_{content}(I, O) + w_{style}\mathcal{L}_{style}(I, O)$[5]. Вес $w_{style}$, векторы $\alpha$ и $\beta$ являются, в некотором смысле, гиперпараметрами алгоритма, которые нужно подбирать.
Авторы статьи показывают, что в качестве начальной инициализации можно брать изображение $I$, изображение $S$ или белый шум — алгоритм даёт похожие результаты в этих случаях.
Histogram Loss
Авторы другой статьи[6] показывают, что результаты, полученные с помощью $\mathcal{L}_{Gatys}$ нестабильны и предложили другую функцию потерь, основанную на сопоставлении гистограмм.
Определение: |
Сопоставление гистограмм (англ. Histogram matching) — метод обработки изображения, после которого гистограмма изображения совпадает с целевой гистограммой[7]. |
Определение: |
Пусть $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
Также добавим ещё одну функцию потерь, которая должна делать картинку более гладкой[8][9].
Определение: |
$\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). |
Глубокая гармонизация картин[10]
Для того чтобы вставить изображение в картину или рисунок нужно не только сделать бесшовный переход и изменить цвета, но ещё и изменить текстуру вставляемого изображения, например сымитировать мазки кистью. Используем для этого комбинацию подходов из других статей[4][9][6].
Алгоритм будет состоять из двух проходов. Первый проход будет делать грубую гармонизацию, а второй — более тщательную. Отличаться они будут стилевым маппингом и функциями потерь.
Определение: |
Стилевым маппингом мы будем называть отображение $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$
Постобработка
Описанный алгоритм даёт хорошие результаты в целом, но при ближайшем рассмотрении могут быть артефакты. Поэтому сделаем двухступенчатую постобработку (подробное описание есть в оригинальной статье[10]):
- Переведём изображение в CIELAB и применим Guided filter для a и b каналов.
- С помощью алгоритма PatchMatch[12] и того же 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\}$[13]
Для того чтобы подбирать $\tau$ авторы статьи использовали классификатор стилей изображений. Они взяли VGG-19, обучили её классифицировать 18 различных стилей. Эти стили были разделены на 3 категории с разными $\tau$. Используя Softmax можно интерполировать необходимый $\tau$ по следующей таблице:
Категория стиля | Примеры стилей | $\tau$ |
---|---|---|
Слабый | Барокко, Высокое Возрождение | $1$ |
Средний | Абстрактное Искусство, Постимпрессионизм | $5$ |
Сильный | Кубизм, Экспрессионизм | $10$ |
Примеры
Исходное изображение | Простая вставка | Результат | Постобработка |
---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Глубокий блендинг
kekkekek
Ссылки
Примечания
- ↑ 1,0 1,1 Deep Image Blending Lingzhi Zhang, Tarmily Wen, Jianbo Shi (2020)
- ↑ Poisson Image Editing Patrick Perez, Michel Gangnet, Andrew Blake (2003)
- ↑ Very Deep Convolutional Networks for Large-Scale Image Recognition Karen Simonyan, Andrew Zisserman (2014)
- ↑ 4,0 4,1 Image Style Transfer Using Convolutional Neural Networks Leon A. Gatys, Alexander S. Ecker, Matthias Bethge (2016)
- ↑ 5,0 5,1 5,2 Здесь используется определение функции потерь, которое отличается от статьи Гатиса, но используется в таком виде в статье про гармонизацию.
- ↑ 6,0 6,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)
- ↑ 9,0 9,1 Perceptual Losses for Real-Time Style Transfer and Super-Resolution Justin Johnson, Alexandre Alahi, Li Fei-Fei (2016)
- ↑ 10,0 10,1 10,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$