Виправити зображення


114

У популярному програмному забезпеченні для редагування зображень є функція, що патчі (Термін, який використовується в обробці зображень, позначає малюнок, як вказував @ mınxomaτ.) Виділену область зображення на основі інформації поза цим патчем. І це робить непогану роботу, вважаючи це просто програмою. Як людина, іноді можна побачити, що щось не так, але якщо ти стискаєш очі або просто коротко поглянеш, патч, здається, заповнив прогалину досить добре.

Наприклад, популярне програмне забезпечення для редагування зображень

Виклик

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

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

Додайте короткий опис того, як працює ваш алгоритм.

Голосування

Виборців просять судити про те, наскільки ефективно працюють алгоритми, і голосувати відповідно.

Деякі поради щодо того, як судити: (Знову дякую @ mınxomaτ за ще кілька критеріїв.)

  • Якщо ви косаєте очима, і малюнок виглядає добре?
  • Ви можете точно сказати, де патч?
  • Наскільки добре продовжуються структури та текстури з фонового зображення та навколишньої області?
  • Скільки пікселів помилкових кольорів містить відредагована область?
  • Чи є в тій місцевості рівномірно забарвлені краплі / блоки, які, здається, не належать до них?
  • Чи змінена область редагування різко змінює колір / контраст чи яскравість порівняно з рештою зображення?

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

Щоб подання було дійсним, вихідне зображення має точно відповідати вхідному зображенню поза вказаною областю.

Тестовий випадок

Зліва вихідне зображення, праворуч відповідна маска:


1
Чи можемо ми прийняти введення маски як текстові аргументи (напр. inpaint.exe left top width height img.jpg)?
mınxomaτ

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

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

6
@Sparr та нарешті некрасиві водяні знаки можна видалити із завантажених медіа;)
Андрас Дек,

2
Вбудовані цілком нормально, але я сумніваюся, що вони будуть дуже популярними.
недолік

Відповіді:


142

AutoIt , VB

Вступ

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

Алгоритм

Мета : Замініть вибране ( замасковане) ) зону (бажано візуально відокремлений передній план) на візуально правдоподібні фони.

У попередній роботі кілька дослідників розглядали синтез текстур як спосіб заповнення великих областей зображення "чистими" текстурами - повторюваними двовимірними текстурними візерунками із помірною стохастичністю. Це ґрунтується на великому дослідженні синтезу текстур, яке прагне повторити текстуру ad infinitum , надаючи невеликий зразок чистої текстури [1] [8] [9] [10] [11] [12] [14] [15] [16] [19] [22] .

Настільки ефективні, як ці прийоми у тиражуванні послідовної текстури, у них виникають труднощі із заповненням отворів на фотографіях реальних сцен, які часто складаються з лінійних структур та складених текстур - множини текстур, що взаємодіють просторово [23] . Основна проблема полягає в тому, що межі між областями зображення є складним продуктом взаємних впливів між різними текстурами. На відміну від двовимірної природи чистих текстур, ці межі утворюють те, що можна вважати більш одновимірними або лінійними структурами зображення.

Image inpainting методи заповнення отворів в зображеннях шляхом поширення лінійних структур ( так звані ізофот в inpainting літературі) в цільової області шляхом дифузії. Вони надихаються частковими диференціальними рівняннями фізичного теплового потоку і переконливо працюють як алгоритми відновлення. Їх недолік полягає в тому, що дифузійний процес вносить деяку розмитість, що помітно.

рис.  2

Область, яку потрібно заповнити, тобто цільова область позначається Ω, а її контур позначається δΩ. Контур розвивається всередину по мірі просування алгоритму, і тому ми також називаємо його "фронтом заливки". Область джерела Φ, яка залишається фіксованою у всьому алгоритмі, забезпечує вибірки, що використовуються в процесі заповнення. Тепер ми зосередимось на одній ітерації алгоритму, щоб показати, як структуру та текстуру адекватно обробляють шляхом зразкового синтезу. Припустимо, що квадратний шаблон Ψp ∈ Ω із центром у точці p (рис. 2b) повинен бути заповнений. Зразок найкращого відповідності з вихідного регіону походить від патча Ψqˆ ∈ Φ, який найбільш схожий на ті частини, які вже заповнені Ψp. У прикладі на рис. 2b, ми бачимо, що якщо Ψp лежить на продовженні краю зображення, найімовірніші кращі сірники будуть лежати вздовж одного (або подібного кольору) краю (наприклад, Ψq 'і Ψq' 'на рис. 2в). Все, що потрібно для поширення ізофота всередину, - це просте перенесення шаблону з найвищого відповідного джерела (рис. 2г). Зауважте, що орієнтація ізофота зберігається автоматично. На малюнку, незважаючи на те, що вихідний край не є ортогональним до цільового контуру δΩ, розповсюджена структура зберігала таку ж орієнтацію, що і в області джерела.

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

Функціональність цієї реалізації інкапсульована в ActiveX COM DLL, який викидається з хост-програми у вигляді двійкового і потім викликається на льоту, викликаючи інпаінтером за допомогою 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 ² пікселів, але на практиці вимагає від користувача встановити його трохи більше, ніж найбільший розрізний елемент текстури, або „тексель”, у регіоні джерела. Додатковою модифікацією вихідного алгоритму є визначений користувачем " розмір блоку ", який визначає площу пікселів, яку слід замінити новим рівномірним кольором. Це збільшує швидкість і знижує якість. Розмір блоків більший за 1 пікс призначений для використання в надзвичайно рівномірних місцях (вода, пісок, хутро тощо), однак Ψ слід тримати не більше. .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 | - площа Ψ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) (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) оновлюється в області, розділеної Ψpˆ, наступним чином:

Це просте правило оновлення дозволяє виміряти відносну довіру виправлень на передній частині заливки, без конкретних параметрів зображення. По мірі заповнення значення довіри падають, що вказує на те, що ми менш впевнені у значеннях кольорів пікселів поблизу центру цільової області. Тут реалізовано (разом з усіма іншими необхідними оновленнями):

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, тим більше схожість відремонтованого зображення з оригіналом. Рівняння для обчислення ПСНР наступне:

Це приголомшливі 2 (два!) Тестові зображення реального світу, якими вони користувалися:

Висновок такий же невтішний, як і якість самого паперу. Це показує дуже незначне поліпшення. Тут головне - можливий метод оцінювання об'єктів для подібного виклику (та інших виправлень, що ремонтують зображення):

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

Мех.

Дослідження потрібно зробити

(Характерно для EBII)

а) Попередня обробка

Все зводиться до принципу "Чарівне стирання", що алгоритм повинен "просто працювати" для всього. Моє наївне рішення для цього - кольорове посилення (див. Вище), але є кращі способи. Я думаю розпізнати геометричне середнє значення всіх простежуваних текстилів, щоб автоматично відрегулювати розмір вікна та зробити розмір штампу (також моє поліпшення) залежним від текстових та цілого зображення. Тут потрібно провести дослідження.

б) Післяобробка

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


[X] - Вилучення об’єкта шляхом малювання на основі прикладу А. Крімінісі, П. Переза, К.
Тоями [1] - М. Ашіхмін. Синтезування природних текстур. У Зб. ACM Symp. Інтерактивна 3D-графіка, с. 217–226, Науково-дослідний трикутний парк, штат Північна Кароліна, березень 2001 року
. Одночасна розфарбування зображення та текстури зображення. з'явитися, 2002
[6] - Р. Борнард, Е. Лекан, Л. Лабореллі та Дж. Хенот. Виправлення відсутніх даних у нерухомих зображеннях та послідовностях зображень. У ACM Multimedia, Франція, грудень 2002 р.
[7] - Т. Ф. Чан та Дж. Шен. Нефарбування фарбування дифузіями, керованими кривизною (CDD). J. Visual Comm. Зображення відп., 4 (12), 2001.
[8] - Ж.С. де Боне. Процедура відбору мультирезолюцій для аналізу та синтезу текстурних зображень. У Зб. Конф. Конф. Склад. Графіка (SIGGRAPH), том 31, с. 361–368, 1997.
[9] - А. Ефрос і В. Т. Фріман. Квілінг зображення для синтезу та передачі текстури. У Зб. Конф. Конф. Склад. Графіка (SIGGRAPH), с. 341–346, Євген Фіуме, серпень 2001 р.
[10] - А. Ефрос та Т. Леунг. Синтез текстури шляхом непараметричного відбору проб. У Зб. ICCV, стор. 1033–1038, Керкіра, Греція, вересень 1999.
[11] - WT Freeman, EC Pasztor та OT Carmichael. Навчання зору низького рівня. Int. J. Computer Vision, 40 (1): 25–47, 2000.
[12] - Д. Гарбер. Обчислювальні моделі для аналізу текстури та синтезу текстур. Кандидатська робота, ун-т. Південної Каліфорнії, США, 1981 рік.
[13] - П. Гаррісон. Неієрархічна процедура повторного синтезу складної текстури. У Зб. Int. Конф. Центральна Європа Comp. Графіка, Visua. і склад. Vision, Plzen, Чехія, лютий 2001.
[14] - DJ Heeger та JR Bergen. Аналіз / синтез текстур на основі пірамід. У Зб. Конф. Конф. Склад. Графіка (SIGGRAPH), том 29, с. 229-233, Лос-Анджелес, Каліфорнія, 1995.
[15] - А. Герцман, К. Джейкобс, Н. Олівер, Б. Керлесс, Д. Салесін. Аналогії зображень. У Зб. Конф. Конф. Склад. Графіка (SIGGRAPH), Євген Фіуме, серпень 2001 р.
[16] - Х. Ігегі та Л. Перейра. Заміна зображення шляхом синтезу текстури. У Зб. Int. Конф. Обработка зображень, С. III: 186–190, 1997.
[17] - Г. Канізса. Організація у баченні. Праегер, Нью-Йорк, 1979.
[19] - Л. Лян, К. Лю, Ю.-К. Сю, Б. Го та Х.-Й. Шум. Синтез текстури в режимі реального часу шляхом вибірки на основі патча. In ACM Transaction on Graphics, 2001.
[22] - L.-W. Вей та М. Левой. Швидкий синтез текстури з використанням структурованого дерева векторного квантування. У Зб. Конф. Конф. Склад. Графіка (SIGGRAPH), 2000.
[23] - А. Залесний, В. Феррарі, Г. Кенен, Л. ван Гол. Паралельний синтез композиційної текстури. У семінарі "Текстура 2002" - (спільно з ECCV02), Копенгаген, Данія, червень 2002 р.
[24] - Акур Кумар Патель, Гуджаратський технологічний університет, комп'ютерні науки та інженерія
[25] - Покращений надійний алгоритм для наглядних зображень на основі екземплярів
[26] - Вікіпедія, співвідношення пік-сигнал-шум


30
Це приголомшливо . Зоряна ніч така гарна. Все-таки, що Мона Ліза ...
Ганнес Карппіла

8
Спершу дозвольте сказати "о, боже, це неймовірно". По-друге: Я вже коментував "Те, що Мона Ліза - це якесь лайно SCP", на інше питання тут, але ця сова насправді виглядає як щось, що може з'явитися на вікі SCP.
підземниймонорельс

3
Чи могли б цитовані абзаци, які ви згадуєте, зробити блоками цитат?
трихоплакс

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

2
Коли я намагався уві сні дивитись на щось дуже уважно, часом справи стають саме такими.
jimmy23013

45

Матлаб

Це простий інтерполяційний підхід. Ідея спочатку відображає те, що є на кожній стороні пластиру. Потім ці пікселі дзеркального зображення інтерполюються тим, наскільки вони близькі до відповідного краю:

Хитра частина полягала в пошуку приємних ваг інтерполяції. Після деякої гри навколо я придумав раціональну функцію, яка дорівнює нулю на всіх краях, крім тієї, де ми дзеркально знаходились. Потім це перетворюється поліномом третього ступеня для деякого згладжування:

Цей простий підхід на диво добре підходить для "природних" зображень, але як тільки ви стикаєтесь з гострими краями, гра закінчена. У американському готичному прикладі шипи вилки сіна добре поєднуються з піксельною сіткою, завдяки чому вона виглядає досить приємно, але інакше було б набагато гірше.

Тож ось результати:

І нарешті, код:

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

Математика

Для цього використовується 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
Це може бути збіг, але я вражений виконанням лабіринту!
недолік

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 Я думаю, що випадкові обличчя, створені комп'ютером, походять прямо з глибини долини .
Андрас Деак

Звідки ви берете PIL?
Елліот А.

@ElliotA. Я розумію, що власне PIL мертвий, але він був відкритим кодом, і тому він живе під назвою "Подушка". Якщо ви "google" подушка python, ви повинні знайти його.
підземниймонорельс

13

Пітон 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')

Тим часом, ось зразки зображень.

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

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

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

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

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

Це не дуже добре з постійним кольором.

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

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


1
О, а ви можете, будь ласка, додати тестові випадки Ч / Б?
недолік

2
На Зоряна ніч виглядає дуже добре.
SuperJedi224

1
Ого, це зараз виглядає неймовірно! Патчі все ще помітні, але чудова нова ідея! Мій улюблений поки що =)
flawr

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

2
Ви виграєте конкурс "найстрашніша Мона Ліза", IMO. 0_o
DLosc

8

Пітон 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

Я насправді спробував це, але мені не вдалося отримати хороших результатів, тому я просто вирішив просто розмити зображення: D
Hannes Karppila

19
Нарешті, Мона Ліза, яка мене не лякає до смерті, але натомість схожа на заарештованого вбивцю.
Андрас Дек

6

Математика

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рcampion

Результати лабіринту та шахівниці мені справді спантеличують. Чому реконструкція відсутнього регіону 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

Ця відповідь реалізує ідею у статті "Глибоке зображення попереднього зображення" Ульянова та ін. (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 до чогось іншого, ніж U-Nets? Схоже, вони отримають кращі результати
лише ASCII

Також ви спробували з кодом Deep Image Prior
лише для ASCII

@ ASCII Тільки в документі вони заявляють, що вони в основному використовують U-Net, але я не можу знайти точні параметри, які вони використовували. Вони могли використовувати мережу з більшою місткістю. У мене був лише комп’ютер з дуже обмеженою потужністю. Тож мені довелося вибирати параметри, які все ще вписуються в пам’ять, і для навчання не потрібно було дуже багато часу. Я не впевнений, скільки часу пройшло, але на комп'ютері, який я використовував (лише з процесором), ці зображення займають кілька днів. (Якщо у вас є запасний графічний процесор із підтримкою
cuda,

Я також підозрюю, що завдяки дизайну мережі, що має прямокутні маски, також не ідеально (і малі маски, ймовірно, також будуть виглядати краще), якщо порівняти, наприклад, перші кілька зображень з останніми двома (в яких не використовуються прямокутні маски) .
дефект

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

шашка

Як бачите, це не найкраще з двома кольоровими.


Mona Lisa отримала підтяжку обличчя
Conor O'Brien

3

Java

Підхід усереднення кольорів. Можливо, можна вдосконалити.

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
Чому ви завжди отримуєте лінії в цьому конкретному куті? Лівий верхній кут, схоже, збігається відносно, тоді як правий нижній кут зовсім не відповідає.
недолік

Я думаю, що це має відношення до того, як я повторюю регіон. Я, мабуть, це з часом зміню.
SuperJedi224

Це завжди схоже на трапеції.
ericw31415

@ ericw31415 Це артефакт порядку ітерації.
SuperJedi224
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.