Patch l'immagine


114

In un popolare software di modifica delle immagini c'è una funzione, che patch (Il termine usato nell'elaborazione delle immagini sta dipingendo come ha sottolineato @ mınxomaτ.) Un'area selezionata di un'immagine, in base alle informazioni al di fuori di quella patch. E fa un ottimo lavoro, considerando che è solo un programma. Come essere umano, a volte puoi vedere che qualcosa non va, ma se ti stringi gli occhi o dai solo una breve occhiata, il cerotto sembra riempire abbastanza bene il vuoto .

esempio dal popolare software di modifica delle immagini

Sfida

Data un'immagine e una maschera che specifica un'area rettangolare dell'immagine dovrebbe essere patchata (anche come immagine o qualsiasi altro formato preferito), il tuo programma dovrebbe provare a riempire l'area specificata con una patch che prova a fondersi con il resto di l'immagine. Il programma non può utilizzare le informazioni dell'immagine originale che si trovavano all'interno dell'area specificata.

Puoi presumere che la patch sia sempre almeno alla sua larghezza dai lati e all'altezza dalla parte superiore e inferiore dell'immagine. Ciò significa che l'area massima di una patch è 1/9 dell'intera immagine.

Aggiungi una breve descrizione del funzionamento dell'algoritmo.

Voto

Agli elettori viene chiesto di giudicare l'efficacia degli algoritmi e di votare di conseguenza.

Alcuni suggerimenti su come giudicare: (Ancora una volta, grazie @ mınxomaτ per alcuni altri criteri.)

  • Se socchiudi gli occhi e l'immagine sembra a posto?
  • Sai esattamente dove si trova la patch?
  • Quanto continuano le strutture e le trame dello sfondo dell'immagine e dell'area circostante?
  • Quanti pixel vaganti di falsi colori contiene l'area modificata?
  • Ci sono blocchi / blocchi di colore uniforme nella zona che non sembrano appartenere a questo?
  • L'area modificata presenta variazioni drastiche di colore / contrasto o luminosità rispetto al resto dell'immagine?

Criterio di validità

Affinché un invio sia valido, l'immagine di output deve corrispondere esattamente all'immagine di input al di fuori dell'area specificata.

Caso di prova

A sinistra l'immagine sorgente, a destra la maschera corrispondente:


1
Possiamo accettare l'input della maschera come argomenti di testo (es. inpaint.exe left top width height img.jpg)?
mınxomaτ,

1
Certo, il formato di input / output non è poi così importante, in quanto è un concorso di popolarità in cui innanzitutto sono importanti le prestazioni del tuo algoritmo .
flawr

24
Questa è una sfida molto pratica. È possibile che i risultati siano migliori degli algoritmi esistenti utilizzati in GIMP e in altri software di editing di immagini open source. La fortuna, la fama e la gloria potrebbero essere tue!
Sparr,

6
@Sparr e infine brutte filigrane possono essere rimosse dai media scaricati;)
Andras Deak

2
I builtin sono perfettamente ok, dubito che saranno molto popolari però.
Flawr

Risposte:


142

AutoIt , VB

introduzione

Questa è un'implementazione dell'algoritmo Inpainting basato su Exemplar-Based Inpainting sviluppato da A. Criminisi, P. Perez (Cambridge Microsoft Research Ltd.) e K. Toyama (Microsoft) [X] . Questo algoritmo è mirato alle immagini ad alta informazione (e ai frame video) e mira ad essere l'equilibrio tra ricostruzione strutturale e ricostruzione organica.I paragrafi di questa risposta contengono citazioni di testo completo dal documento originale (poiché non è più ufficialmente disponibile) per rendere questa risposta più autonoma.

L'algoritmo

Obiettivo : sostituire un selezionato ( mascherato ) (preferibilmente un oggetto in primo piano separato visivamente) con sfondi visivamente plausibili.

In lavori precedenti, diversi ricercatori hanno considerato la sintesi di trame come un modo per riempire grandi aree di immagini con trame "pure" - ripetitivi modelli tessurali bidimensionali con moderata stocastica. Questo si basa su un ampio corpus di ricerche sulla sintesi delle trame, che cerca di replicare la trama all'infinito , dato un piccolo campione di pura trama [1] [8] [9] [10] [11] [12] [14] [15] [16] [19] [22] .

Per quanto efficaci siano queste tecniche nel replicare trame coerenti, hanno difficoltà a riempire buchi nelle fotografie di scene del mondo reale, che spesso consistono in strutture lineari e trame composte - trame multiple che interagiscono spazialmente [23] . Il problema principale è che i confini tra regioni dell'immagine sono un prodotto complesso di influenze reciproche tra diverse trame. Contrariamente alla natura bidimensionale delle trame pure, questi confini formano quelle che potrebbero essere considerate strutture di immagine più monodimensionali o lineari.

Le tecniche di verniciatura delle immagini riempiono i buchi nelle immagini propagando strutture lineari (chiamate isophotes nella letteratura di verniciatura ) nella regione target tramite diffusione. Sono ispirati alle equazioni differenziali parziali del flusso di calore fisico e funzionano in modo convincente come algoritmi di ripristino. Il loro svantaggio è che il processo di diffusione introduce un po 'di sfocatura, che è evidente.

Figura.  2

La regione da riempire, ovvero la regione target è indicata da Ω e il suo contorno è indicato con δΩ. Il contorno si evolve verso l'interno man mano che l'algoritmo progredisce e quindi ci riferiamo anche ad esso come "riempimento frontale". La regione di origine, Φ, che rimane fissa in tutto l'algoritmo, fornisce campioni utilizzati nel processo di riempimento. Ci concentriamo ora su una singola iterazione dell'algoritmo per mostrare come la struttura e la trama sono gestite adeguatamente dalla sintesi basata su esempi. Supponiamo che il modello quadrato Ψp ∈ Ω centrato nel punto p (fig. 2b), debba essere riempito. Il campione con la migliore corrispondenza dalla regione di origine proviene dalla patch Ψqˆ ∈ Φ, che è molto simile a quelle parti che sono già state compilate in Ψp. Nell'esempio di fig. 2b, vediamo che se liesp si trova sulla continuazione di un bordo dell'immagine, le migliori corrispondenze più probabili si troveranno lungo lo stesso bordo (o un colore simile) (ad esempio, Ψq 'e Ψq' 'nella figura 2c). Tutto ciò che serve per propagare l'isofoto verso l'interno è un semplice trasferimento del pattern dalla patch sorgente più adatta (fig. 2d). Si noti che l'orientamento isofoto viene automaticamente preservato. Nella figura, nonostante il bordo originale non sia ortogonale al contorno target δΩ, la struttura propagata ha mantenuto lo stesso orientamento della regione di origine.

Dettagli di implementazione e algoritmo

La funzionalità di questa implementazione è incapsulata in una DLL COM ActiveX che viene rilasciata dal programma host come binario e quindi invocata al volo chiamando l'iPainter da IID. In questo caso specifico, l'API è scritta in VisualBasic e può essere chiamata da qualsiasi linguaggio abilitato COM. La seguente sezione del codice elimina il file binario:

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

La libreria viene successivamente istanziata utilizzando CLSID e IID:

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

La libreria accetta un handle GDIOBJECT, in particolare una DIBSection di qualsiasi bitmap GDI / + (file, stream ecc.). Il file di immagine specificato viene caricato e disegnato su una bitmap vuota costruita a partire Scan0dalle dimensioni dell'immagine di input.

Il file di input per questa implementazione è qualsiasi formato di file compatibile GDI / + contenente dati di immagine mascherati. Le maschere sono una o più regioni di colore uniforme nell'immagine di input. L'utente fornisce un valore di colore RGB per la maschera, solo i pixel con esattamente quel valore di colore verranno abbinati. Il colore di mascheramento predefinito è verde (0, 255, 0). Tutte le regioni mascherate insieme rappresentano la regione target, Ω, da rimuovere e riempire. La regione di origine, Φ, è definita come l'intera immagine meno la regione di destinazione (Φ = I − Ω).

Successivamente, come per tutte le sintesi di texture basate su esempi [10] , è necessario specificare la dimensione della finestra del modello Ψ (nota anche come " raggio di scansione "). Questa implementazione fornisce una dimensione predefinita della finestra di 6² pixel, ma in pratica richiede all'utente di impostarlo in modo che sia leggermente più grande del più grande elemento di trama distinguibile, o "texel", nella regione di origine. Un'ulteriore modifica dell'algoritmo originale è la " dimensione del blocco " definibile dall'utente che determina l'area dei pixel da sostituire con un nuovo colore uniforme. Ciò aumenta la velocità e diminuisce la qualità. Le dimensioni del blocco più grandi di 1px sono pensate per essere utilizzate con aree estremamente uniformi (acqua, sabbia, pelliccia ecc.), Tuttavia, Ψ devono essere mantenute al massimo. .5 volte la dimensione del blocco (che può essere impossibile a seconda della maschera).

Per non bloccare l'algoritmo su immagini a 1 bit, ogni volta che si riceve un'immagine con meno di 5 colori, la dimensione della finestra viene amplificata di 10x.

Una volta determinati questi parametri, il resto del processo di riempimento della regione è completamente automatico. Nel nostro algoritmo, ogni pixel mantiene un valore di colore (o "vuoto", se il pixel non è riempito) e un valore di confidenza, che riflette la nostra fiducia nel valore del pixel e che viene congelato una volta riempito un pixel. Nel corso dell'algoritmo, alle patch lungo il fronte di riempimento viene inoltre assegnato un valore di priorità temporaneo, che determina l'ordine in cui vengono riempite. Quindi, il nostro algoritmo esegue l'iterazione dei tre passaggi seguenti fino a quando tutti i pixel sono stati riempiti.

Passaggio 1: calcolo delle priorità delle patch

L'ordine di riempimento è fondamentale per la sintesi di trama non parametrica [1] [6] [10] [13] . Finora, il preferito di default è stato il metodo della "buccia di cipolla", in cui la regione target è sintetizzata dall'esterno verso l'interno, in strati concentrici. Il nostro algoritmo esegue questa attività attraverso un algoritmo di riempimento best-first che dipende interamente dai valori di priorità assegnati a ciascuna patch sul fronte di riempimento. Il calcolo della priorità è distorto verso quelle patch che si trovano sulla continuazione di spigoli forti e che sono circondate da pixel ad alta confidenza, questi pixel sono il confine, contrassegnato dal valore -2. Il seguente codice ricalcola le priorità:

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

Dato un patch Ψp centrato nel punto p per alcuni p ∈ δΩ (vedere la figura 3), la sua priorità P (p) è definita come il prodotto della fiducia calcolata ( ComputeConfidenceo C (p) ) e il termine dati ( ComputeData, o D (p) ), dove

, dove

| Ψp | è l'area di Ψp, α è un fattore di normalizzazione (ad esempio, α = 255 per una tipica immagine a livello di grigio) e np è un vettore unitario ortogonale alla parte anteriore δΩ nel punto p. La priorità viene calcolata per ogni patch di bordo, con patch distinte per ciascun pixel al limite dell'area di destinazione.

Implementato come

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

Il termine di confidenza C (p) può essere considerato come una misura della quantità di informazioni affidabili che circondano il pixel p. L'intenzione è di riempire prima quelle patch che hanno già più dei loro pixel già riempiti, con un'ulteriore preferenza data ai pixel che sono stati riempiti all'inizio (o che non facevano mai parte della regione target).

Ciò incorpora automaticamente la preferenza verso determinate forme lungo la parte frontale del riempimento. Ad esempio, le patch che includono angoli e viticci sottili della regione di destinazione tenderanno a essere riempite per prime, poiché sono circondate da più pixel dell'immagine originale. Queste patch forniscono informazioni più affidabili rispetto alle quali corrispondere. Al contrario, le patch sulla punta delle "penisole" di pixel riempiti che si protendono nella regione target tenderanno a essere messe da parte fino a quando non saranno riempiti più pixel circostanti. A livello approssimativo, il termine C (p) di (1) circa applica l'ordine di riempimento concentrico desiderabile.

Man mano che il riempimento procede, i pixel negli strati esterni della regione target tenderanno ad essere caratterizzati da maggiori valori di confidenza, e quindi saranno riempiti prima; i pixel al centro della regione target avranno valori di confidenza inferiori. Il termine dati D (p) è una funzione dell'intensità degli isofoti che colpiscono il fronte δΩ ad ogni iterazione. Questo termine aumenta la priorità di una patch in cui "scorre" un isofoto. Questo fattore è di fondamentale importanza nel nostro algoritmo perché incoraggia prima la sintesi delle strutture lineari e, quindi, la propagazione sicura nella regione target. Le linee spezzate tendono a connettersi, realizzando così il "Principio di connettività" della psicologia della visione [7] [17] .

L'ordine di riempimento dipende dalle proprietà dell'immagine, con il risultato di un processo di sintesi organica che elimina il rischio di artefatti a "struttura spezzata" e riduce anche gli artefatti a blocchi senza una costosa fase di taglio della patch [9] o una fase di fusione che induce la sfocatura [19 ] .

Passaggio 2: propagazione delle informazioni sulla trama e sulla struttura

Una volta calcolate tutte le priorità sul fronte di riempimento ( contorno ), viene trovata la patch Ψpˆ con la massima priorità. Lo riempiamo quindi con i dati estratti dalla regione di origine Φ. Propaghiamo la trama delle immagini campionando direttamente la regione di origine. Simile a [10] , cerchiamo nella regione di origine quella patch che è più simile a Ψpˆ. formalmente,

, dove

la distanza d (Ψa, Ψb) tra due patch generiche Ψa e Ψb è semplicemente definita come la somma delle differenze quadrate (SSD) dei pixel già riempiti nelle due patch. In questo passaggio non vengono eseguite ulteriori analisi o manipolazioni ( soprattutto nessuna sfocatura ). Questo calcolo viene eseguito nel ciclo principale e viene implementato come segue:

Ottenere la massima priorità:

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

Trovare la patch più simile:

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

Passaggio 3: aggiornamento dei valori di affidabilità

Dopo che la patch Ψpˆ è stata riempita con nuovi valori di pixel, la confidenza C (p) viene aggiornata nell'area delimitata da Ψpˆ come segue:

Questa semplice regola di aggiornamento ci consente di misurare la relativa sicurezza delle patch sul fronte di riempimento, senza parametri specifici dell'immagine. Man mano che il riempimento procede, i valori di confidenza diminuiscono, indicando che siamo meno sicuri dei valori di colore dei pixel vicino al centro dell'area target. Implementato qui (insieme a tutti gli altri aggiornamenti necessari):

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

Codice completo

Ecco il codice eseguibile, completo di codice sorgente delle librerie come commenti.

Il codice è invocato da

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

Gli esempi sono inclusi sotto forma di

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

basta decommentare l'esempio che si desidera eseguire usando CTRL+ Q.

File di test ufficiali

Questo algoritmo è progettato per essere adattato per ogni immagine. Pertanto, i valori predefiniti (e anche le maschere predefinite) sono completamente non ottimali. I valori predefiniti vengono scelti in modo che ogni campione possa essere elaborato in un ragionevole lasso di tempo. Consiglio vivamente di giocare con maschere di forma irregolare e dimensioni delle finestre migliori. Clicca sulle immagini per ingrandire!

Scacchiera

gotico americano

Labirinto

Monna Lisa

(maschera terribile)

Urlare

Stellato

Esempi del mondo reale

Tutti usano maschere personalizzate disegnate a mano.

Se hai altre immagini interessanti che vorresti vedere incluse, lascia un commento.

Miglioramenti EBII

Esistono diverse varianti di EBII, create da vari ricercatori. AnkurKumar Patel ha attirato la mia attenzione con la sua raccolta di documenti [24] su vari miglioramenti di EBII.

Nello specifico, il documento " Algoritmo migliorato per la verniciatura delle immagini basata su esempi " [25] menziona due miglioramenti nella valutazione dei valori di priorità.

Il miglioramento

La modifica effettiva è nel passaggio 1 (vedi sopra) dell'algoritmo e estende l' effetto C (p) e D (p) sulla classificazione di priorità per questo pixel usando questo:

Nella formula per C e D di cui sopra, e sono rispettivamente il fattore di normalizzazione (ad esempio, α = 255), il vettore isofoto e il vettore unità ortogonale alla parte anteriore nel punto p.

Ulteriore,

La funzione prioritaria è definita come la somma ponderata del termine di confidenza regolarizzato C (p) e del nuovo termine di dati D (p) . Dove α è il coefficiente di regolazione, viene definito 0Rp (p) soddisfacente come segue:

Dove α e β sono rispettivamente i pesi componenti della fiducia e i termini dei dati. Si noti che α + β = 1 .

Punteggio obiettivo

Ciò che è veramente interessante è che questo documento contiene un metodo proposto (e semplice!) Per assegnare un punteggio alle prestazioni degli algoritmi EBII. Prendi questo con un granello di sale, poiché si tratta di un metodo scelto dagli stessi autori di carta per verificare l'efficacia dell'approccio alla varianza proposto e il miglioramento su diverse immagini.

La valutazione del risultato viene eseguita confrontando il PSNR (Rapporto segnale-rumore di picco [26] ) tra l'immagine ripristinata e l'immagine originale. Generalmente più alto è il valore PSNR maggiore è la somiglianza dell'immagine riparata con l'originale. L'equazione per calcolare il PSNR è la seguente:

Queste sono le incredibili 2 (due!) Immagini di test del mondo reale che hanno usato:

La conclusione è deludente quanto la qualità della carta stessa. Mostra pochissimi miglioramenti. La cosa principale qui è un possibile metodo di punteggio dell'oggetto per questo tipo di sfida (e altre sfide di riparazione delle immagini):

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

Meh.

Ricerca da fare

(Specifico per EBII)

a) Pre-elaborazione

Tutto si riduce al principio "Cancellazione magica" secondo cui l'algoritmo dovrebbe "funzionare" per tutto. La mia ingenua soluzione per questo è un'amplificazione basata sul colore (vedi sopra), ma ci sono modi migliori. Sto pensando di riconoscere la media geometrica di tutti i tex tracciabili per regolare automaticamente le dimensioni della finestra e rendere le dimensioni del timbro (anche il mio miglioramento) dipendenti dalla risoluzione del texel e dell'immagine intera. La ricerca deve essere fatta qui.

b) Post-elaborazione

Gli autori originali hanno già fatto un ottimo lavoro di debunking di tutti i filtri di post elaborazione che vengono in mente. Oggi ho provato qualcos'altro, ispirato dalla sempre inquietante Mona Lisa (grazie alla metropolitana sotterranea). Se prendi solo la regione dipinta e applichi una nuova maschera a tutti gli strani blocchi di colore e la inserisci in un algoritmo di despeckling, ti rimane un risultato quasi perfetto. Potrei esplorare questo un po 'di tempo in futuro.


[X] - Rimozione di oggetti mediante pittura a base di esemplare di A. Criminisi, P. Perez, K. Toyama
[1] - M. Ashikhmin. Sintetizzare trame naturali. Nel Proc. ACM Symp. sulla grafica 3D interattiva, pagg. 217–226, Research Triangle Park, NC, marzo 2001.
[5] - M. Bertalmio, L. Vese, G. Sapiro e S. Osher. Verniciatura simultanea di immagini di strutture e strutture. apparire, 2002
[6] - R. Bornard, E. Lecan, L. Laborelli e JH. Chenot. Correzione dei dati mancanti nelle immagini fisse e nelle sequenze di immagini. In ACM Multimedia, Francia, dicembre 2002.
[7] - TF Chan e J. Shen. Verniciatura senza trama mediante diffusioni guidate dalla curvatura (CDD). J. Visual Comm. Image Rep., 4 (12), 2001.
[8] - JS de Bonet. Procedura di campionamento multiresoluzione per analisi e sintesi di immagini di trama. Nel Proc. Conf. ACM Comp. Grafica (SIGGRAPH), volume 31, pagg. 361–368, 1997.
[9] - A. Efros e WT Freeman. Trapuntatura di immagini per la sintesi e il trasferimento di trame. Nel Proc. Conf. ACM Comp. Grafica (SIGGRAPH), pp. 341-346, Eugene Fiume, agosto 2001.
[10] - A. Efros e T. Leung. Sintesi di trama mediante campionamento non parametrico. Nel Proc. ICCV, pagg. 1033-1038, Kerkyra, Grecia, settembre 1999.
[11] - WT Freeman, EC Pasztor e OT Carmichael. Apprendimento della visione a basso livello. Int. J. Computer Vision, 40 (1): 25–47, 2000.
[12] - D. Garber. Modelli computazionali per l'analisi delle trame e la sintesi delle trame. Tesi di dottorato, Univ. della California del sud, USA, 1981.
[13] - P. Harrison. Una procedura non gerarchica per la risintesi di texture complesse. Nel Proc. Int. Conf. Central Europe Comp. Grafica, Visua. e comp. Vision, Plzen, Repubblica Ceca, febbraio 2001.
[14] - DJ Heeger e JR Bergen. Analisi / sintesi di texture a base piramidale. Nel Proc. Conf. ACM Comp. Grafica (SIGGRAPH), volume 29, pp. 229–233, Los Angeles, CA, 1995.
[15] - A. Hertzmann, C. Jacobs, N. Oliver, B. Curless e D. Salesin. Analogie di immagini. Nel Proc. Conf. ACM Comp. Grafica (SIGGRAPH), Eugene Fiume, agosto 2001.
[16] - H. Igehy e L. Pereira. Sostituzione delle immagini attraverso la sintesi delle trame. Nel Proc. Int. Conf. Elaborazione delle immagini, pp. III: 186–190, 1997.
[17] - G. Kanizsa. Organizzazione in visione. Praeger, New York, 1979.
[19] - L. Liang, C. Liu, Y.-Q. Xu, B. Guo e H.-Y. Shum. Sintesi delle texture in tempo reale mediante campionamento basato su patch. In ACM Transactions on Graphics, 2001.
[22] - L.-W. Wey e M. Levoy. Sintesi rapida delle trame mediante quantizzazione vettoriale strutturata ad albero. Nel Proc. Conf. ACM Comp. Grafica (SIGGRAPH), 2000.
[23] - A. Zalesny, V. Ferrari, G. Caenen e L. van Gool. Sintesi di trama composita parallela. Workshop In Texture 2002 - (in collaborazione con ECCV02), Copenaghen, Danimarca, giugno 2002.
[24] - AkurKumar Patel, Gujarat Technological University, Computer Science and Engineering
[25] - Algoritmo robusto migliorato per la pittura di immagini basata su esemplari
[26] - Wikipedia, Rapporto picco-segnale-rumore


30
Questo è impressionante . La notte stellata è così bella. Tuttavia, quella Gioconda ...
Hannes Karppila,

8
Prima di tutto lasciami dire "oh mio Dio, questo è incredibile". Secondo: ho già commentato "Che Mona Lisa è una merda SCP" su un'altra domanda qui, ma quel gufo in realtà sembra qualcosa che potrebbe apparire sul wiki di SCP.
undergroundmonorail

3
I paragrafi citati che potresti citare potrebbero essere citati in blocchi?
trichoplax,

1
@trichoplax Ci sono piccole modifiche in quasi ogni frase, non sono virgolette esatte. Considera la descrizione dell'algoritmo uguale all'organizzazione. carta tranne quando dice modifica o codice. Non voglio più ingombrare la formattazione :)
mınxomaτ

2
Quando ho provato a guardare qualcosa con molta attenzione nei miei sogni, a volte le cose diventano esattamente così.
jimmy23013,

45

Matlab

Questo è un semplice approccio di interpolazione. L'idea è prima di tutto rispecchiare ciò che si trova su ciascun lato della patch. Quindi quei pixel dell'immagine speculare vengono interpolati da quanto sono vicini al bordo corrispondente:

La parte difficile è stata trovare dei pesi di interpolazione gradevoli. Dopo aver suonato un po 'in giro, mi è venuta in mente una funzione razionale che è zero su tutti i bordi tranne quella su cui ci siamo specchiati. Questo viene quindi trasformato da un polinomio di terzo grado per un certo livellamento:

Questo semplice approccio funziona sorprendentemente bene su immagini "naturali", ma non appena ti trovi di fronte a spigoli vivi, il gioco è finito. Nell'esempio del gotico americano le punte della forcella del fieno si allineano perfettamente con la griglia dei pixel, il che la rende piuttosto carina, ma altrimenti sarebbe andata molto peggio.

Quindi qui i risultati:

E infine, il codice:

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 mi fa venire i brividi.
Andras Deak,

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

8
Quella Gioconda è una merda di SCP
undergroundmonorail,

1
L'immagine a scacchi sembra davvero bella.
ETHproductions

1
Non sarei sorpreso se vincessi la tua sfida con questo. Questa è davvero una bella soluzione.
Alex A.

25

matematica

Questo utilizza la Inpaintfunzione di Mathematica . Poiché Mathematica stessa fa tutto il lavoro pesante, questa è una wiki della comunità.

inPaint(sotto) è un semplice adattamento di Inpaint. Per dipinti / foto a colori, utilizza l'impostazione predefinita "TextureSintesi". Se rileva che l'immagine è in bianco e nero (poiché i dati dell'immagine sono uguali ai dati dell'immagine della forma binaria dell'immagine), binarizza l'immagine e applica la patch "TotalVariation". La Ifclausola si applica Binarizeo Identityall'immagine. (La Identityfunzione restituisce il suo argomento invariato.)

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

L'immagine e la maschera vengono inserite come argomenti per inPaint. Partitione Gridsono solo a scopo di formattazione.

ingresso

Le uscite sono state patchate. Non è stato possibile ritoccare le immagini dopo inPaint.

produzione


4
Potrebbe essere una coincidenza, ma sono stupito dall'esibizione del labirinto!
flawr

1
@flawr Aggiungerei qualcosa del genere solo per scherzare con questa soluzione;) (Chissà? Quei bianchi e neri sono davvero sconcertanti.)
Andras Deak

17
Non penso che questo dovrebbe essere un wiki della comunità.
Dennis,

Lawr, Sì, Inpaintsembra cercare simmetrie nell'intera immagine in bianco e nero. - DavidC 9 ore fa
DavidC

Sei sicuro che l'algoritmo in bianco e nero non implichi il sacrificio di capre da nessuna parte? Come --- sulla Terra --- diavolo indovina la struttura centrale dell'immagine, se tutto è mascherato ??
Andras Deak,

18

Python 2 e PIL

Questo programma unisce copie delle regioni Nord, Sud, Est e Ovest per creare pixel sostitutivi che utilizzano colori, trame e ombre dalla regione dell'immagine locale.

L'esempio mostra:

Il codice trova prima il rettangolo di selezione per la patch. Quindi, per ogni pixel da generare, calcola il colore di ciascun canale (RGB) in base alla somma ponderata delle 4 regioni circostanti.

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
Anche questa Gioconda è terrificante! Tutti i Mona Lisas in questa sfida sono condannati a fare paura?
undergroundmonorail,

@undergroundmonorail Immagino che volti accidentali generati dal computer provengano proprio dalle profondità della misteriosa valle .
Andras Deak,

Da dove prendi il PIL?
Elliot A.

@ElliotA. La mia comprensione è che PIL proprio è morto, ma era open source e quindi vive sotto il nome di "Cuscino". Se google "cuscino in pitone" dovresti trovarlo.
undergroundmonorail

13

Python 3, PIL

Questo programma utilizza l'operatore sobel e traccia linee sull'immagine in base a ciò.

L'operatore sobel trova l'angolo di ciascun bordo, quindi eventuali bordi che escono nell'area sconosciuta dovrebbero continuare.

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

Nel frattempo, ecco le immagini di esempio.

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

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

inserisci qui la descrizione dell'immagine L'area nell'immagine sopra è liscia come un cactus

Non è molto buono con il colore costante.

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine


1
Oh, e potresti aggiungere i casi di test in bianco e nero?
Flawr

2
Sembra davvero buono su quello della Notte stellata.
SuperJedi224,

1
Caspita, adesso sembra incredibile! Le patch sono ancora evidenti ma una grande nuova idea! Il mio preferito finora =)
flawr

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

2
Vinci il concorso "Mona Lisa" più spaventoso, IMO. 0_o
DLosc

8

Python 2

Semplice script in Python che crea patch usando i valori dei pixel appena fuori dal gap. Prende i valori di colore dalla fine della riga e della colonna dei pixel e calcola la media ponderata usando la distanza da quei pixel.

L'output non è così carino, ma è arte .

img1 img2 img3 img4 img5 img6

E poi, codice:

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

Al momento vedi quelle strisce orizzontali / verticali, forse puoi migliorarle includendo altre direzioni!
Flawr

In realtà l'ho provato, ma non sono riuscito a ottenere buoni risultati, quindi ho appena deciso di sfocare l'immagine: D
Hannes Karppila,

19
Alla fine, una Gioconda che non mi spaventa a morte, ma sembra invece un assassino arrestato.
Andras Deak,

6

matematica

Inpaint

Accade solo che Mathematica abbia una funzione integrata che svolge esattamente questo compito, e intendo esattamente :

Inpaint[image, region]

  • ritocca parti imagecorrispondenti a elementi diversi da zero region.

Per impostazione predefinita utilizza un "metodo di sintesi delle trame più adatto usando il campionamento casuale" che produce buoni risultati sui dipinti, ma scarsi risultati per il labirinto e la scacchiera:

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

Giocare con le impostazioni non mi ha fatto aumentare la qualità di tutte le immagini, quindi ho usato solo le impostazioni predefinite (per salvare i byte, codegolf.sedopo tutto!).


23
" Accade così che Mathematica abbia una funzione integrata " ... sorpresa, sorpresa;)
Andras Deak

Per il labirinto e la scacchiera è meglio usare il metodo "TotalVariation" insieme a Binarize(per eliminare le macchie grigie). Prova questo: methods = {"TextureSynthesis", "Diffusion", "FastMarching", "NavierStokes", "TotalVariation"};g[pic_, mask_] := Join[{Labeled[Framed@pic, "Original"]}, Labeled[ Binarize@Inpaint[pic, mask, Method -> #], #] & /@ methods]
DavidC

@DavidC Ho provato gli altri metodi, ma TextureSynthesissembra buono solo sui dipinti; e non credo che ci sia permesso di ottimizzare le nostre impostazioni per ogni singolo caso di test. (Se potessimo, allora potremmo fornire banalmente la parte mancante come 'impostazione'.)
Campione 2012

I risultati di labirinto e scacchiera sono davvero sconcertanti per me. Perché la ricostruzione di Mathematica della regione mancante è così irregolare e asimmetrica?
David Zhang

Ciò rileverà automaticamente se un'immagine è in bianco e nero ed eseguirà le opportune regolazioni (binari e metodo "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

Questa risposta implementa l'idea nel documento "Deep Image Prior" di Ulyanov et al. (CVPR 2018) In questo articolo hanno esplorato l'idea che il modo in cui le reti neurali ben funzionanti per l'elaborazione delle immagini sono progettate riflette da vicino la nostra idea di come dovrebbe apparire un'immagine naturale (la distribuzione "precedente").

Hanno proposto un metodo che può essere utilizzato per la pittura e per la rimozione del rumore e degli artefatti che utilizza solo l'immagine fornita senza alcuna formazione su altri dati. Il concetto reale è abbastanza semplice: la rete è addestrata per produrre l'immagine desiderata (per alcuni rumori casuali fissi come input) penalizzando solo gli errori al di fuori di una determinata maschera. Se vuoi rimuovere il rumore, non devi solo mascherare nulla, ma fermati all'inizio dell'allenamento.

Per dipingere si maschera la parte che si desidera dipingere e si allena fino alla convergenza. Non è certamente all'avanguardia, ma volevo ancora postarlo provarlo e pubblicarlo qui a causa della semplicità dell'idea e della performance ancora notevole. Nei miei esperimenti, la verniciatura di patch più grandi non è andata così bene, ma per segmenti più piccoli i risultati possono essere molto più convincenti.

L'ho implementato usando la famosa architettura U-Net di jaxony su github . Di seguito è riportato il codice per la formazione e l'elaborazione delle immagini.

Formazione

Questa è una visualizzazione del processo di formazione. Ogni frame è lo stato di un certo numero di iterazioni:

Esempi

Codice

Si noti che su una CPU ciò può richiedere ore per l'esecuzione di una sola immagine, mentre una buona GPU abilitata per Cuda potrebbe richiedere molto meno tempo.

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

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

Deep Image Prior utilizza qualcosa di diverso dalle reti a U? sembra che otterrebbero risultati migliori
solo ASCII

Inoltre, hai provato con il codice di Deep Image Prior
ASCII, solo

@ Solo ASCII Dichiarano nel documento che in effetti usano principalmente l'U-Net, ma non riesco a trovare i parametri esatti che hanno usato. Avrebbero potuto usare una rete con una capacità maggiore. Avevo solo un computer con una quantità di energia molto limitata. Quindi ho dovuto scegliere parametri che si adattano ancora alla memoria e che non hanno richiesto troppo tempo per allenarsi. Non sono sicuro di quanto tempo abbia impiegato esattamente, ma sul computer ho usato (solo con una CPU) queste immagini impiegano più giorni. (Se hai una GPU di riserva abilitata per Cuda fammi sapere :)
flawr

Ho anche il sospetto che a causa del design della rete con maschere rettangolari non sia l'ideale (e probabilmente anche le maschere più piccole apparirebbero meglio), se si confrontano ad esempio le prime immagini con le ultime due (che non usano maschere rettangolari) .
flawr

4

Python con OpenCV

OpenCV ha una funzione chiamata inpaint. Esistono due tipi di verniciatura, userò il metodo della marcia veloce. Secondo la documentazione, l'algoritmo funziona in questo modo:

Considera una regione nell'immagine da dipingere. L'algoritmo inizia dal confine di questa regione e va all'interno della regione riempiendo gradualmente prima tutto il confine. Ci vuole un piccolo quartiere attorno al pixel del quartiere per essere dipinto. Questo pixel è sostituito dalla somma ponderata normalizzata di tutti i pixel noti nel quartiere. La selezione dei pesi è una questione importante. Viene data maggiore ponderazione a quei pixel che si trovano vicino al punto, vicino alla normale del confine e quelli che si trovano sui contorni del contorno. Una volta che un pixel è dipinto, si sposta sul pixel successivo più vicino usando il metodo Marcia veloce. FMM garantisce che i pixel vicini ai pixel noti vengano prima dipinti, in modo che funzioni come un'operazione euristica manuale.

Ecco il codice *:

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)

Nota come converto BGR in RGB per motivi di stampa. Inoltre, lo giro. Ecco i risultati:

Gotico

notte stellata urlare un'altra inquietante mona lisa!

Torna Monna Lisa!

Linea 1

checker

Come puoi vedere, non è il migliore con i due colori.


Mona Lisa ha avuto un lifting
Conor O'Brien,

3

Giava

Un approccio di media dei colori. Probabilmente può essere migliorato.

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

risultati:

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine


2
Perché ottieni sempre linee in questo particolare angolo? Gli angoli in alto a sinistra sembrano corrispondere relativamente bene, mentre la parte in basso a destra non corrisponde affatto.
Flawr

Penso che abbia a che fare con il modo in cui eseguo l'iterazione nella regione. Probabilmente lo cambierò alla fine.
SuperJedi224,

Sembra sempre che ci siano trapezi.
ericw31415,

@ ericw31415 È un artefatto dell'ordine di iterazione.
SuperJedi224,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.