イメージのパッチ


114

一般的な画像編集ソフトウェアには、パッチのにある情報に基づいて、画像の選択された領域にパッチを当てる機能があります(画像処理で使用される用語は、@mınxomaτが指摘するように修復します)。そして、それが単なるプログラムであることを考えると、かなり良い仕事をします。人間としては、何かが間違っていることを時々見ることができますが、目を絞ったり、ちょっと見たりしただけで、パッチ隙間を非常によく埋めているように見えます。

一般的な画像編集ソフトウェアの例

チャレンジ

画像と画像の矩形領域を指定するマスク(画像、または他の任意の形式)をパッチする必要がある場合、プログラムは指定された領域を残りの部分とブレンドしようとするパッチで塗りつぶそうとします画像。プログラムは、指定された領域内にあった元の画像の情報を使用できません。

パッチは常に少なくとも側面から幅が離れており、画像の上下から高さが離れていると仮定できます。つまり、パッチの最大領域は画像全体の1/9になります。

アルゴリズムの仕組みに関する簡単な説明を追加してください。

投票

投票者は、アルゴリズムのパフォーマンスを判断し、それに応じて投票するよう求められます。

判断方法に関するいくつかの提案:(もう一度、いくつかの基準について@mınxomaτに感謝します。)

  • 目を細めて見ると写真がきれいに見えますか?
  • パッチの場所を正確に知ることができますか?
  • 画像の背景と周囲の領域からの構造とテクスチャはどれくらいうまく継続していますか?
  • 編集領域に含まれる浮遊偽色ピクセルはどれくらいですか?
  • そこに属していないように見える領域に均一に色付けされた塊/ブロックがありますか?
  • 編集された領域には、画像の残りの部分と比較して、大幅な色/コントラストまたは輝度のシフトがありますか?

有効性基準

提出を有効にするには、指定された領域外の出力画像が入力画像と正確に一致する必要があります。

テストケース

左側にソース画像、右側に対応するマスク:


1
マスク入力をテキスト引数(例:)として受け入れることができますinpaint.exe left top width height img.jpgか?
mınxomaτ

1
確かに、入出力フォーマットはそれほど重要ではありません。これは、アルゴリズムのパフォーマンスがまず重要である人気のコンテストです。
-flawr

24
これは非常に実用的な課題です。GIMPや他のオープンソース画像編集ソフトウェアで使用されている既存のアルゴリズムよりも結果が良くなる可能性があります。フォーチュン、フェイム、グローリーはあなたのものかもしれません!
スパー

6
@Sparrおよび最終的にい透かしは、ダウンロードしたメディアから削除できます;)
アンドラスDeak

2
ビルトインはまったく問題ありませんが、非常に人気があるとは思いません。
-flawr

回答:


142

AutoIt、VB

前書き

これは、A。Criminisi、P。Perez(Cambridge Microsoft Research Ltd.)およびK. Toyama(Microsoft)[X]によって開発された、Exemplarベースのインペインティングアルゴリズムによるオブジェクト削除の実装です。このアルゴリズムは、高情報画像(およびビデオフレーム)を対象としており、構造再構築と有機再構築のバランスを目指しています。この回答の段落には、この回答をより充実したものにするために、元の論文の全文引用が含まれています(公式には入手できなくなっているため)。

アルゴリズム

目標:選択された(マスクされた)領域(できれば視覚的に分離された前景オブジェクト)を視覚的にもっともらしい背景に置き換えます。

過去の研究で、いくつかの研究者は、テクスチャ合成を、大きな画像領域を「純粋な」テクスチャで埋める方法、つまり適度な確率を持つ2次元のテクスチャパターンであると考えてきました。これは、純粋なテクスチャ[1] [8] [9] [10] [11] [12] [14]の小さなソースサンプルが与えられたときに、テクスチャを無限に複製しようとするテクスチャ合成研究の大部分に基づいています。[15] [16] [19] [22]

空間的に対話する複数のテクスチャ-これらの技術として有効で一貫性のあるテクスチャーを複製しているので、それらは困難しばしば線状構造および複合テクスチャで構成され、実世界のシーンの撮影の穴を、充填持っている[23] 。主な問題は、画像領域間の境界が異なるテクスチャ間の相互影響の複雑な積であるということです。純粋なテクスチャの2次元の性質とは対照的に、これらの境界は、より1次元または線形の画像構造と考えられるものを形成します。

画像修復テクニックは、拡散を介して線形構造(修復文学ではアイソフォートと呼ばれる)をターゲット領域に伝播することにより、画像の穴を埋めます。それらは物理的熱流の偏微分方程式に触発され、復元アルゴリズムとして説得力を持って機能します。それらの欠点は、拡散プロセスがいくらかのぼけをもたらすことであり、これは顕著です。

イチジク。 2

塗りつぶされる領域、つまりターゲット領域はbyで示され、その輪郭はδΩで示されます。輪郭はアルゴリズムが進むにつれて内側に進化するため、「塗りつぶし前線」とも呼ばれます。アルゴリズム全体を通して固定されたままのソース領域Φは、充填プロセスで使用されるサンプルを提供します。ここで、アルゴリズムの単一の反復に焦点を当て、構造とテクスチャが標本ベースの合成によって適切に処理される方法を示します。点pを中心とする正方形のテンプレートΨp∈Ω(図2b)が満たされると仮定します。ソース領域からの最適なサンプルは、パッチΨqˆ∈Φから取得されます。これは、すでにΨpで埋められている部分に最も類似しています。図の例では 2b、Ψpが画像エッジの連続にある場合、最も可能性の高い最良の一致は、同じ(または同様の色の)エッジ(たとえば、図2cのΨq 'およびΨq' ')に沿って存在します。isophoteを内側に伝播するために必要なのは、最適なソースパッチからパターンを単純に転送することだけです(図2d)。isophoteの向きは自動的に保持されることに注意してください。図では、元のエッジがターゲットの輪郭δΩに直交していないという事実にもかかわらず、伝播された構造はソース領域と同じ方向を維持しています。

実装とアルゴリズムの詳細

この実装の機能は、ホストプログラムからバイナリとしてドロップされたActiveX COM DLLにカプセル化され、IIDによってinpainterを呼び出すことでその場で呼び出されます。この特定のケースでは、APIはVisualBasicで記述されており、COM対応言語から呼び出すことができます。コードの次のセクションは、バイナリをドロップします。

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

ライブラリは、後でCLSIDとIIDを使用してインスタンス化されます。

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

ライブラリは、GDIOBJECTハンドル、特にGDI / +ビットマップ(ファイル、ストリームなど)のDIBSectionを受け入れます。指定された画像ファイルが読み込まScan0れ、入力画像の次元から構築された空のビットマップに描画されます。

この実装の入力ファイルは、マスクされた画像データを含むGDI / +互換のファイル形式です。マスク(S)は、入力画像内の1つ以上の均一に着色された領域です。ユーザーはマスクにRGBカラー値を指定します。正確にそのカラー値を持つピクセルのみが一致します。デフォルトのマスキング色は緑(0、255、0)です。マスクされたすべての領域は、一緒に削除および塗りつぶされるターゲット領域Ωを表します。ソース領域Φは、画像全体からターゲット領域を引いたものとして定義されます(Φ= I−Ω)。

次に、すべての標本ベースのテクスチャ合成[10]と同様に、テンプレートウィンドウのサイズΨ(別名「スキャン半径」)を指定する必要があります。この実装はデフォルトのウィンドウサイズ6²ピクセルを提供しますが、実際には、ソース領域の最大の識別可能なテクスチャ要素、つまり「テクセル」よりもわずかに大きく設定する必要があります。元のアルゴリズムに対する追加の変更は、ユーザー定義可能な「ブロックサイズ」であり、これにより、新しい均一な色で置き換えられるピクセルの領域が決定されます。これにより、速度が向上し、品質が低下します。1pxよりも大きいブロックサイズは、非常に均一な領域(水、砂、毛皮など)で使用することを意図していますが、Ψは最大に維持する必要があります。.5xブロックサイズ(マスクによっては不可能な場合があります)。

1ビット画像でアルゴリズムを停止させないために、5色未満の画像を受信するたびに、ウィンドウサイズが10倍に増幅されます。

これらのパラメータが決定されると、残りの領域充填プロセスは完全に自動化されます。このアルゴリズムでは、各ピクセルは色の値(または、ピクセルが塗りつぶされていない場合は「空」)と信頼値を維持します。信頼値は、ピクセル値に対する信頼を反映し、ピクセルが塗りつぶされると固定されます。アルゴリズムの過程で、フィルフロントに沿ったパッチにも一時的な優先度の値が与えられます。これにより、パッチが満たされる順序が決まります。次に、すべてのピクセルが満たされるまで、次の3つのステップをアルゴリズムが繰り返します。

ステップ1:パッチの優先度を計算する

充填順序は、ノンパラメトリックテクスチャ合成[1] [6] [10] [13]にとって重要です。これまでのところ、デフォルトのお気に入りは「タマネギの皮」法で、ターゲット領域は外側から内側に同心円状に合成されます。このアルゴリズムは、フィルフロントの各パッチに割り当てられた優先度値に完全に依存するベストファーストフィルアルゴリズムを介してこのタスクを実行します。優先度の計算は、強いエッジの連続上にあり、信頼性の高いピクセルに囲まれているパッチに偏っています。これらのピクセルは、値-2でマークされた境界です。次のコードは、優先順位を再計算します。

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

いくつかのp∈δΩ(図3を参照)の点pを中心としたパッチΨpが与えられると、その優先度P(p)は計算された信頼度(ComputeConfidence、またはC(p))とデータ項ComputeData、またはD(p))、ここで

、 どこ

|Ψp| はΨpの面積、αは正規化係数(たとえば、標準的なグレーレベルイメージの場合はα= 255)、npは点pのフロントδΩに直交する単位ベクトルです。優先度はすべての境界パッチに対して計算され、ターゲット領域の境界上のピクセルごとに個別のパッチが適用されます。

として実装

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

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

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

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

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

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

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

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

信頼性項C(p)は、ピクセルpを囲む信頼できる情報の量の尺度と考えることができます。意図は、ピクセルの多くが既に塗りつぶされているパッチを最初に塗りつぶすことであり、早期に塗りつぶされたピクセル(またはターゲット領域の一部ではなかったピクセル)が優先されます。

これにより、塗りつぶし前線に沿った特定の形状に対する設定が自動的に組み込まれます。たとえば、元の画像のより多くのピクセルに囲まれているため、ターゲット領域の角と細いthinを含むパッチが最初に塗りつぶされる傾向があります。これらのパッチは、一致する信頼性の高い情報を提供します。逆に、ターゲット領域に突き出ている塗りつぶされたピクセルの「半島」の先端にあるパッチは、周囲のピクセルがさらに塗りつぶされるまで脇に置かれる傾向があります。大まかなレベルでは、(1)望ましい同心円の充填順序を強制します。

塗りつぶしが進むと、ターゲット領域の外側のレイヤーのピクセルは、より高い信頼値によって特徴付けられる傾向があるため、より早く塗りつぶされます。ターゲット領域の中心にあるピクセルの信頼度は低くなります。データ項D(p)は、反復ごとにフロントδΩに当たるアイソフォテートの強度の関数です。この用語は、アイソフォアが「流れ込む」パッチの優先度を高めます。この要因は、線形構造を最初に合成し、ターゲット領域に安全に伝搬することを促進するため、アルゴリズムで基本的に重要です。破線は接続する傾向があるため、視覚心理学の「接続原理」を実現します[7] [17]

塗りつぶしの順序は画像の特性に依存するため、「壊れた構造」のアーティファクトのリスクを排除し、高価なパッチカットステップ[9]またはブラー誘導ブレンディングステップ[19 ]

ステップ2:テクスチャと構造の情報を伝播する

フィルフロント(境界)のすべての優先度が計算されると、最高の優先度を持つパッチΨpˆが見つかります。次に、ソース領域Φから抽出したデータを入力します。ソース領域の直接サンプリングにより画像テクスチャを伝播します。同様に[10] 、我々はΨpに最も類似しているパッチのソース領域に検索します。正式に、

、 どこ

2つの一般的なパッチΨaとΨbの間の距離d(Ψa、Ψb)は、2つのパッチの既に塗りつぶされたピクセルの差の平方和(SSD)として単純に定義されます。このステップではそれ以上の分析や操作(特にぼかしなし)は行われません。この計算はメインサイクルループで実行され、次のように実装されます。

最大の優先度を取得する:

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

最も類似したパッチを見つける:

min = 99999999

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

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

ステップ3:信頼値を更新する

パッチΨpˆが新しいピクセル値で満たされた後、次のように、Ψpˆで区切られた領域の信頼度C(p)が更新されます。

この単純な更新ルールにより、画像固有のパラメーターなしで、フィルフロントのパッチの相対的な信頼度を測定できます。塗りつぶしが進むと、信頼値が低下し、ターゲット領域の中心付近のピクセルのカラー値の確信度が低くなることを示します。ここに実装されます(他のすべての必要な更新とともに):

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

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

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

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

完全なコード

ここだコメントとしてのライブラリのソースコードとの完全な実行可能なコードは、。

コードはによって呼び出されます

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

例は次の形式で含まれています

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

CTRL+ を使用して実行する例をコメント解除しますQ

公式テストファイル

このアルゴリズムはさなさ画像毎に調整します。したがって、デフォルト値(およびデフォルトマスク)は完全に最適ではありません。すべてのサンプルが妥当な時間内に処理されるように、デフォルト値が選択されています。不規則な形のマスクとより良いウィンドウサイズで遊ぶことを強くお勧めします。画像をクリックすると拡大します!

チェッカーボード

ゴシックアメリカ

迷路

モナリザ

(ひどいマスク)

悲鳴

星空

実世界の例

これらはすべて、カスタムの手描きのマスクを使用します。

他の興味深い画像を含めたい場合は、コメントを残してください。

EBIIの改善

EBIIには、さまざまな研究者によって作成された複数のバリアントがあります。AnkurKumar Patelは、さまざまなEBIIの改善に関する論文集[24]に注目しました。

具体的には、論文「模範に基づく画像修復のためのロバストなアルゴリズムの改善[25]は、優先度値の重み付けに関する2つの改善点に言及しています。

改善

効果的な変更はアルゴリズムのステップ1(上記を参照)にあり、これを使用してこのピクセルの優先度評価に対するC(p)およびD(p)効果を拡張します。

上記のCおよびDの式では、それぞれ正規化係数(たとえば、α= 255)、等値線ベクトル、および点pの正面に直交する単位ベクトルです。

さらに、

優先度関数は、正規化された信頼性項C(p)と新しいデータ項D(p)の重み合計として定義されます。αが調整係数である場合、0Rp(p)を満たすことは次のように定義されます。

ここで、αとβはそれぞれ信頼性とデータ項の成分の重みです。α+β= 1であることに注意してください。

客観的なスコアリング

しかし、本当に興味深いのは、このペーパーに、EBIIアルゴリズムのパフォーマンスを評価するための提案された(そして簡単な!)方法が含まれていることです。ただし、これは論文著者自身が選択した方法であり、提案された分散アプローチの有効性といくつかの画像の改善を検証するためのものです。

結果の評価は、復元された画像と元の画像のPSNR(ピーク信号対ノイズ比[26])を比較することにより実行されます。一般に、PSNR値が高いほど、修復された画像と元の画像の類似性が大きくなります。PSNRを計算する式は次のとおりです。

これらは、彼らが使用した驚異的な2(2!)実世界のテスト画像です。

結論は、論文自体の品質と同じくらい残念です。改善はほとんどありません。ここでの主なことは、この種の課題(およびその他の画像修復の課題)の可能なオブジェクトスコアリング方法です。

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

えー

行われる研究

(EBIIに固有)

a)前処理

すべては、アルゴリズムがすべてに対して「機能する」という「Magic Erase」の原則に基づいています。これに対する私の素朴な解決策は色ベースの増幅です(上記参照)が、もっと良い方法があります。トレース可能なすべてのテクセルの幾何平均を認識してウィンドウサイズを自動調整し、スタンプサイズ(また、私の改善)をテクセルおよび画像全体の解像度に依存させることを考えています。ここで研究を行う必要があります。

b)後処理

オリジナルの著者は、頭に浮かんだすべての後処理フィルターを暴く素晴らしい仕事をすでにしました。今日、私はいつも不気味なモナリザにインスパイアされた他の何かを試しました(undergroundmonorailに感謝します)。ペイントされた領域だけを取り、すべての奇妙な色のブロックに新しいマスクを適用し、それをスペックル除去アルゴリズムに入力すると、ほぼ完璧な結果が残ります。将来的にはこれを検討するかもしれません。


[X] — A.クリミニシ、P。ペレス、K。富山による標本ベースのインペインティングによるオブジェクトの削除
[1] — M.アシクミン。自然な質感を合成します。Procで ACM Symp。on Interactive 3D Graphics、pp。217–226、Research Triangle Park、NC、2001
Mar. [5] — M. Bertalmio、L。Vese、G。Sapiro、およびS. Osher。構造とテクスチャ画像の同時修復。2002年
[6] — R. Bornard、E。Lecan、L。Laborelli、およびJH。シェノ。静止画像および画像シーケンスの欠落データ修正。ACMマルチメディア、フランス、12月2002年
[7] - TFチャン及びJ.シェン。曲率駆動拡散(CDD)による非テクスチャ修復。J.ビジュアル通信。Image Rep。、4(12)、2001
[8] — JS de Bonet。テクスチャ画像の分析と合成のための多重解像度サンプリング手順。Procで ACM設定 比較 グラフィックス(SIGGRAPH)、第31巻、361〜368、1997年。
[9] — A.エフロスとWTフリーマン。テクスチャの合成と転送のための画像キルティング。Procで。ACM設定 比較 グラフィックス(SIGGRAPH)、341〜346ページ、Eugene Fiume、2001年8月。
[10] — A. EfrosおよびT. Leung。ノンパラメトリックサンプリングによるテクスチャ合成。Procで ICCV、pp。1033-1038、ケルキラ、ギリシャ、1999年9月。
[11] — WTフリーマン、ECパスター、およびOTカーマイケル。低レベルの視覚の学習。Int。J.コンピュータービジョン、40(1):25–47、2000。
[12] — D.ガーバー。テクスチャ分析およびテクスチャ合成の計算モデル。博士論文、大学 米国、南カリフォルニア、1981年。
[13] — P.ハリソン。複雑なテクスチャの再合成のための非階層的な手順。Procで Int。確認 中央ヨーロッパコンプ。グラフィック、Visua。とコンプ。Vision、Plzen、チェコ共和国、2001年2月。
[14] — DJ HeegerとJR Bergen。ピラミッドベースのテクスチャ分析/合成。Procで ACM設定 比較 グラフィックス(SIGGRAPH)、第29巻、229〜233ページ、カリフォルニア州ロサンゼルス、1995年
。画像のアナロジー。Procで ACM設定 比較 グラフィックス(SIGGRAPH)、ユージンフィウメ、2001年8月。
[16] — H.イゲヒおよびL.ペレイラ。テクスチャ合成による画像置換。Procで Int。確認 画像処理、pp。III:186–190、1997。
[17] — G.カニツァ。ビジョンの組織。1979年、ニューヨークのPraeger。
[19] — L. Liang、C。Liu、Y.-Q。Xu、B。Guo、およびH.-Y. シャム。パッチベースのサンプリングによるリアルタイムテクスチャ合成。ACM Transactions on Graphics、2001。
[22] — L.-W. ウェイとM.レボイ。ツリー構造のベクトル量子化を使用した高速テクスチャ合成。Procで ACM設定 比較 グラフィックス(SIGGRAPH)、2000。
[23] — A.ザレスニー、V。フェラーリ、G。カーネン、およびL.ファングール。並列複合テクスチャ合成。(ECCV02と共に)、コペンハーゲン、デンマーク、6月2002 -テクスチャ2002のワークショップでは
、[24] - AkurKumarパテル、グジャラート州工科大学、コンピュータサイエンスとエンジニアリング
[25] - 模範ベースの画像インペインティングのための改良された堅牢なアルゴリズム
- [26] ウィキペディア、ピーク信号対ノイズ比


30
これはすごいです。星空の夜はとても良いです。それでも、そのモナリザ...
ハンネスKarppila

8
最初に「ああ、これはすごい」と言いましょう。2番目:ここで別の質問で「そのモナリザはSCPの糞だ」とコメントしましたが、実際にはそのフクロウはSCP wikiに表示されるもののように見えます。
地下

3
引用した段落を引用ブロックにすることはできますか?
-trichoplax

1
@trichoplaxほとんどすべての文に小さな変更がありますが、それらは正確な引用ではありません。組織と同じアルゴリズムの説明を検討してください。変更またはコードを言う場合を除き、紙。私はもうフォーマットを乱雑にしたくない:)
mınxomaτ16年

2
夢の中で何かを非常に注意深く見ようとしたとき、時々物事はまさにこのようになります。
jimmy23013 16

45

Matlab

これは単純な補間アプローチです。このアイデアは、まずパッチの両側にあるものをミラーリングすることです。次に、それらの鏡像ピクセルは、対応するエッジにどれだけ近いかによって補間されます。

トリッキーな部分は、素敵な補間重みを見つけることでした。いくつか遊んだ後、ミラーリングしたエッジを除くすべてのエッジでゼロになる有理関数を思いつきました。次に、これは何らかの平滑化のために3次多項式によって変換されます。

この単純なアプローチは、「自然な」画像では驚くほどうまく機能しますが、鋭いエッジに直面するとすぐにゲームオーバーになります。ではアメリカン・ゴシックそれはかなり素敵に見えるのですが、それははるかに悪いそうであったであろうピクセルグリッドとうまく干し草フォークラインアップの例のスパイク。

結果は次のとおりです。

そして最後に、コード:

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

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

10
モナリザは私をゾッとさせる。
アンドラスディーク

46
Ḿ̳̜͇͓͠o̢̓ǹ̰͎̣͙a̤̩̖̞̝ͧ̈ͤͤLISA
mınxomaτ

8
モナリザはSCPのたわごとです
地下

1
市松模様の画像は本当にかっこいいです。
ETHproductions

1
あなたがこれであなた自身の挑戦に勝ったならば、私は驚かないでしょう。これは本当に素晴らしい解決策です。
アレックスA.

25

Mathematica

これはMathematicaのInpaint関数を使用します。Mathematica自体がすべての面倒な作業を行うため、これはコミュニティwikiです。

inPaint(以下)はの単純な適応ですInpaint。色付きの絵画/写真の場合、デフォルトの「TextureSynthesis」設定を使用します。画像が白黒であることを検出した場合(画像の画像データは画像のバイナリ形式の画像データと同じであるため)、画像を二値化し、「TotalVariation」パッチを適用します。If句が適用されますいずれかBinarizeまたはIdentity絵に。(Identity関数は引数を変更せずに返します。)

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

画像とマスクはの引数として入力されますinPaintPartitionそしてGrid書式設定の目的のために過ぎません。

入力

出力にはパッチが適用されています。その後の画像のレタッチはありませんでしたinPaint

出力


4
偶然かもしれませんが、迷宮のパフォーマンスには驚かされます!
-flawr

1
@flawr このソリューションを台無しにするために、このようなものを投げ入れます;)(誰が知っていますか?それらの白黒は本当に困惑しています。)
アンドラスディーク

17
これはコミュニティwikiであるとは思わない。
デニス

弁護士は、はい、Inpaint白黒画像全体で対称性を探しているようです。– DavidC 9時間前
DavidC

あなたは必ず黒と白のアルゴリズムはどこでもヤギを犠牲に関与していないのですか?どのように---地球上で---地獄は、すべてがマスクされている場合、画像の中心構造を推測しますか?
アンドラスDeak

18

Python 2およびPIL

このプログラムは、北、南、東、西の地域のコピーをブレンドして、ローカル画像地域の色、テクスチャ、シェーディングを使用する置換ピクセルを作成します。

出力例:

コードは最初にパッチの境界ボックスを見つけます。次に、生成される各ピクセルについて、周囲の4つの領域の加重和に基づいて各チャネルの色(RGB)を計算します。

import sys
from PIL import Image

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

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

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

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

imageobj.save(outfile)

3
このモナリザも恐ろしいです!このチャレンジのすべてのモナリザは恐ろしい運命にありますか??
地下

@undergroundmonorailコンピューターで生成された偶発的な顔は、不気味な谷の奥深くから来ていると思います。
アンドラスDeak

PILはどこから入手しますか?
エリオットA.

@ElliotA。私の理解では、PIL自体は死んでいますが、それはオープンソースであったため、「Pillow」という名前で生き続けています。「python Pillow」をGoogleで検索すると、見つかるはずです。
地下

13

Python 3、PIL

このプログラムはsobel演算子を使用し、それに基づいて画像に線を描画します。

ソーベル演算子は各エッジの角度を検出するため、未知の領域に押し出されたエッジは継続するはずです。

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

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

一方、ここにサンプル画像があります。

ここに画像の説明を入力してください

ここに画像の説明を入力してください

ここに画像の説明を入力してください

モナ・リザモナ・リサ・ムショナ̾̇リサ・ムショナズナ̣̖̠̮̘̹̠̾̇ͣリサ・ムショナズナ̣̖̠̮̘̹̠̾̇ͣリシズナ

ここに画像の説明を入力してください 上の画像の領域はサボテンと同じくらい滑らかです

一定の色ではあまり良くありません。

ここに画像の説明を入力してください

ここに画像の説明を入力してください


1
ああ、B / Wテストケースを追加してください。
flawr

2
Starry Nightの1つで本当によさそうです。
SuperJedi224 16

1
うわー、これは今信じられないほどに見えます!パッチはまだ目立っていますが、素晴らしい新しいアイデアです!これまでのところ私のお気に入り=)
flawr

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

2
「最も恐ろしいモナリザ」コンテストIMOで優勝します。0_o
DLosc

8

Python 2

ギャップのすぐ外側のピクセルの値を使用してパッチを作成する単純なPythonスクリプト。ピクセルの行と列の端からカラー値を取得し、それらのピクセルからの距離を使用して加重平均を計算します。

出力はそれほどきれいではありませんが、芸術です。

img1 img2 img3 img4 img5 img6

そして、コード:

IMGN = "6"

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

BLUR = 5


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

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

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

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

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

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

# create patch
patch = pygame.Surface(patch_size)

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

cpixels = top+left+right+bottom

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

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

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

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

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

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

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

現在、これらの水平/垂直ストライプが表示されていますが、おそらく他の方向を含めることで改善できます!
-flawr

私は実際にそれを試してみましたが、私は良い結果を得ることができなかったので、私はちょうどちょうど画像ぼかすことにしました:D
ハンネスKarppila

19
最後に、モナリザ私を死ぬほど怖がらせませんが、代わりに逮捕された殺人者のように見えます。
アンドラスDeak

6

Mathematica

Inpaint

ちょうどので、たまたまMathematicaはまさにこのタスクを実行する組み込み関数を持っている、と私は意味を正確に

Inpaint[image, region]

  • image非ゼロ要素に対応する部分をレタッチしregionます。

デフォルトでは、「ランダムサンプリングを使用した最適なテクスチャ合成方法」を使用します。これにより、絵画では良好な結果が得られますが、迷路やチェッカーボードでは不十分な結果が生成されます。

ここに画像の説明を入力してください ここに画像の説明を入力してください ここに画像の説明を入力してください ここに画像の説明を入力してください ここに画像の説明を入力してください ここに画像の説明を入力してください

設定をいじってみても、すべての画像の品質が向上したわけではないので、デフォルトを使用しました(バイトを節約するためです-これはcodegolf.se結局です!)。


23
たまたまMathematicaに組み込み関数があるのは …」驚き、驚き。)
アンドラス・デック

迷路とチェッカーボードの場合は、「TotalVariation」メソッドを併用することをお勧めしますBinarize(グレーの汚れを除去するため)。これを試してください: methods = {"TextureSynthesis", "Diffusion", "FastMarching", "NavierStokes", "TotalVariation"};g[pic_, mask_] := Join[{Labeled[Framed@pic, "Original"]}, Labeled[ Binarize@Inpaint[pic, mask, Method -> #], #] & /@ methods]
DavidC

@DavidC私は他の方法を試しましたがTextureSynthesis、絵画でのみ見栄えがします。また、個々のテストケースごとに設定を調整することは許可されていないと思います。(できれば、不足している部分を「設定」として簡単に提供できます。)
2012rcampion

迷路と市松模様の結果は本当に私にとって不可解です。欠損領域のMathematicaの再構築がなぜ不規則で非対称なのですか?
デビッドチャン

これにより、画像が白黒かどうかが自動的に検出され、適切な調整が行われます(バイナリと「TotalVariation」メソッド)。 inPaint[picture_, mask_] := If[bw = ImageData@Rasterize[Binarize[picture]] == ImageData[picture], Binarize, Identity]@ Inpaint[picture, mask, Method -> If[bw, "TotalVariation", "TextureSynthesis"]]
DavidC

5

Python3

この答えは、ウリヤノフらによる論文「Deep Image Prior」のアイデアを実装しています。(CVPR 2018)この論文では、画像処理用のパフォーマンスの良いニューラルネットを設計する方法が、自然画像がどのように見えるべきかという考え方(「前の」分布)を密接に反映しているという考えを探りました。

彼らは、他のデータの訓練なしで与えられた画像を使用するだけでなく、修復やノイズやアーティファクトの除去に使用できる方法を提案しました。実際の概念は非常に単純です:ネットは、特定のマスクの外側のエラーのみにペナルティを課すことにより、(入力としての固定ランダムノイズに対して)目的の画像を出力するようにトレーニングされます。ノイズを除去する場合は、何もマスクする必要はありませんが、トレーニングの早い段階で停止してください。

修復する場合は、修復する部分をマスクし、収束するまでトレーニングします。確かに最先端の技術ではありませんが、アイデアのシンプルさと優れたパフォーマンスのために、試して投稿してみたいと思いました。私の実験では、大きなパッチの修復はうまくいきませんでしたが、小さなセグメントの場合、結果は非常に説得力があります。

私は人気使ってこれを実装U-Netのアーキテクチャからgithubの上jaxonyを。画像のトレーニングと処理のコードは以下にあります。

トレーニング

これは、トレーニングプロセスの視覚化です。すべてのフレームは、一定の反復回数の状態です:

コード

CPUでこれを実行するには1つのイメージだけで数時間かかる場合がありますが、適切なcudaを有効にしたGPUでは時間がかかりません。

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

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

Deep Image PriorはU-Netとは異なるものを使用しますか?彼らはより良い結果を取得したいように思える
ASCIIのみ

また、あなたはディープ画像との事前のコードを試してみました
ASCIIのみ

@ASCIIのみ論文では、実際に主にU-Netを使用していると述べていますが、使用した正確なパラメーターは見つかりません。彼らはより大きな容量のネットを使用したかもしれません。私は非常に限られた電力量のコンピューターしか持っていませんでした。そのため、まだメモリに収まり、トレーニングにそれほど時間がかからないパラメータを選択する必要がありました。どのくらい正確にかかったのかはわかりませんが、使用したコンピューター(CPUのみ)では、これらの画像に数日かかります。(予備のcuda対応GPUがある場合はお知らせください:)
flawr

また、たとえば最初のいくつかの画像と最後の2つの画像(長方形のマスクを使用しない)を比較する場合、長方形のマスクを持つネットワークの設計により、理想的ではない(および小さいマスクもおそらく見栄えが良い)と思われます。
flawr

4

OpenCVを使用したPython

OpenCVには、inpaintと呼ばれる機能があります。使用される修復には2つのタイプがあります。高速マーチング法を使用します。ドキュメントによると、アルゴリズムは次のように機能します。

修復する画像内の領域を考慮します。アルゴリズムは、この領域の境界から始まり、最初に境界内のすべてを徐々に満たす領域内に入ります。修復するために、近隣のピクセルの周りに小さな近傍が必要です。このピクセルは、近傍内のすべての既知のピクセルの正規化された加重合計に置き換えられます。重みの選択は重要な問題です。ポイントの近く、境界の法線の近くにあるピクセル、および境界の輪郭上にあるピクセルには、より多くの重みが与えられます。ピクセルが修復されると、高速マーチング法を使用して次に近いピクセルに移動します。FMMでは、既知のピクセルに近いピクセルが最初に修復されるため、手動のヒューリスティック操作のように機能します。

コードは次のとおりです*:

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

プロットの理由でBGRをRGBに変換する方法に注意してください。また、私はそれを回転させます。結果は次のとおりです。

ゴシック

星が輝く夜 悲鳴 別の不気味なモナリザ!

モナリザが帰ってきた!

ライン1

チェッカー

あなたが見ることができるように、それは私が2色のもので最高ではありません。


モナリザは改築されました
コナーオブライエン

3

Java

カラー平均化アプローチ。おそらく改善できます。

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

import javax.imageio.ImageIO;


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

結果:

ここに画像の説明を入力してください ここに画像の説明を入力してください ここに画像の説明を入力してください ここに画像の説明を入力してください ここに画像の説明を入力してください ここに画像の説明を入力してください


2
なぜこの特定の角度で常に線を引くのですか?左上隅は比較的よく一致しているように見えますが、右下部分はまったく一致していません。
-flawr

それは、この地域を反復する方法に関係していると思います。おそらく最終的には変更するでしょう。
SuperJedi224

台形があるように常に見えます。
ericw31415

@ ericw31415これは反復順序のアーティファクトです。
SuperJedi224
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.