Paint by Numbers (ใช้โปรแกรมมิใช่ตัวเลข)


56

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

ตัวอย่างเช่น:

เอาต์พุตสำหรับตัวอย่าง 1

อย่างที่คุณเห็นฉันเป็นศิลปินที่มีความสามารถเหนือกว่าอย่างเห็นได้ชัดเมื่อพูดถึง MS Paint


เกณฑ์การให้คะแนน

นี่คือการประกวดความนิยมดังนั้นคำตอบที่ได้คะแนนโหวตมากที่สุดจะเป็นฝ่ายชนะ ผู้มีสิทธิเลือกตั้งได้รับการสนับสนุนให้ตัดสินคำตอบด้วย

  • เกณฑ์การป้อนข้อมูล: รูปภาพใด ๆ ที่ประกอบด้วยพื้นหลังสีขาว / เทาอ่อนและโครงร่างสีดำ / เทาเข้ม
  • การระบายสีทำได้ดีเพียงใด หมายถึงพื้นที่น้อยหรือไม่มีเลยมีสีขาวแตกต่างจากด้านบน (ยกเว้นกรณีที่คุณตั้งใจจะใช้สีขาวเช่นสำหรับเมฆ)
  • ปรับแต่งสีที่ใช้ในบางส่วนได้
  • ระบบทำงานในช่วงของภาพต่าง ๆ ได้ดีเพียงใด (รายละเอียดที่แตกต่างกัน)
  • โพสต์ระยะเวลาที่โปรแกรมของคุณใช้ต่อภาพ เราอาจไม่เล่นกอล์ฟรหัส แต่ควรสั้นกว่าเร็วกว่าและมีประสิทธิภาพมากขึ้น
  • ควรส่งภาพใหม่ไปยังหน้าจอหรือไฟล์ (ไม่เกิน 2MB เพื่อให้สามารถแสดงในคำตอบได้)
  • โปรดระบุเหตุผลที่คุณเลือกที่จะแสดงผลให้กับภาพประเภทนั้นและแสดงความคิดเห็น / อธิบายการทำงานของรหัสของคุณ
  • การบังคับใช้ของสีที่ใช้กับรูปร่างนั้นถูกผูกไว้ด้วย (รูปแบบสีจริงเช่นหญ้าเป็นสีเขียว, รั้วไม้เป็นสีน้ำตาล ฯลฯ )

    "ฉันสามารถสุ่มสีในแต่ละพื้นที่ได้ แต่ถ้าฉันสามารถระบุ" รั้ว "และทำให้สีเหมือนกันได้นั่นก็เป็นสิ่งที่สมควรได้รับการยกระดับ" - NathanMerrill

เนื่องจากนี่คือการประกวดความนิยมคุณยังสามารถเลือกที่จะตัดสินโดย:

  • ความดึงดูดโดยรวม (ภาพดูดีแค่ไหน)
  • ไหวพริบศิลปะ; หากคุณสามารถโปรแกรมในการแรเงาหรือสีน้ำสีสไตล์ ฯลฯ

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

หากคุณมีข้อกำหนดการตัดสินอื่น ๆ ที่คุณคิดว่าควรใช้โปรดแนะนำพวกเขาในความคิดเห็นของโพสต์นี้


ตัวอย่าง

ฉันไม่ได้เป็นเจ้าของอะไรเลย ภาพตัวอย่างทั้งหมดเป็นลิขสิทธิ์ของครีเอทีฟคอมมอนส์

ตัวอย่างที่ 1 ในสีดำ / ขาว ที่มา: https://pixabay.com/ro/stejar-arbore-schi%C5%A3%C4%83-natura-303890/ ตัวอย่างที่ 2 ในสีดำ / ขาว ที่มา: http://www.freestockphotos.biz/stockphoto/10665 ตัวอย่างที่ 3 ในสีดำ / ขาว ที่มา: http: / /crystal-rose1981.deviantart.com/art/Dragon-Tattoo-Outline-167320011 ตัวอย่างที่ 4 ในสีดำ / ขาว ที่มา: http://jaclynonacloudlines.deviantart.com/art/Gryphon-Lines-PF-273195317 ตัวอย่างที่ 5 ในสีดำ / ขาว ที่มา: http://captaincyprus.deviantart.com / art / Dragon-OutLine-331748686 ตัวอย่างที่ 6 ในสีดำ / ขาว ที่มา: http://electric-meat.deviantart.com/art/A-Heroes-Farewell-280271639 ตัวอย่างที่ 7 ในสีดำ / ขาว ที่มา: http://movillefacepalmplz.deviantart.com/art/Background-The-Pumpkin -Farm ของดีเก่า-Days-342865938


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


4
สำหรับทุกคน: โปรดอย่า downvote / ปิดนี่เป็น "การประกวดศิลปะ" - มีอีกมากสำหรับมัน
edc65

16
ยินดีต้อนรับสู่ PPCG! ฉันขอยกย่องคุณที่มีความกล้าหาญไม่เพียง แต่การโพสต์ครั้งแรกของคุณเท่านั้นที่เป็นสิ่งที่ท้าทายและไม่เพียง แต่เป็นการท้าทายป๊อป - คอน แต่เป็นความท้าทายทางศิลปะเหนือสิ่งอื่นใด โชคดีฉันขอให้คุณดีที่สุดและถ้าคุณติดอยู่รอบ ๆ ฉันคิดว่าคุณจะไปไกลที่นี่
AdmBorkBork

4
@OliverGriffin ฉันโหวตให้กับการปิดและฉันได้เพิ่มรูปภาพที่คุณเชื่อมโยงไว้สำหรับคุณแล้ว คุณสามารถลบความคิดเห็นได้หากต้องการ
Addison Crump

2
ในที่สุดฉันก็พบวิธีการที่อาจจะไม่ล้นมากเกินไป แต่ตอนนี้มันทำงานช้า
SuperJedi224

4
ฉันโหวตให้เปิดคำถามของคุณอีกครั้งและเปลี่ยน -1 เป็น +1 แก้ไขงานได้ดีและเพิ่มข้อมูลเพิ่มเติม นอกจากนี้ฉันปรบมือคุณที่เปิดกว้างต่อการวิจารณ์ชุมชน ยินดีต้อนรับสู่ PPCG! หวังว่าคุณจะสนุกกับมัน.
Zach Gates

คำตอบ:


30

การฟอกอากาศแบบผี (Python, PIL, scipy)

สิ่งนี้ใช้อัลกอริทึมทางคณิตศาสตร์ที่ซับซ้อนในการผลิตเรื่องไร้สาระที่มีสีสัน อัลกอริทึมเกี่ยวข้องกับอัลกอริทึม PageRank ของ Google แต่สำหรับพิกเซลแทนที่จะเป็นหน้าเว็บ

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

สำหรับความคิดทางคณิตศาสตร์: สิ่งที่มันทำคือการสร้างกราฟ adjacency ของพิกเซลในขณะที่ภาพจากนั้นหา eigenvectors 25 อันดับแรกของกราฟ Laplacian (ยกเว้นว่าไม่ใช่เพราะเรารวมพิกเซลมืดเราแค่ให้การเชื่อมต่อของพวกเขามีน้ำหนักน้อยลงซึ่งจะช่วยในการจัดการกับการลดรอยหยักและดูเหมือนว่าจะให้ผลลัพธ์ที่ดีกว่าโดยทั่วไป) เมื่อพบ eigenvectors มันสร้าง การรวมกันเชิงเส้นแบบสุ่มของพวกเขาถ่วงน้ำหนักโดยค่าลักษณะเฉพาะผกผันของพวกเขาในรูปแบบองค์ประกอบ RGB ของภาพที่ส่งออก

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

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

นี่คือภาพที่ส่งออก:

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

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

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

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

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

(มันทำงานได้ไม่ดีนักกับฟักทองดังนั้นฉันเลยตัดมัน)

และนี่คือรหัส:

import sys
from PIL import Image
import numpy as np
import scipy.sparse as sp
import scipy.sparse.linalg as spl
import os
import time

start_time = time.time()

filename = sys.argv[1]
img = Image.open(filename)
orig_w, orig_h = img.size

# convert to monochrome and remove any alpha channel
# (quite a few of the inputs are transparent pngs)
img = img.convert('LA')
pix = img.load()
for x in range(orig_w):
    for y in range(orig_h):
        l, a = pix[x,y]
        l = (255-a) + a*l/255
        a = 255
        pix[x,y] = l,a
img = img.convert('L')

orig_img = img.copy()

# resize to 300 pixels wide - you can get better results by increasing this,
# but it takes ages to run
orig_w, orig_h = img.size
print "original size:", str(orig_w)+ ', ' + str(orig_h)
new_w = 300
img = img.resize((new_w, orig_h*new_w/orig_w), Image.ANTIALIAS)

pix = img.load()
w, h = img.size
print "resizing to", str(w)+', '+str(h)

def coords_to_index(x, y):
    return x*h+y

def index_to_coords(i):
    return (int(i/h), i%h)

print "creating matrix"

A = sp.lil_matrix((w*h,w*h))

def setlink(p1x, p1y, p2x, p2y):
    i = coords_to_index(p1x,p1y)
    j = coords_to_index(p2x,p2y)
    ci = pix[p1x,p1y]/255.
    cj = pix[p2x,p2y]/255.
    if ci*cj > 0.9:
        c = 1
    else:
        c =  0.01
    A[i,j] = c
    return c

for x in range(w):
    for y in range(h):
        d = 0.
        if x>0:
            d += setlink(x,y,x-1,y)
        if x<w-1:
            d += setlink(x,y,x+1,y)
        if y>0:
            d += setlink(x,y,x,y-1)
        if y<h-1:
            d += setlink(x,y,x,y+1)
        i = coords_to_index(x,y)
        A[i,i] = -d

A = A.tocsr()

# the greater this number, the more details it will pick up on. But it increases
# execution time, and after a while increasing it won't make much difference
n_eigs = 25

print "finding eigenvectors (this may take a while)"
L, V = spl.eigsh(A, k=n_eigs, tol=1e-12, which='LA')

print "found eigenvalues", L

out = Image.new("RGB", (w, h), "white")
out_pix = out.load()

print "painting picutre"

V = np.real(V)
n = np.size(V,0)
R = np.zeros(n)
G = np.zeros(n)
B = np.zeros(n)

for k in range(n_eigs-1):
    weight = 1./L[k]
    R = R + V[:,k]*np.random.randn()*weight
    G = G + V[:,k]*np.random.randn()*weight
    B = B + V[:,k]*np.random.randn()*weight

R -= np.min(R)
G -= np.min(G)
B -= np.min(B)
R /= np.max(R)
G /= np.max(G)
B /= np.max(B)

for x in range(w):
    for y in range(h):
        i = coords_to_index(x,y)
        r = R[i]
        g = G[i]
        b = B[i]
        pixval = tuple(int(v*256) for v in (r,g,b))
        out_pix[x,y] = pixval

out = out.resize((orig_w, orig_h), Image.ANTIALIAS)
out_pix = out.load()
orig_pix = orig_img.load()

for x in range(orig_w):
    for y in range(orig_h):
        r,g,b = out_pix[x,y]
        i = orig_pix[x,y]/255.
        out_pix[x,y] = tuple(int(v*i) for v in (r,g,b))

fname, extension = os.path.splitext(filename)
out.save('out_' + fname + '.png')

print("completed in %s seconds" % (time.time() - start_time))

4
นี่มันเจ๋งจริงๆ อาจเป็นหนึ่งในรายการโปรดของฉันจนถึงขณะนี้ คุณทำได้ดีมากในการจัดการลดรอยหยักและพื้นที่เปิดโล่งและในที่สุดก็มีใครบางคนระบายสีในลิงก์! (เคยรอสิ่งนั้น :-P บันทึกการ ตั้งค่าไว้ที่เดสก์ท็อป ) ฉันสงสัยว่าครูสอนภาษาอังกฤษคนเก่าของฉันจะพูดเกี่ยวกับเรื่องนี้อย่างไรในฐานะที่เป็นรูปภาพนิ่ง ... "มันแสดงให้เห็นทั้งสองด้านของหัวใจของเขา อื่น ๆ มีการต่อสู้ที่จำเป็นเพื่อให้ได้สันติภาพ " พอเกี่ยวกับความรักในเกม Legend of Zelda ... มันเป็นความอัปยศที่ใช้เวลานานมาก ไฟล์ผลลัพธ์มีขนาดใหญ่เพียงใด Ps Love images 4 & 5
OliverGriffin

2
@ donbright นักเรียนชั้นประถมปีที่ 3 ที่เข้าใจ eigenvector จะเป็นเด็กที่สดใสมากแน่นอน - ฉันไม่แน่ใจว่าเป็นไปได้สำหรับฉันที่จะอธิบายอัลกอริทึมในระดับนั้น แต่ให้ฉันลองต่อไป: ลองจินตนาการว่าเราพิมพ์รูปภาพลงบนแผ่นโลหะที่แข็ง จากนั้นเราก็ตัดเส้นสีดำทั้งหมดออกอย่างระมัดระวังและแทนที่ด้วยสิ่งที่มีความยืดหยุ่นมากกว่าเช่นยืดหยุ่น ดังนั้นชิ้นส่วนสีขาวคือแผ่นโลหะและชิ้นส่วนสีดำเป็นผ้ายืดหยุ่น ต่อไปเราแขวนทุกสิ่งไว้ในอากาศจากสายดังนั้นจึงเป็นอิสระในการเคลื่อนย้าย ตอนนี้ถ้าเราแตะแผ่นโลหะที่พวกเขาจะสั่น ...
นาธาเนียล

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

2
@donbright คุณสามารถดูเพิ่มเติมที่นี่เพื่อดูการสั่นสะเทือนของแผ่นโลหะ
นาธาเนียล

2
@donbright (คำอธิบายทางเทคนิคมากกว่านี้อาจทำให้คุณสูญเสียนิดหน่อย แต่คำอธิบายนี้ใช้งานได้เพราะโหมดการสั่นของจานถูกคำนวณโดยใช้การคำนวณแบบ eigenvector แม้ว่ามันจะเป็นไปได้ แต่การคำนวณแบบเดียวกับที่ฉันทำก็ไม่ใช่ฉัน ไม่แน่ใจจริงๆ)
นาธาเนียล

25

Python 2 + PIL สมุดระบายสีเล่มแรกของฉันด้วย

import sys, random
from PIL import Image

def is_whitish(color):
    return sum(color)>500

def get_zone(image, point, mask):
    pixels = image.load()
    w, h = image.size
    s = [point]
    while s:
        x, y = current = s.pop()
        mask[current] = 255
        yield current
        s+=[(i,j) for (i,j) in [(x,y-1),(x,y+1),(x-1,y),(x+1,y)] if 0<=i<w and 0<=j<h and mask[i,j]==0 and is_whitish(pixels[i,j])]

def get_zones(image):
    pixels = I.load()
    mask = Image.new('1',image.size).load()
    w,h = image.size
    for y in range(h):
        for x in range(w):
            p = x,y
            if mask[p]==0 and is_whitish(pixels[p]):
                yield get_zone(image, p, mask)



def apply_gradient(image, mincolor, maxcolor, points):
    minx = min([x for x,y in points])
    maxx = max([x for x,y in points])
    miny = min([y for x,y in points])
    maxy = max([y for x,y in points])
    if minx == maxx or miny==maxy:
        return
    diffx, diffy = (maxx - minx), (maxy-miny)
    stepr = (maxcolor[0] - mincolor[0] * 1.0) / diffy
    stepg = (maxcolor[1] - mincolor[1] * 1.0) / diffy
    stepb = (maxcolor[2] - mincolor[2] * 1.0) / diffy
    r,g,b = mincolor
    w, h = (abs(diffx+1),abs(diffy+1))
    tmp = Image.new('RGB', (w,h))
    tmppixels = tmp.load()
    for y in range(h):
        for x in range(w):
            tmppixels[x,y] = int(r), int(g), int(b)
        r+=stepr; g+=stepg; b+=stepb
    pixels = image.load()
    minx, miny = abs(minx), abs(miny)
    for x,y in points:
        try:
        pixels[x,y] = tmppixels[x-minx, y-miny]
    except Exception, e:
            pass

def colors_seq():
   yield (0,255,255)
   c = [(255,0,0),(0,255,0),(0,0,139)]
   i=0
   while True:i%=len(c);yield c[i];i+=1

def colorize(image):
    out = image.copy()
        COLORS = colors_seq()
    counter = 0
    for z in get_zones(image):
        c1 = COLORS.next()
        c2 = (0,0,0) if counter == 0 else (255,255,255)
        if counter % 2 == 1:
            c2, c1 = c1, c2
        apply_gradient(out, c1, c2, list(z))
        counter +=1
    return out

if __name__ == '__main__':
    I = Image.open(sys.argv[-1]).convert('RGB')
    colorize(I).show()

ฉันทำแบบเดียวกันกับที่ CarpetPython ทำยกเว้นว่าฉันเติมภูมิภาคด้วย 'การไล่ระดับสี' และใช้วงจรสีอื่น

colorings งดงามที่สุดของฉัน: ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่

เวลาคำนวณบนเครื่องของฉัน:

  • ภาพ 1 (มังกรจีน): ผู้ใช้ 0m2.862s จริง 0m2.801s sys 0m0.061s

  • ภาพ 2 (gryffon): ผู้ใช้จริง 0m0.991s 0m0.963s sys 0m0.029s

  • ภาพ 3 (มังกรยูนิคอร์น): จริง 0m2.260s ผู้ใช้ 0m2.239s sys 0m0.021s


การไล่ระดับสีที่ดี! เมื่อคุณติดห่วงสำหรับวงในสำหรับวงที่ไม่มีอะไรอื่นในวงแรกคุณไม่จำเป็นต้องเยื้องต่อไปหรือไม่?
OliverGriffin

คุณแน่ใจ! มันเป็นคัดลอก / วางปัญหา ...
หิวโหย

23

Python 2 และ PIL: โลกประสาทหลอน

ฉันใช้อัลกอริธึมง่ายๆในการเติมเต็มพื้นที่สีขาวด้วยสีจากจานสีปั่นจักรยาน ผลลัพธ์มีสีสันมาก แต่ไม่เหมือนจริงมาก

โปรดทราบว่าส่วน "สีขาว" ในรูปภาพเหล่านี้ไม่ขาวมาก คุณจะต้องทดสอบเฉดสีเทาด้วย

รหัสใน Python 2.7:

import sys
from PIL import Image

WHITE = 200 * 3
cs = [60, 90, 120, 150, 180]
palette = [(199,199,199)] + [(R,G,B) for R in cs for G in cs for B in cs]

def fill(p, color):
    perim = {p}
    while perim:
        p = perim.pop()
        pix[p] = color
        x,y = p
        for u,v in [(x+dx, y+dy) for dx,dy in [(-1,0), (1,0), (0,1), (0,-1)]]:
            if 0 <= u < W and 0 <= v < H and sum(pix[(u,v)]) >= WHITE:
                perim.add((u,v))

for fname in sys.argv[1:]:
    print 'Processing', fname
    im = Image.open(fname)
    W,H = im.size
    pix = im.load()
    colornum = 0
    for y in range(H):
        for x in range(W):
            if sum(pix[(x,y)]) >= WHITE:
                thiscolor = palette[colornum % len(palette)]
                fill((x,y), thiscolor)
                colornum += 1
    im.save('out_' + fname)

ภาพตัวอย่าง:

มังกรสีสันสดใส

ฟักทองบน LSD


3
ส่วนที่น่ากลัวคือสีดูเหมือนจะใช้ได้จริง มันใช้เวลานานแค่ไหนในการระบายสีในแต่ละภาพและไฟล์มีขนาดใหญ่แค่ไหน?
OliverGriffin

1
โปรแกรมระบายสีแต่ละภาพในเวลาประมาณ 2 วินาที ขนาดอิมเมจของเอาต์พุตเหมือนกับไฟล์อินพุต ขนาดไฟล์ส่วนใหญ่จะเล็กกว่าต้นฉบับ 10% ถึง 40% (อาจเป็นเพราะใช้การตั้งค่าการบีบอัด jpeg ที่แตกต่างกัน)
Logic Knight

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

@OliverGriffin ฉันดีใจที่คุณชอบมัน ฉันกำลังเล็งหาจานสีที่ไม่มีสีสว่างหรือมืด แต่ก็ยังมีความแตกต่างอยู่บ้าง ช่วงสีนี้ดูเหมือนจะให้ผลลัพธ์ที่น่าพอใจที่สุด
Logic Knight

11

Matlab

function [output_image] = m3(input_file_name)
a=imread(input_file_name);
b=im2bw(a,0.85);
c=bwlabel(b);
h=vision.BlobAnalysis;
h.MaximumCount=10000;
ar=power(double(step(h,b)),0.15);
ar=[ar(1:max(max(c))),0];
f=cat(3,mod((ar(c+(c==0))-min(ar(1:end-1)))/ ...
    (max(ar(1:end-1))-min(ar(1:end-1)))*0.9+0.8,1),c*0+1,c*0+1);
g=hsv2rgb(f);
output_image=g.*cat(3,c~=0,c~=0,c~=0);

เราใช้พื้นที่สี HSV และเลือกแต่ละพื้นที่เว้โดยพิจารณาจากขนาดที่สัมพันธ์กันระหว่างพื้นที่สีขาว พื้นที่ที่ใหญ่ที่สุดจะเป็นสีฟ้า ( Hue = 0.7) และบริเวณที่เล็กที่สุดจะเป็นสีม่วง ( Hue = 0.8) 0.7 -> 1=0 -> 0.8ภูมิภาคระหว่างทั้งสองขนาดจะได้รับเฉดสีในช่วง area^0.15เว้ในช่วงที่ถูกเลือกเป็นเส้นตรงในส่วนที่เกี่ยวกับฟังก์ชั่น ความอิ่มตัวและความคุ้มค่าเป็น 1 เสมอสำหรับทุกพิกเซลที่ไม่ใช่สีดำ

ใช้เวลาน้อยกว่า 1 วินาทีในการระบายสีภาพ

รูปภาพ 3 รูปที่มีพื้นที่ปิดซึ่งอัลกอริทึมทำงานได้ดี:

มังกร

มังกรอีกตัว

อาจเป็นมังกรอีกตัว

และส่วนที่เหลือของภาพ:

มังกร

มังกรอีกตัว

อาจเป็นมังกรอีกตัว

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


รหัสที่ดีและสั้นสำหรับผลการประสานงานสีสวย ๆ ! ฉันชอบวิธีที่คุณใช้พื้นที่เพื่อช่วยกำหนดสีสัน ใช้เวลานานแค่ไหนในการประมวลผลภาพเฉลี่ยและทำไมมันไม่ทำงานกับภาพที่มีรายละเอียดมากขึ้น? พื้นที่เล็กเกินไปหรือไม่
OliverGriffin

1
@OliverGriffin ตอบกลับในโพสต์ของฉันและเพิ่มรูปภาพที่เหลือ
Randomra

7

Python 3 พร้อมหมอน

รหัสเป็นบิตนานจะรวมอยู่ในคำตอบนี้ แต่นี่เป็นส่วนสำคัญของมัน

  1. ถ่ายภาพอินพุทและถ้ามันมีช่องอัลฟารวมไว้บนพื้นหลังสีขาว (จำเป็นอย่างน้อยสำหรับภาพไก่เพราะภาพทั้งหมดเป็นสีดำโดดเด่นด้วยความโปร่งใสเท่านั้นดังนั้นการวางอัลฟ่าจึงไม่เป็นประโยชน์)
  2. แปลงผลลัพธ์เป็น greyscale เราไม่ต้องการให้การบีบอัดหรือการลบรอยหยักหรือเส้นสีเทานั่นไม่ได้ค่อนข้างเทาเพื่อทำให้เรายุ่งเหยิง
  3. สร้างสำเนาของผลลัพธ์แบบ bi-level (ขาวดำ) เฉดสีเทาจะถูกแปลงเป็นสีดำหรือสีขาวตามเกณฑ์การตัดยอดที่กำหนดค่าได้ระหว่างสีขาวและสีที่มืดที่สุดในภาพ
  4. เติมน้ำท่วมทุกส่วนสีขาวของภาพ สีจะถูกเลือกแบบสุ่มโดยใช้พาเล็ตที่เลือกได้ซึ่งคำนึงถึงตำแหน่งของจุดเริ่มต้นสำหรับการดำเนินการเติมน้ำท่วม
  5. เติมสีดำด้วยสีเพื่อนบ้านที่ใกล้ที่สุด สิ่งนี้ช่วยให้เรารื้อฟื้นการต่อต้านนามแฝงอีกครั้งโดยป้องกันไม่ให้ทุกพื้นที่สีถูกล้อมรอบด้วยสีดำขรุขระ
  6. ถ่ายภาพเฉดขาวดำจากขั้นตอนที่ 2 และสร้างหน้ากากอัลฟาจากนั้น: สีที่มืดที่สุดคือทึบแสงเต็มที่สีที่สว่างที่สุดนั้นโปร่งใสอย่างสมบูรณ์
  7. รวมภาพสีเทาลงบนภาพสีจากขั้นตอนที่ 5 โดยใช้หน้ากากอัลฟ่านี้

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

จนถึงตอนนี้มีเพียงสองพาเล็ตที่เลือกได้สำหรับขั้นตอนที่ 4: หนึ่งสุ่มอย่างหมดจดและหนึ่ง "ธรรมชาติ" หยาบซึ่งพยายามที่จะกำหนดสีของท้องฟ้าไปที่มุมด้านบนสีหญ้าไปที่มุมด้านล่างสีน้ำตาล (หินหรือไม้ ) สีอยู่ตรงกลางของแต่ละด้านและสีที่แตกต่างกันลงตรงกลาง ความสำเร็จได้รับการ ... จำกัด


การใช้งาน:

usage: paint_by_prog.py [-h] [-p PALETTE] [-t THRESHOLD] [-f | -F] [-d]
                        FILE [FILE ...]

Paint one or more line-art images.

positional arguments:
  FILE                  one or more image filenames

optional arguments:
  -h, --help            show this help message and exit
  -p PALETTE, --palette PALETTE
                        a palette from which to choose colours; one of
                        "random" (the default) or "natural"
  -t THRESHOLD, --threshold THRESHOLD
                        the lightness threshold between outlines and paintable
                        areas (a proportion from 0 to 1)
  -f, --proper-fill     fill under black lines with proper nearest-neighbour
                        searching (slow)
  -F, ---no-proper-fill
                        fill under black lines with approximate nearest-
                        neighbour searching (fast)
  -d, --debug           output debugging information

ตัวอย่าง:

paint_by_prog.py -t 0.7 Gryphon-Lines.png กริฟฟินสี

paint_by_prog.py Dragon-Tattoo-Outline.jpg มังกร cartoony สี

paint_by_prog.py -t 0.85 -p natural The-Pumpkin-Farm-of-Good-old-Days.jpg ฉากฟาร์มสี

paint_by_prog.py -t 0.7 Dragon-OutLine.jpg มังกรกรันจ์สี

paint_by_prog.py stejar-arbore-schiţă-natura.png ต้นไม้สีมองเหมือนธงมาก

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


ประสิทธิภาพ:

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


ภาพแรกดูน่าอัศจรรย์โดยเฉพาะดวงตาสีน้ำตาล ทำได้ดีมาก ฉันยังชื่นชมคุณในการรับหญ้าสีเขียวทุ่งฟักทองและเมฆสีม่วงสีน้ำตาล
OliverGriffin

3

ชวา

การเลือกสีแบบสุ่มจากจานสีที่คุณเลือก

คำเตือน: การค้นหาภูมิภาคช้ามากยกเว้นบริเวณสีขาวมีขนาดเล็กผิดปกติ

import java.awt.Color;
import java.awt.image.*;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.Scanner;
import java.util.function.Supplier;

import javax.imageio.ImageIO;


public class Colorer{
    public static boolean isProbablyWhite(int x,int y){
        Color c=new Color(image.getRGB(x, y));
        if(c.getRed()<240)return false;
        if(c.getBlue()<240)return false;
        if(c.getGreen()<240)return false;
        return true;
    }
    static class Point{
        int x,y;
        public boolean equals(Object o){
            if(o instanceof Point){
                Point p=(Point)o;
                return x==p.x&&y==p.y;
            }
            return false;
        }
        public Point(int x,int y){
            this.x=x;
            this.y=y;
        }
    }
    static BufferedImage image;
    static int W,H;
    public static void check(Point p,List<Point>l1,List<Point>l2,List<Point>l3){
        if(!isProbablyWhite(p.x,p.y))return;
        if(l1.contains(p))return;
        if(l2.contains(p))return;
        if(l3.contains(p))return;
        l1.add(p);
    }
    public static void process(int x,int y,Color c){
        List<Point>plist=new LinkedList<>();
        int rgb=c.getRGB();
        plist.add(new Point(x,y));
        List<Point>l3=new LinkedList<>();
        int k=0;
        for(int i=0;i<W*H;i++){
            System.out.println(k=l3.size());
            List<Point>l2=new LinkedList<>();
            for(Point p:plist){
                int x1=p.x;
                int y1=p.y;
                if(x1>0){
                    check(new Point(x1-1,y1),l2,plist,l3);
                }
                if(y1>0){
                    check(new Point(x1,y1-1),l2,plist,l3);
                }
                if(x1<W-1){
                    check(new Point(x1+1,y1),l2,plist,l3);
                }
                if(y1<H-1){
                    check(new Point(x1,y1+1),l2,plist,l3);
                }
            }
            while(!plist.isEmpty()){
                l3.add(plist.remove(0));
            }
            if(l3.size()==k)break;
            plist=l2;
        }
        plist=l3;
        for(Point p:plist){
            image.setRGB(p.x,p.y,rgb);
        }
    }
    public static void main(String[]args) throws Exception{
        Random rand=new Random();
        List<Supplier<Color>>colgen=new ArrayList<>();
        colgen.add(()->{return new Color(rand.nextInt(20),50+rand.nextInt(200),70+rand.nextInt(180));});
        colgen.add(()->{return new Color(rand.nextInt(20),rand.nextInt(40),70+rand.nextInt(180));});
        colgen.add(()->{return new Color(150+rand.nextInt(90),10+rand.nextInt(120),rand.nextInt(5));});
        colgen.add(()->{int r=rand.nextInt(200);return new Color(r,r,r);});
        colgen.add(()->{return Arrays.asList(new Color(255,0,0),new Color(0,255,0),new Color(0,0,255)).get(rand.nextInt(3));});
        colgen.add(()->{return Arrays.asList(new Color(156,189,15),new Color(140,173,15),new Color(48,98,48),new Color(15,56,15)).get(rand.nextInt(4));});
        Scanner in=new Scanner(System.in);
        image=ImageIO.read(new File(in.nextLine()));
        final Supplier<Color>sup=colgen.get(in.nextInt());
        W=image.getWidth();
        H=image.getHeight();
        for(int x=0;x<W;x++){
            for(int y=0;y<H;y++){
                if(isProbablyWhite(x,y))process(x,y,sup.get());
            }
        }
        ImageIO.write(image,"png",new File("out.png"));
    }
}

ต้องใช้สองอินพุต: ชื่อไฟล์และจานสี ID รวมถึงการแก้ไขการลดรอยหยักบางส่วน แต่ไม่รวมตรรกะสำหรับพิกเซลแบบโปร่งใส

จานสีต่อไปนี้ได้รับการยอมรับในปัจจุบัน:

0: Blue and greeen
1: Blue
2: Red
3: Greyscale
4: Three-color Red, Green, and Blue
5: Classic Game Boy pallette (four shades of green)

ผล:

Dragon, Game Boy palette:

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

อีกมังกร, จานสีฟ้า + สีเขียว:

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

GOL ยังคงมีชีวิต mona lisa (ที่แสดงโดยโปรแกรมนี้ ), จานสีสามสี:

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


+1 สำหรับการปรับแต่งสีของคุณ! :) หากคุณสามารถแก้ไขปัญหาการลดรอยหยักได้ คุณใช้เวลาในการส่งภาพเหล่านี้นานแค่ไหน
OliverGriffin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.