Menambal Gambar


114

Dalam perangkat lunak pengedit gambar yang populer terdapat fitur, yaitu tambalan (Istilah yang digunakan dalam pemrosesan gambar adalah pewarnaan seperti yang ditunjukkan oleh @ mınxomaτ.) Area gambar yang dipilih, berdasarkan informasi di luar tambalan itu. Dan itu melakukan pekerjaan yang cukup baik, mengingat itu hanya sebuah program. Sebagai manusia, kadang-kadang Anda bisa melihat ada sesuatu yang salah, tetapi jika Anda memicingkan mata atau hanya melirik sebentar, tambalan itu sepertinya mengisi kekosongan dengan cukup baik.

contoh dengan perangkat lunak pengedit gambar populer

Tantangan

Diberikan gambar dan topeng yang menentukan area persegi panjang dari gambar harus ditambal (juga sebagai gambar, atau format lain yang disukai), program Anda harus mencoba untuk mengisi area yang ditentukan dengan tambalan yang mencoba untuk berbaur dengan sisa foto. Program tidak dapat menggunakan informasi dari gambar asli yang berada dalam area yang ditentukan.

Anda dapat mengasumsikan bahwa tambalan selalu setidaknya lebarnya dari sisi, dan tinggi dari bagian atas dan bawah gambar. Itu berarti area maksimum tambalan adalah 1/9 dari keseluruhan gambar.

Harap tambahkan deskripsi singkat tentang cara kerja algoritme Anda.

Pemungutan suara

Para pemilih diminta untuk menilai seberapa baik algoritma bekerja dan memberikan suara yang sesuai.

Beberapa saran tentang cara menilai: (Sekali lagi, terima kasih @ mınxomaτ untuk beberapa kriteria lainnya.)

  • Jika Anda menyipitkan mata dan gambar terlihat bagus?
  • Bisakah Anda tahu persis di mana tambalan itu berada?
  • Seberapa baik struktur dan tekstur dari latar belakang gambar dan area sekitarnya dilanjutkan?
  • Berapa banyak piksel warna salah yang mengandung area yang diedit?
  • Apakah ada gumpalan / blok berwarna seragam di daerah yang tampaknya tidak termasuk di sana?
  • Apakah area yang diedit memiliki perubahan drastis / kontras atau kecerahan dibandingkan dengan gambar lainnya?

Kriteria validitas

Agar kiriman valid, gambar output harus sama persis dengan gambar input di luar area yang ditentukan.

Kasus cobaan

Di sebelah kiri gambar sumber, di sebelah kanan topeng yang sesuai:


1
Bisakah kita menerima input topeng sebagai argumen teks (mis. inpaint.exe left top width height img.jpg)?
mınxomaτ

1
Tentu, format input / output tidak terlalu penting, karena ini adalah kontes popularitas di mana pertama-tama kinerja algoritma Anda adalah penting.
flawr

24
Ini adalah tantangan yang sangat praktis. Mungkin saja hasilnya akan lebih baik daripada algoritma yang ada yang digunakan dalam GIMP dan perangkat lunak pengedit gambar open source lainnya. Keberuntungan, Ketenaran, dan Kemuliaan bisa menjadi milik Anda!
Sparr

6
@Sparr dan tanda air yang jelek akhirnya bisa dihapus dari media yang diunduh;)
Andras Deak

2
Builtin benar-benar ok, saya ragu mereka akan menjadi sangat populer.
flawr

Jawaban:


142

Otomatis , VB

pengantar

Ini adalah implementasi dari Penghapusan Objek oleh algoritma Inpainting Berbasis-Exemplar yang dikembangkan oleh A. Criminisi, P. Perez (Cambridge Microsoft Research Ltd.) dan K. Toyama (Microsoft) [X] . Algoritma ini ditargetkan pada gambar informasi tinggi (dan bingkai video) dan bertujuan untuk menjadi keseimbangan antara rekonstruksi struktural dan rekonstruksi organik. Paragraf jawaban ini berisi kutipan teks lengkap dari kertas asli (karena tidak lagi tersedia secara resmi) untuk membuat jawaban ini lebih mandiri.

Algoritma

Sasaran : Ganti area yang dipilih ( bertopeng ) (lebih disukai objek latar depan yang dipisahkan secara visual) dengan latar belakang yang masuk akal secara visual.

Dalam karya sebelumnya, beberapa peneliti telah mempertimbangkan sintesis tekstur sebagai cara untuk mengisi daerah gambar besar dengan tekstur "murni" - pola tekstur dua dimensi berulang dengan stokastik moderat. Ini didasarkan pada sejumlah besar penelitian sintesis-tekstur, yang berupaya mereplikasi tekstur ad infinitum , dengan sampel sumber kecil tekstur murni [1] [8] [9] [10] [11] [12] [14] [15] [16] [19] [22] .

Sama efektifnya dengan teknik-teknik ini dalam mereplikasi tekstur yang konsisten, mereka mengalami kesulitan mengisi lubang pada foto-foto pemandangan dunia nyata, yang seringkali terdiri dari struktur linear dan tekstur komposit - banyak tekstur berinteraksi secara spasial [23] . Masalah utama adalah bahwa batas-batas antara daerah gambar adalah produk kompleks yang saling mempengaruhi antara tekstur yang berbeda. Berbeda dengan sifat dua dimensi dari tekstur murni, batas-batas ini membentuk apa yang mungkin dianggap lebih satu-dimensi, atau linear, struktur gambar.

Teknik pengecatan gambar mengisi lubang pada gambar dengan menyebarkan struktur linier (disebut isofot dalam literatur pewarnaan ) ke dalam wilayah target melalui difusi. Mereka terinspirasi oleh persamaan diferensial parsial dari aliran panas fisik, dan bekerja secara meyakinkan sebagai algoritma restorasi. Kelemahan mereka adalah proses difusi menghasilkan beberapa blur, yang terlihat jelas.

ara.  2

Wilayah yang akan diisi, yaitu, wilayah target ditandai dengan Ω, dan konturnya dilambangkan δΩ. Kontur berkembang ke dalam seiring dengan perkembangan algoritma, dan karenanya kami juga menyebutnya sebagai "bagian depan isi". Wilayah sumber, Φ, yang tetap diperbaiki di seluruh algoritma, menyediakan sampel yang digunakan dalam proses pengisian. Kami sekarang fokus pada satu iterasi algoritma untuk menunjukkan bagaimana struktur dan tekstur ditangani secara memadai oleh sintesis berbasis contoh. Misalkan templat kotak Ψp ∈ Ω yang berpusat pada titik p (gb. 2b), harus diisi. Sampel yang paling cocok dari wilayah sumber berasal dari patch Ψqˆ ∈ Φ, yang paling mirip dengan bagian-bagian yang sudah diisi dengan Ψp. Dalam contoh dalam gambar. 2b, kita melihat bahwa jika liesp terletak pada kelanjutan tepi gambar, kecocokan terbaik yang paling mungkin akan terletak di sepanjang tepi yang sama (atau berwarna sama) (misalnya, Ψq 'dan Ψq' 'dalam gambar 2c). Semua yang diperlukan untuk menyebarkan isophote ke dalam adalah transfer sederhana dari pola dari patch sumber yang paling cocok (gbr. 2d). Perhatikan bahwa orientasi isophote secara otomatis dipertahankan. Dalam gambar, meskipun fakta bahwa tepi asli tidak ortogonal dengan kontur target δΩ, struktur yang diperbanyak telah mempertahankan orientasi yang sama seperti di wilayah sumber.

Detail Implementasi dan Algoritma

Fungsionalitas implementasi ini diringkas dalam ActiveX COM DLL yang dikeluarkan dari program host sebagai biner dan kemudian dipanggil saat itu dengan memanggil inpainter oleh IID. Dalam kasus khusus ini, API ditulis dalam VisualBasic dan dapat dipanggil dari bahasa yang mengaktifkan COM. Bagian berikut dari kode menjatuhkan biner:

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

Perpustakaan kemudian dipakai menggunakan CLSID dan IID:

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

Perpustakaan menerima pegangan GDIOBJECT, khususnya DIBSection dari setiap bitmap GDI / + (file, stream dll.). File gambar yang ditentukan dimuat, dan ditarik ke bitmap kosong yang dibangun dari Scan0dimensi gambar input.

File input untuk implementasi ini adalah format file yang kompatibel dengan GDI / + yang berisi data gambar yang di-mask. The mask (s) adalah satu atau lebih seragam berwarna daerah di citra input. Pengguna memasok nilai warna RGB untuk topeng, hanya piksel dengan nilai warna yang tepat yang akan cocok. Warna masking default adalah hijau (0, 255, 0). Semua wilayah bertopeng bersama-sama mewakili wilayah target, Ω, untuk dihapus dan diisi. Wilayah sumber, Φ, didefinisikan sebagai seluruh gambar dikurangi wilayah target (Φ = I − Ω).

Selanjutnya, seperti semua sintesis tekstur berbasis contoh [10] , ukuran jendela templat Ψ (alias " jari-jari pindai ") harus ditentukan. Implementasi ini memberikan ukuran jendela default 6 ² piksel, tetapi dalam praktiknya mengharuskan pengguna untuk mengaturnya menjadi sedikit lebih besar dari elemen tekstur terbesar yang dapat dibedakan, atau "texel", di wilayah sumber. Modifikasi tambahan untuk algoritma asli adalah " ukuran blok " yang dapat ditentukan pengguna yang menentukan area piksel yang akan diganti dengan warna seragam yang baru. Ini meningkatkan kecepatan dan menurunkan kualitas. Ukuran blok geater dari 1px dimaksudkan untuk digunakan dengan area yang sangat seragam (air, pasir, bulu, dll.), Namun, kept harus dijaga maksimal. .5x ukuran blok (yang tidak mungkin tergantung pada mask).

Untuk tidak menghentikan algoritme pada gambar 1bit, setiap kali gambar dengan kurang dari 5 warna diterima, ukuran jendela diperkuat oleh 10x.

Setelah parameter ini ditentukan, sisa proses pengisian wilayah sepenuhnya otomatis. Dalam algoritme kami, setiap piksel mempertahankan nilai warna (atau "kosong", jika piksel tidak terisi) dan nilai kepercayaan, yang mencerminkan kepercayaan kami pada nilai piksel, dan yang dibekukan setelah piksel diisi. Selama algoritma berlangsung, tambalan di sepanjang bagian depan isian juga diberikan nilai prioritas sementara, yang menentukan urutan pengisiannya. Kemudian, algoritma kami mengulangi tiga langkah berikut hingga semua piksel terisi.

Langkah 1: Komputasi prioritas tambalan

Mengisi urutan sangat penting untuk sintesis tekstur non-parametrik [1] [6] [10] [13] . Sejauh ini, favorit default adalah metode "bawang bawang", di mana wilayah target disintesis dari luar ke dalam, di lapisan konsentris. Algoritme kami melakukan tugas ini melalui algoritma pengisian terbaik-pertama yang sepenuhnya bergantung pada nilai-nilai prioritas yang ditetapkan untuk setiap tambalan di bagian depan pengisian. Komputasi prioritas bias terhadap tambalan-tambalan yang berada di kelanjutan tepi yang kuat dan yang dikelilingi oleh piksel kepercayaan tinggi, piksel ini adalah batas, ditandai dengan nilai -2. Kode berikut menghitung ulang prioritas:

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

Diberikan patch Ψp yang berpusat pada titik p untuk beberapa p ∈ δΩ (lihat gambar 3), prioritasnya P (p) didefinisikan sebagai produk dari kepercayaan yang dihitung ( ComputeConfidence, atau C (p) ) dan istilah data ( ComputeData, atau D (p) ), di mana

dimana

| Ψp | adalah area Ψp, α adalah faktor normalisasi (misalnya, α = 255 untuk gambar tingkat abu-abu khas), dan np adalah vektor satuan ortogonal ke depan δΩ pada titik p. Prioritas dihitung untuk setiap tambalan perbatasan, dengan tambalan berbeda untuk setiap piksel pada batas wilayah target.

Diimplementasikan sebagai

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

Istilah kepercayaan C (p) dapat dianggap sebagai ukuran dari jumlah informasi yang dapat dipercaya seputar piksel p. Tujuannya adalah untuk mengisi dulu tambalan-tambalan yang memiliki lebih banyak pikselnya yang sudah terisi, dengan preferensi tambahan yang diberikan pada piksel yang diisi sejak dini (atau yang tidak pernah menjadi bagian dari wilayah target).

Ini secara otomatis menggabungkan preferensi terhadap bentuk-bentuk tertentu di sepanjang bagian depan isian. Misalnya, tambalan yang menyertakan sudut dan sulur tipis wilayah target akan cenderung terisi lebih dulu, karena dikelilingi oleh lebih banyak piksel dari gambar asli. Tambalan ini memberikan informasi yang lebih andal untuk dicocokkan. Sebaliknya, tambalan di ujung "semenanjung" piksel terisi yang menjorok ke wilayah target akan cenderung disisihkan sampai lebih banyak piksel sekitarnya diisi. Pada tingkat kasar, istilah C (p) dari (1) kira-kira memberlakukan perintah pengisian konsentris yang diinginkan.

Saat pengisian berlangsung, piksel di lapisan luar wilayah target akan cenderung ditandai dengan nilai kepercayaan yang lebih besar, dan karenanya diisi lebih awal; piksel di tengah wilayah target akan memiliki nilai kepercayaan yang lebih rendah. Istilah data D (p) adalah fungsi dari kekuatan isofot yang mengenai bagian depan δΩ pada setiap iterasi. Istilah ini meningkatkan prioritas tambalan yang isophote "mengalir" ke dalamnya. Faktor ini sangat penting dalam algoritma kami karena ini mendorong struktur linear untuk disintesis terlebih dahulu, dan, oleh karena itu, disebarkan dengan aman ke wilayah target. Garis putus-putus cenderung terhubung, dengan demikian mewujudkan "Prinsip Konektivitas" psikologi penglihatan [7] [17] .

Urutan isian tergantung pada sifat-sifat gambar, menghasilkan proses sintesis organik yang menghilangkan risiko artefak "struktur-rusak" dan juga mengurangi artefak gumpal tanpa langkah patch-cutting yang mahal [9] atau langkah blending-inducing blending [19] ] .

Langkah 2: Menyebarkan informasi tekstur dan struktur

Setelah semua prioritas pada bidang isian ( batas ) telah dihitung, tambalan Ψpˆ dengan prioritas tertinggi ditemukan. Kami kemudian mengisinya dengan data yang diekstraksi dari wilayah sumber Φ. Kami menyebarkan tekstur gambar dengan pengambilan sampel langsung dari wilayah sumber. Mirip dengan [10] , kami mencari di daerah sumber untuk tambalan yang paling mirip dengan Ψpˆ. Secara formal,

dimana

jarak d (Ψa, Ψb) antara dua tambalan generik Ψa dan Ψb secara sederhana didefinisikan sebagai jumlah perbedaan kuadrat (SSD) dari piksel yang sudah terisi di dua tambalan. Tidak ada analisis atau manipulasi lebih lanjut ( terutama tidak kabur ) dilakukan pada langkah ini. Perhitungan ini berjalan di loop siklus utama dan diimplementasikan sebagai berikut:

Mendapatkan prioritas maksimum:

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

Menemukan tambalan yang paling mirip:

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

Langkah 3: Memperbarui nilai kepercayaan

Setelah patch Ψpˆ diisi dengan nilai-nilai piksel baru, kepercayaan C (p) diperbarui di area yang dibatasi oleh Ψpˆ sebagai berikut:

Aturan pembaruan sederhana ini memungkinkan kami untuk mengukur kepercayaan relatif tambalan di bagian depan pengisian, tanpa parameter khusus gambar. Saat pengisian berlangsung, nilai kepercayaan menurun, yang menunjukkan bahwa kami kurang yakin dengan nilai warna piksel di dekat pusat wilayah target. Diimplementasikan di sini (bersama dengan semua pembaruan lain yang diperlukan):

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

Kode Lengkap

Inilah kode yang bisa dijalankan, lengkap dengan kode sumber perpustakaan sebagai komentar.

Kode ini dipanggil oleh

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

Contohnya termasuk dalam bentuk

;~ 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")

batalkan komentar contoh yang ingin Anda jalankan menggunakan CTRL+ Q.

File Tes Resmi

Algoritma ini dibuat agar dapat disesuaikan untuk setiap gambar. Oleh karena itu, nilai default (dan juga masker default) sepenuhnya suboptimal. Nilai default dipilih sehingga setiap sampel dapat diproses dalam jumlah waktu yang wajar. Saya sangat merekomendasikan untuk bermain dengan topeng berbentuk tidak teratur dan ukuran jendela yang lebih baik. Klik gambar untuk memperbesar!

Papan main dam

American Gothic

Labirin

Mona lisa

(topeng mengerikan)

Berteriak

Penuh bintang

Contoh Dunia Nyata

Ini semua menggunakan topeng digambar tangan kustom.

Jika Anda memiliki gambar menarik lainnya yang ingin Anda sertakan, tinggalkan komentar.

Perbaikan EBII

Ada beberapa varian EBII di luar sana, yang dibuat oleh berbagai peneliti. AnkurKumar Patel menarik perhatian saya dengan koleksi makalahnya [24] tentang berbagai peningkatan EBII.

Secara khusus makalah " Algoritma Robust Peningkatan Untuk Contoh Berbasis Gambar Inpainting " [25] menyebutkan dua perbaikan pada penimbangan nilai prioritas.

Perbaikan

Modifikasi efektif ada pada Langkah 1 (lihat di atas) dari algoritma, dan memperluas efek C (p) dan D (p) pada peringkat prioritas untuk piksel ini menggunakan ini:

Dalam rumus untuk C dan D yang diberikan di atas, dan masing-masing merupakan faktor normalisasi (misalnya, α = 255), vektor isophote, dan vektor satuan ortogonal ke depan di titik p.

Lebih lanjut,

Fungsi prioritas didefinisikan sebagai jumlah bobot dari istilah kepercayaan teregulasi C (p) dan istilah data baru D (p) . Di mana α adalah koefisien penyesuaian, memuaskan 0Rp (p) didefinisikan sebagai berikut:

Di mana α dan β masing-masing merupakan bobot komponen dari kepercayaan dan istilah data. Perhatikan bahwa α + β = 1 .

Penilaian obyektif

Apa yang benar-benar menarik adalah bahwa makalah ini berisi metode yang diusulkan (dan sederhana!) Untuk mencetak kinerja jika algoritma EBII. Ambil ini dengan sebutir garam, karena ini adalah metode yang dipilih oleh penulis kertas sendiri untuk memverifikasi efektivitas pendekatan varians yang diusulkan dan perbaikan pada beberapa gambar.

Evaluasi hasil dilakukan dengan membandingkan PSNR (Peak Signal-to-Noise Ratio [26] ) antara gambar yang dipulihkan dan gambar asli. Umumnya semakin tinggi nilai PSNR, semakin besar kesamaan gambar yang diperbaiki dengan aslinya. Persamaan untuk menghitung PSNR adalah sebagai berikut:

Ini adalah 2 (dua!) Gambar uji dunia nyata mengejutkan yang mereka gunakan:

Kesimpulannya sama mengecewakannya dengan kualitas kertas itu sendiri. Ini menunjukkan sedikit peningkatan. Hal utama di sini adalah metode skoring objek yang mungkin untuk tantangan semacam ini (dan tantangan perbaikan citra lainnya):

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

Ah.

Penelitian harus dilakukan

(Khusus untuk EBII)

a) Pra-Pemrosesan

Semuanya bermuara pada prinsip "Magic Erase" bahwa algoritma harus "hanya bekerja" untuk semuanya. Solusi naif saya untuk ini adalah amplifikasi berbasis warna (lihat di atas), tetapi ada cara yang lebih baik. Saya berpikir untuk mengenali rata-rata geometrik dari semua texel yang dapat dilacak untuk secara otomatis menyesuaikan ukuran jendela dan membuat ukuran perangko (juga peningkatan saya) tergantung pada resolusi texel dan seluruh gambar. Penelitian harus dilakukan di sini.

b) Pasca Pemrosesan

Penulis asli telah melakukan pekerjaan yang baik untuk menghilangkan prasangka semua filter pemrosesan pos yang ada dalam pikiran. Hari ini, saya mencoba sesuatu yang lain, terinspirasi oleh Mona Lisa yang selalu aneh (terima kasih undergroundmonorail). Jika Anda hanya mengambil daerah yang dicat dan menerapkan topeng baru ke semua blok warna aneh dan memasukkannya ke dalam algoritma despeckling, Anda akan mendapatkan hasil yang hampir sempurna. Saya dapat menjelajahi ini beberapa waktu di masa depan.


[X] - Penghapusan Objek oleh Inpainting Berbasis Contoh oleh A. Criminisi, P. Perez, K. Toyama
[1] - M. Ashikhmin. Mensintesis tekstur alami. Di Proc. ACM Symp. pada Interactive 3D Graphics, hlm. 217–226, Research Triangle Park, NC, Mar 2001.
[5] - M. Bertalmio, L. Vese, G. Sapiro, dan S. Osher. Struktur dan tekstur gambar yang simultan. tampil, 2002
[6] - R. Bornard, E. Lecan, L. Laborelli, dan JH. Chenot. Koreksi data tidak ada dalam gambar diam dan urutan gambar. Dalam ACM Multimedia, Prancis, Desember 2002.
[7] - TF Chan dan J. Shen. Pewarnaan non-tekstur dengan difusi curvature-driven (CDD). J. Visual Comm. Image Rep., 4 (12), 2001.
[8] - JS de Bonet. Prosedur pengambilan sampel multiresolusi untuk analisis dan sintesis gambar tekstur. Di Proc. ACM Conf. Comp. Grafik (SIGGRAPH), volume 31, hlm. 361-368, 1997.
[9] - A. Efros dan WT Freeman. Gambar quilting untuk sintesis dan transfer tekstur. Di Proc. ACM Conf. Comp. Grafik (SIGGRAPH), hlm. 341–346, Eugene Fiume, Agustus 2001.
[10] - A. Efros dan T. Leung. Sintesis tekstur dengan pengambilan sampel non-parametrik. Di Proc. ICCV, hlm. 1033-1038, Kerkyra, Yunani, Sep 1999.
[11] - WT Freeman, EC Pasztor, dan PL Carmichael. Mempelajari visi tingkat rendah. Int. J. Computer Vision, 40 (1): 25–47, 2000.
[12] - D. Garber. Model Komputasi untuk Analisis Tekstur dan Sintesis Tekstur. Tesis PhD, Univ. California Selatan, AS, 1981.
[13] - P. Harrison. Prosedur non-hierarkis untuk sintesis ulang tekstur kompleks. Di Proc. Int. Conf. Eropa Tengah Comp. Grafik, Visua. dan Comp. Visi, Plzen, Republik Ceko, Februari 2001.
[14] - DJ Heeger dan JR Bergen. Analisis / sintesis tekstur berbasis piramida. Di Proc. ACM Conf. Comp. Grafik (SIGGRAPH), volume 29, hlm. 229–233, Los Angeles, CA, 1995.
[15] - A. Hertzmann, C. Jacobs, N. Oliver, B. Curless, dan D. Salesin. Analogi gambar. Di Proc. ACM Conf. Comp. Grafik (SIGGRAPH), Eugene Fiume, Agustus 2001.
[16] - H. Igehy dan L. Pereira. Penggantian gambar melalui sintesis tekstur. Di Proc. Int. Conf. Pemrosesan Gambar, hal. III: 186–190, 1997.
[17] - G. Kanizsa. Organisasi dalam Visi. Praeger, New York, 1979.
[19] - L. Liang, C. Liu, Y.-Q. Xu, B. Guo, dan H.-Y. Shum. Sintesis tekstur waktu nyata dengan pengambilan sampel berbasis tambalan. Dalam ACM Transactions on Graphics, 2001.
[22] - L.-W. Wey dan M. Levoy. Sintesis tekstur cepat menggunakan kuantisasi vektor terstruktur pohon. Dalam Proc. ACM Conf. Comp. Grafik (SIGGRAPH), 2000.
[23] - A. Zalesny, V. Ferrari, G. Caenen, dan L. van Gool. Sintesis tekstur komposit paralel. Dalam lokakarya Texture 2002 - (dalam hubungannya dengan ECCV02), Kopenhagen, Denmark, Jun 2002.
[24] - AkurKumar Patel, Universitas Teknologi Gujarat, Ilmu Komputer dan Teknik
[25] - Peningkatan Algoritma Kuat Untuk Contoh Gambar Berbasis Gambar
[26] - Wikipedia, Peak-Signal-to-Noise-Ratio


30
Ini luar biasa . The Starry Night sangat bagus. Tetap saja, Mona Lisa itu ...
Hannes Karppila

8
Pertama, izinkan saya mengatakan, "Ya Tuhan, ini luar biasa". Kedua: Saya sudah berkomentar "Mona Lisa itu omong kosong SCP" pada pertanyaan lain di sini, tapi burung hantu itu sebenarnya terlihat seperti sesuatu yang bisa muncul di wiki SCP.
undergroundmonorail

3
Bisakah paragraf kutipan yang Anda sebutkan itu dijadikan blok kutipan?
trichoplax

1
@trichoplax Ada sedikit modifikasi di hampir setiap kalimat, itu bukan kutipan yang tepat. Pertimbangkan deskripsi algoritma yang sama dengan org. kertas kecuali ketika tertulis modifikasi atau kode. Saya tidak ingin mengacaukan pemformatan lagi :)
mınxomaτ

2
Ketika saya mencoba untuk melihat sesuatu dengan sangat hati-hati dalam mimpi saya, kadang-kadang segalanya menjadi persis seperti ini.
jimmy23013

45

Matlab

Ini adalah pendekatan interpolasi sederhana. Idenya adalah mencerminkan apa yang ada di setiap sisi tambalan. Kemudian piksel gambar cermin tersebut diinterpolasi oleh seberapa dekat mereka dengan tepi yang sesuai:

Bagian yang sulit adalah menemukan bobot interpolasi yang bagus. Setelah beberapa bermain-main saya datang dengan fungsi rasional yang nol di semua tepi kecuali yang kita cerminkan. Ini kemudian ditransformasikan oleh polinomial tingkat ketiga untuk beberapa perataan:

Pendekatan sederhana ini sangat mengejutkan pada gambar "alami", tetapi segera setelah Anda berhadapan dengan tepi yang tajam, permainan berakhir. Dalam contoh gothic Amerika , paku garpu jerami berbaris dengan baik dengan kisi-kisi pixel, yang membuatnya terlihat cukup bagus, tetapi sebaliknya akan jauh lebih buruk.

Jadi di sini hasilnya:

Dan akhirnya, kodenya:

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
Mona Lisa membuatku takut.
Andras Deak

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

8
Bahwa Mona Lisa adalah orang SCP
undergroundmonorail

1
Gambar kotak-kotak terlihat IMHO sangat keren.
ETHproduksi

1
Saya tidak akan terkejut jika Anda memenangkan tantangan Anda sendiri dengan ini. Ini solusi yang sangat bagus.
Alex A.

25

Mathematica

Ini menggunakan Inpaintfungsi Mathematica . Karena Mathematica sendiri melakukan semua hal yang berat, ini adalah wiki komunitas.

inPaint(di bawah) adalah adaptasi sederhana dari Inpaint. Untuk lukisan / foto berwarna, menggunakan pengaturan default, "TextureSynthesis",. Jika mendeteksi bahwa gambar berwarna hitam dan putih (karena data gambar gambar sama dengan data gambar dari bentuk biner gambar), maka ia akan meng-binari gambar dan menerapkan tambalan "TotalVariation". The Ifklausul baik berlaku Binarizeatau Identitygambar. ( IdentityFungsi mengembalikan argumennya tidak berubah.)

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

Gambar dan topeng dimasukkan sebagai argumen inPaint. Partitiondan Gridhanya untuk keperluan format.

memasukkan

Keluaran telah ditambal. Tidak ada retouching gambar setelahnya inPaint.

keluaran


4
Ini mungkin kebetulan, tapi saya kagum dengan kinerja labirin!
flawr

1
@ flawr Saya akan melempar sesuatu seperti ini hanya untuk mengacaukan solusi ini;) (Siapa yang tahu? Orang kulit hitam dan putih itu benar-benar membingungkan.)
Andras Deak

17
Saya pikir ini bukan wiki komunitas.
Dennis

lawr, Ya, Inpainttampaknya mencari simetri di seluruh gambar hitam dan putih. - DavidC 9 jam yang lalu
DavidC

Apakah Anda yakin algoritma hitam-putih tidak melibatkan pengorbanan kambing di mana saja? Bagaimana --- di Bumi --- sih apakah itu menebak struktur pusat gambar, jika semuanya tertutup ??
Andras Deak

18

Python 2 dan PIL

Program ini memadukan salinan wilayah Utara, Selatan, Timur, dan Barat untuk membuat piksel pengganti yang menggunakan warna, tekstur, dan bayangan dari wilayah gambar lokal.

Contoh output:

Kode pertama kali menemukan kotak pembatas untuk tambalan. Kemudian untuk setiap piksel yang akan dihasilkan, itu menghitung warna setiap saluran (RGB) berdasarkan jumlah tertimbang dari 4 wilayah sekitarnya.

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
Mona Lisa ini juga menakutkan! Apakah semua Mona Lisas dalam tantangan ini pasti menakutkan ??
undergroundmonorail

@undergroundmonorail Saya kira wajah tak disengaja yang dihasilkan komputer datang langsung dari kedalaman lembah yang tidak biasa .
Andras Deak

Dari mana Anda mendapatkan PIL?
Elliot A.

@ElliotA. Pemahaman saya adalah bahwa PIL yang tepat sudah mati, tetapi itu open source dan karena itu hidup di bawah nama "Bantal". Jika Anda google "bantal python" Anda harus menemukannya.
undergroundmonorail

13

Python 3, PIL

Program ini menggunakan operator sobel, dan menggambar garis berdasarkan gambar itu.

Operator sobel menemukan sudut setiap tepi, sehingga setiap tepi yang mengekstrusi ke area yang tidak diketahui harus dilanjutkan.

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')

Sementara itu, berikut adalah contoh gambarnya.

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

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

masukkan deskripsi gambar di sini Area pada gambar di atas sehalus kaktus

Ini tidak terlalu bagus dengan warna konstan.

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini


1
Oh, dan bisakah Anda menambahkan kasus uji B / W?
flawr

2
Terlihat sangat bagus di Starry Night.
SuperJedi224

1
Wow, ini terlihat luar biasa sekarang! Tambalan masih terlihat tetapi ide baru yang hebat! Favorit saya sejauh ini =)
flawr

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

2
Anda memenangkan kontes "Mona Lisa paling menakutkan", IMO. 0_o
DLosc

8

Python 2

Skrip python sederhana yang membuat tambalan menggunakan nilai dari piksel di luar celah. Dibutuhkan nilai warna dari akhir baris dan kolom piksel dan menghitung rata-rata tertimbang menggunakan jarak dari piksel tersebut.

Outputnya tidak begitu cantik, tapi itu seni .

img1 img2 img3 img4 img5 img6

Dan kemudian, kode:

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")

Pada saat Anda melihat garis-garis horizontal / vertikal itu, mungkin Anda dapat memperbaikinya dengan memasukkan arah lain!
flawr

Saya benar-benar mencobanya, tetapi saya tidak dapat memperoleh hasil yang baik, jadi saya hanya memutuskan untuk mengaburkan gambar: D
Hannes Karppila

19
Akhirnya, seorang Mona Lisa yang tidak membuatku takut sampai mati, tetapi malah terlihat sebagai pembunuh yang ditangkap.
Andras Deak

6

Mathematica

Inpaint

Kebetulan bahwa Mathematica memiliki fungsi bawaan yang melakukan tugas ini dengan tepat, dan maksud saya persis :

Inpaint[image, region]

  • retouch bagian imageyang sesuai dengan elemen bukan nol di region.

Secara default ia menggunakan "metode sintesis tekstur terbaik menggunakan random sampling" yang menghasilkan hasil yang baik pada lukisan, tetapi hasil yang buruk untuk labirin dan kotak-kotak:

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

Bermain-main dengan pengaturan tidak membuat saya mengalami peningkatan kualitas di semua gambar, jadi saya hanya menggunakan default (untuk menyimpan byte — codegolf.sebagaimanapun juga!).


23
" Kebetulan bahwa Mathematica memiliki fungsi bawaan " ... kejutan, kejutan;)
Andras Deak

Untuk labirin dan papan pemeriksa, lebih baik menggunakan metode "TotalVariation" bersama Binarize(untuk menghilangkan noda abu-abu). Coba ini: methods = {"TextureSynthesis", "Diffusion", "FastMarching", "NavierStokes", "TotalVariation"};g[pic_, mask_] := Join[{Labeled[Framed@pic, "Original"]}, Labeled[ Binarize@Inpaint[pic, mask, Method -> #], #] & /@ methods]
DavidC

@ Davidvid Saya mencoba metode lain, tetapi hanya TextureSynthesisterlihat bagus di lukisan; dan saya pikir kami tidak diizinkan untuk menyetel pengaturan kami untuk setiap kasus uji individual. (Jika kita bisa, maka kita bisa dengan sepele memasok bagian yang hilang sebagai 'pengaturan'.)
Arcampion

Hasil labirin dan kotak-kotak benar-benar membingungkan bagi saya. Mengapa rekonstruksi Mathematica untuk daerah yang hilang begitu tidak teratur dan asimetris?
David Zhang

Ini akan secara otomatis mendeteksi apakah suatu gambar hitam dan putih dan membuat penyesuaian yang sesuai (binari dan metode "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

Jawaban ini mengimplementasikan ide dalam makalah "Deep Image Prior" oleh Ulyanov et al. (CVPR 2018) Dalam makalah ini mereka mengeksplorasi gagasan bahwa cara jaring saraf berkinerja baik untuk pemrosesan gambar dirancang secara dekat mencerminkan gagasan kami tentang seperti apa gambar alami (distribusi "sebelum").

Mereka mengusulkan metode yang dapat digunakan untuk pewarnaan serta penghapusan noise dan artefak yang hanya menggunakan gambar yang diberikan tanpa pelatihan tentang data lainnya. Konsep aktualnya cukup sederhana: Jaring dilatih untuk menampilkan gambar yang diinginkan (untuk beberapa gangguan acak tetap sebagai input) dengan hanya menghukum erros di luar beberapa topeng yang diberikan. Jika Anda ingin menghilangkan kebisingan, Anda tidak perlu menutupi apa pun, tetapi berhenti saja di awal pelatihan.

Untuk pewarnaan, Anda menutupi bagian yang ingin Anda cat dan latih sampai konvergensi. Ini tentu saja tidak canggih, tetapi saya masih ingin mempostingnya dan mempostingnya di sini karena kesederhanaan ide dan kinerja yang masih luar biasa. Dalam eksperimen saya, pengecatan tambalan yang lebih besar ternyata tidak terlalu baik, tetapi untuk segmen yang lebih kecil hasilnya bisa jauh lebih meyakinkan.

Saya menerapkan ini menggunakan arsitektur U-Net populer dari jaxony on github . Kode untuk pelatihan dan pemrosesan gambar dapat ditemukan di bawah.

Latihan

Ini adalah visualisasi dari proses pelatihan. Setiap frame adalah status ofter sejumlah iterasi:

Contohnya

Kode

Perhatikan bahwa pada sebuah cpu, ini bisa memakan waktu berjam-jam hanya untuk satu gambar, sementara gpu yang diaktifkan dengan cpu yang baik mungkin membutuhkan waktu yang jauh lebih sedikit.

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))

Apakah Deep Image Before menggunakan sesuatu yang berbeda dari U-Nets? sepertinya mereka akan mendapatkan hasil yang lebih baik
hanya ASCII

Anda juga sudah mencoba dengan kode Deep Image Prior
ASCII-only

@ ASCII-only Mereka menyatakan dalam makalah bahwa mereka memang terutama menggunakan U-Net, tetapi saya tidak dapat menemukan parameter yang tepat yang mereka gunakan. Mereka mungkin menggunakan jaring dengan kapasitas lebih besar. Saya hanya punya komputer dengan daya yang sangat terbatas. Jadi saya harus memilih parameter yang masih sesuai dengan memori dan tidak butuh waktu terlalu lama untuk berlatih. Saya tidak yakin berapa lama tepatnya tetapi pada komputer yang saya gunakan (hanya dengan CPU) gambar-gambar ini memakan waktu beberapa hari. (Jika Anda memiliki GPU cadangan yang diaktifkan, beri tahu saya :)
flawr

Saya juga menduga bahwa karena desain jaringan yang memiliki topeng persegi panjang juga tidak ideal (dan topeng yang lebih kecil mungkin juga akan terlihat lebih baik), jika Anda membandingkan misalnya beberapa gambar pertama dengan dua yang terakhir (yang tidak menggunakan topeng persegi panjang) .
flawr

4

Python dengan OpenCV

OpenCV memiliki fungsi yang disebut inpaint. Ada dua jenis pewarnaan yang digunakan, saya akan menggunakan Metode Marching Cepat. Menurut dokumentasi, algoritma bekerja seperti ini:

Pertimbangkan suatu daerah di dalam gambar yang akan dicat. Algoritma dimulai dari batas wilayah ini dan masuk ke dalam wilayah tersebut secara bertahap mengisi semua yang ada di batas terlebih dahulu. Dibutuhkan lingkungan kecil di sekitar piksel pada tetangga untuk menjadi akrab. Pixel ini digantikan oleh jumlah tertimbang yang dinormalisasi dari semua piksel yang dikenal di lingkungan tersebut. Pemilihan bobot adalah masalah penting. Lebih banyak bobot diberikan pada piksel yang berada di dekat titik, dekat dengan batas normal dan piksel yang terletak pada kontur batas. Setelah sebuah piksel dicat, ia bergerak ke piksel terdekat berikutnya menggunakan Metode Marching Cepat. FMM memastikan piksel-piksel yang dekat dengan piksel yang diketahui itu sudah dicat terlebih dahulu, sehingga hanya berfungsi seperti operasi heuristik manual.

Ini kode *:

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)

Perhatikan bagaimana saya mengonversi BGR ke RGB karena alasan alasan. Saya juga memutarnya. Inilah hasilnya:

Gotik

malam berbintang berteriak mona lisa menyeramkan lainnya!

Mona Lisa kembali!

baris 1

pemeriksa

Seperti yang Anda lihat, ini bukan yang terbaik dengan dua warna.


Mona Lisa mendapatkan facelift
Conor O'Brien

3

Jawa

Pendekatan rata-rata warna. Mungkin bisa ditingkatkan.

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"));
    }
}

Hasil:

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini


2
Mengapa Anda selalu mendapatkan garis di sudut tertentu ini? Sudut kiri atas tampaknya cocok dengan relatif baik, sedangkan bagian kanan bawah tidak cocok sama sekali.
flawr

Saya pikir itu ada hubungannya dengan cara di mana saya beralih melalui wilayah. Saya mungkin akan mengubahnya pada akhirnya.
SuperJedi224

Itu selalu terlihat seperti ada trapesium.
ericw31415

@ ericw31415 Ini adalah artefak dari urutan iterasi.
SuperJedi224
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.