แก้ไขภาพ


114

ในซอฟต์แวร์แก้ไขภาพที่ได้รับความนิยมนั้นมีคุณสมบัติหนึ่งนั่นคือแพทช์ (คำที่ใช้ในการประมวลผลภาพinpaintingตามที่ @ mınxomaτชี้ให้เห็น) พื้นที่ที่เลือกของรูปภาพโดยอ้างอิงจากข้อมูลภายนอกของแพทช์นั้น และมันก็ใช้งานได้ค่อนข้างดีโดยพิจารณาว่าเป็นเพียงโปรแกรม ในฐานะที่เป็นมนุษย์บางครั้งคุณอาจเห็นว่ามีบางอย่างผิดปกติ แต่ถ้าคุณบีบตาหรือมองแวบ ๆ สั้น ๆดูเหมือนว่าแพทช์จะเติมช่องว่างได้ค่อนข้างดี

ตัวอย่างโดยซอฟต์แวร์แก้ไขภาพยอดนิยม

ท้าทาย

เมื่อกำหนดรูปภาพและรูปแบบที่ระบุพื้นที่สี่เหลี่ยมของรูปภาพควรได้รับการแก้ไข (เช่นเดียวกับรูปภาพหรือรูปแบบที่ต้องการอื่น ๆ ) โปรแกรมของคุณควรพยายามเติมพื้นที่ที่ระบุด้วยแพตช์ที่พยายามผสมผสานกับส่วนที่เหลือของ รูปภาพ. โปรแกรมไม่สามารถใช้ข้อมูลของภาพต้นฉบับที่อยู่ในพื้นที่ที่ระบุ

คุณสามารถสันนิษฐานได้ว่าแพทช์มักมีความกว้างห่างจากด้านข้างเสมอและความสูงนั้นอยู่ห่างจากด้านบนและด้านล่างของภาพ นั่นหมายความว่าพื้นที่สูงสุดของแพตช์คือ 1/9 ของภาพทั้งหมด

โปรดเพิ่มคำอธิบายสั้น ๆ เกี่ยวกับวิธีการทำงานของอัลกอริทึมของคุณ

การออกเสียง

ผู้ลงคะแนนจะถูกขอให้ตัดสินว่าอัลกอริทึมทำงานได้ดีเพียงใดและลงคะแนนให้เหมาะสม

ข้อเสนอแนะบางอย่างเกี่ยวกับวิธีตัดสิน: (ขอขอบคุณอีกครั้งที่ @ mınxomaτสำหรับหลักเกณฑ์เพิ่มเติม)

  • ถ้าคุณเหล่ตาและรูปภาพดูดี?
  • คุณสามารถบอกได้อย่างแม่นยำว่าแพตช์คืออะไร?
  • โครงสร้างและพื้นผิวจากพื้นหลังภาพและพื้นที่โดยรอบดีแค่ไหนต่อเนื่องกัน?
  • พื้นที่ที่แก้ไขมีพิกเซลสีผิดเพี้ยนจำนวนเท่าใด
  • มี blobs / บล็อกสีที่เหมือนกันในพื้นที่ที่ดูเหมือนจะไม่ได้อยู่ที่นั่นหรือไม่?
  • พื้นที่ที่แก้ไขมีสี / ความคมชัดหรือความสว่างที่เปลี่ยนแปลงไปอย่างมากเมื่อเทียบกับส่วนที่เหลือของภาพหรือไม่?

เกณฑ์ความถูกต้อง

เพื่อให้การส่งที่ถูกต้องภาพที่ส่งออกจะต้องตรงกับภาพที่ป้อนนอกพื้นที่ที่ระบุ

กรณีทดสอบ

ด้านซ้ายภาพต้นฉบับด้านขวาจะมีหน้ากากที่เกี่ยวข้อง:


1
เราสามารถยอมรับรูปแบบการป้อนข้อมูลเป็นอาร์กิวเมนต์ข้อความ (เช่นinpaint.exe left top width height img.jpg) ได้หรือไม่?
mınxomaτ

1
แน่นอนว่ารูปแบบการป้อนข้อมูล / ส่งออกไม่ได้จริงๆที่สำคัญที่มันเป็นประกวดความนิยมที่แรกของทุกการทำงานของคุณขั้นตอนวิธีเป็นสิ่งสำคัญ
ข้อบกพร่อง

24
นี่คือความท้าทายในทางปฏิบัติมาก เป็นไปได้ที่ผลลัพธ์จะดีกว่าอัลกอริธึมที่มีอยู่ที่ใช้ใน GIMP และซอฟต์แวร์แก้ไขภาพโอเพ่นซอร์สอื่น ๆ โชคลาภชื่อเสียงและเกียรติยศเป็นของคุณ!
Sparr

6
@Sparr และลายน้ำที่น่าเกลียดที่สุดสามารถลบออกจากสื่อที่ดาวน์โหลดได้)
Andras Deak

2
Buildins โอเคอย่างสมบูรณ์ฉันสงสัยว่าพวกเขาจะได้รับความนิยมมาก
ข้อบกพร่อง

คำตอบ:


142

AutoIt , VB

บทนำ

นี่คือการดำเนินการของการกำจัดวัตถุโดยการจัดการระบบสีแบบอย่างตามขั้นตอนวิธีการพัฒนาโดยเอ Criminisi, P. เปเรซ (เคมบริดจ์ Microsoft Research จำกัด ) และเคโทะยะมะ (Microsoft) [x] อัลกอริทึมนี้มีการกำหนดเป้าหมายที่รูปภาพข้อมูลสูง (และเฟรมวิดีโอ) และมีจุดมุ่งหมายเพื่อให้เกิดความสมดุลระหว่างการสร้างโครงสร้างใหม่และการสร้างใหม่แบบอินทรีย์ ย่อหน้าของคำตอบนี้ประกอบด้วยคำพูดแบบเต็มจากกระดาษต้นฉบับ (เนื่องจากไม่มีให้บริการอย่างเป็นทางการอีกต่อไป) เพื่อทำให้คำตอบนี้มีอยู่ในตัวเองมากขึ้น

อัลกอริทึม

เป้าหมาย : แทนที่พื้นที่ ( ปิดบัง ) ที่เลือก ( ควรเป็นวัตถุเบื้องหน้าที่แยกออกมาทางสายตา) โดยใช้พื้นหลังที่เป็นไปได้

ในงานก่อนหน้านักวิจัยหลายคนคิดว่าการสังเคราะห์พื้นผิวเป็นวิธีหนึ่งในการเติมเต็มพื้นที่ภาพขนาดใหญ่ด้วยพื้นผิวที่ "บริสุทธิ์" - ลวดลายพื้นผิวสองมิติแบบซ้ำ ๆ ที่มีความเสถียรปานกลาง นี่คือพื้นฐานของการวิจัยการสังเคราะห์เนื้อ - ร่างใหญ่ซึ่งพยายามที่จะทำซ้ำโฆษณาพื้นผิวinfinitumให้เป็นแหล่งเล็ก ๆ ของตัวอย่างเนื้อบริสุทธิ์[1] [8] [9] [10] [11] [12] [14] [15] [16] [19] [22]

ที่มีประสิทธิภาพเป็นเทคนิคเหล่านี้อยู่ในการจำลองพื้นผิวที่สอดคล้องกันที่พวกเขามีความยากลำบากในการกรอกหลุมในภาพของฉากจริงของโลกซึ่งมักจะประกอบด้วยโครงสร้างเชิงเส้นและพื้นผิวคอมโพสิต - พื้นผิวหลายปฏิสัมพันธ์เชิงพื้นที่[23] ปัญหาหลักคือขอบเขตระหว่างพื้นที่ภาพเป็นผลิตภัณฑ์ที่ซับซ้อนของอิทธิพลร่วมกันระหว่างพื้นผิวที่แตกต่างกัน ตรงกันข้ามกับธรรมชาติสองมิติของพื้นผิวบริสุทธิ์ขอบเขตเหล่านี้ก่อให้เกิดสิ่งที่อาจถูกพิจารณาว่าเป็นโครงสร้างภาพแบบหนึ่งมิติหรือเป็นเส้นตรงมากขึ้น

เทคนิคการระบายสีภาพเติมช่องว่างในภาพโดยแพร่กระจายโครงสร้างเชิงเส้น (เรียกว่าisophotesในวรรณคดีการทาสี ) เข้าสู่พื้นที่เป้าหมายผ่านการแพร่กระจาย พวกเขาได้รับแรงบันดาลใจจากสมการเชิงอนุพันธ์ย่อยของการไหลของความร้อนทางกายภาพและทำงานเป็นอัลกอริทึมการฟื้นฟู ข้อเสียเปรียบของพวกเขาคือกระบวนการแพร่กระจายนั้นทำให้เกิดภาพเบลอซึ่งเห็นได้ชัด

รูปที่.  2

ภูมิภาคที่จะเติมเต็มคือพื้นที่เป้าหมายถูกระบุด้วย and และเส้นชั้นความสูงจะแสดงเป็น den เส้นชั้นความสูงวิวัฒนาการเข้าด้านในขณะที่อัลกอริธึมดำเนินไปดังนั้นเราจึงเรียกมันว่าเป็น“ ส่วนเติมด้านหน้า” ขอบเขตแหล่งที่มา, which ซึ่งยังคงอยู่ตลอดทั้งอัลกอริทึมให้ตัวอย่างที่ใช้ในกระบวนการเติม ตอนนี้เรามุ่งเน้นไปที่การวนซ้ำของอัลกอริทึมเพื่อแสดงว่าโครงสร้างและพื้นผิวได้รับการจัดการอย่างเพียงพอโดยการสังเคราะห์แบบอย่าง สมมติว่าเทมเพลตสแควร์Ψp∈Ωอยู่กึ่งกลางที่จุด p (รูปที่ 2b) จะต้องถูกเติม ตัวอย่างการจับคู่ที่ดีที่สุดจากภูมิภาคต้นทางมาจากแพตช์ Ψqˆ ∈Φซึ่งคล้ายกับชิ้นส่วนเหล่านั้นที่เติมในΨpอยู่แล้ว ในตัวอย่างในรูป 2b เราจะเห็นว่าถ้าΨpอยู่บนขอบภาพต่อเนื่อง การจับคู่ที่ดีที่สุดที่น่าจะเป็นไปตามขอบ (หรือสีที่คล้ายกัน) (เช่น'q 'และΨq' 'ในรูปที่ 2c) ทั้งหมดที่จำเป็นในการเผยแพร่ isophote เข้ามาคือการถ่ายโอนรูปแบบง่าย ๆ จากแพทช์แหล่งที่มาที่ตรงที่สุด (รูปที่ 2d) ขอให้สังเกตว่าการวางแนว isophote จะถูกเก็บไว้โดยอัตโนมัติ ในภาพแม้ว่าความจริงที่ว่าขอบดั้งเดิมไม่ได้เป็นมุมฉากกับรูปร่างเป้าหมาย structure โครงสร้างที่แพร่กระจายยังคงมีการวางแนวเดียวกันกับในภูมิภาคต้นทาง

รายละเอียดการใช้งานและอัลกอริทึม

ฟังก์ชันการทำงานของการใช้งานนี้ถูกห่อหุ้มใน ActiveX COM DLL ซึ่งถูกปล่อยออกจากโปรแกรมโฮสต์เป็นไบนารีแล้วเรียกใช้งานได้ทันทีโดยการเรียกใช้ inpainter โดย 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 โดยเฉพาะ DIBSection ของ GDI / + บิตแมปใด ๆ (ไฟล์สตรีมและอื่น ๆ ) ไฟล์รูปภาพที่ระบุถูกโหลดและวาดลงบนบิตแมปเปล่าที่สร้างขึ้นจากScan0ขนาดภาพอินพุต

ไฟล์อินพุตสำหรับการนำไปใช้งานนี้เป็นรูปแบบไฟล์ใด ๆ ที่เข้ากันได้กับ GDI / + ซึ่งมีข้อมูลภาพที่ถูกปิดบังไว้ มาสก์ (s)เป็นหนึ่งหรือหลายภูมิภาคที่มีสีสม่ำเสมอในภาพอินพุต ผู้ใช้ป้อนค่าสี RGB สำหรับมาสก์มีเพียงพิกเซลที่มีค่าสีตรงกันเท่านั้น สีกำบังเริ่มต้นคือสีเขียว (0, 255, 0) พื้นที่ที่ถูกพรางทั้งหมดรวมกันเป็นตัวแทนของภูมิภาคเป้าหมายΩที่จะถูกลบออกและเติมเต็ม ขอบเขตของแหล่งที่มาคือΦถูกกำหนดให้เป็นรูปภาพทั้งหมดลบด้วยพื้นที่เป้าหมาย (Φ = I − Ω)

ถัดไปเช่นเดียวกับการสังเคราะห์พื้นผิวที่เป็นแบบอย่างทั้งหมด[10] ต้องระบุขนาดของหน้าต่างเทมเพลตΨ (หรือที่เรียกว่า " scan radius ") การใช้งานนี้มีขนาดหน้าต่างเริ่มต้นที่6²พิกเซล แต่ในทางปฏิบัติผู้ใช้จำเป็นต้องตั้งค่าให้ใหญ่กว่าองค์ประกอบพื้นผิวที่แตกต่างที่ใหญ่ที่สุดหรือ“ texel” ในพื้นที่ต้นทางเล็กน้อย การปรับเปลี่ยนเพิ่มเติมสำหรับอัลกอริทึมดั้งเดิมคือ " ขนาดบล็อก " ที่ผู้ใช้กำหนดซึ่งกำหนดพื้นที่ของพิกเซลที่จะถูกแทนที่ด้วยสีสม่ำเสมอใหม่ สิ่งนี้จะเพิ่มความเร็วและลดคุณภาพ ขนาดตัวต่อบล็อกที่มากกว่า 1px นั้นมีไว้เพื่อใช้กับพื้นที่ที่มีความสม่ำเสมอมาก (น้ำทรายขน ฯลฯ ) อย่างไรก็ตามควรรักษา kept ให้สูงสุด .5x ขนาดบล็อก (ซึ่งเป็นไปไม่ได้ขึ้นอยู่กับหน้ากาก)

เพื่อไม่ให้อัลกอริทึมหยุดภาพ 1 บิตทุกครั้งที่รับภาพที่มีสีน้อยกว่า 5 สีขนาดของหน้าต่างจะถูกขยายเป็น 10 เท่า

เมื่อกำหนดพารามิเตอร์เหล่านี้แล้วส่วนที่เหลือของกระบวนการเติมภูมิภาคจะเป็นไปโดยอัตโนมัติอย่างสมบูรณ์ ในอัลกอริทึมของเราแต่ละพิกเซลรักษาค่าสี (หรือ "ว่างเปล่า" หากพิกเซลไม่ได้บรรจุ) และค่าความเชื่อมั่นซึ่งสะท้อนถึงความมั่นใจของเราในค่าพิกเซลและจะถูกแช่แข็งเมื่อเติมพิกเซลแล้ว ในระหว่างอัลกอริทึมแพทช์ตามแนวเติมจะได้รับค่าลำดับความสำคัญชั่วคราวซึ่งกำหนดลำดับการเติม จากนั้นอัลกอริทึมของเราจะทำซ้ำสามขั้นตอนต่อไปนี้จนกว่าพิกเซลจะเต็ม

ขั้นตอนที่ 1: การคำนวณลำดับความสำคัญของโปรแกรมแก้ไข

กรอกข้อมูลเพื่อเป็นสิ่งสำคัญในการสังเคราะห์เนื้อไม่ใช่ตัวแปร[1] [6] [10] [13] จนถึงตอนนี้สิ่งที่ชื่นชอบเริ่มต้นคือวิธีการ "หัวหอมปอกเปลือก" ซึ่งพื้นที่เป้าหมายจะถูกสังเคราะห์จากด้านนอกเข้ามาด้านในในเลเยอร์ศูนย์กลาง อัลกอริทึมของเราดำเนินงานนี้ผ่านอัลกอริธึมการบรรจุขั้นแรกที่ดีที่สุดโดยขึ้นอยู่กับค่าลำดับความสำคัญที่กำหนดให้แต่ละชุดข้อมูลด้านหน้าการเติม การคำนวณลำดับความสำคัญจะเอนเอียงไปทางแพตช์ที่อยู่บนขอบของขอบที่แข็งแกร่งและล้อมรอบด้วยพิกเซลที่มีความมั่นใจสูงพิกเซลเหล่านี้เป็นขอบเขตที่ทำเครื่องหมายด้วยค่า -2 รหัสต่อไปนี้คำนวณลำดับความสำคัญใหม่:

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

เมื่อให้ patchp อยู่ตรงกลางที่จุด 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 ความตั้งใจคือการเติมแพทช์แรกที่มีจำนวนพิกเซลมากกว่าแล้วโดยมีการกำหนดค่าเพิ่มเติมให้กับพิกเซลที่ได้รับการเติมก่อนหน้า (หรือไม่เคยเป็นส่วนหนึ่งของภูมิภาคเป้าหมาย)

สิ่งนี้จะรวมการตั้งค่าไปยังรูปร่างบางรูปร่างโดยอัตโนมัติตามแนวหน้าเติม ตัวอย่างเช่นแพทช์ที่มีมุมและเส้นเอ็นบาง ๆ ของพื้นที่เป้าหมายจะมีแนวโน้มที่จะถูกเติมเต็มก่อนเนื่องจากมีการล้อมรอบด้วยพิกเซลจำนวนมากจากภาพต้นฉบับ แพทช์เหล่านี้ให้ข้อมูลที่น่าเชื่อถือมากกว่าที่จะจับคู่ ในทางกลับกันแพทช์ที่ส่วนปลายของ“ เพนนิซิลัส” ของพิกเซลที่เติมเต็มซึ่งยื่นออกไปในพื้นที่เป้าหมายจะมีการตั้งค่าไว้จนกว่าจะมีการเติมพิกเซลโดยรอบในระดับหยาบคำ C (p) ของ (1) ประมาณ บังคับใช้คำสั่งการเติมแบบศูนย์กลางที่ต้องการ

เมื่อเติมรายได้พิกเซลในเลเยอร์ด้านนอกของพื้นที่เป้าหมายจะมีแนวโน้มที่จะโดดเด่นด้วยค่าความเชื่อมั่นที่มากขึ้นและดังนั้นจึงจะเต็มไปก่อนหน้านี้ พิกเซลที่อยู่ตรงกลางของภูมิภาคเป้าหมายจะมีค่าความมั่นใจน้อยลง data data D (p) เป็นฟังก์ชั่นของความแรงของ isophotes ที่กดปุ่มδΩด้านหน้าในแต่ละการวนซ้ำ คำนี้ช่วยเพิ่มความสำคัญของแพทช์ที่ไอโซโฟท "ไหล" เข้ามา ปัจจัยนี้มีความสำคัญขั้นพื้นฐานในอัลกอริทึมของเราเพราะมันสนับสนุนโครงสร้างเชิงเส้นที่จะสังเคราะห์ก่อนและจึงแพร่กระจายอย่างปลอดภัยในภูมิภาคเป้าหมาย สายหักมีแนวโน้มที่จะเชื่อมต่อจึงตระหนักถึง "การเชื่อมต่อ" หลักการของวิสัยทัศน์จิตวิทยา[7] [17]

การสั่งซื้อที่เติมขึ้นอยู่กับคุณสมบัติของภาพที่เกิดในกระบวนการสังเคราะห์สารอินทรีย์ที่ช่วยลดความเสี่ยงของการ“เสียโครงสร้าง” สิ่งประดิษฐ์และยังช่วยลดสิ่งประดิษฐ์บล็อกโดยไม่ต้องแพทช์ตัดขั้นตอนที่มีราคาแพง[9]หรือขั้นตอนการผสมเบลอชักนำ[19 ] .

ขั้นตอนที่ 2: การกระจายข้อมูลพื้นผิวและโครงสร้าง

เมื่อคำนวณลำดับความสำคัญทั้งหมดในส่วนเติมด้านหน้า ( ขอบเขต ) แล้วจะพบ patch ˆpˆ ที่มีลำดับความสำคัญสูงสุด จากนั้นเราจะกรอกข้อมูลที่ดึงมาจากขอบเขตแหล่งที่มาΦ เราเผยแพร่ภาพพื้นผิวโดยการสุ่มตัวอย่างโดยตรงของภูมิภาคต้นทาง คล้ายกับ[10]เราค้นหาในพื้นที่ต้นทางสำหรับแพทช์นั้นซึ่งคล้ายกับ similarpˆ มากที่สุด อย่างเป็นทางการ

ที่ไหน

ระยะทาง d (Ψa, Ψb) ระหว่างสองแพตช์ทั่วไปΨ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 patch เต็มไปด้วยค่าพิกเซลใหม่ความเชื่อมั่น C (p) จะถูกอัปเดตในพื้นที่ที่คั่นด้วย imitedpˆ ดังนี้:

กฎการอัปเดตอย่างง่ายนี้ช่วยให้เราสามารถวัดความมั่นใจสัมพัทธ์ของแพทช์ที่ด้านหน้าเติมได้โดยไม่ต้องใช้พารามิเตอร์เฉพาะภาพ เมื่อกรอกรายได้แล้วค่าความเชื่อมั่นจะลดลงซึ่งบ่งชี้ว่าเราไม่แน่ใจเกี่ยวกับค่าสีของพิกเซลใกล้กับกึ่งกลางของพื้นที่เป้าหมาย ดำเนินการที่นี่ (พร้อมกับการอัปเดตที่จำเป็นอื่น ๆ ทั้งหมด):

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

เพียง uncomment ตัวอย่างที่คุณต้องการที่จะทำงานโดยใช้+CTRLQ

ไฟล์ทดสอบอย่างเป็นทางการ

อัลกอริทึมนี้ถูกสร้างขึ้นเพื่อปรับสำหรับแต่ละภาพ ดังนั้นค่าเริ่มต้น (และมาสก์เริ่มต้น) จึงไม่สมบูรณ์ เลือกค่าเริ่มต้นเพื่อให้ทุกตัวอย่างสามารถประมวลผลได้ในเวลาที่เหมาะสม ฉันขอแนะนำให้เล่นกับมาสก์ที่มีรูปร่างผิดปกติและขนาดของหน้าต่างที่ดีขึ้น คลิกที่ภาพเพื่อดูภาพขยาย!

กระดานหมากรุก

โกธิคอเมริกัน

เขาวงกต

Mona Lisa

(หน้ากากน่ากลัว)

กรี๊ด

แจ่มจรัส

ตัวอย่างโลกแห่งความจริง

เหล่านี้ทั้งหมดใช้มาสก์แบบวาดด้วยมือที่กำหนดเอง

หากคุณมีภาพที่น่าสนใจอื่น ๆ ที่คุณต้องการเห็นรวมไว้แสดงความคิดเห็น

การปรับปรุง EBII

EBII นั้นมีหลายสายพันธุ์ที่สร้างขึ้นโดยนักวิจัยหลายคน AnkurKumar Patelให้ความสนใจกับการรวบรวมเอกสาร[24]เกี่ยวกับการปรับปรุง EBII ต่างๆ

โดยเฉพาะกระดาษ " อัลกอริธึมที่ปรับปรุงแล้วที่มีประสิทธิภาพสำหรับภาพสีอิงจาก Exemplar " [25]กล่าวถึงการปรับปรุงสองอย่างเกี่ยวกับการชั่งน้ำหนักค่าลำดับความสำคัญ

การปรับปรุง

การปรับเปลี่ยนที่มีประสิทธิภาพอยู่ในขั้นตอนที่ 1 (ดูด้านบน) ของอัลกอริทึมและขยายเอฟเฟกต์C (p)และD (p)ในการจัดลำดับความสำคัญสำหรับพิกเซลนี้โดยใช้:

ในสูตรสำหรับCและD ที่ให้ไว้ด้านบนและเป็นปัจจัยการทำให้เป็นมาตรฐาน (เช่นα = 255), เวกเตอร์ isophote และเวกเตอร์หน่วยมุมฉากด้านหน้าด้านหน้าจุด p

เพิ่มเติม

ฟังก์ชั่นความสำคัญจะถูกกำหนดเป็นผลรวมน้ำหนักของ regularized ระยะความเชื่อมั่นC (P)และระยะข้อมูลใหม่D (P) โดยที่αคือค่าสัมประสิทธิ์การปรับค่า 0Rp (p) ที่น่าพอใจถูกกำหนดดังนี้

ที่αและβตามลำดับส่วนประกอบน้ำหนักของความเชื่อมั่นและข้อกำหนดของข้อมูล โปรดทราบว่าα + β = 1

คะแนนวัตถุประสงค์

สิ่งที่น่าสนใจจริงๆก็คือว่าบทความนี้มีวิธีที่เสนอ (และง่าย!) สำหรับการให้คะแนนประสิทธิภาพหากอัลกอริทึม EBII ใช้เวลานี้กับเม็ดเกลือแม้ว่าเป็นวิธีที่เลือกโดยผู้เขียนกระดาษตัวเองเพื่อตรวจสอบประสิทธิภาพของวิธีการแปรปรวนที่เสนอและการปรับปรุงในภาพหลายภาพ

การประเมินผลจะดำเนินการโดยการเปรียบเทียบ PSNR (อัตราส่วนสัญญาณต่อสัญญาณรบกวนสูงสุด[26] ) ระหว่างภาพที่ถูกเรียกคืนและภาพต้นฉบับ โดยทั่วไปค่า PSNR ที่สูงกว่าจะมีความคล้ายคลึงกันของภาพซ่อมแซมที่ใหญ่กว่าเดิม สมการในการคำนวณ PSNR มีดังนี้:

ภาพทดสอบในโลกแห่งความจริงอันตระการตา 2 (สอง!) เหล่านี้คือ:

บทสรุปนั้นน่าผิดหวังเหมือนคุณภาพของกระดาษเอง มันแสดงการปรับปรุงน้อยมาก สิ่งสำคัญที่นี่เป็นวิธีการให้คะแนนวัตถุที่เป็นไปได้สำหรับความท้าทายประเภทนี้ (และความท้าทายการซ่อมแซมภาพอื่น ๆ ):

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

Meh

การวิจัยที่จะทำ

(เฉพาะ EBII)

a) การประมวลผลล่วงหน้า

ทุกอย่างมาถึงหลักการ "การลบ Magic" ที่อัลกอริทึมควร "ทำงานได้" สำหรับทุกสิ่ง คำตอบที่ไร้เดียงสาของฉันสำหรับสิ่งนี้คือแอมพลิฟายเออร์ตามสี (ดูด้านบน) แต่มีวิธีที่ดีกว่า ฉันกำลังคิดที่จะตระหนักถึงค่าเฉลี่ยทางเรขาคณิตของข้อความที่ติดตามได้ทั้งหมดเพื่อปรับขนาดหน้าต่างโดยอัตโนมัติและทำให้ขนาดแสตมป์ (รวมถึงการปรับปรุงของฉัน) ขึ้นอยู่กับความละเอียดของเท็กเซลและภาพทั้งหมด การวิจัยจะต้องทำที่นี่

b) การดำเนินการภายหลัง

ผู้แต่งดั้งเดิมทำงานได้ดีมากในการลบล้างตัวกรองการประมวลผลโพสต์ทั้งหมดที่คำนึง วันนี้ฉันลองอย่างอื่นโดยได้แรงบันดาลใจจากโมนาลิซาที่น่าประหลาดใจเสมอ ถ้าคุณใช้เวลาเพียงแค่ภูมิภาค inpainted และใช้หน้ากากใหม่เพื่อบล็อกแปลกทุกสีและให้อาหารที่เป็นอัลกอริทึม despeckling คุณจะเหลือผลที่เกือบจะสมบูรณ์แบบ ฉันอาจสำรวจบางครั้งในอนาคต


[X] - การกำจัดวัตถุโดย Inpainting จาก Exemplar โดย A. Criminisi, P. Perez, K. Toyama
[1] - M. Ashikhmin การสังเคราะห์พื้นผิวธรรมชาติ ในพรอ. ACM Symp บนกราฟิก 3 มิติเชิงโต้ตอบหน้า 217–226, Research Triangle Park, NC, มี.ค. 2001
[5] - M. Bertalmio, L. Vese, G. Vese, G. Sapiro และ S. Osher โครงสร้างพร้อมกันและภาพพื้นผิว inpainting จะปรากฏ 2545
[6] - ร. Bornard อี. Lecan, L. Laborelli และ JH Chenot ไม่มีการแก้ไขข้อมูลในภาพนิ่งและลำดับภาพ ใน ACM Multimedia ประเทศฝรั่งเศส ธ.ค. 2545
[7] - TF Chan และ J. Shen การทาสีแบบไม่มีพื้นผิวโดยการกระจายความโค้ง (CDD) J. Visual Comm ภาพตัวแทน 4 (12), 2544
[8] - JS de Bonet ขั้นตอนการสุ่มตัวอย่างแบบหลายจุดสำหรับการวิเคราะห์และการสังเคราะห์ภาพพื้นผิว ในพรอ. ACM Conf คอมพ์ กราฟิก (SIGGRAPH) เล่ม 31, pp. 361–368, 1997
[9] - A. Efros และ WT Freeman ภาพ quilting สำหรับการสังเคราะห์พื้นผิวและการถ่ายโอน ในพรอ. ACM Conf คอมพ์ กราฟิก (SIGGRAPH), pp. 341–346, Eugene Fiume, ส.ค. 2001
[10] - A. Efros และ T. Leung การสังเคราะห์พื้นผิวโดยการสุ่มตัวอย่างแบบไม่มีพารามิเตอร์ ในพรอ. ICCV, pp 1576-1081, Kerkyra, กรีซ, ก.ย. 1999
[11] - WT อิสระ, EC Pasztor และ OT Carmichael การเรียนรู้การมองเห็นในระดับต่ำ int J. Computer Vision, 40 (1): 25–47, 2000
[12] - D. Garber แบบจำลองการคำนวณสำหรับการวิเคราะห์พื้นผิวและการสังเคราะห์พื้นผิว. วิทยานิพนธ์ปริญญาเอก, มหาวิทยาลัย ทางใต้ของแคลิฟอร์เนียสหรัฐอเมริกา 2524
[13] - พีแฮร์ริสัน ขั้นตอนที่ไม่เป็นลำดับชั้นสำหรับการสังเคราะห์พื้นผิวที่ซับซ้อนอีกครั้ง ในพรอ. int conf บริษัท ยุโรปกลาง กราฟิก, Visua และคอมพ์ วิสัยทัศน์, Plzen, สาธารณรัฐเช็ก, ก.พ. 2001
[14] - DJ Heeger และ JR Bergen การวิเคราะห์ / สังเคราะห์เนื้อพีระมิด ในพรอ. ACM Conf คอมพ์ กราฟิก (SIGGRAPH) เล่มที่ 29, pp. 229–233, ลอสแองเจลิส, แคลิฟอร์เนีย, 1995
[15] - A. Hertzmann, C. Jacobs, N. Oliver, B. Curless และ D. Salesin การเปรียบเทียบภาพ ในพรอ. ACM Conf คอมพ์ กราฟิก (SIGGRAPH), Eugene Fiume, ส.ค. 2001
[16] - H. Igehy และ L. Pereira การแทนที่ภาพผ่านการสังเคราะห์พื้นผิว ในพรอ. int conf การประมวลผลภาพ, pp. III: 186–190, 1997.
[17] - G. Kanizsa องค์กรในวิสัยทัศน์ Praeger นิวยอร์ก 2522
[19] - แอล. เหลียง, ซีหลิว, วาย.-คิว Xu, B. Guo และ H.-Y Shum การสังเคราะห์พื้นผิวแบบเรียลไทม์โดยการสุ่มตัวอย่างด้วยแพทช์ ในธุรกรรม ACM บนกราฟิกส์ 2001
[22] - L.-W Wey และ M. Levoy การสังเคราะห์พื้นผิวอย่างรวดเร็วโดยใช้การแปลงเวกเตอร์เชิงโครงสร้างของต้นไม้ ในพรอ. ACM Conf คอมพ์ กราฟิก (SIGGRAPH), 2000.
[23] - A. Zalesny, V. Ferrari, G. Caenen และ L. van Gool การสังเคราะห์พื้นผิวคอมโพสิตแบบขนาน ในเนื้อ 2002 การประชุมเชิงปฏิบัติการ - (ร่วมกับ ECCV02), โคเปนเฮเกน, เดนมาร์ก, มิถุนายน 2002
[24] - AkurKumar เทลรัฐคุชราตมหาวิทยาลัยเทคโนโลยีวิทยาการคอมพิวเตอร์และวิศวกรรม
[25] - การปรับปรุงขั้นตอนวิธีการที่มีประสิทธิภาพสำหรับแบบอย่างตามภาพการจัดการระบบสี
[26] - Wikipedia, อัตราส่วนสัญญาณต่อเสียงรบกวน


30
นี้เป็นที่น่ากลัว เดอะสตาร์รี่ไนท์นั้นดีมาก ยังคงว่าโมนาลิซ่า ...
Hannes Karppila

8
ก่อนอื่นให้ฉันพูดว่า "โอพระเจ้านี่ช่างเหลือเชื่อจริงๆ" ข้อที่สอง: ฉันได้แสดงความคิดเห็นแล้วว่า "โมนาลิซ่านั้นเป็น SCP ที่น่ารังเกียจ" ในคำถามอื่นที่นี่ แต่ที่จริงแล้วนกฮูกดูเหมือนสิ่งที่สามารถปรากฏบนวิกิ SCP
undergroundmonorail

3
ย่อหน้าที่ยกมาที่คุณพูดถึงสามารถทำเป็นบล็อกคำพูดได้หรือไม่?
trichoplax

1
@trichoplax มีการแก้ไขเล็กน้อยในเกือบทุกประโยคพวกเขาไม่ได้ราคาที่แน่นอน พิจารณาคำอธิบายอัลกอริทึมเหมือนกับ org กระดาษยกเว้นเมื่อมีข้อความแจ้งว่ามีการดัดแปลงหรือรหัส ฉันไม่ต้องการถ่วงรูปแบบอีกต่อไป :)
mınxomaτ

2
เมื่อฉันพยายามที่จะดูบางสิ่งบางอย่างอย่างระมัดระวังในความฝันของฉันบางครั้งสิ่งที่กลายเป็นเช่นนี้
jimmy23013

45

Matlab

นี่เป็นวิธีการแก้ไขอย่างง่าย แนวคิดนี้เป็นครั้งแรกที่สะท้อนสิ่งที่อยู่ในแต่ละด้านของแพทช์ จากนั้นพิกเซลภาพสะท้อนเหล่านั้นจะถูกแก้ไขโดยความใกล้ชิดกับขอบที่เกี่ยวข้อง:

ส่วนที่ยากคือการหาน้ำหนักการแก้ไขที่ดี หลังจากเล่นไปรอบ ๆ ฉันก็พบกับฟังก์ชั่นเหตุผลที่เป็นศูนย์ในทุก ๆ ขอบยกเว้นอันที่เราทำมิเรอร์ นี่คือพหุนามระดับที่สามจากนั้นก็เปลี่ยนให้เรียบ:

วิธีการง่าย ๆ นี้ทำได้ดีอย่างน่าประหลาดใจกับภาพที่ "เป็นธรรมชาติ" แต่ทันทีที่คุณเผชิญหน้ากับขอบที่คมชัดเกมจะจบลง ในตัวอย่างแบบกอธิคอเมริกันแหลมของหญ้าแห้งเป็นแนวตรงกับกริดพิกเซลซึ่งทำให้ดูดี แต่มันก็แย่กว่านี้มาก

ดังนั้นนี่คือผลลัพธ์:

และสุดท้ายรหัส:

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
Mona Lisa นั้นเป็น SCP ตัวหนึ่งที่น่ารังเกียจ
undergroundmonorail

1
ภาพตาหมากรุกดูดีมาก IMHO
ETHproductions

1
ฉันจะไม่แปลกใจถ้าคุณได้รับการท้าทายด้วยสิ่งนี้ นี่เป็นทางออกที่ดีจริงๆ
Alex A.

25

มาติกา

สิ่งนี้ใช้Inpaintฟังก์ชันของ Mathematica เนื่องจาก Mathematica เองนั้นทำการยกที่หนักทั้งหมดนี่คือ wiki ชุมชน

inPaint(ด้านล่าง) Inpaintคือการปรับตัวที่เรียบง่ายของ สำหรับภาพวาด / ภาพถ่ายสีจะใช้การตั้งค่าเริ่มต้น "TextureSynthesis" หากตรวจพบว่าภาพเป็นสีดำและสีขาว (เพราะข้อมูลภาพของภาพนั้นเหมือนกับข้อมูลภาพของรูปแบบไบนารีของรูปภาพ) จากนั้นภาพจะนำภาพสองภาพมาใช้กับแพทช์ "TotalVariation" ส่วนIfคำสั่งที่ใช้BinarizeหรือIdentityรูปภาพ ( Identityฟังก์ชั่นส่งคืนอาร์กิวเมนต์ของมันไม่เปลี่ยนแปลง)

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

inPaintภาพและหน้ากากจะป้อนเป็นข้อโต้แย้งที่จะ PartitionและGridเป็นเพียงเพื่อวัตถุประสงค์ในการจัดรูปแบบ

อินพุต

เอาต์พุตได้รับการแก้ไขแล้ว inPaintมีการตกแต่งภาพหลังจากที่ไม่ได้

เอาท์พุต


4
อาจเป็นเรื่องบังเอิญ แต่ฉันประหลาดใจกับประสิทธิภาพของเขาวงกต!
ข้อบกพร่อง

1
@ flawr ฉันจะโยนบางสิ่งเช่นนี้เพียงเพื่อยุ่งกับการแก้ปัญหานี้;) (ใครจะรู้ว่าสีดำและสีขาวเหล่านั้นจริงๆทำให้งงงัน)
Andras Deak

17
ฉันไม่คิดว่านี่เป็นวิกิชุมชน
Dennis

lawr, ใช่Inpaintดูเหมือนว่าจะมองหาสมมาตรในภาพขาวดำทั้งหมด - DavidC 9 ชั่วโมงที่ผ่านมา
DavidC

คุณแน่ใจหรือไม่ว่าอัลกอริธึมขาวดำไม่ได้เกี่ยวข้องกับการเสียสละแพะเลย --- บนโลก --- มันเดาได้ไหมว่าโครงสร้างส่วนกลางของภาพหากถูกปกปิดทั้งหมด?
Andras Deak

18

Python 2 และ PIL

โปรแกรมนี้ผสมสำเนาของพื้นที่ทางทิศเหนือทิศใต้ทิศตะวันออกและทิศตะวันตกเพื่อสร้างพิกเซลที่ใช้ทดแทนซึ่งใช้สีพื้นผิวและการแรเงาจากภูมิภาคของภาพในพื้นที่

ตัวอย่างผลลัพธ์:

รหัสก่อนค้นหากล่อง bounding สำหรับการแก้ไข จากนั้นสำหรับแต่ละพิกเซลที่จะสร้างจะคำนวณสีของแต่ละช่องสัญญาณ (RGB) ตามผลรวมถ่วงน้ำหนักของ 4 พื้นที่โดยรอบ

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 Lisas ทุกคนในความท้าทายนี้ถึงเวลาจะน่ากลัวหรือไม่?
undergroundmonorail

@undergroundmonorail ฉันเดาคอมพิวเตอร์สร้างใบหน้าโดยไม่ได้ตั้งใจมาทางขวาจากส่วนลึกของหุบเขาลึกลับ
Andras Deak

คุณได้รับ PIL จากที่ไหน
Elliot A.

@ElliotA ความเข้าใจของฉันคือ PIL ที่เหมาะสมนั้นตายไปแล้ว แต่มันก็เป็นโอเพนซอร์สดังนั้นมันจึงอยู่ภายใต้ชื่อ "หมอน" หากคุณ google "หมอนหลาม" คุณควรหามัน
undergroundmonorail

13

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

ในขณะเดียวกันนี่คือภาพตัวอย่าง

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

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

ป้อนคำอธิบายรูปภาพที่นี่ พื้นที่ในภาพด้านบนนั้นราบเรียบเหมือนกับตะบองเพชร

มันไม่ได้ดีมากกับสีคงที่

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่


1
โอ้และคุณช่วยเพิ่มกรณีทดสอบ B / W ได้ไหม?
ข้อบกพร่อง

2
ดูดีจริงๆใน Starry Night หนึ่ง
SuperJedi224

1
ว้าวนี่มันดูเหลือเชื่อแล้ว! แผ่นแปะยังคงเด่นชัด แต่เป็นแนวคิดใหม่ที่ยอดเยี่ยม! ที่ชื่นชอบของฉันจนถึง =)
ข้อบกพร่อง

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

2
คุณชนะการแข่งขัน "IMA ที่น่ากลัวที่สุด" IMO 0_o
DLosc

8

Python 2

สคริปต์ python แบบง่ายที่สร้างแพทช์โดยใช้ค่าจากพิกเซลนอกช่องว่าง จะใช้ค่าสีจากจุดสิ้นสุดของแถวและคอลัมน์ของพิกเซลและคำนวณค่าเฉลี่ยถ่วงน้ำหนักโดยใช้ระยะทางจากพิกเซลเหล่านั้น

เอาท์พุทจะไม่สวยมาก แต่มันเป็นศิลปะ

img1 img2 img3 img4 img5 img6

จากนั้นรหัส:

IMGN = "6"

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

BLUR = 5


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

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

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

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

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

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

# create patch
patch = pygame.Surface(patch_size)

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

cpixels = top+left+right+bottom

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

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

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

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

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

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

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

ในขณะที่คุณเห็นลายเส้นแนวนอน / แนวตั้งเหล่านั้นบางทีคุณสามารถปรับปรุงมันได้ด้วยการรวมทิศทางอื่น ๆ !
ข้อบกพร่อง

จริง ๆ แล้วฉันลองมัน แต่ฉันไม่สามารถได้รับผลลัพธ์ที่ดีดังนั้นฉันเพิ่งตัดสินใจที่จะเบลอภาพ: D
Hannes Karppila

19
ในที่สุด Mona Lisa ที่ไม่ทำให้ฉันกลัว แต่ดูเหมือนฆาตกรที่ถูกจับแทน
Andras Deak

6

มาติกา

Inpaint

มันเกิดขึ้นเพียงเพื่อที่Mathematicaมีฟังก์ชั่นที่มีประสิทธิภาพตรงงานนี้และฉันหมายความว่า :

Inpaint[image, region]

  • ช่วยปรับแต่งส่วนของที่สอดคล้องกับองค์ประกอบในภัณฑ์imageregion

โดยค่าเริ่มต้นจะใช้ "วิธีการสังเคราะห์พื้นผิวที่ดีที่สุดโดยใช้การสุ่มแบบสุ่ม" ซึ่งให้ผลลัพธ์ที่ดีในภาพวาด แต่ผลลัพธ์ที่ไม่ดีสำหรับเขาวงกตและกระดานหมากรุก:

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่

การเล่นโดยใช้การตั้งค่าไม่ได้ทำให้ฉันมีคุณภาพเพิ่มขึ้นในทุกภาพดังนั้นฉันจึงใช้ค่าเริ่มต้น (เพื่อประหยัดไบต์ - นี่เป็น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ดูดีในภาพวาดเท่านั้น และฉันไม่คิดว่าเราได้รับอนุญาตให้ปรับการตั้งค่าของเราสำหรับแต่ละกรณีทดสอบ (ถ้าทำได้เราสามารถจัดหาส่วนที่ขาดหายไปเป็น 'การตั้งค่า' ได้เล็กน้อย)
2012rcampion

ผลลัพธ์ของเขาวงกตและกระดานหมากรุกทำให้ฉันงงงวยจริงๆ เหตุใดการสร้างใหม่ของ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

Python3

คำตอบนี้ใช้ความคิดในกระดาษ"Deep Image ก่อน"โดย Ulyanov และคณะ (CVPR 2018) ในบทความนี้พวกเขาสำรวจความคิดที่ว่าวิธีที่มีประสิทธิภาพดีสำหรับการประมวลผลภาพได้รับการออกแบบอย่างใกล้ชิดสะท้อนความคิดของเราในสิ่งที่ภาพธรรมชาติควรมีลักษณะ (การกระจาย "ก่อน")

พวกเขาเสนอวิธีการที่สามารถใช้สำหรับการทาสีเช่นเดียวกับการกำจัดเสียงรบกวนและสิ่งประดิษฐ์ที่ใช้ภาพที่กำหนดโดยไม่มีการฝึกอบรมเกี่ยวกับข้อมูลอื่น ๆ แนวคิดที่เกิดขึ้นจริงนั้นค่อนข้างง่าย: ตาข่ายได้รับการฝึกฝนเพื่อให้ได้ภาพที่ต้องการ (สำหรับสัญญาณรบกวนแบบสุ่มคงที่บางส่วน) โดยการลงโทษเฉพาะ erros ที่อยู่นอกหน้ากากบางอัน หากคุณต้องการขจัดเสียงรบกวนคุณไม่จำเป็นต้องปิดบังอะไรเลย แต่เพียงแค่หยุดก่อนเวลาในการฝึก

สำหรับการทาสีคุณสวมหน้ากากชิ้นส่วนที่คุณต้องการทาสีและฝึกซ้อมจนกว่าจะบรรจบกัน แน่นอนว่ามันไม่ได้ทันสมัย ​​แต่ฉันยังต้องการโพสต์ลองและโพสต์ที่นี่เนื่องจากความเรียบง่ายของความคิดและประสิทธิภาพที่โดดเด่นยังคง ในการทดลองของฉันภาพวาดที่มีขนาดใหญ่ขึ้นของแพทช์ไม่ได้ผลออกมาดีนัก แต่สำหรับเซ็กเมนต์ที่เล็กกว่าผลลัพธ์อาจน่าเชื่อถือมากขึ้น

ผมดำเนินการนี้โดยใช้ความนิยมสถาปัตยกรรม U-Netจากjaxony บน GitHub รหัสสำหรับการฝึกอบรมและการประมวลผลภาพสามารถดูได้ที่ด้านล่าง

การอบรม

นี่คือภาพของกระบวนการฝึกอบรม เฟรมทุกเฟรมเป็นสถานะซ้ำจำนวนหนึ่ง:

ตัวอย่าง

รหัส

โปรดทราบว่าใน cpu อาจใช้เวลาหลายชั่วโมงในการรันเพียงภาพเดียวในขณะที่ cuda ที่เปิดใช้งาน cuda ที่ดีอาจใช้เวลาน้อยลง

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 ก่อนใช้สิ่งที่แตกต่างจาก U-Nets ดูเหมือนว่าพวกเขาจะได้ผลลัพธ์ที่ดีขึ้น
ASCII- เท่านั้น

นอกจากนี้คุณได้ลองใช้โค้ดของ Deep Image Prior
ASCII เพียง

@ ASCII-only พวกเขาระบุในกระดาษว่าพวกเขาใช้ U-Net เป็นส่วนใหญ่ แต่ฉันไม่สามารถหาพารามิเตอร์ที่แน่นอนที่พวกเขาใช้ พวกเขาอาจใช้เน็ตด้วยความจุที่มากขึ้น ฉันมีคอมพิวเตอร์ที่มีพลังงาน จำกัด มาก ดังนั้นฉันต้องเลือกพารามิเตอร์ที่ยังพอดีกับหน่วยความจำและไม่ใช้เวลาในการฝึกนานเกินไป ฉันไม่แน่ใจว่าใช้เวลานานเท่าไหร่ แต่ในคอมพิวเตอร์ที่ฉันใช้ (เฉพาะกับ CPU) ภาพเหล่านี้ใช้เวลาหลายวัน (ถ้าคุณมี GPU ที่เปิดใช้งาน cuda สำรองให้ฉันรู้ :)
ข้อบกพร่อง

ฉันยังสงสัยว่าเนื่องจากการออกแบบเครือข่ายที่มีรูปแบบมาสก์สี่เหลี่ยมไม่เหมาะ (และมาสก์ขนาดเล็กอาจดูดีกว่า) ถ้าคุณเปรียบเทียบตัวอย่างสองสามภาพแรกไปยังสองรูปสุดท้าย (ที่ไม่ใช้รูปแบบมาสก์สี่เหลี่ยมผืนผ้า) .
ข้อบกพร่อง

4

Python พร้อม OpenCV

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 ด้วยเหตุผลการพล็อตได้อย่างไร นอกจากนี้ฉันหมุนมัน นี่คือผลลัพธ์:

สไตล์โกธิค

คืนเต็มไปด้วยดวงดาว กรี๊ด mona lisa น่าขนลุกอีก!

โมนาลิซ่ากลับมาแล้ว!

บรรทัด 1

ตาหมากรุก

อย่างที่คุณเห็นมันไม่ใช่สิ่งที่ดีที่สุดสำหรับสองสี


Mona Lisa ได้รับการปรับโฉมใหม่
Conor O'Brien

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
ทำไมคุณถึงได้เส้นในมุมนี้โดยเฉพาะ? มุมซ้ายบนดูเหมือนจะค่อนข้างดีในขณะที่ส่วนล่างขวาไม่ตรงกันเลย
ข้อบกพร่อง

ฉันคิดว่ามันเกี่ยวข้องกับวิธีการที่ฉันย้ำผ่านภูมิภาค ฉันอาจจะเปลี่ยนสิ่งนั้นในที่สุด
SuperJedi224

ดูเหมือนว่าจะมีรูปสี่เหลี่ยมคางหมูเสมอ
ericw31415

@ ericw31415 มันเป็นสิ่งประดิษฐ์ของคำสั่งการทำซ้ำ
SuperJedi224
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.