Патч изображение


114

В популярном программном обеспечении для редактирования изображений есть функция, которая исправляет (термин, используемый при обработке изображения, обозначает, что @ mınxomaτ указывает) выбранную область изображения, основываясь на информации вне этого исправления. И это делает неплохую работу, учитывая, что это просто программа. Как человек, вы можете иногда видеть, что что-то не так, но если вы сжимаете глаза или просто смотрите коротко, патч, кажется, заполняет пробел довольно хорошо.

пример популярного программного обеспечения для редактирования изображений

Вызов

Учитывая изображение и маску, которая задает прямоугольную область изображения, должны быть исправлены (также как изображение, или любой другой предпочтительный формат), ваша программа должна попытаться заполнить указанную область исправлением, которое пытается смешаться с остальными изображение. Программа не может использовать информацию исходного изображения, которая была в указанной области.

Вы можете предположить, что патч всегда имеет по крайней мере ширину от сторон и высоту от верха и низа изображения. Это означает, что максимальная площадь патча составляет 1/9 от всего изображения.

Пожалуйста, добавьте краткое описание того, как работает ваш алгоритм.

голосование

Избирателей просят судить, насколько хорошо работают алгоритмы, и голосовать соответственно.

Несколько советов о том, как судить: (Опять же, спасибо @ mınxomaτ за еще несколько критериев.)

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

Критерий достоверности

Для того чтобы отправка была действительной, выходное изображение должно точно соответствовать входному изображению за пределами указанной области.

Прецедент

Слева исходное изображение, справа соответствующая маска:


1
Можем ли мы принять ввод маски в качестве текстовых аргументов (например inpaint.exe left top width height img.jpg)?
mynxomaτ

1
Конечно, формат ввода / вывода не так уж важен, так как это соревнование в популярности, где важна производительность вашего алгоритма .
flawr

24
Это очень практичная задача. Возможно, результаты будут лучше, чем существующие алгоритмы, используемые в GIMP и другом программном обеспечении для редактирования изображений с открытым исходным кодом. Фортуна, слава и слава могут быть вашими!
Спарр

6
@Sparr и, наконец, уродливые водяные знаки можно удалить с загруженных носителей;)
Андрас Дик

2
Встроенные системы в порядке, я сомневаюсь, что они будут очень популярны.
flawr

Ответы:


142

AutoIt , VB

Введение

Это реализация алгоритма удаления объектов с помощью наглядного примера , разработанного А. Криминиси, П. Пересом (Cambridge Microsoft Research Ltd.) и К. Тоямой (Microsoft) [X] . Этот алгоритм предназначен для высокоинформативных изображений (и видеокадров) и призван обеспечить баланс между структурной реконструкцией и органической реконструкцией. Параграфы этого ответа содержат полнотекстовые цитаты из оригинального документа (поскольку он больше не является официально доступным), чтобы сделать этот ответ более автономным.

Алгоритм

Цель : заменить выбранную ( замаскированную ) область (предпочтительно визуально отделенный объект переднего плана) визуально правдоподобными фонами.

В предыдущей работе несколько исследователей рассматривали синтез текстур как способ заполнения больших областей изображения «чистыми» текстурами - повторяющимися двумерными текстурными узорами с умеренной стохастичностью. Это основано на большом количестве исследований по синтезу текстур, которые стремятся воспроизвести текстуру до бесконечности , учитывая небольшой исходный образец чистой текстуры [1] [8] [9] [10] [11] [12] [14] [15] [16] [19] [22] .

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

Image inpainting методы заполнения отверстий в изображениях путем распространения линейных структур ( так называемые изофот в inpainting литературе) в целевой области путем диффузии. Они основаны на дифференциальных уравнениях в частных производных физического теплового потока и убедительно работают как алгоритмы восстановления. Их недостаток в том, что процесс диффузии вносит некоторое размытие, что заметно.

Рис.  2

Заполняемая область, т. Е. Целевая область, обозначена как Ω, а ее контур обозначен как δΩ. Контур эволюционирует внутрь по мере продвижения алгоритма, поэтому мы также называем его «фронтом заполнения». Исходная область Φ, которая остается фиксированной на протяжении всего алгоритма, предоставляет образцы, используемые в процессе заполнения. Теперь мы сосредоточимся на одной итерации алгоритма, чтобы показать, как структура и текстура адекватно обрабатываются при синтезе на основе примеров. Предположим, что квадратный шаблон Ψp ∈ Ω с центром в точке p (рис. 2b) должен быть заполнен. Образец с наилучшим соответствием из области источника получен из патча Ψqˆ ∈ Φ, который наиболее похож на те части, которые уже заполнены в Ψp. В примере на рис. 2б, мы видим, что если liesp лежит на продолжении края изображения, наиболее вероятные наилучшие совпадения будут лежать вдоль одного и того же (или одинаково окрашенного) края (например, Ψq 'и Ψq' 'на рис. 2c). Все, что требуется для распространения изофоты внутрь, - это простая передача паттерна из исходного патча с наилучшим соответствием (рис. 2d). Обратите внимание, что ориентация изофот автоматически сохраняется. На рисунке, несмотря на то, что исходное ребро не ортогонально целевому контуру δΩ, распространяющаяся структура сохранила ту же ориентацию, что и в исходной области.

Детали реализации и алгоритма

Функциональность этой реализации заключена в ActiveX COM DLL, которая отбрасывается из основной программы в виде двоичного файла, а затем вызывается на лету, вызывая inpainter по IID. В этом конкретном случае API написан на VisualBasic и может вызываться с любого языка с поддержкой COM. Следующий раздел кода удаляет двоичный файл:

Func deflate($e=DllStructCreate,$f=@ScriptDir&"\inpaint.dll")
    If FileExists($f) Then Return
    !! BINARY CODE OMITTED FOR SIZE REASONS !!
    $a=$e("byte a[13015]")
    DllCall("Crypt32.dll","bool","CryptStringToBinaryA","str",$_,"int",0,"int",1,"struct*",$a,"int*",13015,"ptr",0,"ptr",0)
    $_=$a.a
    $b=$e('byte a[13015]')
    $b.a=$_
    $c=$e("byte a[14848]")
    DllCall("ntdll.dll","int","RtlDecompressBuffer","int",2,"struct*",$c,"int",14848,"struct*",$b,"int",13015,"int*",0)
    $d=FileOpen(@ScriptDir&"\inpaint.dll",18)
    FileWrite($d,Binary($c.a))
    FileClose($d)
EndFunc

Позже библиотека создается с использованием CLSID и IID:

Local $hInpaintLib = DllOpen("inpaint.dll")
Local $oInpaintLib = ObjCreate("{3D0C8F8D-D246-41D6-BC18-3CF18F283429}", "{2B0D9752-15E8-4B52-9569-F64A0B12FFC5}", $hInpaintLib)

Библиотека принимает дескриптор GDIOBJECT, в частности DIBSection любой битовой карты GDI / + (файлы, потоки и т. Д.). Указанный файл изображения загружается и рисуется на пустом растровом изображении, построенном из Scan0размеров входного изображения.

Входным файлом для этой реализации является любой совместимый с GDI / + формат файла, содержащий данные маскированного изображения. Маска (ы) представляет собой один или более равномерно окрашенные области в входном изображении. Пользователь предоставляет значение цвета RGB для маски, только пиксели с точно таким значением цвета будут сопоставлены. Цвет маскирования по умолчанию - зеленый (0, 255, 0). Все замаскированные области вместе представляют целевую область, Ω, которая будет удалена и заполнена. Исходная область Φ определяется как все изображение за вычетом целевой области (Φ = I − Ω).

Далее, как и для всех примеров синтеза текстур на основе примеров [10] , необходимо указать размер окна шаблона Ψ (он же « радиус сканирования »). Эта реализация обеспечивает размер окна по умолчанию 6² пикселей, но на практике пользователь должен установить, чтобы он был немного больше, чем самый крупный различимый элемент текстуры, или «texel», в исходной области. Дополнительной модификацией исходного алгоритма является определяемый пользователем « размер блока », который определяет область пикселей, подлежащих замене новым однородным цветом. Это увеличивает скорость и снижает качество. Размеры блоков, превышающие 1px, предназначены для использования с чрезвычайно однородными участками (вода, песок, мех и т. Д.), Однако Ψ следует поддерживать на макс. .5x размер блока (который может быть невозможен в зависимости от маски).

Чтобы не останавливать алгоритм на 1-битных изображениях, каждый раз, когда принимается изображение с менее чем 5 цветами, размер окна увеличивается в 10 раз.

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

Шаг 1: Вычисление приоритетов патчей

Порядок заполнения имеет решающее значение для непараметрического синтеза текстуры [1] [6] [10] [13] . До сих пор фаворитом по умолчанию был метод «луковой кожуры», где целевая область синтезируется снаружи внутрь в концентрических слоях. Наш алгоритм выполняет эту задачу с помощью алгоритма наилучшего заполнения, который полностью зависит от значений приоритета, назначенных каждому патчу на фронте заполнения. Вычисление приоритета смещено к тем участкам, которые находятся на продолжении сильных краев и которые окружены пикселями высокой достоверности, эти пиксели являются границей, отмеченной значением -2. Следующий код пересчитывает приоритеты:

For j = m_top To m_bottom: Y = j * m_width: For i = m_left To m_right
    If m_mark(Y + i) = -2 Then m_pri(Y + i) = ComputeConfidence(i, j) * ComputeData(i, j)
Next i: Next j

Для данного участка Ψp с центром в точке p для некоторого p ∈ δΩ (см. Рис. 3) его приоритет P (p) определяется как произведение вычисленной достоверности ( ComputeConfidenceили C (p) ) и члена данных ( ComputeData, или D (p) ), где

, где

| Фр | - площадь Ψp, α - коэффициент нормализации (например, α = 255 для типичного изображения уровня серого), а np - единичный вектор, ортогональный фронту δΩ в точке p. Приоритет вычисляется для каждого патча границы с различными патчами для каждого пикселя на границе целевой области.

Реализовано как

Private Function ComputeConfidence(ByVal i As Long, ByVal j As Long) As Double
    Dim confidence As Double
    Dim X, Y As Long

    For Y = IIf(j - Winsize > 0, j - Winsize, 0) To IIf(j + Winsize < m_height - 1, j + Winsize, m_height - 1): For X = IIf(i - Winsize > 0, i - Winsize, 0) To IIf(i + Winsize < m_width - 1, i + Winsize, m_width - 1)
        confidence = confidence + m_confid(Y * m_width + X)
    Next X: Next Y

    ComputeConfidence = confidence / ((Winsize * 2 + 1) * (Winsize * 2 + 1))
End Function

Private Function ComputeData(ByVal i As Long, ByVal j As Long) As Double
    Dim grad As CPOINT
    Dim temp As CPOINT
    Dim grad_T As CPOINT
    Dim result As Double
    Dim magnitude As Double
    Dim max As Double
    Dim X As Long
    Dim Y As Long
    Dim nn As CPOINT
    Dim Found As Boolean
    Dim Count, num As Long
    Dim neighbor_x(8) As Long
    Dim neighbor_y(8) As Long
    Dim record(8) As Long
    Dim n_x As Long
    Dim n_y As Long
    Dim tempL As Long
    Dim square As Double

    For Y = IIf(j - Winsize > 0, j - Winsize, 0) To IIf(j + Winsize < m_height - 1, j + Winsize, m_height - 1): For X = IIf(i - Winsize > 0, i - Winsize, 0) To IIf(i + Winsize < m_width - 1, i + Winsize, m_width - 1)
        If m_mark(Y * m_width + X) >= 0 Then
            Found = False
            Found = m_mark(Y * m_width + X + 1) < 0 Or m_mark(Y * m_width + X - 1) < 0 Or m_mark((Y + 1) * m_width + X) < 0 Or m_mark((Y - 1) * m_width + X) < 0
            If Found = False Then
                temp.X = IIf(X = 0, m_gray(Y * m_width + X + 1) - m_gray(Y * m_width + X), IIf(X = m_width - 1, m_gray(Y * m_width + X) - m_gray(Y * m_width + X - 1), (m_gray(Y * m_width + X + 1) - m_gray(Y * m_width + X - 1)) / 2#))
                temp.Y = IIf(Y = 0, m_gray((Y + 1) * m_width + X) - m_gray(Y * m_width + X), IIf(Y = m_height - 1, m_gray(Y * m_width + X) - m_gray((Y - 1) * m_width + X), (m_gray((Y + 1) * m_width + X) - m_gray((Y - 1) * m_width + X)) / 2#))
                magnitude = temp.X ^ 2 + temp.Y ^ 2
                If magnitude > max Then
                    grad.X = temp.X
                    grad.Y = temp.Y
                    max = magnitude
                End If
            End If
        End If
    Next X: Next Y

    grad_T.X = grad.Y
    grad_T.Y = -grad.X

    For Y = IIf(j - 1 > 0, j - 1, 0) To IIf(j + 1 < m_height - 1, j + 1, m_height - 1): For X = IIf(i - 1 > 0, i - 1, 0) To IIf(i + 1 < m_width - 1, i + 1, m_width - 1): Count = Count + 1
        If X <> i Or Y <> j Then
            If m_mark(Y * m_width + X) = -2 Then
                num = num + 1
                neighbor_x(num) = X
                neighbor_y(num) = Y
                record(num) = Count
            End If
        End If
    Next X: Next Y

    If num = 0 Or num = 1 Then
        ComputeData = Abs((0.6 * grad_T.X + 0.8 * grad_T.Y) / 255)
    Else
        n_x = neighbor_y(2) - neighbor_y(1)
        n_y = neighbor_x(2) - neighbor_x(1)
        square = CDbl(n_x ^ 2 + n_y ^ 2) ^ 0.5
        ComputeData = Abs((IIf(n_x = 0, 0, n_x / square) * grad_T.X + IIf(n_y = 0, 0, n_y / square) * grad_T.Y) / 255)
    End If
End Function

Доверительный член C (p) можно рассматривать как меру количества надежной информации, окружающей пиксель p. Намерение состоит в том, чтобы сначала заполнить те патчи, у которых больше пикселей уже заполнено, с дополнительным предпочтением отдается пикселям, которые были заполнены рано (или которые никогда не были частью целевой области).

Это автоматически включает предпочтения определенных форм вдоль фронта заливки. Например, пятна, которые включают в себя углы и тонкие усики целевой области, будут иметь тенденцию заполняться первыми, так как они окружены большим количеством пикселей от исходного изображения. Эти патчи предоставляют более надежную информацию для сравнения. И наоборот, пятна на кончиках «полуостровов» заполненных пикселей, выступающих в целевой области, будут иметь тенденцию откладываться до тех пор, пока не будет заполнено больше окружающих пикселей. На приблизительном уровне термин C (p) of (1) приблизительно обеспечивает желательный порядок концентрического заполнения.

По мере заполнения пиксели во внешних слоях целевой области будут иметь тенденцию характеризоваться более высокими значениями достоверности и, следовательно, будут заполняться раньше; пиксели в центре целевой области будут иметь меньшие значения достоверности. Член данных D (p) является функцией силы изофот, ударяющих фронт δΩ на каждой итерации. Этот термин повышает приоритет патча, в который «перетекает» изофот. Этот фактор имеет фундаментальное значение в нашем алгоритме, потому что он стимулирует сначала синтезировать линейные структуры и, следовательно, безопасно распространяться в целевой области. Прерывистые линии имеют тенденцию соединяться, таким образом реализуя «Принцип связности» психологии зрения [7] [17] .

Порядок заполнения зависит от свойств изображения, что приводит к процессу органического синтеза, который устраняет риск появления артефактов с «нарушенной структурой», а также уменьшает блочные артефакты без дорогостоящего этапа обрезки пятен [9] или этапа смешивания с размытием [19]. ] .

Шаг 2: Распространение информации о структуре и структуре

Как только все приоритеты на фронте заливки ( границе ) были вычислены, найден патч Ψpˆ с наивысшим приоритетом. Затем мы заполняем его данными, извлеченными из области источника Φ. Мы распространяем текстуру изображения путем прямой выборки исходного региона. Подобно [10] , мы ищем в области источника тот патч, который наиболее похож на Ψpˆ. Формально,

, где

расстояние d (Ψa, Ψb) между двумя общими участками Ψa и Ψb просто определяется как сумма квадратов разностей (SSD) уже заполненных пикселей в двух участках. На этом этапе не проводится никакого дальнейшего анализа или манипуляций ( особенно без размытия ). Этот расчет выполняется в цикле основного цикла и реализуется следующим образом:

Получение максимального приоритета:

For j = m_top To m_bottom: Jidx = j * m_width: For i = m_left To m_right
    If m_mark(Jidx + i) = -2 And m_pri(Jidx + i) > max_pri Then
        pri_x = i
        pri_y = j
        max_pri = m_pri(Jidx + i)
    End If
Next i: Next j

Нахождение самого похожего патча:

min = 99999999

For j = PatchT To PatchB: Jidx = j * m_width: For i = PatchL To PatchR
    If m_source(Jidx + i) Then
        sum = 0
        For iter_y = -Winsize To Winsize: target_y = pri_y + iter_y
            If target_y > 0 And target_y < m_height Then
                target_y = target_y * m_width: For iter_x = -Winsize To Winsize: target_x = pri_x + iter_x
                    If target_x > 0 And target_x < m_width Then
                        Tidx = target_y + target_x
                        If m_mark(Tidx) >= 0 Then
                            source_x = i + iter_x
                            source_y = j + iter_y
                            Sidx = source_y * m_width + source_x
                            temp_r = m_r(Tidx) - m_r(Sidx)
                            temp_g = m_g(Tidx) - m_g(Sidx)
                            temp_b = m_b(Tidx) - m_b(Sidx)
                            sum = sum + temp_r * temp_r + temp_g * temp_g + temp_b * temp_b
                        End If
                    End If
                Next iter_x
            End If
        Next iter_y

        If sum < min Then: min = sum: patch_x = i: patch_y = j
    End If
Next i: Next j

Шаг 3: Обновление значений достоверности

После того, как патч Ψpˆ заполнен новыми значениями пикселей, достоверность C (p) обновляется в области, ограниченной bypˆ, следующим образом:

Это простое правило обновления позволяет нам измерять относительную достоверность исправлений на фронте заливки без параметров, специфичных для изображения. По мере заполнения значения достоверности уменьшаются, что указывает на то, что мы менее уверены в цветовых значениях пикселей вблизи центра целевой области. Реализовано здесь (вместе со всеми другими необходимыми обновлениями):

x0 = -Winsize
For iter_y = -Winsize To Winsize: For iter_x = -Winsize To Winsize
    x0 = patch_x + iter_x
    y0 = patch_y + iter_y
    x1 = pri_x + iter_x
    y1 = pri_y + iter_y
    X1idx = y1 * m_width + x1
    If m_mark(X1idx) < 0 Then
        X0idx = y0 * m_width + x0
        PicAr1(x1, y1) = m_color(X0idx)
        m_color(X1idx) = m_color(X0idx)
        m_r(X1idx) = m_r(X0idx)
        m_g(X1idx) = m_g(X0idx)
        m_b(X1idx) = m_b(X0idx)
        m_gray(X1idx) = CDbl((m_r(X0idx) * 3735 + m_g(X0idx) * 19267 + m_b(X0idx) * 9765) / 32767)
        m_confid(X1idx) = ComputeConfidence(pri_x, pri_y)
    End If
Next iter_x: Next iter_y

For Y = IIf(pri_y - Winsize - 2 > 0, pri_y - Winsize - 2, 0) To IIf(pri_y + Winsize + 2 < m_height - 1, pri_y + Winsize + 2, m_height - 1): Yidx = Y * m_width: For X = IIf(pri_x - Winsize - 2 > 0, pri_x - Winsize - 2, 0) To IIf(pri_x + Winsize + 2 < m_width - 1, pri_x + Winsize + 2, m_width - 1)
    m_mark(Yidx + X) = IIf(PicAr1(X, Y).rgbRed = MaskRed And PicAr1(X, Y).rgbgreen = MaskGreen And PicAr1(X, Y).rgbBlue = MaskBlue, -1, Source)
Next X: Next Y

For Y = IIf(pri_y - Winsize - 2 > 0, pri_y - Winsize - 2, 0) To IIf(pri_y + Winsize + 2 < m_height - 1, pri_y + Winsize + 2, m_height - 1): Yidx = Y * m_width: For X = IIf(pri_x - Winsize - 2 > 0, pri_x - Winsize - 2, 0) To IIf(pri_x + Winsize + 2 < m_width - 1, pri_x + Winsize + 2, m_width - 1)
    If m_mark(Yidx + X) = -1 Then
        Found = (Y = m_height - 1 Or Y = 0 Or X = 0 Or X = m_width - 1) Or m_mark(Yidx + X - 1) = Source Or m_mark(Yidx + X + 1) = Source Or m_mark((Y - 1) * m_width + X) = Source Or m_mark((Y + 1) * m_width + X) = Source
        If Found Then: Found = False: m_mark(Yidx + X) = -2
    End If
Next X: Next Y

For i = IIf(pri_y - Winsize - 3 > 0, pri_y - Winsize - 3, 0) To IIf(pri_y + Winsize + 3 < m_height - 1, pri_y + Winsize + 3, m_height - 1): Yidx = i * m_width: For j = IIf(pri_x - Winsize - 3 > 0, pri_x - Winsize - 3, 0) To IIf(pri_x + Winsize + 3 < m_width - 1, pri_x + Winsize + 3, m_width - 1)
    If m_mark(Yidx + j) = -2 Then m_pri(Yidx + j) = ComputeConfidence(j, i) * ComputeData(j, i)
Next j: Next i

Полный код

Вот исполняемый код, дополненный исходным кодом библиотек в качестве комментариев.

Код вызывается

inpaint(infile, outfile, blocksize, windowsize, r, g, b)

Примеры включены в виде

;~ inpaint("gothic_in.png", "gothic_out.png")
;~ inpaint("starry_in.png", "starry_out.png")
;~ inpaint("scream_in.png", "scream_out.png")
;~ inpaint("mona_in.png", "mona_out.png")
;~ inpaint("maze_in.png", "maze_out.png")
;~ inpaint("checker_in.png", "checker_out.png")

просто раскомментируйте пример, который вы хотите запустить, используя CTRL+ Q.

Официальные тестовые файлы

Этот алгоритм сделан , чтобы быть отрегулирован для каждого изображения. Поэтому значения по умолчанию (а также маски по умолчанию) полностью неоптимальны. Значения по умолчанию выбираются таким образом, чтобы каждая выборка могла быть обработана за разумное количество времени. Я настоятельно рекомендую играть с масками неправильной формы и с лучшими размерами окон. Нажмите на изображение, чтобы увеличить!

шахматная доска

американская готика

Лабиринт

Мона Лиза

(ужасная маска)

Крик

звездный

Примеры из реального мира

Все они используют пользовательские нарисованные от руки маски.

Если у вас есть другие интересные изображения, которые вы хотели бы видеть включенными, оставьте комментарий.

EBII Улучшения

Существует множество вариантов EBII, созданных разными исследователями. AnkurKumar Patel привлек мое внимание своим сборником работ [24] по различным улучшениям EBII.

В частности, в статье « Улучшенный робастный алгоритм для наложения изображений на основе примеров » [25] упоминаются два улучшения взвешивания значений приоритетов.

Улучшение

Эффективная модификация находится на шаге 1 (см. Выше) алгоритма и расширяет эффект C (p) и D (p) на рейтинг приоритетов для этого пикселя, используя это:

В формуле для C и D , приведенный выше, и , соответственно , коэффициент нормализации (например, α = 255), то изофоте вектор, а единичный вектор , ортогональный к фронту в точке р.

В дальнейшем,

Функция приоритета определяется как весовая сумма регуляризованного доверительного члена C (p) и нового члена данных D (p) . Где α - поправочный коэффициент, удовлетворяющий 0Rp (p), определяется следующим образом:

Где α и β - соответственно веса компонентов доверительной вероятности и членов данных. Обратите внимание, что α + β = 1 .

Объективная оценка

Что действительно интересно, так это то, что эта статья содержит предложенный (и простой!) Метод оценки производительности алгоритмов EBII. Примите это с некоторой долей соли, поскольку этот метод выбран самими авторами статьи для проверки эффективности предложенного дисперсионного подхода и улучшения на нескольких изображениях.

Оценка результата выполняется путем сравнения PSNR (пикового отношения сигнал / шум [26] ) между восстановленным изображением и исходным изображением. Как правило, чем выше значение PSNR, тем больше сходство восстановленного изображения с оригиналом. Уравнение для расчета PSNR выглядит следующим образом:

Это потрясающие 2 (два!) Реальных тестовых изображения, которые они использовали:

Вывод столь же разочаровывающий, как и качество самой бумаги. Это показывает очень небольшое улучшение. Главное здесь - это возможный метод оценки объекта для такой задачи (и других задач по восстановлению изображения):

+-------+---------------+----------+
| Image | EBII Original | Improved |
+-------+---------------+----------+
|     1 |       52.9556 |  53.7890 |
|     2 |       53.9098 |  53.8989 |
+-------+---------------+----------+

Мех.

Исследование должно быть сделано

(Специфично для EBII)

а) Предварительная обработка

Все сводится к принципу «волшебного стирания», согласно которому алгоритм должен «просто работать» для всего. Мое наивное решение для этого - усиление на основе цвета (см. Выше), но есть и лучшие способы. Я имею в виду распознавание среднего геометрического значения всех отслеживаемых текселей для автоматической настройки размера окна и зависимости размера штампа (а также моего улучшения) от разрешения текселей и целого изображения. Исследование должно быть сделано здесь.

б) постобработка

Первоначальные авторы уже проделали большую работу по устранению всех фильтров пост-обработки, которые приходят на ум. Сегодня я попробовал кое-что еще, вдохновленный всегда сверхъестественной Моной Лизой (спасибо подземный монорельс). Если вы возьмете только неокрашенную область и примените новую маску ко всем странным цветным блокам и добавите ее в алгоритм удаления пятен, вы получите почти идеальный результат. Я могу исследовать это когда-нибудь в будущем.


[X] - Удаление объектов путем рисования на основе образцов А. Криминиси, П. Перес, К. Тояма
[1] - М. Ашихмин. Синтез натуральных текстур. В учеб. ACM Symp. «Интерактивная трехмерная графика», стр. 217–226, Research Triangle Park, Северная Каролина, март 2001 г.
[5] - М. Бертальмио, Л. Весе, Г. Сапиро и С. Ошер. Одновременная структура и текстура изображения. появиться в 2002 году
[6] - Р. Борнард, Э. Лекан, Л. Лаборелли и Дж. Х. Шено. Отсутствует коррекция данных в неподвижных изображениях и последовательностях изображений. В ACM Multimedia, Франция, декабрь 2002 г.
[7] - Т. Ф. Чан и Дж. Шен. Нетекстурное окрашивание диффузиями, обусловленными искривлением (CDD). J. Visual Comm. Изображение Реп., 4 (12), 2001.
[8] - JS де Бонет. Процедура многократного отбора проб для анализа и синтеза текстурных изображений. В учеб. ACM Conf. Комп. Графика (SIGGRAPH), том 31, с. 361–368, 1997.
[9] - A. Efros и WT Freeman. Стегание изображений для синтеза и передачи текстур. В учеб. ACM Conf. Комп. Графика (SIGGRAPH), с. 341–346, Юджин Фиуме, август 2001 г.
[10] - А. Эфрос и Т. Люн. Синтез текстуры непараметрической выборкой. В учеб. ICCV, pp. 1033–1038, Kerkyra, Greece, Sep 1999.
[11] - WT Freeman, EC Pasztor и OT Carmichael. Изучение низкого уровня зрения. Int. J. Computer Vision, 40 (1): 25–47, 2000.
[12] - D. Garber. Вычислительные модели для анализа текстур и синтеза текстур. Кандидатская диссертация, Univ. Южной Калифорнии, США, 1981.
[13] - П. Харрисон. Неиерархическая процедура повторного синтеза сложной текстуры. В учеб. Int. Conf. Центральная Европа Comp. Графика, Visua. и комп. Видение, Пльзень, Чешская Республика, февраль 2001 г.
[14] - DJ Heeger и JR Bergen. Анализ / синтез текстур на основе пирамид. В учеб. ACM Conf. Комп. Графика (SIGGRAPH), том 29, с. 229-233, Лос-Анджелес, Калифорния, 1995.
[15] - А. Герцманн, К. Джейкобс, Н. Оливер, Б. Керлесс и Д. Салесин. Изображение аналогии. В учеб. ACM Conf. Комп. Графика (SIGGRAPH), Eugene Fiume, август 2001 г.
[16] - H. Igehy и L. Pereira. Замена изображения посредством синтеза текстур. В учеб. Int. Conf. Обработка изображений, с. III: 186–190, 1997.
[17] - Г. Канижа. Организация в Vision. Прегер, Нью-Йорк, 1979.
[19] - Л. Лян, К. Лю, Ю.-К. Сюй, Б. Го и Х.-Ю. Shum. Синтез текстур в реальном времени с помощью патч-сэмплирования. В ACM Транзакции на графике, 2001.
[22] - Л.-В. Вей и М. Левой. Быстрый синтез текстур с использованием древовидного векторного квантования. В учеб. ACM Conf. Комп. Графика (SIGGRAPH), 2000.
[23] - А. Залесный, В. Феррари, Г. Канен, Л. Ван Гул. Параллельный синтез композитных текстур. В текстурном семинаре 2002 года - (совместно с ECCV02), Копенгаген, Дания, июнь 2002 года.
[24] - AkurKumar Patel, Технологический университет Гуджарата, компьютерные науки и инженерия
[25] - Улучшенный надежный алгоритм для наглядного наложения изображений
[26] - Википедия, отношение пикового сигнала к шуму


30
Это потрясающе . Звездная ночь так хороша. Тем не менее, эта Мона Лиза ...
Ханнес Карппила

8
Сначала позвольте мне сказать «Боже мой, это невероятно». Второе: я уже прокомментировал «Эта Мона Лиза - это какое-то дерьмо SCP» по другому вопросу здесь, но эта сова на самом деле выглядит как нечто, что может появиться в вики SCP.
подземный

3
Могут ли упомянутые вами абзацы быть заключены в блоки цитат?
trichoplax

1
@trichoplax Есть небольшие изменения почти в каждом предложении, они не являются точными кавычками. Рассмотрим описание алгоритма так же, как и в организации. бумага, кроме случаев, когда написано изменение или код. Я не хочу больше загромождать форматирование :)
mınxomaτ

2
Когда я пытался смотреть на что-то очень тщательно во сне, иногда все складывается так, как это.
jimmy23013

45

Matlab

Это простой интерполяционный подход. Идея состоит в том, чтобы сначала отразить то, что находится на каждой стороне патча. Затем эти пиксели зеркального отображения интерполируются по тому, насколько близко они находятся к соответствующему краю:

Сложно было найти хорошие интерполяционные веса. После некоторой игры я придумал рациональную функцию, которая равна нулю на всех гранях, кроме той, на которую мы отразили зеркальное отражение. Затем он преобразуется полиномом третьей степени для некоторого сглаживания:

Этот простой подход на удивление хорошо работает на «естественных» изображениях, но как только вы столкнетесь с острыми краями, игра закончится. В американском готическом примере шипы вилки сена хорошо сочетаются с пиксельной сеткой, что делает его довольно красивым, но в противном случае было бы намного хуже.

Итак, вот результаты:

И, наконец, код:

imgfile= 'filename.png';
maskfile = [imgfile(1:end-4),'_mask.png'];
img = double(imread(imgfile));
mask = rgb2gray(imread(maskfile));
%% read mask
xmin = find(sum(mask,1),1,'first');
xmax = find(sum(mask,1),1,'last');
ymin = find(sum(mask,2),1,'first');
ymax = find(sum(mask,2),1,'last');
%% weight transformation functiosn
third = @(x)-2* x.^3 + 3* x.^2;
f=@(x)third(x);
w=@(x,y)y.*(x-1).*(y-1)./( (x+y).*(x+1-y));

for x=xmin:xmax
    for y=ymin:ymax
        %Left Right Up Down;
        P = [img(y,xmin-(x-xmin)-1,:);img(y,xmax+(xmax-x)+1,:);img(ymin-(y-ymin)-1,x,:);img(ymax+(ymax-y)+1,x,:)];
        % normalize coordinates
        rx = (x-xmin)/(xmax-xmin); 
        ry = (y-ymin)/(ymax-ymin);
        % calculate the weights
        W = [w(rx,ry),w(1-rx,ry),w(ry,rx),w(1-ry,rx)]';
        W = f(W);
        W(isnan(W))=1;
        img(y,x,:) = sum(bsxfun(@times,P,W),1)/sum(W); 
    end
end
imshow(img/255);
imwrite(img/255,[imgfile(1:end-4),'_out.png']);

10
Мона Лиза выводит меня из себя.
Андрас Дик

46
Ḿ̳̜͇͓͠o̢̎̓̀ǹ̰͎̣͙a̤̩̖̞̝ͧ̈ͤͤ ̣̖̠̮̘̹̠̾̇ͣL͉̻̭͌i̛̥͕̱͋͌ş̠͔̏̋̀ạ̫͕͎ͨͮͪ̐͡ͅ
mınxomaτ

8
Эта Мона Лиза - какое-то дерьмо SCP
подземный

1
Клетчатое изображение выглядит действительно круто ИМХО.
ETHproductions

1
Я не удивлюсь, если ты выиграл свой собственный вызов с этим. Это действительно хорошее решение.
Алекс А.

25

Mathematica

Это использует Inpaintфункцию Mathematica . Поскольку Mathematica сама выполняет всю тяжелую работу, это вики-сообщество.

inPaint(ниже) это простая адаптация Inpaint. Для цветных картин / фотографий используется настройка по умолчанию «TextureSynthesis». Если он обнаруживает, что изображение является черно-белым (поскольку данные изображения изображения совпадают с данными изображения двоичной формы изображения), он затем преобразовывает изображение в двоичную форму и применяет исправление «TotalVariation». IfПункт либо относится Binarizeили Identityк изображению. ( IdentityФункция возвращает свой аргумент без изменений.)

inPaint[picture_, mask_] :=  
 If[bw = ImageData@Rasterize[Binarize[picture]] == ImageData[picture], Binarize, Identity]@
  Inpaint[picture, mask, Method -> If[bw, "TotalVariation", "TextureSynthesis"]]

Изображение и маска вводятся в качестве аргументов inPaint . Partitionи Gridтолько для форматирования.

вход

Выходы были исправлены. После этого не было никакой ретуши изображений inPaint.

выход


4
Это может быть совпадением, но я поражен работой лабиринта!
flawr 30.01.16

1
@flawr Я бы добавил что- то вроде этого, чтобы просто поиграть с этим решением;) (Кто знает? Эти черно-белые действительно сбивают с толку.)
Андрас Дик

17
Я не думаю, что это должно быть сообщество вики.
Дэннис

Lawr, да, Inpaintпохоже, ищет симметрию по всему черно-белому изображению. - DavidC 9 часов назад
DavidC

Вы уверены, что черно-белый алгоритм нигде не приносит в жертву коз? Как, черт возьми, угадать центральную структуру изображения, если все это замаскировано?
Андрас Дик

18

Python 2 и PIL

Эта программа смешивает копии областей Север, Юг, Восток и Запад для создания замещающих пикселей, которые используют цвета, текстуры и штриховку из локальной области изображения.

Пример вывода:

Код сначала находит ограничивающую рамку для патча. Затем для каждого пикселя, который должен быть сгенерирован, он вычисляет цвет каждого канала (RGB) на основе взвешенной суммы 4 окружающих областей.

import sys
from PIL import Image

infile, maskfile, outfile = sys.argv[1:4]
imageobj = Image.open(infile)
maskobj = Image.open(maskfile)
image = imageobj.load()
mask = maskobj.load()

assert imageobj.size == maskobj.size
W, H = imageobj.size
pixels = [(x,y) for x in range(W) for y in range(H)]
whitepart = [xy for xy in pixels if sum(mask[xy]) > 230*3]
xmin = min(x for x,y in whitepart)
xmax = max(x for x,y in whitepart)
ymin = min(y for x,y in whitepart)
ymax = max(y for x,y in whitepart)
xspan = xmax - xmin + 1
yspan = ymax - ymin + 1

def mkcolor(channel):
    value = image[(xmin-dx, y)][channel] * 0.5*(xspan - dx)/xspan
    value += image[(xmax+1 + xspan - dx, y)][channel] * 0.5*dx/xspan
    value += image[(x, ymin-dy)][channel] * 0.5*(yspan - dy)/yspan
    value += image[(x, ymax+1 + yspan - dy)][channel] * 0.5*dy/yspan
    return int(value)

for dx in range(xspan):
    for dy in range(yspan):
        x = xmin + dx
        y = ymin + dy
        image[(x, y)] = (mkcolor(0), mkcolor(1), mkcolor(2))

imageobj.save(outfile)

3
Эта Мона Лиза тоже ужасна! Неужели все Моны Лиз в этом испытании обречены быть страшными?
подземный

@undergroundmonorail Полагаю, случайные лица, созданные компьютером, появляются прямо из глубины жуткой долины .
Андрас Дик

Где вы получаете ПИЛ?
Эллиот А.

@ElliotA. Насколько я понимаю, собственно PIL мертв, но это был открытый исходный код, и поэтому он живет под названием «Подушка». Если вы гуглите «подушку питона», вы должны найти ее.
подземный

13

Python 3, PIL

Эта программа использует оператор sobel и на этом основании рисует линии на изображении.

Оператор sobel находит угол каждого ребра, поэтому любые ребра, выталкиваемые в неизвестную область, должны продолжаться.

from PIL import Image, ImageFilter, ImageDraw
import time
im=Image.open('2.png')
im1=Image.open('2 map.png')
a=list(im.getdata())
b=list(im1.getdata())
size=list(im.size)
'''
def dist(a,b):
    d=0
    for x in range(0,3):
        d+=(a[x]-b[x])**2
    return(d**0.5)
#'''
C=[]
d=[]
y=[]
for x in range(0,len(a)):
    if(b[x][0]==255):
        C.append((0,0,0))
    else:
        y=(a[x][0],a[x][1],a[x][2])
        C.append(y)
im1.putdata(C)
k=(-1,0,1,-2,0,2,-1,0,1)
k1=(-1,-2,-1,0,0,0,1,2,1)
ix=im.filter(ImageFilter.Kernel((3,3),k,1,128))
iy=im.filter(ImageFilter.Kernel((3,3),k1,1,128))
ix1=list(ix.getdata())
iy1=list(iy.getdata())
d=[]
im2=Image.new('RGB',size)
draw=ImageDraw.Draw(im2)
c=list(C)
Length=0
for L in range(100,0,-10):
    for x in range(0,size[0]):
        for y in range(0,size[1]):
            n=x+(size[0]*y)
            if(c[n]!=(0,0,0)):
                w=(((iy1[n][0]+iy1[n][1]+iy1[n][2])//3)-128)
                z=(((ix1[n][0]+ix1[n][1]+ix1[n][2])//3)-128)
                Length=(w**2+z**2)**0.5
                if Length==0:
                    w+=1
                    z+=1
                Length=(w**2+z**2)**0.5
                w/=(Length/L)
                z/=(Length/L)
                w=int(w)
                z=int(z)
                draw.line(((x,y,w+x,z+y)),c[n])

d=list(im2.getdata())
S=[]
d1=[]
A=d[0]
for x in range(0,size[0]):
    for y in range(0,size[1]):
        n=y+(size[1]*x)
        nx=y+(size[1]*x)-1
        ny=y+(size[1]*x)-size[0]
        if d[n]==(0,0,0):
            S=[0,0,0]
            for z in range(0,3):
                S[z]=(d[nx][z]+d[ny][z])//2
            #print(S)
            d1.append(tuple(S))
        else:
            d1.append(tuple(d[n]))
d=list(d1)
im2.putdata(d)
#im2=im2.filter(ImageFilter.GaussianBlur(radius=0.5))
d=im2.getdata()
f=[]
#'''
for v in range(0,len(a)):
    if(b[v][0]*b[v][1]*b[v][2]!=0):
        f.append(d[v])
    else:
        f.append(C[v])
#'''
im1.putdata(f)
im1.save('pic.png')

Между тем, вот примеры изображений.

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

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

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

Мона Лиза Мона Лиза Ḿ͠o̾̇a ̾̇Лиза Ḿ͠o̢̎̓̀ǹ̰͎̣aͧ̈ͤ ̣̖̠̮̘̹̠̾̇ͣЛиза Ḿ̳̜͇͓͠o̢̎̓̀ǹ̰͎̣͙a̤̩̖̞̝ͧ̈ͤͤ ̣̖̠̮̘̹̠̾̇ͣL͉̻̭͌i̛̥͕̱͋͌ş̠͔̏̋̀ạ̫͕͎ͨͮͪ̐͡ͅ

введите описание изображения здесь Область на изображении выше гладкая, как кактус

Это не очень хорошо с постоянным цветом.

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

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


1
О, и не могли бы вы добавить черно-белые тесты?
flawr

2
Очень хорошо смотрится на Starry Night.
SuperJedi224

1
Вау, это выглядит невероятно сейчас! Патчи все еще заметны, но отличная новая идея! Мой любимый до сих пор =)
flawr

8
+1 за "Мона Лиза Мона Лиза Ḿ͠oḾ͠a ̾̇Лиза Ḿ͠o̢̎̓̀ǹ̰͎̣aͧ̈ͤ ̣̖̠̮̘̹̠̾̇ͣЛиза Ḿ̳̜͇͓͠o̢̎̓̀ǹ̰͎̣͙a̤̩̖̞̝ͧ̈ͤͤ ̣̖̠̮̘̹̠̾̇ͣL͉̻̭͌i̛̥͕̱͋͌ş̠͔̏̋̀ạ̫͕͎ͨͮͪ̐͡ͅ"
mbomb007

2
Вы выиграли конкурс «Самая страшная Мона Лиза», ИМО. 0_o
DLosc

8

Python 2

Простой скрипт Python, который создает патч, используя значения из пикселей за пределами промежутка. Он берет значения цвета из конца строки и столбца пикселей и вычисляет средневзвешенное значение, используя расстояние от этих пикселей.

Вывод не такой красивый, но это искусство .

img1 img2 img3 img4 img5 img6

А затем код:

IMGN = "6"

IMGFILE = "images/img%s.png" % (IMGN,)
MASKFILE = "images/img%s_mask.png" % (IMGN,)

BLUR = 5


def getp(img,pos):
    return img.get_at(pos)[:3]
def setp(img,pos,color):
    img.set_at(pos, map(int, color))

def pixelavg(L):
    return map(int, [sum([i[q] for i in L])/float(len(L)) for q in [0,1,2]])
def pixelavg_weighted(L, WL):   # note: "inverse" weights. More weight => less weight
    # colors [sum, max]
    color_data = [[0, 0], [0, 0], [0, 0]]
    for color,weight in zip(L, WL):
        for i in [0, 1, 2]: # r,g,b
            color_data[i][0] += inv_w_approx(weight) * color[i]
            color_data[i][1] += inv_w_approx(weight) * 255
    return [255*(float(s)/m) for s,m in color_data]
def inv_w_approx(x):
    return (1.0/(x+1e-10))

import pygame
image = pygame.image.load(IMGFILE)
mask = pygame.image.load(MASKFILE)

size = image.get_size()
assert(size == mask.get_size())

# get square from mask
min_pos = None
max_pos = [0, 0]
for x in range(size[0]):
    for y in range(size[1]):
        if getp(mask, [x, y]) == (255, 255, 255):
            if min_pos == None:
                min_pos = [x, y]
            max_pos = [x, y]
if not min_pos:
    exit("Error: no mask found.")
# patch area info
patch_position = min_pos[:]
patch_size = [max_pos[0]-min_pos[0], max_pos[1]-min_pos[1]]

# remove pixels from orginal image (fill black)
for dx in range(patch_size[0]):
    for dy in range(patch_size[1]):
        setp(image, [patch_position[0]+dx, patch_position[1]+dy], [0, 0, 0])

# create patch
patch = pygame.Surface(patch_size)

# take pixels around the patch
top = [getp(image, [patch_position[0]+dx, patch_position[1]-1]) for dx in range(patch_size[0])]
bottom = [getp(image, [patch_position[0]+dx, patch_position[1]+patch_size[1]+1]) for dx in range(patch_size[0])]
left = [getp(image, [patch_position[0]-1, patch_position[1]+dy]) for dy in range(patch_size[1])]
right = [getp(image, [patch_position[0]+patch_size[0]+1, patch_position[1]+dy]) for dy in range(patch_size[1])]

cpixels = top+left+right+bottom

# set area to average color around it
average = [sum([q[i] for q in cpixels])/float(len(cpixels)) for i in [0, 1, 2]]

for dx in range(patch_size[0]):
    for dy in range(patch_size[1]):
        setp(patch, [dx, dy], average)

# create new pixels
for dx in range(patch_size[0]):
    for dy in range(patch_size[1]):
        setp(patch, [dx, dy], pixelavg_weighted([top[dx], bottom[dx], left[dy], right[dy]], [dy, patch_size[1]-dy, dx, patch_size[0]-dx]))

# apply patch
for dx in range(patch_size[0]):
    for dy in range(patch_size[1]):
        setp(image, [patch_position[0]+dx, patch_position[1]+dy], getp(patch, [dx, dy]))

# blur patch?
for r in range(BLUR):
    for dx in range(patch_size[0]):
        for dy in range(patch_size[1]):
            around = []
            for ddx in [-1,0,1]:
                for ddy in [-1,0,1]:
                    around.append(getp(image, [patch_position[0]+dx+ddx, patch_position[1]+dy+ddy]))
            setp(patch, [dx, dy], pixelavg(around))

    # apply blurred patch
    for dx in range(patch_size[0]):
        for dy in range(patch_size[1]):
            setp(image, [patch_position[0]+dx, patch_position[1]+dy], getp(patch, [dx, dy]))

# save result
pygame.image.save(image, "result.png")

В тот момент, когда вы видите эти горизонтальные / вертикальные полосы, возможно, вы можете улучшить их, включив другие направления!
flawr 30.01.16

Я действительно попробовал это, но я не смог получить хорошие результаты, поэтому я просто решил просто размыть изображение: D
Hannes Karppila

19
Наконец, Мона Лиза, которая не пугает меня до смерти, но вместо этого выглядит как арестованный убийца.
Андрас Дик

6

Mathematica

Inpaint

Просто так получилось, что в Mathematica есть встроенная функция, которая выполняет именно эту задачу, и я имею в виду именно :

Inpaint[image, region]

  • ретуширует части, imageкоторые соответствуют ненулевым элементам в region.

По умолчанию он использует «наилучший метод синтеза текстур с использованием случайной выборки», который дает хорошие результаты на рисунках, но плохие результаты для лабиринта и шахматной доски:

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

Игра с настройками не принесла мне увеличения качества по всем изображениям, поэтому я просто использовал значения по умолчанию (для сохранения байтов - это в codegolf.seконце концов!).


23
« Так получилось, что Mathematica имеет встроенную функцию » ... сюрприз, сюрприз;)
Андрас Дик

Для лабиринта и доски проверки лучше использовать метод "TotalVariation" вместе с Binarize(для устранения серых пятен). Попробуйте это: methods = {"TextureSynthesis", "Diffusion", "FastMarching", "NavierStokes", "TotalVariation"};g[pic_, mask_] := Join[{Labeled[Framed@pic, "Original"]}, Labeled[ Binarize@Inpaint[pic, mask, Method -> #], #] & /@ methods]
DavidC

@DavidC Я пробовал другие методы, но только TextureSynthesisхорошо смотрится на картинах; и я не думаю, что нам разрешено настраивать наши настройки для каждого отдельного теста. (Если бы мы могли, тогда мы могли бы тривиально представить недостающую часть как «сеттинг».)
2012 rcampion

Результаты лабиринта и шахматной доски действительно озадачивают меня. Почему реконструкция недостающей области у Mathematica такая нерегулярная и асимметричная?
Дэвид Чжан

Это автоматически определит, является ли изображение черно-белым, и выполнит соответствующие настройки (двоичные файлы и метод "TotalVariation"). inPaint[picture_, mask_] := If[bw = ImageData@Rasterize[Binarize[picture]] == ImageData[picture], Binarize, Identity]@ Inpaint[picture, mask, Method -> If[bw, "TotalVariation", "TextureSynthesis"]]
DavidC

5

python3

Этот ответ реализует идею в статье «Deep Image Prior» Ульянова и соавт. (CVPR 2018) В этой статье они исследовали идею о том, что хорошо спроектированные нейронные сети для обработки изображений тесно отражают нашу идею о том, как должно выглядеть естественное изображение («предыдущее» распределение).

Они предложили метод, который можно использовать для рисования, а также для удаления шума и артефактов, который просто использует данное изображение без обучения другим данным. Реальная концепция довольно проста: сеть обучается выводить желаемое изображение (для некоторого фиксированного случайного шума в качестве входных данных), штрафуя только ошибки за пределами заданной маски. Если вы хотите убрать шум, вам просто не нужно ничего маскировать, просто остановитесь на ранней стадии тренировки.

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

Я реализовал это, используя популярную архитектуру U-Net от jaxony на github . Код для обучения и обработки изображений можно найти ниже.

Тренировка

Это визуализация тренировочного процесса. Каждый кадр - это состояние определенного количества итераций:

Примеры

Код

Обратите внимание, что на процессоре для запуска одного изображения может потребоваться несколько часов, в то время как для хорошего gpu с поддержкой cuda может потребоваться намного меньше времени.

import torch
import numpy as np
unet = __import__('unet-pytorch')
import PIL.ImageOps
#specify device (cpu/cuda)
device = "cpu"
#specify file and size
file = 'mona'
size = 512 #pad to this size (no smaller than original image), must be divisible by 2^5
img_pil = PIL.Image.open(file +'.png').convert('RGB')
mask_pil = PIL.Image.open(file +'-mask.png').convert('RGB')

net = unet.UNet(num_classes=3, in_channels=32, depth=6, start_filts=64).to(device)
h,w = img_pil.size
pad = (0, 0, size - h, size - w)
img = PIL.ImageOps.expand(img_pil, border=pad)
img = torch.Tensor(np.array(img).transpose([2, 0, 1])[None, :, :, :].astype(np.double)).to(device)
mask = PIL.ImageOps.expand(mask_pil, border=pad)
mask = torch.Tensor((np.array(mask)==0).transpose([2, 0, 1])[None, 0:3, :, :].astype(np.double)).to(device)
mean = img.mean()
std = img.std()
img = (img - mean)/std
optimizer = torch.optim.Adam(net.parameters(), lr=0.0001)
criterion = torch.nn.MSELoss()
input = torch.rand((1, 32, size, size)).to(device)
for it in range(5000):
    if it == 1000:
        optimizer.param_groups[0]['lr'] = 0.00003
    out = net(input)
    loss = criterion(out * mask, img * mask)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
out = out.detach().cpu().numpy()[0].transpose([1,2,0])*std.item() + mean.item()
out = np.clip(out, 0, 255).astype(np.uint8)[0:w, 0:h, :]
mask_np = (np.array(mask_pil) > 0).astype(np.uint8)
img_np = np.array(img_pil)
inpaint = (img_np * (1-mask_np) + mask_np * out).astype(np.uint8)
PIL.Image.fromarray(inpaint).save('./{}_inpainted.png'.format(file))

Deep Image Prior использует что-то отличное от U-Nets? похоже, что они получат лучшие результаты
только ASCII

Кроме того, вы пробовали использовать код Deep Image Prior
только ASCII

@ ASCII-only В статье утверждается, что они в основном используют U-Net, но я не могу найти точные параметры, которые они использовали. Они могли бы использовать сеть с большей емкостью. У меня был только компьютер с очень ограниченным количеством энергии. Поэтому мне пришлось выбирать параметры, которые все еще вписываются в память, и это не заняло слишком много времени для обучения. Я не уверен, сколько именно времени это заняло, но на компьютере, который я использовал (только с процессором), эти изображения занимают несколько дней. (Если у вас есть запасной графический процессор с поддержкой cuda, дайте мне знать :)
flawr

Я также подозреваю, что из-за дизайна сети наличие прямоугольных масок также не является идеальным (и меньшие маски, вероятно, также будут выглядеть лучше), если сравнить, например, первые несколько изображений с двумя последними (в которых не используются прямоугольные маски) ,
flawr

4

Python с OpenCV

В OpenCV есть функция inpaint. Используются два типа рисования, я буду использовать метод быстрого марширования. Согласно документации, алгоритм работает так:

Считайте область на изображении неокрашенной. Алгоритм начинается с границы этой области и идет внутрь области, постепенно заполняя все на границе. Требуется небольшая окрестность вокруг пиксела в окрестности, чтобы быть окрашенной. Этот пиксель заменяется нормализованной взвешенной суммой всех известных пикселей в окрестности. Выбор веса является важным вопросом. Больше веса дано тем пикселям, которые лежат близко к точке, около нормали границы, и тем, которые лежат на контурах границы. Как только пиксель неокрашен, он перемещается к следующему ближайшему пикселю, используя метод быстрого марширования. FMM гарантирует, что эти пиксели рядом с известными пикселями будут сначала окрашены, так что он просто работает как эвристическая операция вручную.

Вот код *:

import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('gothic.jpg')
b,g,r = cv2.split(img)
img2 = cv2.merge([r,g,b])
mask = cv2.imread('mask.jpg',0)
dst = cv2.inpaint(img2,mask,3,cv2.INPAINT_TELEA)
(h, w) = dst.shape[:2]
center = (w / 2, h / 2)
# rotate the image by 180 degrees
M = cv2.getRotationMatrix2D(center, 180, 1.0)
rotated = cv2.warpAffine(dst, M, (w, h))
plt.imshow(rotated)

Обратите внимание, как я конвертирую BGR в RGB для построения графиков. Кроме того, я вращаю его. Вот результаты:

готика

Звездная ночь орать еще одна жуткая Мона Лиза!

Мона Лиза возвращается!

линия 1

шашка

Как видите, я не лучший из двух цветовых.


Мона Лиза получила фейслифтинг
Конор О'Брайен

3

Ява

Подход усреднения цвета. Возможно, может быть улучшено.

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Scanner;

import javax.imageio.ImageIO;


public class ImagePatcher{
    public static void main(String[]args) throws Exception{
        Scanner in=new Scanner(System.in);
        int white=Color.WHITE.getRGB();
        int black=Color.BLACK.getRGB();
        BufferedImage image=ImageIO.read(new File(in.nextLine())),mask=ImageIO.read(new File(in.nextLine()));
        assert(image.getWidth()==mask.getWidth()&&image.getHeight()==mask.getHeight());
        boolean bool=true;
        while(bool){
            bool=false;
        for(int x=0;x<image.getWidth();x+=2){
            for(int y=0;y<image.getHeight();y+=2){
                if(mask.getRGB(x,y)!=white)continue;
                int r=0,g=0,b=0,n=0;
                for(int dx=-1;dx<=1;dx++){
                    if(x+dx<0)continue;
                    if(x+dx>=image.getWidth())continue;
                    for(int dy=-1;dy<=1;dy++){
                        if(y+dy<0)continue;
                        if(y+dy>=image.getHeight())continue;
                        if(mask.getRGB(x+dx,y+dy)==white)continue;
                        Color c=new Color(image.getRGB(x+dx,y+dy));
                        r+=c.getRed();
                        g+=c.getGreen();
                        b+=c.getBlue();
                        n++;
                    }
                }
                if(n==0){bool=true;continue;}
                Color c=n>0?new Color(r/n,g/n,b/n):new Color(100,100,100);
                image.setRGB(x,y,c.getRGB());
                mask.setRGB(x, y, black);
            }           
        }
        for(int x=0;x<image.getWidth();x+=2){
            for(int y=1;y<image.getHeight();y+=2){
                if(mask.getRGB(x,y)!=white)continue;
                int r=0,g=0,b=0,n=0;
                for(int dx=-1;dx<=1;dx++){
                    if(x+dx<0)continue;
                    if(x+dx>=image.getWidth())continue;
                    for(int dy=-1;dy<=1;dy++){
                        if(y+dy<0)continue;
                        if(y+dy>=image.getHeight())continue;
                        if(mask.getRGB(x+dx,y+dy)==white)continue;
                        Color c=new Color(image.getRGB(x+dx,y+dy));
                        r+=c.getRed();
                        g+=c.getGreen();
                        b+=c.getBlue();
                        n++;
                    }
                }
                if(n==0){bool=true;continue;}
                Color c=n>0?new Color(r/n,g/n,b/n):new Color(100,100,100);
                image.setRGB(x,y,c.getRGB());
                mask.setRGB(x, y, black);
            }
        }
        for(int x=1;x<image.getWidth();x+=2){
            for(int y=0;y<image.getHeight();y+=2){
                if(mask.getRGB(x,y)!=white)continue;
                int r=0,g=0,b=0,n=0;
                for(int dx=-1;dx<=1;dx++){
                    if(x+dx<0)continue;
                    if(x+dx>=image.getWidth())continue;
                    for(int dy=-1;dy<=1;dy++){
                        if(y+dy<0)continue;
                        if(y+dy>=image.getHeight())continue;
                        if(mask.getRGB(x+dx,y+dy)==white)continue;
                        Color c=new Color(image.getRGB(x+dx,y+dy));
                        r+=c.getRed();
                        g+=c.getGreen();
                        b+=c.getBlue();
                        n++;
                    }
                }
                if(n==0){bool=true;continue;}
                Color c=n>0?new Color(r/n,g/n,b/n):new Color(100,100,100);
                image.setRGB(x,y,c.getRGB());
                mask.setRGB(x, y, black);
            }           
        }
        for(int x=1;x<image.getWidth();x+=2){
            for(int y=1;y<image.getHeight();y+=2){
                if(mask.getRGB(x,y)!=white)continue;
                int r=0,g=0,b=0,n=0;
                for(int dx=-1;dx<=1;dx++){
                    if(x+dx<0)continue;
                    if(x+dx>=image.getWidth())continue;
                    for(int dy=-1;dy<=1;dy++){
                        if(y+dy<0)continue;
                        if(y+dy>=image.getHeight())continue;
                        if(mask.getRGB(x+dx,y+dy)==white)continue;
                        Color c=new Color(image.getRGB(x+dx,y+dy));
                        r+=c.getRed();
                        g+=c.getGreen();
                        b+=c.getBlue();
                        n++;
                    }
                }
                if(n==0){bool=true;continue;}
                Color c=n>0?new Color(r/n,g/n,b/n):new Color(100,100,100);
                image.setRGB(x,y,c.getRGB());
                mask.setRGB(x, y, black);
            }
        }
        };
        ImageIO.write(image, "png", new File("output.png"));
    }
}

Результаты:

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


2
Почему вы всегда получаете линии под этим конкретным углом? Верхние левые углы, кажется, соответствуют относительно хорошо, в то время как нижняя правая часть не совпадает вообще.
flawr

Я думаю, что это связано с тем, как я перебираю регион. Я вероятно изменю это в конечном счете.
SuperJedi224

Похоже, что есть трапеции.
ericw31415

@ ericw31415 Это артефакт порядка итераций.
SuperJedi224
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.