이미지 패치


114

인기있는 이미지 편집 소프트웨어에서 패치 (이미지 처리에 사용 된 용어가되어, 기능이 인 페인팅 @ mınxomaτ 지적한다.) 이미지의 선택 영역을, 정보를 기반으로 외부에서 해당 패치의. 그리고 그것은 단지 프로그램 일 뿐이라는 점에서 꽤 잘합니다. 사람은 때때로 무언가 잘못되었다는 것을 알 수 있지만, 눈을 짜거나 잠깐 훑어 보면 패치 가 그 차이를 상당히 잘 채우는 것처럼 보입니다 .

인기있는 이미지 편집 소프트웨어의 예

도전

이미지의 사각형 영역을 지정하는 이미지와 마스크를 패치해야합니다 (이미지 또는 기타 선호되는 형식). 프로그램은 지정된 영역을 나머지 영역과 혼합하려는 패치로 채워야합니다. 이미지. 프로그램은 지정된 영역 내에있는 원본 이미지의 정보를 사용할 수 없습니다.

패치는 항상 너비가 측면에서 멀리 떨어져 있고 이미지의 상단과 하단에서 멀리 떨어져 있다고 가정 할 수 있습니다. 이는 패치의 최대 영역이 전체 이미지의 1/9임을 의미합니다.

알고리즘 작동 방식에 대한 간단한 설명을 추가하십시오.

투표

유권자들은 알고리즘이 얼마나 잘 수행되는지 판단하고 그에 따라 투표해야합니다.

판단하는 방법에 대한 몇 가지 제안 : (다시 말해 몇 가지 더 많은 기준에 대해 @ mınxomaτ에게 감사드립니다.)

  • 눈을 가늘게 뜨고 사진이 잘 보입니까?
  • 패치가 어디에 있는지 정확히 알 수 있습니까?
  • 이미지 배경과 주변 영역의 구조와 질감이 얼마나 잘 유지됩니까?
  • 편집 된 영역에 스트레이 된 잘못된 컬러 픽셀이 얼마나 포함되어 있습니까?
  • 해당 영역에 속하지 않는 영역에 균일 한 색상의 얼룩 / 블록이 있습니까?
  • 편집 된 영역에 나머지 이미지와 비교하여 색상 / 대비 또는 밝기가 급격하게 변합니까?

유효성 기준

제출이 유효하려면 출력 이미지가 지정된 영역 외부의 입력 이미지와 정확히 일치해야합니다.

테스트 사례

왼쪽의 소스 이미지, 오른쪽의 해당 마스크 :


1
마스크 입력을 텍스트 인수로 받아 들일 수 있습니까 (예 :) inpaint.exe left top width height img.jpg?
mınxomaτ

1
물론 입력 / 출력 형식은 알고리즘 의 모든 성능 이 중요한 인기 경연 대회이기 때문에 실제로 중요하지 않습니다 .
flawr

24
이것은 매우 실용적인 도전입니다. 김프 및 기타 오픈 소스 이미지 편집 소프트웨어에 사용되는 기존 알고리즘보다 결과가 더 좋을 수 있습니다. 운, 명성 및 영광은 당신이 될 수 있습니다!
Sparr

6
@Sparr 및 마지막으로 못생긴 워터 마크는 다운로드 한 미디어에서 제거 할 수 있습니다.)
Andras Deak

2
내장은 완벽하게 괜찮습니다.하지만 그들의 인기가 높을지는 의심합니다.
flawr

답변:


142

AutoIt , VB

소개

A. Criminisi, P. Perez (Cambridge Microsoft Research Ltd.) 및 K. Toyama (Microsoft) [X]가 개발 한 예제 기반 Inpainting 알고리즘에 의한 객체 제거 구현입니다 . 이 알고리즘은 고 정보 이미지 (및 비디오 프레임)를 대상으로하며 구조적 재구성과 유기적 재구성 사이의 균형을 목표로합니다. 이 답변의 단락에는 더 이상 공식적으로 제공되지 않으므로 원본 논문의 전문 인용문이 포함되어 있습니다.

알고리즘

목표 : 선택한 ( 마스크 된 ) 영역 (가급적 시각적으로 분리 된 전경 개체)을 시각적으로 적합한 배경으로 바꿉니다 .

이전 연구에서 몇몇 연구자들은 큰 이미지 영역을 "순수한"텍스처로 채우는 방법으로 텍스처 합성을 고려했습니다. 즉, 중간 정도의 확률을 갖는 반복적 인 2 차원 텍스처 패턴입니다. 이것은 순수한 텍스처의 작은 소스 샘플을 고려할 때 텍스처 광고 인피니티 움 을 복제하려는 대규모 텍스처 합성 연구를 기반으로한다 [1] [8] [9] [10] [11] [12] [14] [15] [16] [19] [22] .

이러한 기법이 일관된 질감을 재현하는 데 효과적이므로 실제 장면의 사진에 구멍을 채우는 데 어려움이 있습니다.이 장면은 종종 선형 구조와 복합 텍스처 (공간적으로 상호 작용하는 다중 텍스처)로 구성됩니다 [23] . 주요 문제는 이미지 영역 사이의 경계가 다른 텍스처간에 상호 영향을 미치는 복잡한 결과라는 것입니다. 순수한 텍스처의 2 차원 적 성질과 대조적으로, 이러한 경계는보다 1 차원 적이거나 선형적인 이미지 구조로 간주 될 수있는 것을 형성합니다.

이미지 인 페인팅 기술은 확산을 통해 선형 구조 (인 페인팅 문헌 에서 이소 포트 로 불림 )를 대상 영역 으로 전파함으로써 이미지의 구멍을 채 웁니다 . 물리적 열 흐름의 부분 미분 방정식에서 영감을 얻어 복원 알고리즘으로 설득력있게 작동합니다. 그들의 단점은 확산 과정에서 약간의 흐림 현상이 발생한다는 점입니다.

무화과.  2

채워질 영역, 즉 목표 영역은 Ω로 표시되고 윤곽은 δΩ로 표시됩니다. 알고리즘이 진행됨에 따라 윤곽이 안쪽으로 진화하므로이를 "전면 채우기"라고도합니다. 알고리즘 전체에서 고정 된 소스 영역 (Φ)은 충전 공정에 사용되는 샘플을 제공합니다. 이제 예제 기반 합성으로 구조와 질감을 적절히 처리하는 방법을 보여주기 위해 알고리즘의 단일 반복에 중점을 둡니다. 점 p (그림 2b)를 중심으로하는 정사각형 템플릿 Ψp ∈ Ω을 채운다 고 가정합니다. 소스 영역에서 가장 일치하는 샘플은 패치 Ψqˆ ∈ Φ에서 가져옵니다.이 패치는 이미 Ψp로 채워진 부분과 가장 유사합니다. 그림의 예에서 도 2b에서, 우리는 Ψp가 이미지 에지의 연속에 있다면, 가장 가능성이 가장 높은 일치 항목은 동일한 (또는 유사하게 채색 된) 가장자리를 따라 배치됩니다 (예 : 그림 2c의 Ψq '및 Ψq' '). 동위 원소를 안쪽으로 전파하는 데 필요한 모든 것은 가장 일치하는 소스 패치에서 패턴을 간단히 전송하는 것입니다 (그림 2d). 동위 원소 방향이 자동으로 유지됩니다. 그림에서, 원래의 가장자리가 목표 윤곽선 δΩ에 직교하지 않는다는 사실에도 불구하고, 전파 된 구조는 소스 영역에서와 동일한 방향을 유지합니다.

구현 및 알고리즘 정보

이 구현의 기능은 ActiveX COM DLL에 캡슐화되어 호스트 프로그램에서 이진 파일로 삭제 된 다음 IID로 인 페인터를 호출하여 즉시 호출됩니다. 이 특정 경우 API는 VisualBasic으로 작성되며 모든 COM 사용 언어에서 호출 할 수 있습니다. 코드의 다음 섹션은 바이너리를 삭제합니다.

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

라이브러리는 나중에 CLSID 및 IID를 사용하여 인스턴스화됩니다.

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

라이브러리는 GDIOBJECT 핸들, 특히 모든 GDI / + 비트 맵 (파일, 스트림 등)의 DIBSection을 허용합니다. 지정된 이미지 파일이로드 Scan0되고 입력 이미지 크기 로 구성된 빈 비트 맵에 그려집니다 .

이 구현을위한 입력 파일은 마스크 된 이미지 데이터를 포함하는 모든 GDI / + 호환 파일 형식입니다. 마스크 (들)는 입력 화상의 하나 개 이상의 균일하게 착색 영역이다. 사용자는 마스크에 RGB 색상 값을 제공하며 해당 색상 값을 가진 픽셀 만 일치합니다. 기본 마스킹 색상은 녹색 (0, 255, 0)입니다. 마스크 된 모든 영역은 함께 제거되고 채워질 대상 영역 Ω을 나타냅니다. 소스 영역 Φ는 전체 이미지에서 대상 영역을 뺀 것으로 정의됩니다 (Φ = I-Ω).

다음으로, 모든 예시 기반 텍스처 합성 [10]에서 와 같이, 템플릿 창 Ψ (일명 " 스캔 반경 ") 의 크기를 지정해야합니다. 이 구현은 기본 창 크기 6² 픽셀을 제공하지만 실제로는 사용자가 소스 영역에서 가장 큰 구별 가능한 텍스처 요소 또는 "텍셀"보다 약간 크게 설정해야합니다. 원래 알고리즘에 대한 추가 수정 은 새로운 균일 한 색상으로 대체 될 픽셀 영역을 결정하는 사용자 정의 가능 " 블록 크기 "입니다. 속도가 빨라지고 품질이 떨어집니다. 1px보다 큰 블록 크기는 매우 균일 한 영역 (물, 모래, 모피 등)과 함께 사용하기위한 것이지만 Ψ는 최대로 유지해야합니다. .5x 블록 크기 (마스크에 따라 불가능할 수 있음)

1 비트 이미지에서 알고리즘을 정지시키지 않기 위해 5 색 미만의 이미지를 수신 할 때마다 창 크기가 10 배로 증폭됩니다.

이러한 매개 변수가 결정되면 나머지 영역 채우기 프로세스가 완전히 자동으로 수행됩니다. 알고리즘에서 각 픽셀은 색상 값 (또는 픽셀이 채워지지 않은 경우 "빈")과 픽셀 값에 대한 신뢰를 반영하고 픽셀이 채워지면 고정되는 신뢰도 값을 유지합니다. 알고리즘을 진행하는 동안 채우기 영역을 따라 패치에 채워지는 순서를 결정하는 임시 우선 순위 값도 제공됩니다. 그런 다음 알고리즘은 모든 픽셀이 채워질 때까지 다음 세 단계를 반복합니다.

1 단계 : 패치 우선 순위 계산

채움 순서는 비모수 적 텍스처 합성에 중요합니다 [1] [6] [10] [13] . 지금까지 가장 선호되는 것은 "양파 껍질 (onion peel)"방법인데, 여기서 대상 영역은 바깥 쪽에서 동 심층으로 합성됩니다. 우리의 알고리즘은 필 프론트의 각 패치에 할당 된 우선 순위 값에 전적으로 의존하는 최상의 필링 알고리즘을 통해이 작업을 수행합니다. 우선 순위 계산은 강한 가장자리가 계속 유지되고 높은 신뢰도의 픽셀로 둘러싸인 패치에 편향됩니다. 이러한 픽셀은 경계이며 값 -2로 표시됩니다. 다음 코드는 우선 순위를 다시 계산합니다.

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

일부 p ∈ δΩ에 대해 점 p를 중심으로 한 패치 Ψp가 주어지면 (그림 3 참조), 우선 순위 P (p)는 계산 된 신뢰도 ( ComputeConfidence, 또는 C (p) )와 데이터 항 ( ComputeData, 또는 D (p) )

, 어디

| Ψp | 는 Ψp의 면적이고, α는 정규화 인자 (예를 들어, 전형적인 그레이 레벨 이미지의 경우 α = 255)이고, np는 포인트 p에서 정면 δΩ에 직교하는 단위 벡터이다. 우선 순위는 모든 경계 패치에 대해 계산되며 대상 영역의 경계에있는 각 픽셀에 대한 개별 패치가 있습니다.

로 구현

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

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

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

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

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

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

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

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

신뢰 항 C (p)는 픽셀 p를 둘러싼 신뢰할 수있는 정보량의 척도로서 생각 될 수있다. 초기에 채워진 (또는 대상 영역의 일부가 아닌) 픽셀을 우선적으로 추가하여 더 많은 픽셀이 이미 채워진 패치를 먼저 채 웁니다.

이것은 채우기 정면을 따라 특정 모양에 대한 환경 설정을 자동으로 통합합니다. 예를 들어, 대상 영역의 모서리와 얇은 덩굴손을 포함하는 패치는 원래 이미지에서 더 많은 픽셀로 둘러싸여 있기 때문에 먼저 채워지는 경향이 있습니다. 이 패치는보다 안정적인 정보를 제공합니다. 반대로, 대상 영역으로 튀어 나온 채워진 픽셀의 "반도"끝에있는 패치는 더 많은 주변 픽셀이 채워질 때까지 옆에 놓이는 경향이 있습니다. 대략적인 수준에서, 대략 (1)의 C (p) 바람직한 동심 채우기 순서를 시행합니다.

충전이 진행됨에 따라, 타겟 영역의 외층에있는 픽셀은 더 큰 신뢰도 값을 특징으로하는 경향이 있으므로, 더 일찍 충전된다; 대상 영역 중앙의 픽셀은 신뢰도가 떨어집니다. 데이터 항 D (p)는 각 반복에서 전면 δΩ에 충돌하는 동위 원소 강도의 함수입니다. 이 용어는 동위 원소가 "흐르는"패치의 우선 순위를 높입니다. 이 요소는 선형 구조가 먼저 합성되어 타겟 영역으로 안전하게 전파되기 때문에 알고리즘에서 근본적으로 중요합니다. 파선은 서로 연결되는 경향이있어 시력 심리학의 "연결 원리"를 실현합니다 [7] [17] .

채우기 순서는 이미지 속성에 따라 달라지며, "파손 된 구조"아티팩트의 위험을 제거하고 값 비싼 패치 절단 단계 [9] 또는 블러 유도 블렌딩 단계 [19 ] 없이 블록 아티팩트를 감소시키는 유기 합성 프로세스가 발생 합니다 [19]. ] .

2 단계 : 텍스처 및 구조 정보 전파

성토 전면 ( 경계 ) 의 모든 우선 순위 가 계산되면 우선 순위가 가장 높은 패치 Ψpˆ를 찾습니다. 그런 다음 소스 영역 Φ에서 추출한 데이터로 채 웁니다. 소스 영역을 직접 샘플링하여 이미지 텍스처를 전파합니다. [10] 과 유사하게 소스 영역에서 Ψpˆ와 가장 유사한 패치를 검색합니다. 공식적으로

, 어디

두 일반 패치 Ψa와 Ψb 사이의 거리 d (Ψa, Ψb)는 단순히 두 패치에서 이미 채워진 픽셀의 제곱 차이 (SSD)의 합으로 정의됩니다. 이 단계에서는 추가 분석 또는 조작 ( 특히 흐림 없음 )이 수행 되지 않습니다 . 이 계산은 메인 사이클 루프에서 실행되며 다음과 같이 구현됩니다.

최대 우선 순위 얻기 :

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

가장 유사한 패치 찾기 :

min = 99999999

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

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

3 단계 : 신뢰도 값 업데이트

패치 Ψpˆ가 새로운 픽셀 값으로 채워진 후, 신뢰도 C (p)는 다음과 같이 Ψpˆ로 구분 된 영역에서 업데이트됩니다.

이 간단한 업데이트 규칙을 사용하면 이미지 별 매개 변수없이 성토 정면의 패치의 상대적 신뢰도를 측정 할 수 있습니다. 채우기가 진행됨에 따라 신뢰도 값이 감소하여 대상 영역의 중심 근처에있는 픽셀의 색상 값이 확실하지 않음을 나타냅니다. 여기에 구현되었습니다 (필요한 다른 모든 업데이트와 함께).

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

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

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

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

완전한 코드

다음 은 실행 가능한 코드이며 라이브러리 소스 코드가 주석으로 완성되어 있습니다.

코드는

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

예는

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

CTRL+를 사용하여 실행하려는 예제의 주석을 해제하십시오 Q.

공식 테스트 파일

이 알고리즘이되어 만들어진 각각의 이미지에 대해 조정되어야한다. 따라서 기본값 (및 기본 마스크)은 완전히 차선책입니다. 모든 샘플을 적당한 시간 안에 처리 할 수 ​​있도록 기본값이 선택됩니다. 불규칙한 모양의 마스크와 더 나은 창 크기로 게임하는 것이 좋습니다. 이미지를 클릭하면 확대됩니다!

서양 장기판

미국 고딕

미로

모나리자

(끔찍한 마스크)

비명

별이 빛나는

실제 사례

이들은 모두 사용자 정의 손으로 그린 ​​마스크를 사용합니다.

포함하고 싶은 다른 흥미로운 이미지가 있으면 의견을 남기십시오.

EBII 개선

다양한 연구원들이 만든 EBII의 여러 변형이 있습니다. AnkurKumar Patel 은 다양한 EBII 개선에 관한 논문 모음 [24] 에 주목을 받았습니다.

구체적으로, " 예시적인 이미지 인 페인팅을위한 개선 된 견고한 알고리즘 " [25] 논문 은 우선 순위 값의 계량에 대한 두 가지 개선점을 언급한다.

개선

효과적인 수정은 알고리즘의 1 단계 (위 참조)에 있으며 다음을 사용하여이 픽셀의 우선 순위 등급에 대한 C (p)D (p) 효과를 확장합니다.

위한 화학식 CD 위에서 주어진, 그리고 각각 정규화 인자이다 (예를 들면, α = 255)의 isophote 벡터 및 전면 단위 벡터 직교 P 점이다.

더욱이,

우선 순위 함수는 정규화 된 신뢰 항 C (p) 와 새로운 데이터 항 D (p) 의 가중치 합계로 정의됩니다 . α가 조정 계수 인 경우 만족하는 0Rp (p)는 다음과 같이 정의됩니다.

여기서 α와 β는 각각 신뢰도와 데이터 항의 성분 가중치입니다. 참고 α + β = 1 .

객관적인 점수

실제로 흥미로운 점은이 백서에는 EBII 알고리즘 인 경우 성능을 평가하는 제안 된 (그리고 간단한!) 방법이 포함되어 있다는 것입니다. 논문 작성자가 제안한 분산 접근 방식의 효과와 여러 이미지의 개선을 검증하기 위해 선택한 방법이므로 소금 한 알로 이것을 사용하십시오.

결과 평가는 복원 된 이미지와 원본 이미지 사이의 PSNR (피크 신호 ​​대 잡음비 [26] ) 을 비교하여 수행됩니다 . 일반적으로 PSNR 값이 높을수록 수리 된 이미지와 원본의 유사성이 커집니다. PSNR을 계산하는 방정식은 다음과 같습니다.

다음은 그들이 사용한 두 가지 실제 테스트 이미지입니다.

결론은 종이 자체의 품질만큼이나 실망 스럽다. 거의 개선되지 않았습니다. 여기서 중요한 것은 이런 종류의 도전 (및 다른 이미지 복구 도전)에 가능한 객체 스코어링 방법입니다.

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

Meh.

수행 할 연구

(EBII에만 해당)

a) 전처리

모든 것이 알고리즘이 모든 것을 위해 "작동"해야한다는 "매직 지우기"원칙에 따라 결정됩니다. 이것에 대한 나의 순진한 해결책은 색상 기반 증폭 (위 참조)이지만 더 좋은 방법이 있습니다. 나는 추적 가능한 모든 텍셀의 기하학적 평균을 인식하여 창 크기를 자동 조정하고 스탬프 크기 (또한 개선)를 텍셀 전체 이미지 해상도 에 의존하게하려고 합니다. 여기서 연구를 수행해야합니다.

b) 후 처리

최초의 저자들은 이미 염두에두고있는 모든 포스트 프로세싱 필터를 해제하는 데 큰 역할을했습니다. 오늘, 나는 항상 언캐니 모나리자에서 영감을 얻은 다른 것을 시도했습니다. 페인트 칠 된 영역 가져 와서 모든 이상한 색상 블록에 새 마스크를 적용하고 얼룩 제거 알고리즘에 공급하면 거의 완벽한 결과를 얻을 수 있습니다. 나는 앞으로 언젠가 이것을 탐구 할지도 모른다.


[X] — A. Criminisi, P. Perez, K. Toyama의
예제 기반 Inpainting에 의한 객체 제거 [1] — M. Ashikhmin. 자연스러운 질감을 합성합니다. Proc. ACM 증상 대화 형 3D 그래픽, 217–226 페이지, Research Triangle Park, NC, 2001 년 3 월.
[5] — M. Bertalmio, L. Vese, G. Sapiro 및 S. Osher. 동시 구조 및 텍스처 이미지 인 페인팅. 2002
[6] — R. Bornard, E. Lecan, L. Laborelli 및 JH. not. 스틸 이미지 및 이미지 시퀀스에서 데이터 수정이 누락되었습니다. ACM Multimedia, France, 2002 년 12 월.
[7] — TF Chan and J. Shen. 곡률 중심 확산 (CDD)에 의한 비 텍스처 인 페인팅 J. 비주얼 커뮤니케이션. Image Rep., 4 (12), 2001 년.
[8] — JS 드 보넷. 텍스처 이미지의 분석 및 합성을위한 다중 해상도 샘플링 절차. Proc. ACM 회의 Comp. 그래픽 (SIGGRAPH), 권 31, pp. 361–368, 1997.
[9] — A. Efros 및 WT Freeman. 텍스처 합성 및 전송을위한 이미지 퀼팅. Proc. ACM 회의 Comp. Graphics (SIGGRAPH), pp. 341–346, Eugene Fiume, 2001 년 8 월.
[10] — A. Efros 및 T. Leung. 비모수 샘플링에 의한 텍스처 합성. Proc. ICCV, pp. 1033–1038, Kerkerra, Greece, 1999 년 9 월.
[11] — WT Freeman, EC Pasztor 및 OT Carmichael. 저시력 학습. Int. J. 컴퓨터 비전, 40 (1) : 25–47, 2000.
[12] — D. Garber. 질감 분석 및 질감 합성을위한 계산 모델. 박사 학위 논문, Univ. 1981 년 미국 남부 캘리포니아
[13] — P. 해리슨. 복잡한 텍스처의 재 합성을위한 비 계층 적 절차. Proc. Int. 교수 중부 유럽 Comp. 그래픽, Visua. 그리고 Comp. Vision, Plzen, Czech Republic, 2001 년 2 월.
[14] — DJ Heeger 및 JR Bergen. 피라미드 기반 텍스처 분석 / 합성. Proc. ACM 회의 Comp. 그래픽 (SIGGRAPH), 제 29 권, 229-233 페이지, 캘리포니아 주 로스 앤젤레스, 1995.
[15] — A. Hertzmann, C. Jacobs, N. Oliver, B. Curless 및 D. Salesin. 이미지 비유. Proc. ACM 회의 Comp. Graphics (SIGGRAPH), Eugene Fiume, 2001 년 8 월.
[16]-H. Igehy 및 L. Pereira. 텍스처 합성을 통한 이미지 교체. Proc. Int. 교수 이미지 처리, pp. III : 186–190, 1997.
[17] — G. Kanizsa. 비전 조직. Praeger, 뉴욕, 1979.
-L. Liang, C. Liu, Y.-Q. Xu, B. Guo 및 H.-Y. .. 패치 기반 샘플링으로 실시간 텍스처 합성. ACM Transactions on Graphics, 2001.
[22]-L.-W. 웨이와 엠 레보이 트리 구조 벡터 양자화를 사용한 빠른 텍스처 합성. Proc. ACM 회의 Comp. 그래픽 (SIGGRAPH), 2000.
[23] — A. Zalesny, V. Ferrari, G. Caenen 및 L. van Gool. 병렬 합성 텍스처 합성. Texture 2002 워크숍 (ECCV02와 함께), 덴마크 코펜하겐, 2002 년 6 월.
[24] — AkurKumar Patel, 구자라트 기술 대학교, 컴퓨터 과학 및 공학
[25] — 모범적 인 이미지 인 페인팅을위한 향상된 알고리즘
[26] — Wikipedia, 피크 신호 ​​대 잡음비


30
이다 멋진 . 별이 빛나는 밤은 너무 좋습니다. 아직도, 그 모나리자 ...
Hannes Karppila

8
먼저 "오 세상에 이건 정말 대단하다"고 말씀 드리겠습니다. 둘째 : 이미 다른 질문에 "모나리자는 SCP 똥이다"라고 언급했지만 그 올빼미는 실제로 SCP 위키에 나타날 수있는 것 같습니다.
undergroundmonorail

3
언급 한 인용문을 인용문으로 만들 수 있습니까?
trichoplax

1
@trichoplax 거의 모든 문장에서 약간의 수정이 있지만 정확한 인용 부호는 아닙니다. 알고리즘 설명을 조직과 동일하게 고려하십시오. 수정 또는 코드라고 할 때를 제외하고는 종이. 더 이상 서식을 어수선하게 만들고 싶지 않습니다 :)
mınxomaτ

2
꿈에서 매우 신중하게 무언가를 보려고했을 때 가끔 상황이 이와 똑같습니다.
jimmy23013

45

MATLAB

이것은 간단한 보간 접근법입니다. 아이디어는 먼저 패치의 각 측면에있는 내용을 반영합니다. 그런 다음 해당 미러 이미지 픽셀은 해당 가장자리에 얼마나 가까운 지 보간됩니다.

까다로운 부분은 멋진 보간 가중치를 찾는 것이 었습니다. 몇 가지 놀아 본 후에 나는 우리가 미러링 한 것을 제외한 모든 가장자리에서 0 인 합리적인 기능을 생각해 냈습니다. 그런 다음 일부 평활화를 위해 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
모나리자가 나를 놀라게한다.
Andras Deak

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

8
그 모나리자는 SCP 똥이다
undergroundmonorail

1
체크 무늬 이미지는 정말 멋져 보입니다.
ETHproductions

1
당신이 이것으로 당신 자신의 도전에서 이겼더라도 나는 놀라지 않을 것입니다. 이것은 정말 좋은 해결책입니다.
Alex A.

25

매스 매 티카

이것은 Mathematica의 Inpaint기능을 사용 합니다. Mathematica 자체는 모든 무거운 작업을 수행하므로 커뮤니티 위키입니다.

inPaint(아래)는의 간단한 적응입니다 Inpaint. 컬러 페인팅 / 사진의 경우 기본 "TextureSynthesis"설정을 사용합니다. 그림이 흑백 인 것을 감지하면 (그림의 이미지 데이터가 그림의 이진 형태의 이미지 데이터와 동일하기 때문에) 이미지를 이진화하고 "TotalVariation"패치를 적용합니다. If절 중 하나를 적용 Binarize하거나 Identity사진에. 이 Identity함수는 인수를 변경하지 않고 반환합니다.

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

이미지와 마스크는에 대한 인수로 입력됩니다 inPaint. PartitionGrid서식 목적을 위해 단지이다.

입력

출력이 패치되었습니다. 후에 이미지를 수정하지 않았습니다 inPaint.

산출


4
우연의 일치 일지 모르지만 미로의 성능에 놀랐습니다!
flawr

1
@flawr 내가 던질 것 같은 것을 ? 알고 그 누가 검은 색과 백인 정말 이해할 수있다 ());이 솔루션 단지 엉망.
안드라스 디크

17
나는 이것이 커뮤니티 위키라고 생각하지 않습니다.
Dennis

Lawr, Yes, Inpaint흑백 이미지 전체에서 대칭을 찾는 것 같습니다. – DavidC 9 시간 전
DavidC

당신은 확실히 흑백 알고리즘은 어디 염소를 희생 포함하지 않는 이유는 무엇입니까? 지구상에서 어떻게 --- 이미지가 모두 가려져 있다면 이미지의 중심 구조를 추측합니까?
Andras Deak

18

파이썬 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

@undergroundmonorail 나는 컴퓨터로 생성 된 우발적 인 얼굴이 엉뚱한 계곡 의 깊이에서 나온 것 같아요 .
Andras Deak

PIL은 어디서 구합니까?
Elliot A.

@ElliotA. 필자는 PIL Proper가 죽었다는 것을 알고 있지만 오픈 소스이므로 "Pillow"라는 이름으로 존재합니다. Google "python pillow"을 찾으면 찾아야합니다.
undergroundmonorail

13

파이썬 3, PIL

이 프로그램은 sobel 연산자를 사용하고이를 기반으로 이미지에 선을 그립니다.

sobel 연산자는 각 모서리의 각도를 찾으므로 알 수없는 영역으로 돌출되는 모서리는 계속되어야합니다.

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

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

한편, 샘플 이미지는 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

모나리자 모나리자 모나리자 ́ 리사

여기에 이미지 설명을 입력하십시오 위 이미지의 영역은 선인장만큼 매끄 럽습니다.

일정한 색상으로는 좋지 않습니다.

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오


1
아, 그리고 당신은 B / W 테스트 케이스를 추가 할 수 있습니까?
flawr

2
별이 빛나는 밤에 정말 좋아 보인다.
SuperJedi224

1
와우, 지금은 믿어지지 않습니다! 패치는 여전히 눈에 띄지 만 새로운 아이디어입니다! 내가 가장 좋아하는 것 =)
flawr

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

2
당신은 "scariest Mona Lisa"공모전 IMO에서 이깁니다. 0_o
DLosc

8

파이썬 2

간격을 벗어난 픽셀의 값을 사용하여 패치를 생성하는 간단한 파이썬 스크립트. 픽셀 행과 열의 끝에서 색상 값을 가져와 해당 픽셀과의 거리를 사용하여 가중 평균을 계산합니다.

출력은 그렇게 예쁘지 않지만 예술 입니다.

img1 img2 img3 img4 img5 img6

그런 다음 코드 :

IMGN = "6"

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

BLUR = 5


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

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

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

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

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

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

# create patch
patch = pygame.Surface(patch_size)

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

cpixels = top+left+right+bottom

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

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

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

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

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

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

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

현재 가로 / 세로 줄무늬가 보이면 다른 방향을 포함 시켜서 향상시킬 수 있습니다.
flawr

나는 실제로 그것을 시도했지만 좋은 결과를 얻을 수 없었기 때문에 그냥 이미지를 흐리게하기로 결정했습니다 : D
Hannes Karppila

19
마지막으로, 모나리자 나를 두려워 하지 않고 체포 된 살인자처럼 보입니다.
Andras Deak

6

매스 매 티카

Inpaint

Mathematica 에는 정확히이 작업을 수행하는 내장 함수가 있으므로 정확히 의미 합니다 .

Inpaint[image, region]

  • image에서 0이 아닌 요소에 해당하는 부분을 ​​수정합니다 region.

기본적으로 "랜덤 샘플링을 사용하는 최적의 텍스처 합성 방법"을 사용하여 그림에는 좋은 결과를 낳지 만 미로와 바둑판에는 좋지 않은 결과를 얻습니다.

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

설정을 가지고 놀아도 모든 이미지의 품질이 향상되지는 않았으므로 기본값을 사용했습니다 (바이트를 절약하기 위해 codegolf.se).


23
" Mathematica에는 내장 기능이 있습니다 "... 놀라움, 놀라움;)
Andras Deak

미로와 체커 보드의 경우 "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그림 에만 잘 보인다. 개별 테스트 사례마다 설정을 조정할 수 없다고 생각합니다. (가능한 경우, 누락 된 부분을 '설정'으로
간단하게 공급할 수

미로와 바둑판 결과는 정말 수수께끼입니다. 왜 Mathematica 의 누락 된 영역의 재구성이 불규칙하고 비대칭 적인가?
David Zhang

이미지가 흑백인지 자동으로 감지하여 적절히 조정합니다 (바이너리 및 "TotalVariation"방법). inPaint[picture_, mask_] := If[bw = ImageData@Rasterize[Binarize[picture]] == ImageData[picture], Binarize, Identity]@ Inpaint[picture, mask, Method -> If[bw, "TotalVariation", "TextureSynthesis"]]
DavidC

5

파이썬 3

이 답변 은 Ulyanov et al.의 "Deep Image Prior" 논문의 아이디어를 구현합니다 . (CVPR 2018)이 백서에서는 이미지 처리를위한 우수한 성능의 신경망을 설계하는 방식이 자연 이미지가 어떻게 보이는지에 대한 아이디어 ( "선점"분포)를 자세히 반영한다는 아이디어를 탐구했습니다.

그들은 다른 데이터에 대한 훈련없이 주어진 이미지를 사용하는 노이즈 및 아티팩트 제거뿐만 아니라 인 페인팅에 사용할 수있는 방법을 제안했습니다. 실제 개념은 매우 간단합니다. 네트는 주어진 마스크 ​​외부의 에로스 만 페널티로하여 원하는 이미지 (일부 고정 랜덤 노이즈의 경우)를 출력하도록 훈련됩니다. 소음을 제거하려면 아무것도 가리지 않아도되지만 훈련 초기에 중단하면됩니다.

페인트 칠을하려면 페인트 칠하려는 부분을 마스크하고 수렴 할 때까지 훈련하십시오. 확실히 최신 기술은 아니지만 아이디어의 단순성과 여전히 뛰어난 성능으로 인해 게시하고 여기에 게시하고 싶었습니다. 내 실험에서 더 큰 패치의 페인트 칠은 그다지 좋지 않았지만 더 작은 세그먼트의 경우 결과가 훨씬 더 설득력이 있습니다.

나는 github의 jaxony에서 인기있는 U-Net 아키텍처 를 사용하여 이것을 구현했습니다 . 이미지 훈련 및 처리를위한 코드는 아래에서 확인할 수 있습니다.

훈련

이것은 훈련 과정의 시각화입니다. 모든 프레임은 특정 반복 횟수의 상태입니다.

암호

CPU에서 단 하나의 이미지로 실행하는 데 몇 시간이 걸릴 수 있지만 좋은 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-only 그들은 논문에서 실제로 U-Net을 주로 사용한다고 말하지만, 그들이 사용한 정확한 매개 변수를 찾을 수 없습니다. 용량이 더 큰 네트를 사용했을 수도 있습니다. 나는 전력이 매우 제한된 컴퓨터 만 가지고있었습니다. 그래서 나는 여전히 메모리에 맞는 매개 변수를 선택해야했고 훈련하는 데 너무 오래 걸리지 않았습니다. 정확히 얼마나 오래 걸 렸는지 모르겠지만 (CPU 만 사용하는) 컴퓨터 에서이 이미지는 며칠이 걸립니다. (여러분이 여분의 cuda를 지원하는 GPU를 가지고 있다면 알려주세요 :)
flawr

또한 직사각형 마스크가있는 네트워크의 디자인으로 인해 이상적이지 않다고 생각합니다 (예 : 작은 마스크는 아마도 더 좋아 보일 것입니다). 예를 들어 처음 몇 개의 이미지를 마지막 두 이미지와 비교하면 (사각형 마스크를 사용하지 않음) .
flawr

4

OpenCV가 포함 된 Python

OpenCV에는 inpaint라는 기능이 있습니다. 사용되는 도장에는 두 가지 유형이 있습니다. 빠른 행진 방법을 사용하겠습니다. 설명서에 따르면 알고리즘은 다음과 같이 작동합니다.

이미지에서 칠할 영역을 고려하십시오. 알고리즘은이 영역의 경계에서 시작하여 경계의 모든 것을 먼저 채우는 영역으로 이동합니다. 페인트 칠하려면 주변의 픽셀 주변에 작은 이웃이 필요합니다. 이 픽셀은 주변에서 알려진 모든 픽셀의 정규화 된 가중치 합계로 대체됩니다. 무게의 선택은 중요한 문제입니다. 점 근처, 경계의 법선에 가까운 픽셀 및 경계 윤곽에있는 픽셀에 더 많은 가중치가 부여됩니다. 픽셀이 페인트되면 빠른 행진 방법을 사용하여 가장 가까운 다음 픽셀로 이동합니다. FMM은 알려진 픽셀 근처의 픽셀을 먼저 페인트하여 수동 휴리스틱 작업처럼 작동하도록합니다.

코드는 다음과 같습니다 * :

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

플로팅 이유로 BGR을 RGB로 변환하는 방법에 주목하십시오. 또한 회전시킵니다. 결과는 다음과 같습니다.

고딕

별이 빛나는 밤 비명 또 다른 소름 끼치는 모나리자!

모나리자가 돌아옵니다!

1 호선

체커

보시다시피, 나는 두 가지 색상으로 최고가 아닙니다.


모나리자는 성형 수술을 받았습니다
코너 오브라이언

3

자바

색상 평균 접근 방식. 아마 향상 될 수 있습니다.

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

import javax.imageio.ImageIO;


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

결과 :

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오


2
왜 항상이 특정 각도로 선을 얻습니까? 왼쪽 상단 모서리는 상대적으로 잘 일치하는 반면 오른쪽 하단 부분은 전혀 일치하지 않습니다.
flawr

나는 그것이 지역을 반복하는 방식과 관련이 있다고 생각합니다. 나는 아마 그것을 결국 바꿀 것입니다.
SuperJedi224

사다리꼴이있는 것처럼 보입니다.
ericw31415

@ ericw31415 반복 순서의 결과물입니다.
SuperJedi224
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.