Vá hình ảnh


114

Trong một phần mềm biên tập hình ảnh phổ biến có một tính năng, mà các bản vá lỗi (Thuật ngữ này được sử dụng trong xử lý hình ảnh được inpainting như @ mınxomaτ chỉ ra.) Một khu vực được lựa chọn của một hình ảnh, dựa trên thông tin bên ngoài của miếng vá đó. Và nó làm một công việc khá tốt, coi đó chỉ là một chương trình. Là một con người, đôi khi bạn có thể thấy có gì đó không ổn, nhưng nếu bạn nhắm mắt lại hoặc chỉ nhìn thoáng qua, miếng vá dường như lấp đầy khoảng trống khá tốt.

ví dụ bằng phần mềm chỉnh sửa ảnh phổ biến

Thử thách

Đưa ra một hình ảnh và mặt nạ chỉ định một khu vực hình chữ nhật của hình ảnh nên được vá (cũng như hình ảnh, hoặc bất kỳ định dạng ưa thích nào khác), chương trình của bạn nên cố gắng lấp đầy khu vực được chỉ định bằng một miếng vá cố gắng hòa trộn với phần còn lại của bức hình. Chương trình không thể sử dụng thông tin của hình ảnh gốc trong khu vực được chỉ định.

Bạn có thể giả định rằng miếng vá luôn có chiều rộng tối thiểu từ các cạnh và chiều cao của nó cách xa đỉnh và đáy của hình ảnh. Điều đó có nghĩa là diện tích tối đa của một bản vá là 1/9 của toàn bộ hình ảnh.

Vui lòng thêm một mô tả ngắn gọn về cách thuật toán của bạn hoạt động.

Bỏ phiếu

Cử tri được yêu cầu đánh giá các thuật toán thực hiện tốt như thế nào và bỏ phiếu phù hợp.

Một số gợi ý về cách đánh giá: (Một lần nữa, cảm ơn @ mınxomaτ vì một số tiêu chí khác.)

  • Nếu bạn nheo mắt và hình ảnh trông ổn?
  • Bạn có thể biết chính xác nơi vá là?
  • Làm thế nào tốt cấu trúc và kết cấu từ nền hình ảnh và khu vực xung quanh tiếp tục?
  • Khu vực chỉnh sửa chứa bao nhiêu pixel sai màu?
  • Có bất kỳ đốm / khối màu đồng nhất trong khu vực dường như không thuộc về không?
  • Có khu vực chỉnh sửa có bất kỳ thay đổi màu sắc / độ tương phản hoặc độ sáng mạnh mẽ so với phần còn lại của hình ảnh?

Tiêu chí hiệu lực

Để đệ trình hợp lệ, hình ảnh đầu ra phải khớp chính xác với hình ảnh đầu vào bên ngoài khu vực được chỉ định.

Trường hợp thử nghiệm

Ở bên trái hình ảnh nguồn, bên phải mặt nạ tương ứng:


1
Chúng ta có thể chấp nhận đầu vào mặt nạ làm đối số văn bản (ví dụ. inpaint.exe left top width height img.jpg)?
mınxomaτ

1
Chắc chắn, định dạng đầu vào / đầu ra không thực sự quan trọng, vì đây là một cuộc thi phổ biến trong đó trước hết hiệu suất của thuật toán của bạn là quan trọng.
flawr

24
Đây là một thách thức rất thực tế. Có thể kết quả sẽ tốt hơn các thuật toán hiện có được sử dụng trong GIMP và phần mềm chỉnh sửa ảnh nguồn mở khác. Vận may, Danh tiếng và Vinh quang có thể là của bạn!
Sparr

6
@Sparr và cuối cùng là hình mờ xấu xí có thể được xóa khỏi phương tiện đã tải xuống;)
Andras Deak

2
Nội dung hoàn toàn ổn, tôi nghi ngờ rằng chúng sẽ rất phổ biến.
flawr

Câu trả lời:


142

Tự động , VB

Giới thiệu

Đây là một triển khai của Loại bỏ đối tượng bằng thuật toán Inpainting dựa trên Exemplar được phát triển bởi A. Criminisi, P. Perez (Cambridge Microsoft Research Ltd.) và K. Toyama (Microsoft) [X] . Thuật toán này được nhắm mục tiêu vào hình ảnh thông tin cao (và khung hình video) và nhằm mục đích là sự cân bằng giữa tái cấu trúc và tái cấu trúc hữu cơ. Đoạn của câu trả lời này chứa các trích dẫn toàn văn từ bài báo gốc (vì nó không còn chính thức nữa) để làm cho câu trả lời này trở nên khép kín hơn.

Thuật toán

Mục tiêu : Thay thế một khu vực ( mặt nạ ) đã chọn ( tốt nhất là một đối tượng tiền cảnh được phân tách trực quan) bằng các hình nền hợp lý trực quan.

Trong công việc trước đây, một số nhà nghiên cứu đã coi tổng hợp kết cấu như một cách để lấp đầy các vùng hình ảnh lớn với kết cấu "thuần túy" - các mẫu kết cấu hai chiều lặp đi lặp lại với độ ngẫu nhiên vừa phải. Điều này dựa trên một cơ thể lớn của nghiên cứu tổng hợp kết cấu, tìm cách tái tạo kết cấu quảng cáo infinitum , đưa ra một mẫu nguồn nhỏ của kết cấu tinh khiết [1] [8] [9] [10] [11] [12] [14] [15] [16] [19] [22] .

Hiệu quả như các kỹ thuật này trong việc tái tạo kết cấu nhất quán, chúng gặp khó khăn trong việc lấp đầy các lỗ hổng trong các bức ảnh về cảnh trong thế giới thực, thường bao gồm các cấu trúc tuyến tính và kết cấu tổng hợp - nhiều kết cấu tương tác không gian [23] . Vấn đề chính là ranh giới giữa các vùng hình ảnh là một sản phẩm phức tạp của ảnh hưởng lẫn nhau giữa các kết cấu khác nhau. Trái ngược với bản chất hai chiều của kết cấu thuần túy, các ranh giới này tạo thành những gì có thể được coi là cấu trúc hình ảnh một chiều hoặc tuyến tính hơn.

Hình ảnh inpainting kỹ thuật điền vào lỗ hổng trong hình ảnh bằng cách tuyên truyền cấu trúc tuyến tính (gọi tắt là isophotes trong inpainting văn học) vào khu vực mục tiêu thông qua sự khuếch tán. Chúng được lấy cảm hứng từ các phương trình vi phân từng phần của dòng nhiệt vật lý và hoạt động thuyết phục như các thuật toán phục hồi. Hạn chế của họ là quá trình khuếch tán giới thiệu một số mờ, đáng chú ý.

quả sung.  2

Vùng cần điền, nghĩa là vùng đích được biểu thị bằng và đường viền của nó được ký hiệu là. Đường viền phát triển vào bên trong khi thuật toán tiến triển, và vì vậy chúng tôi cũng gọi nó là đường viền điền vào trước. Vùng nguồn,, vẫn cố định trong suốt thuật toán, cung cấp các mẫu được sử dụng trong quy trình điền. Bây giờ chúng tôi tập trung vào một lần lặp duy nhất của thuật toán để chỉ ra cách cấu trúc và kết cấu được xử lý đầy đủ bằng cách tổng hợp dựa trên mẫu. Giả sử rằng mẫu hình vuông Ψp ∈ Ω tập trung tại điểm p (hình 2b), sẽ được điền. Mẫu phù hợp nhất từ ​​vùng nguồn đến từ bản vá qˆ ∈, tương tự nhất với các phần đã được điền vào Ψp. Trong ví dụ trong hình. 2b, chúng ta thấy rằng nếu Ψp nằm trên phần tiếp theo của cạnh hình ảnh, các trận đấu tốt nhất có khả năng nhất sẽ nằm dọc theo cùng một cạnh (hoặc có cùng màu) (ví dụ: Ψq 'và Ψq' 'trong hình. 2c). Tất cả những gì được yêu cầu để truyền isophote vào bên trong là việc chuyển mẫu đơn giản từ bản vá nguồn phù hợp nhất (hình 2d). Lưu ý rằng định hướng isophote được bảo quản tự động. Trong hình, mặc dù thực tế là cạnh ban đầu không trực giao với đường viền đích, cấu trúc lan truyền vẫn duy trì hướng tương tự như trong vùng nguồn.

Chi tiết triển khai và thuật toán

Chức năng của việc triển khai này được gói gọn trong một ActiveX COM COM ActiveX được loại bỏ khỏi chương trình máy chủ dưới dạng nhị phân và sau đó được gọi một cách nhanh chóng bằng cách gọi Ipai bằng inpainter. Trong trường hợp cụ thể này, API được viết bằng VisualBasic và có thể được gọi từ bất kỳ ngôn ngữ hỗ trợ COM nào. Phần sau của mã giảm nhị phân:

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

Thư viện sau đó được khởi tạo bằng CLSID và IID:

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

Thư viện chấp nhận xử lý GDIOB DỰ ÁN, cụ thể là DIBSection của bất kỳ bitmap GDI / + nào (tệp, luồng, v.v.). Tệp hình ảnh đã chỉ định được tải và được vẽ lên một bitmap trống được xây dựng Scan0từ các kích thước hình ảnh đầu vào.

Tệp đầu vào cho việc triển khai này là bất kỳ định dạng tệp tương thích GDI / + nào chứa dữ liệu hình ảnh bị che. (Các) mặt nạ là một hoặc nhiều vùng có màu đồng nhất trong ảnh đầu vào. Người dùng cung cấp giá trị màu RGB cho mặt nạ, chỉ các pixel có chính xác giá trị màu đó sẽ được khớp. Màu mặt nạ mặc định là màu xanh lá cây (0, 255, 0). Tất cả các vùng bị che cùng nhau đại diện cho vùng đích, Ω, sẽ bị xóa và điền. Vùng nguồn, Φ, được định nghĩa là toàn bộ hình ảnh trừ đi vùng đích (= I −).

Tiếp theo, như với tất cả các tổng hợp kết cấu dựa trên mẫu [10] , kích thước của cửa sổ mẫu (còn gọi là " bán kính quét ") phải được chỉ định. Việc triển khai này cung cấp kích thước cửa sổ mặc định là 6² pixel, nhưng trong thực tế, người dùng yêu cầu người dùng phải đặt nó lớn hơn một chút so với thành phần kết cấu có thể phân biệt lớn nhất, hoặc tex texel, trong khu vực nguồn. Một sửa đổi bổ sung cho thuật toán ban đầu là " kích thước khối " có thể xác định được của người dùng , xác định vùng pixel sẽ được thay thế bằng màu đồng nhất mới. Điều này làm tăng tốc độ và giảm chất lượng. Kích thước khối geater lớn hơn 1px có nghĩa là được sử dụng với các khu vực cực kỳ đồng đều (nước, cát, lông, v.v.), tuy nhiên, nên được giữ ở mức tối đa. .5x kích thước khối (có thể là không thể tùy thuộc vào mặt nạ).

Để không trì hoãn thuật toán trên hình ảnh 1 bit, mỗi khi nhận được hình ảnh có ít hơn 5 màu, kích thước cửa sổ được khuếch đại lên gấp 10 lần.

Khi các tham số này được xác định, phần còn lại của quy trình điền vào khu vực là hoàn toàn tự động. Trong thuật toán của chúng tôi, mỗi pixel duy trì một giá trị màu (hoặc trống rỗng, nếu pixel không được lấp đầy) và giá trị độ tin cậy, phản ánh niềm tin của chúng tôi vào giá trị pixel và bị đóng băng sau khi pixel được lấp đầy. Trong quá trình thuật toán, các bản vá dọc theo mặt trước điền cũng được đưa ra một giá trị ưu tiên tạm thời, xác định thứ tự mà chúng được điền. Sau đó, thuật toán của chúng tôi lặp lại ba bước sau cho đến khi tất cả các pixel được lấp đầy.

Bước 1: Tính toán ưu tiên bản vá

Thứ tự điền rất quan trọng đối với tổng hợp kết cấu không tham số [1] [6] [10] [13] . Cho đến nay, yêu thích mặc định là phương pháp vỏ hành tây của hành tinh, trong đó vùng đích được tổng hợp từ bên ngoài vào trong, trong các lớp đồng tâm. Thuật toán của chúng tôi thực hiện nhiệm vụ này thông qua thuật toán điền đầu tiên tốt nhất phụ thuộc hoàn toàn vào các giá trị ưu tiên được gán cho mỗi bản vá ở mặt trước điền. Tính toán ưu tiên được thiên về các miếng vá nằm trên các cạnh mạnh và được bao quanh bởi các pixel có độ tin cậy cao, các pixel này là ranh giới, được đánh dấu bằng giá trị -2. Đoạn mã sau tính toán lại các ưu tiên:

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

Đưa ra một bản vá centeredp tập trung tại điểm p cho một số p ∈ (xem hình 3), mức ưu tiên P (p) của nó được định nghĩa là sản phẩm của độ tin cậy được tính toán ( ComputeConfidencehoặc C (p) ) và thuật ngữ dữ liệu ( ComputeData, hoặc D (p) ), trong đó

, Ở đâu

| |p | là diện tích của Ψp, α là hệ số chuẩn hóa (ví dụ: α = 255 cho hình ảnh mức xám điển hình) và np là một vectơ đơn vị trực giao ở phía trước δΩ trong điểm p. Mức độ ưu tiên được tính cho mọi bản vá viền, với các bản vá riêng biệt cho từng pixel trên đường biên của vùng mục tiêu.

Thực hiện như

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

Thuật ngữ độ tin cậy C (p) có thể được coi là thước đo lượng thông tin đáng tin cậy xung quanh pixel p. Mục đích là để lấp đầy những bản vá đầu tiên có nhiều pixel của chúng hơn, với ưu tiên bổ sung được cung cấp cho các pixel được lấp đầy sớm (hoặc không bao giờ là một phần của vùng mục tiêu).

Điều này tự động kết hợp ưu tiên đối với các hình dạng nhất định dọc theo mặt trước điền. Ví dụ, các bản vá bao gồm các góc và các đường gân mỏng của vùng đích sẽ có xu hướng được lấp đầy trước tiên, vì chúng được bao quanh bởi nhiều pixel hơn từ ảnh gốc. Những bản vá này cung cấp thông tin đáng tin cậy hơn để phù hợp với. Ngược lại, các bản vá ở đầu Penins công thức Các pixel được lấp đầy nhô vào vùng đích sẽ có xu hướng được đặt sang một bên cho đến khi nhiều pixel xung quanh được điền vào. Ở mức độ thô, thuật ngữ C (p) của (1) xấp xỉ thực thi lệnh điền đồng tâm mong muốn.

Khi tiến hành điền, các pixel ở các lớp bên ngoài của vùng đích sẽ có xu hướng được đặc trưng bởi các giá trị độ tin cậy cao hơn và do đó được điền trước đó; pixel ở trung tâm của vùng mục tiêu sẽ có giá trị độ tin cậy thấp hơn. Thuật ngữ dữ liệu D (p) là một hàm của cường độ của các isophotes chạm vào phía trước tại mỗi lần lặp. Thuật ngữ này tăng mức độ ưu tiên của một bản vá mà một isophote "chảy" vào. Yếu tố này có tầm quan trọng cơ bản trong thuật toán của chúng tôi vì nó khuyến khích các cấu trúc tuyến tính được tổng hợp trước, và do đó được truyền bá an toàn vào vùng mục tiêu. Các đường gãy có xu hướng kết nối, do đó hiện thực hóa "Nguyên tắc kết nối" của tâm lý học tầm nhìn [7] [17] .

Thứ tự điền phụ thuộc vào thuộc tính hình ảnh, dẫn đến quá trình tổng hợp hữu cơ giúp loại bỏ nguy cơ các tạo tác cấu trúc bị phá vỡ và cũng làm giảm các tạo tác khối mà không cần bước cắt vá đắt tiền [9] hoặc bước trộn mờ gây ra [19] ] .

Bước 2: Tuyên truyền thông tin về kết cấu và cấu trúc

Khi tất cả các ưu tiên trên mặt trước điền ( ranh giới ) đã được tính toán, bản vá pˆ với mức ưu tiên cao nhất được tìm thấy. Sau đó chúng tôi điền nó với dữ liệu được trích xuất từ ​​vùng nguồn. Chúng tôi tuyên truyền kết cấu hình ảnh bằng cách lấy mẫu trực tiếp của khu vực nguồn. Tương tự như [10] , chúng tôi tìm kiếm trong vùng nguồn cho bản vá đó tương tự như pˆ. Chính thức

, Ở đâu

khoảng cách d (a, Ψb) giữa hai bản vá chung Ψa và Ψb được định nghĩa đơn giản là tổng số chênh lệch bình phương (SSD) của các pixel đã được lấp đầy trong hai bản vá. Không có phân tích hoặc thao tác thêm ( đặc biệt là không làm mờ ) được thực hiện trong bước này. Tính toán này chạy trong vòng lặp chu kỳ chính và được thực hiện như sau:

Lấy ưu tiên tối đa:

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

Tìm bản vá tương tự nhất:

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

Bước 3: Cập nhật giá trị độ tin cậy

Sau khi bản vá pˆ được lấp đầy bằng các giá trị pixel mới, độ tin cậy C (p) được cập nhật trong khu vực được giới hạn bởi Ψpˆ như sau:

Quy tắc cập nhật đơn giản này cho phép chúng tôi đo lường độ tin cậy tương đối của các bản vá ở mặt trước, không có thông số cụ thể về hình ảnh. Khi tiến hành điền, giá trị độ tin cậy bị phân rã, chỉ ra rằng chúng ta ít chắc chắn hơn về các giá trị màu của các pixel gần trung tâm của vùng đích. Được triển khai tại đây (cùng với tất cả các cập nhật cần thiết khác):

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

Mã hoàn chỉnh

Đây là mã có thể chạy, hoàn thành với mã nguồn của thư viện dưới dạng nhận xét.

Mã được gọi bởi

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

Ví dụ được bao gồm trong các hình thức

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

chỉ cần bỏ qua ví dụ bạn muốn chạy bằng CTRL+ Q.

Hồ sơ kiểm tra chính thức

Thuật toán này được thực hiện để được điều chỉnh cho mỗi hình ảnh. Do đó, các giá trị mặc định (và cả mặt nạ mặc định) là hoàn toàn tối ưu. Các giá trị mặc định được chọn sao cho mọi mẫu có thể được xử lý trong một khoảng thời gian hợp lý. Tôi rất khuyên bạn nên chơi với mặt nạ có hình dạng bất thường và kích thước cửa sổ tốt hơn. Nhấp vào hình ảnh để phóng to!

Bàn cờ

Mỹ gothic

mê cung

nàng mô na Li Sa

(mặt nạ khủng khiếp)

Hét lên

Đầy sao

Ví dụ thực tế

Tất cả đều sử dụng mặt nạ vẽ tay tùy chỉnh.

Nếu bạn có những hình ảnh thú vị khác mà bạn muốn xem kèm theo, hãy để lại nhận xét.

Cải tiến EBII

Có nhiều biến thể của EBII ngoài kia, được tạo ra bởi các nhà nghiên cứu khác nhau. AnkurKumar Patel thu hút sự chú ý của tôi với bộ sưu tập giấy tờ của ông [24] về các cải tiến EBII khác nhau.

Cụ thể, bài viết " Cải tiến thuật toán mạnh mẽ cho hình ảnh dựa trên mẫu mực " [25] đề cập đến hai cải tiến về việc cân nhắc các giá trị ưu tiên.

Sự cải thiện

Việc sửa đổi hiệu quả nằm ở Bước 1 (xem bên trên) của thuật toán và mở rộng hiệu ứng C (p)D (p) trên xếp hạng ưu tiên cho pixel này bằng cách sử dụng:

Trong công thức của CD đã cho ở trên, tương ứng là hệ số chuẩn hóa (ví dụ: α = 255), vectơ isophote và vectơ đơn vị trực giao ở phía trước trong điểm p.

Thêm nữa,

Hàm ưu tiên được định nghĩa là tổng trọng số của thuật ngữ tin cậy thường xuyên C (p) và thuật ngữ dữ liệu mới D (p) . Trong đó α là hệ số điều chỉnh, thỏa mãn 0Rp (p) được xác định như sau:

Trong đó α và respectively tương ứng là các trọng số thành phần của độ tin cậy và các điều khoản dữ liệu. Lưu ý rằng α + = 1 .

Ghi điểm khách quan

Điều thực sự thú vị là bài báo này chứa một phương pháp được đề xuất (và đơn giản!) Để chấm điểm hiệu suất nếu thuật toán EBII. Mặc dù vậy, hãy thực hiện điều này với một hạt muối, vì đây là phương pháp được lựa chọn bởi chính các tác giả giấy để xác minh tính hiệu quả của phương pháp phương sai được đề xuất và cải thiện trên một số hình ảnh.

Việc đánh giá kết quả được thực hiện bằng cách so sánh PSNR (Tỷ lệ nhiễu tín hiệu cực đại [26] ) giữa hình ảnh được khôi phục và hình ảnh gốc. Nói chung, giá trị PSNR càng cao thì độ tương tự của hình ảnh được sửa chữa với bản gốc càng lớn. Phương trình tính PSNR như sau:

Đây là những hình ảnh thử nghiệm trong thế giới thực 2 (hai!) Đáng kinh ngạc mà họ đã sử dụng:

Kết luận cũng đáng thất vọng như chính chất lượng của bài báo. Nó cho thấy rất ít cải thiện. Điều chính ở đây là một phương pháp chấm điểm đối tượng khả thi cho loại thử thách này (và các thử thách sửa chữa hình ảnh khác):

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

Meh.

Nghiên cứu được thực hiện

(Cụ thể cho EBII)

a) Tiền xử lý

Mọi thứ bắt nguồn từ nguyên tắc "Magic Erase" rằng thuật toán sẽ "chỉ hoạt động" cho mọi thứ. Giải pháp ngây thơ của tôi cho điều này là khuếch đại dựa trên màu sắc (xem ở trên), nhưng có nhiều cách tốt hơn. Tôi đang nghĩ đến việc nhận ra giá trị trung bình hình học của tất cả các tex có thể theo dõi để tự động điều chỉnh kích thước cửa sổ và làm cho kích thước tem (cũng là cải tiến của tôi) phụ thuộc vào độ phân giải toàn bộ hình ảnh toàn bộ hình ảnh. Nghiên cứu phải được thực hiện ở đây.

b) Hậu xử lý

Các tác giả ban đầu đã làm rất tốt việc gỡ lỗi tất cả các bộ lọc xử lý bài đăng trong tâm trí. Hôm nay, tôi đã thử một cái gì đó khác, lấy cảm hứng từ Mona Lisa luôn luôn kỳ lạ (cảm ơn ngầm). Nếu bạn chỉ lấy vùng không được tô màu và áp dụng mặt nạ mới cho tất cả các khối màu lạ và đưa vào thuật toán xem thường, bạn sẽ có kết quả gần như hoàn hảo. Tôi có thể khám phá điều này một thời gian trong tương lai.


[X] - Loại bỏ đối tượng bằng cách khai thác dựa trên mẫu mực của A. Criminisi, P. Perez, K. Toyama
[1] - M. Ashikhmin. Tổng hợp kết cấu tự nhiên. Trong Proc. Biểu tượng ACM. trên Đồ họa 3D tương tác, trang 217 Từ226, Công viên tam giác nghiên cứu, NC, tháng 3 năm 2001.
[5] - M. Bertalmio, L. Vese, G. Sapiro và S. Osher. Cấu trúc đồng thời và hình ảnh kết cấu inpainting. xuất hiện, 2002
[6] - R. Bornard, E. Lecan, L. Labelli và JH. Chenot. Thiếu chỉnh sửa dữ liệu trong các hình ảnh tĩnh và chuỗi hình ảnh. Trong ACM đa phương tiện, Pháp, tháng 12 năm 2002.
[7] - TF Chan và J. Shen. Không kết cấu không xuất hiện bởi các khuếch tán theo hướng cong (CDD). J. Thị giác. Đại diện hình ảnh, 4 (12), 2001.
[8] - JS de Bonet. Quy trình lấy mẫu đa biến để phân tích và tổng hợp hình ảnh kết cấu. Trong Proc. ACM Conf. Comp. Đồ họa (SIGGRAPH), tập 31, trang 361 Tiết368, 1997.
[9] - A. Efros và WT Freeman. Quilting hình ảnh để tổng hợp kết cấu và chuyển giao. Trong Proc. ACM Conf. Comp. Đồ họa (SIGGRAPH), trang 341 Đi346, Eugene Fiume, tháng 8 năm 2001.
[10] - A. Efros và T. Leung. Tổng hợp kết cấu bằng cách lấy mẫu không tham số. Trong Proc. ICCV, trang 1033 Từ1038, Kerkyra, Hy Lạp, tháng 9 năm 1999.
[11] - WT Freeman, EC Pasztor, và OT Carmichael. Học tầm nhìn thấp. Nội bộ J. Thị giác máy tính, 40 (1): 25 Ném47, 2000.
[12] - D. Garber. Mô hình tính toán để phân tích kết cấu và tổng hợp kết cấu. Luận án tiến sĩ, Univ. Nam California, Hoa Kỳ, 1981.
[13] - P. Harrison. Một thủ tục không phân cấp để tổng hợp lại kết cấu phức tạp. Trong Proc. Nội bộ Conf. Trung Âu Comp. Đồ họa, Visua. và Comp. Vision, Plzen, Cộng hòa Séc, tháng 2 năm 2001.
[14] - DJ Heeger và JR Bergen. Phân tích / tổng hợp kết cấu dựa trên kim tự tháp. Trong Proc. ACM Conf. Comp. Đồ họa (SIGGRAPH), tập 29, trang 229 Từ233, Los Angeles, CA, 1995.
[15] - A. Hertzmann, C. Jacobs, N. Oliver, B. Curless và D. Salesin. Tương tự hình ảnh. Trong Proc. ACM Conf. Comp. Đồ họa (SIGGRAPH), Eugene Fiume, tháng 8 năm 2001.
[16] - H. Igehy và L. Pereira. Thay thế hình ảnh thông qua tổng hợp kết cấu. Trong Proc. Nội bộ Conf. Xử lý ảnh, trang III: 186 Hàng190, 1997.
[17] - G. Kanizsa. Tổ chức trong Tầm nhìn. Praeger, New York, 1979.
[19] - L. Liang, C. Liu, Y.-Q. Xu, B. Guo và H.-Y. Shum. Tổng hợp kết cấu thời gian thực bằng cách lấy mẫu dựa trên bản vá. Trong Giao dịch ACM trên Đồ họa, 2001.
[22] - L.-W. Wey và M. Levoy. Tổng hợp kết cấu nhanh bằng cách sử dụng lượng tử hóa vector cấu trúc cây. Trong Proc. ACM Conf. Comp. Đồ họa (SIGGRAPH), 2000.
[23] - A. Zalesny, V. Ferrari, G. Caenen và L. van Gool. Tổng hợp kết cấu song song. Trong hội thảo Texture 2002 - (kết hợp với ECCV02), Copenhagen, Đan Mạch, tháng 6 năm 2002.
[24] - AkurKumar Patel, Đại học Công nghệ Gujarat, Khoa học và Kỹ thuật Máy tính
[25] - Thuật toán mạnh mẽ được cải tiến cho hình ảnh dựa trên mẫu mực
[26] - Wikipedia, Tỷ lệ tín hiệu-nhiễu-nhiễu


30
Điều này thật tuyệt vời . Đêm đầy sao thật tuyệt. Tuy nhiên, Mona Lisa đó ...
Hannes Karppila

8
Đầu tiên hãy để tôi nói "trời ơi, điều này thật phi thường". Thứ hai: Tôi đã nhận xét "Mona Lisa là một số shit SCP" ở một câu hỏi khác ở đây, nhưng con cú đó thực sự trông giống như một cái gì đó có thể xuất hiện trên wiki SCP.
undergroundmonorail

3
Các đoạn trích dẫn bạn đề cập có thể được thực hiện khối trích dẫn?
trichoplax

1
@trichoplax Có một số sửa đổi nhỏ trong hầu hết các câu, chúng không phải là trích dẫn chính xác. Xem xét mô tả thuật toán giống như org. giấy ngoại trừ khi nó nói sửa đổi hoặc mã. Tôi không muốn làm lộn xộn định dạng nữa :)
mınxomaτ

2
Khi tôi cố gắng nhìn vào một cái gì đó rất cẩn thận trong giấc mơ của mình, đôi khi mọi thứ trở nên chính xác như thế này.
jimmy23013

45

Matlab

Đây là một cách tiếp cận nội suy đơn giản. Ý tưởng đầu tiên là phản ánh những gì ở mỗi bên của miếng vá. Sau đó, các pixel hình ảnh phản chiếu đó được nội suy bằng cách chúng gần với cạnh tương ứng:

Phần khó khăn là tìm một trọng số nội suy đẹp. Sau một vài lần chơi xung quanh, tôi đã nghĩ ra một hàm hợp lý bằng 0 trên tất cả các cạnh trừ cái mà chúng ta nhân đôi. Điều này sau đó được biến đổi bởi một đa thức bậc ba cho một số làm mịn:

Cách tiếp cận đơn giản này thực hiện tốt đáng ngạc nhiên trên các hình ảnh "tự nhiên", nhưng ngay khi bạn phải đối mặt với các cạnh sắc nét, trò chơi đã kết thúc. Trong ví dụ gothic của Mỹ, các gai của ngã ba hay xếp thẳng hàng với lưới pixel, khiến nó trông khá đẹp, nhưng nó sẽ tệ hơn nhiều nếu không.

Vì vậy, đây là kết quả:

Và cuối cùng, mã:

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 làm tôi khó chịu.
Andras Deak

46
O̢̎̓̀ǹ̰͎̣͙a̤̩̖̞̝ͧ̈ͤͤ L͉̻̭͌i̛̥͕̱͋͌ş̠͔̏̋̀ạ̫͕͎ͨͮͪ̐͡ͅ
mınxomaτ

8
Đó Mona Lisa là một số shit SCP
undergroundmonorail

1
Hình ảnh rô trông thật tuyệt vời IMHO.
Sản phẩm ETH

1
Tôi sẽ không ngạc nhiên nếu bạn chiến thắng thử thách của chính mình với điều này. Đây là một giải pháp thực sự tốt đẹp.
Alex A.

25

Toán học

Điều này sử dụng Inpaintchức năng của Mathicala. Bởi vì chính Mathicala thực hiện tất cả các công việc nặng nhọc, đây là một wiki cộng đồng.

inPaint(dưới đây) là một thích ứng đơn giản của Inpaint. Đối với tranh / ảnh màu, nó sử dụng cài đặt mặc định, "TextureSynt tổng hợp". Nếu nó phát hiện ra rằng hình ảnh là đen trắng (vì dữ liệu hình ảnh của hình ảnh giống với dữ liệu hình ảnh ở dạng nhị phân của hình ảnh), thì nó sẽ tạo thành hình ảnh và áp dụng bản vá "TotalVariation". Các Ifkhoản hoặc áp dụng Binarizehoặc Identityđể hình ảnh. ( IdentityHàm trả về đối số của nó không thay đổi.)

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

Hình ảnh và mặt nạ được nhập làm đối số inPaint. PartitionGridchỉ đơn thuần cho mục đích định dạng.

đầu vào

Các đầu ra đã được vá. Không có chỉnh sửa các hình ảnh sau inPaint.

đầu ra


4
Nó có thể là một sự trùng hợp ngẫu nhiên, nhưng tôi ngạc nhiên về hiệu suất của mê cung!
flawr

1
@flawr Tôi sẽ ném vào thứ gì đó như thế này chỉ để gây rối với giải pháp này;) (Ai biết được? Những người da đen đó thực sự đang gặp khó khăn.)
Andras Deak

17
Tôi không nghĩ đây nên là một wiki cộng đồng.
Dennis

lawr, Có, Inpaintdường như tìm kiếm sự đối xứng trên toàn bộ hình ảnh đen trắng. - DavidC 9 giờ trước
DavidC

Bạn có chắc rằng thuật toán đen trắng không liên quan đến việc hy sinh dê ở bất cứ đâu không? Làm thế nào --- trên Trái đất --- địa ngục nó đoán cấu trúc trung tâm của hình ảnh, nếu tất cả chúng bị che lấp ??
Andras Deak

18

Python 2 và PIL

Chương trình này pha trộn các bản sao của các vùng Bắc, Nam, Đông và Tây để tạo các pixel thay thế sử dụng màu sắc, họa tiết và tô bóng từ vùng hình ảnh cục bộ.

Các ví dụ đầu ra:

Mã đầu tiên tìm thấy hộp giới hạn cho các bản vá. Sau đó, với mỗi pixel được tạo, nó sẽ tính màu của từng kênh (RGB) dựa trên tổng trọng số của 4 vùng xung quanh.

import sys
from PIL import Image

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

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

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

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

imageobj.save(outfile)

3
Mona Lisa này cũng thật đáng sợ! Có phải tất cả Mona Lisas trong thử thách này đều đáng sợ?
undergroundmonorail

@undergroundmonorail Tôi đoán khuôn mặt tình cờ do máy tính tạo ra đến từ sâu trong thung lũng kỳ lạ .
Andras Deak

Bạn lấy PIL từ đâu?
Elliot A.

@ElliotA. Hiểu biết của tôi là PIL thích hợp đã chết, nhưng nó là nguồn mở và vì vậy nó tồn tại dưới cái tên "Gối". Nếu bạn google "gối trăn", bạn nên tìm nó.
undergroundmonorail

13

Python 3, PIL

Chương trình này sử dụng toán tử sobel và vẽ các đường lên hình ảnh dựa trên đó.

Toán tử sobel tìm góc của mỗi cạnh, do đó, bất kỳ cạnh nào đùn vào khu vực không xác định sẽ tiếp tục.

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

Trong khi đó, đây là những hình ảnh mẫu.

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

Mona Lisa Mona Lisa oǹ̰͎̣a Lisa o̢̎̓̀ǹ̰͎̣aͧ̈ͤ Lisa o̢̎̓̀ǹ̰͎̣͙a̤̩̖̞̝ͧ̈ͤͤ L͉̻̭͌i̛̥͕̱͋͌ş̠͔̏̋̀ạ̫͕͎ͨͮͪ̐͡ͅ

nhập mô tả hình ảnh ở đây Khu vực trong hình trên mịn như một cây xương rồng

Nó không phải là rất tốt với màu sắc liên tục.

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây


1
Oh, và bạn có thể vui lòng thêm các trường hợp thử nghiệm B / W không?
flawr

2
Trông thật tuyệt trên Starry Night one.
SuperJedi224

1
Wow, điều này có vẻ đáng kinh ngạc bây giờ! Các bản vá vẫn còn đáng chú ý nhưng một ý tưởng mới tuyệt vời! Yêu thích của tôi cho đến nay =)
flawr

8
+1 cho "Mona Lisa Mona Lisa oǹ̰͎̣a Lisa o̢̎̓̀ǹ̰͎̣aͧ̈ͤ Lisa o̢̎̓̀ǹ̰͎̣͙a̤̩̖̞̝ͧ̈ͤͤ L͉̻̭͌i̛̥͕̱͋͌ş̠͔̏̋̀ạ̫͕͎ͨͮͪ̐͡ͅ"
mbomb007 5/2/2016

2
Bạn giành chiến thắng trong cuộc thi "Mona Lisa đáng sợ nhất", IMO. 0_o
DLosc

8

Con trăn 2

Kịch bản python đơn giản tạo bản vá bằng cách sử dụng các giá trị từ pixel ngay bên ngoài khoảng cách. Nó lấy các giá trị màu từ cuối hàng và cột của pixel và tính toán trung bình có trọng số bằng khoảng cách từ các pixel đó.

Đầu ra không đẹp lắm, nhưng đó là nghệ thuật .

img1 img2 img3 img4 img5 img6

Và sau đó, mã:

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

Tại thời điểm bạn nhìn thấy các sọc ngang / dọc, có lẽ bạn có thể cải thiện nó bằng cách bao gồm các hướng khác!
flawr

Tôi thực sự đã thử nó, nhưng tôi không thể có được kết quả tốt, vì vậy tôi chỉ quyết định làm mờ hình ảnh: D
Hannes Karppila

19
Cuối cùng, một Mona Lisa không làm tôi sợ đến chết, nhưng trông giống như một kẻ giết người bị bắt giữ thay vào đó.
Andras Deak

6

Toán học

Inpaint

Điều đó xảy ra khi Mathicala có một hàm dựng sẵn thực hiện chính xác nhiệm vụ này và ý tôi là chính xác :

Inpaint[image, region]

  • chỉnh sửa các phần của imagetương ứng với các phần tử khác không trong region.

Theo mặc định, nó sử dụng "phương pháp tổng hợp kết cấu phù hợp nhất bằng cách sử dụng lấy mẫu ngẫu nhiên", tạo ra kết quả tốt trên các bức tranh, nhưng kết quả kém cho mê cung và bàn cờ:

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

Chơi xung quanh với các cài đặt không giúp tôi tăng chất lượng trên tất cả các hình ảnh, vì vậy tôi chỉ sử dụng các giá trị mặc định (để lưu byte byte này codegolf.sesau tất cả!).


23
" Thật đúng là Mathicala có chức năng tích hợp sẵn " ... ngạc nhiên, ngạc nhiên;)
Andras Deak

Đối với mê cung và bảng kiểm tra, tốt hơn là sử dụng phương pháp "TotalVariation" cùng với Binarize(để loại bỏ các vết bẩn màu xám). Hãy thử điều này: methods = {"TextureSynthesis", "Diffusion", "FastMarching", "NavierStokes", "TotalVariation"};g[pic_, mask_] := Join[{Labeled[Framed@pic, "Original"]}, Labeled[ Binarize@Inpaint[pic, mask, Method -> #], #] & /@ methods]
DavidC

@DavidC Tôi đã thử các phương pháp khác, nhưng chỉ TextureSynthesisnhìn tốt trên tranh; và tôi không nghĩ rằng chúng tôi được phép điều chỉnh cài đặt của mình cho từng trường hợp thử nghiệm riêng lẻ. (Nếu chúng ta có thể, thì chúng ta có thể cung cấp một phần tầm thường dưới dạng 'cài đặt'.)
2012rcampion

Kết quả mê cung và bàn cờ thực sự gây hoang mang cho tôi. Tại sao là Mathematica tái thiết khu vực bị mất nên không thường xuyên và không đối xứng 's?
David Zhang

Điều này sẽ tự động phát hiện xem một hình ảnh có phải là đen trắng hay không và thực hiện các điều chỉnh thích hợp (nhị phân và phương pháp "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

Câu trả lời này thực hiện ý tưởng trong bài báo "Deep Image Prior" của Ulyanov et al. (CVPR 2018) Trong bài báo này, họ đã khám phá ý tưởng rằng cách thức hoạt động tốt của mạng lưới thần kinh để xử lý hình ảnh phản ánh chặt chẽ ý tưởng của chúng ta về hình ảnh tự nhiên sẽ trông như thế nào (phân phối "trước").

Họ đã đề xuất một phương pháp có thể được sử dụng để làm mờ cũng như loại bỏ nhiễu và giả tạo, chỉ sử dụng hình ảnh đã cho mà không được đào tạo về bất kỳ dữ liệu nào khác. Khái niệm thực tế khá đơn giản: Mạng được đào tạo để tạo ra hình ảnh mong muốn (đối với một số nhiễu ngẫu nhiên cố định làm đầu vào) bằng cách chỉ phạt các erros bên ngoài một số mặt nạ nhất định. Nếu bạn muốn loại bỏ tiếng ồn, bạn không cần phải che giấu bất cứ điều gì, mà chỉ cần dừng lại sớm trong khóa đào tạo.

Để inpainting bạn che dấu phần bạn muốn inpaint và đào tạo cho đến khi hội tụ. Nó chắc chắn không phải là hiện đại, nhưng tôi vẫn muốn đăng thử và đăng nó ở đây vì sự đơn giản của ý tưởng và hiệu suất vẫn đáng chú ý. Trong các thí nghiệm của tôi, việc phát hiện ra các bản vá lớn hơn không thành công nhưng đối với các phân đoạn nhỏ hơn, kết quả có thể thuyết phục hơn rất nhiều.

Tôi đã thực hiện điều này bằng cách sử dụng kiến trúc U-Net phổ biến từ jaxony trên github . Mã cho đào tạo và xử lý các hình ảnh có thể được tìm thấy dưới đây.

Đào tạo

Đây là một hình dung của quá trình đào tạo. Mỗi khung là trạng thái của một số lần lặp nhất định:

Ví dụ

Lưu ý rằng trên một cpu, việc này có thể mất hàng giờ để chạy chỉ một hình ảnh, trong khi một gpu hỗ trợ cuda tốt có thể mất ít thời gian hơn.

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 có sử dụng cái gì đó khác với U-Nets không? có vẻ như họ sẽ nhận được kết quả tốt hơn
ASCII - chỉ

Ngoài ra, bạn đã thử với mã của Deep Image Prior
ASCII - chỉ

@ ASCII-only Họ nêu trong bài báo rằng họ thực sự chủ yếu sử dụng U-Net, nhưng tôi không thể tìm thấy các tham số chính xác mà họ đã sử dụng. Họ có thể đã sử dụng một mạng lưới với công suất lớn hơn. Tôi chỉ có một máy tính với một lượng điện năng rất hạn chế. Vì vậy, tôi đã phải chọn các tham số vẫn phù hợp với bộ nhớ và không mất quá nhiều thời gian để đào tạo. Tôi không chắc chính xác mất bao lâu nhưng trên máy tính tôi đã sử dụng (chỉ với CPU) những hình ảnh này mất nhiều ngày. (Nếu bạn có GPU hỗ trợ
cuda

Tôi cũng nghi ngờ rằng do thiết kế của mạng có mặt nạ hình chữ nhật cũng không lý tưởng (và mặt nạ nhỏ hơn có thể cũng sẽ đẹp hơn), nếu bạn so sánh ví dụ như một vài hình ảnh đầu tiên với hai hình ảnh cuối cùng (không sử dụng mặt nạ hình chữ nhật) .
flawr

4

Python với OpenCV

OpenCV có một chức năng gọi là inpaint. Có hai loại inpainting được sử dụng, tôi sẽ sử dụng Phương pháp Marching nhanh. Theo tài liệu, thuật toán hoạt động như thế này:

Xem xét một khu vực trong hình ảnh được inpained. Thuật toán bắt đầu từ ranh giới của khu vực này và đi vào bên trong khu vực dần dần lấp đầy mọi thứ trong ranh giới trước. Phải mất một khu phố nhỏ xung quanh điểm ảnh trên vùng lân cận mới được tô màu. Pixel này được thay thế bằng tổng trọng số chuẩn hóa của tất cả các pixel đã biết trong vùng lân cận. Lựa chọn các trọng số là một vấn đề quan trọng. Nhiều trọng số hơn được trao cho những pixel nằm gần điểm, gần với mức bình thường của đường biên và những điểm nằm trên đường viền biên. Khi một pixel không được tô màu, nó sẽ di chuyển đến pixel gần nhất tiếp theo bằng Phương pháp di chuyển nhanh. FMM đảm bảo các pixel gần các pixel đã biết này được in trước, để nó chỉ hoạt động giống như một thao tác heuristic thủ công.

Đây là mã *:

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)

Lưu ý cách tôi chuyển đổi BGR sang RGB vì lý do âm mưu. Ngoài ra, tôi xoay nó. Đây là kết quả:

Gô tích

đêm đầy sao hét lên một lisa mona đáng sợ!

Mona Lisa trở lại!

dòng 1

người kiểm tra

Như bạn có thể thấy, nó không phải là tốt nhất với hai màu.


Mona Lisa đã có một sự đổi mới
Conor O'Brien

3

Java

Một cách tiếp cận trung bình màu. Có lẽ có thể được cải thiện.

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

Các kết quả:

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây


2
Tại sao bạn luôn luôn có được các đường trong góc đặc biệt này? Các góc trên bên trái dường như khớp tương đối tốt, trong khi phần dưới bên phải hoàn toàn không khớp.
flawr

Tôi nghĩ rằng nó phải làm theo cách mà tôi lặp qua khu vực. Có lẽ tôi sẽ thay đổi điều đó cuối cùng.
SuperJedi224

Nó luôn luôn trông giống như có hình thang.
ericw31415

@ ericw31415 Đó là một tạo tác của thứ tự lặp.
SuperJedi224
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.