Maluj według numerów (używając programowania, a nie liczb)

56

Twoim zadaniem jest stworzenie programu, który pobiera czarno-biały obrysowany obraz (przykładowe obrazy są poniżej) i wypełnia go kolorem. Od Ciebie zależy, w jaki sposób oddzielisz każdy region i jaki kolor go wypełnić (możesz nawet użyć RNG).

Na przykład:

wyjście na przykład 1

Jak widać, jestem wyraźnie artystą wyższego kalibru, jeśli chodzi o MS Paint.


Punktacja

To konkurs popularności, więc wygrywa odpowiedź z największą liczbą głosów netto. Zachęca się wyborców do oceniania odpowiedzi według

  • Kryterium wejściowe: każdy obraz, który składa się z białego / jasnoszarego tła i czarnych / ciemnoszarych konturów
  • Jak dobrze wykonuje się kolorowanie; co oznacza, że ​​kilka obszarów lub wcale nie są białe w przeciwieństwie do powyższego (chyba że oczywiście zamierzasz użyć białego, np. do chmur)
  • Możliwość dostosowania kolorów używanych w niektórych sekcjach
  • Jak dobrze system działa na wielu różnych obrazach (o różnych szczegółach)
  • Opublikuj, ile czasu zajmuje program na zdjęcie. Być może nie gramy w golfa kodowego, ale krótszy, szybszy i wydajniejszy kod należy uznać za lepszy
  • Powinien wyprowadzić nowy obraz na ekran lub do pliku (nie większy niż 2 MB, aby można go było wyświetlić w odpowiedzi)
  • Wyjaśnij, dlaczego zdecydowałeś się na wyjście do tego typu obrazu i skomentuj / wyjaśnij działanie twojego kodu
  • Możliwość zastosowania koloru zastosowanego do odpowiedniego kształtu, z którym jest związany (realistyczna kolorystyka, tj. Trawa jest zielona, ​​ogrodzenia drewniane są brązowe itp.)

    „Mógłbym losowo pokolorować każdy obszar, ale jeśli uda mi się zidentyfikować„ ogrodzenie ”i sprawić, by było podobnie pokolorowane, to jest to coś, co zasługuje na pozytywne głosy”. - NathanMerrill

Ponieważ jest to konkurs popularności, możesz też opcjonalnie ocenić według:

  • Ogólny urok (jak dobrze wygląda obraz)
  • Talent artystyczny; jeśli możesz programować w cieniowaniu lub kolorystyce w stylu akwareli itp.

Ogólnie rzecz biorąc, wygra najmniejszy obraz wyjściowy (rozmiar pliku) o najwyższej jakości, z programem na czczo i najwyższym publicznym głosowaniem.

Jeśli masz inne specyfikacje oceniania, które Twoim zdaniem powinny być zastosowane, zalecamy je w komentarzach do tego postu.


Przykłady

Nic nie mam; wszystkie przykładowe obrazy są na licencji Creative Commons.

przykład 1 czarno-biały Źródło: https://pixabay.com/ro/stejar-arbore-schi%C5%A3%C4%83-natura-303890/ przykład 2 czarno-biały Źródło: http://www.freestockphotos.biz/stockphoto/10665 przykład 3 w czerni / bieli Źródło: http: / /crystal-rose1981.deviantart.com/art/Dragon-Tattoo-Outline-167320011 przykład 4 czarno-biały Źródło: http://jaclynonacloudlines.deviantart.com/art/Gryphon-Lines-PF-273195317 przykład 5 w kolorze czarno-białym Źródło: http://captaincyprus.deviantart.com / art / Dragon-OutLine-331748686 przykład 6 w kolorze czarno-białym Źródło: http://electric-meat.deviantart.com/art/A-Heroes-Farewell-280271639 przykład 7 w kolorze czarno-białym Źródło: http://movillefacepalmplz.deviantart.com/art/Background-The-Pumpkin -Farm-of-Good-old-Days-342865938


EDYCJA: Ze względu na wygładzanie linii powodujących nie czarno-białe piksele i niektóre obrazy, które mogą zawierać szary zamiast czarno-białego, jako dodatkowe wyzwanie możesz spróbować sobie z tym poradzić. Moim zdaniem powinno to być dość łatwe.

OliverGriffin
źródło
4
Do wszystkich: proszę nie głosować / zamykać tego jako „konkursu plastycznego” - jest więcej
edc65
16
Witamy w PPCG! Podziwiam cię za odwagę, aby Twój pierwszy post był nie tylko wyzwaniem, a nie tylko pop-conem, ale przede wszystkim wyzwaniem artystycznym. Powodzenia, życzę ci wszystkiego najlepszego, a jeśli zostaniesz w pobliżu, myślę, że pojedziesz tu daleko.
AdmBorkBork
4
@OliverGriffin Głosuję przeciwko zamknięciu, a także dodałem do zdjęć, które dla Ciebie łączyłeś . Możesz usunąć komentarze, jeśli chcesz.
Addison Crump,
2
W końcu znalazłem podejście, które prawdopodobnie nie spowoduje przepełnienia stosu, ale teraz działa trochę powoli.
SuperJedi224
4
Głosowałem za ponownym otwarciem twojego pytania i zmieniłem moje -1 na +1. Dobra edycja pracy i dodawanie dodatkowych informacji. Oklaskuję również za otwartość na krytykę społeczności. Witamy w PPCG! Mam nadzieję, że ci się spodoba.
Zach Gates

Odpowiedzi:

30

Spektralny aerograf (Python, PIL, scipy)

Wykorzystuje wyrafinowany algorytm matematyczny do generowania kolorowych bzdur. Algorytm jest powiązany z algorytmem Google PageRank Google, ale dotyczy pikseli zamiast stron internetowych.

Przyjąłem to podejście, ponieważ myślałem, że w przeciwieństwie do metod opartych na zalewaniu może poradzić sobie z obrazami takimi jak kurczak i drzewo, gdzie istnieją kształty, które nie są całkowicie zamknięte przez czarne linie. Jak widać, działa to w pewnym sensie, chociaż ma tendencję do kolorowania w różnych częściach nieba różnymi kolorami

Dla matematycznie myślących: w gruncie rzeczy konstruuje wykres sąsiedztwa pikseli while na obrazie, a następnie znajduje 25 najlepszych wektorów własnych wykresu Laplaciana. (Z wyjątkiem tego, że nie do końca tak, ponieważ uwzględniamy ciemne piksele, po prostu nadajemy ich połączeniom mniejszą wagę. Pomaga to w radzeniu sobie z antyaliasingiem, a także ogólnie wydaje się dawać lepsze wyniki.) Po znalezieniu wektorów własnych tworzy ich losowa liniowa kombinacja, ważona ich odwrotnymi wartościami własnymi, w celu utworzenia składowych RGB obrazu wyjściowego.

Ze względu na czas obliczeń obraz jest skalowany w dół przed wykonaniem tego wszystkiego, a następnie ponownie skalowany w górę, a następnie mnożony przez oryginalny obraz. Mimo to nie działa szybko, w zależności od obrazu wejściowego, zajmuje od około 2 do 10 minut, chociaż z jakiegoś powodu kurczak zajął 17 minut.

Być może rzeczywiście można przekształcić ten pomysł w coś użytecznego, tworząc interaktywną aplikację, w której można kontrolować kolor i intensywność każdego z wektorów własnych. W ten sposób możesz wyciszyć te, które dzielą niebo na różne sekcje, i zanikać te, które wychwytują odpowiednie cechy obrazu. Ale nie mam planów, aby zrobić to sam :)

Oto obrazy wyjściowe:

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

(Nie działało to tak dobrze na dyniach, więc pomijam tę.)

A oto kod:

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))
Nataniel
źródło
4
To jest naprawdę fajne. Prawdopodobnie jeden z moich ulubionych do tej pory. Świetnie poradziłeś sobie z antyaliasingiem i obszarami otwartymi, a ktoś w końcu pomalował link! (Czekałem na ten :-P zapisz ustawiony na pulpicie ) Zastanawiam się, co mój stary nauczyciel angielskiego powiedziałby o tym jako statycznego obrazu ... „To pokazuje dwie strony swego serca, z jednej strony jest spokój i na inne toczy się walka niezbędna do uzyskania tego pokoju ". Dość o mojej miłości do gier Legend of Zelda ... Naprawdę szkoda, że ​​tak długo to trwa. Jak duże były powstałe pliki? Ps Love images 4 & 5
OliverGriffin
2
@donbright 3. równiarka, który rozumiałby wektory własne, byłby naprawdę bardzo bystrym dzieckiem - nie jestem pewien, czy mogę wyjaśnić algorytm na tym poziomie. Ale i tak spróbuję: wyobraź sobie, że wydrukujemy zdjęcie na sztywnej blachy. Następnie ostrożnie odcinamy wszystkie czarne linie i zastępujemy je czymś znacznie bardziej elastycznym, na przykład elastycznym. Więc białe części są metalowymi płytkami, a czarne części są elastyczną tkaniną. Następnie zawieszamy całość w powietrzu na sznurku, więc można się swobodnie poruszać. Teraz, jeśli stukamy w metalowe płytki, będą wibrować ...
Nathaniel
2
@donbright (ciąg dalszy) ... W zależności od tego, jak uderzysz w metalową płytę, będzie wibrować na różne sposoby. Może czasami tylko jedna z metalowych części będzie wibrować, a nie inne, ale innym razem (ponieważ są połączone elastycznie), uderzenie w jedną płytę spowoduje, że druga również się poruszy. Te różne sposoby wibracji nazywane są trybami wibracji . Ten program symuluje niektóre tryby wibracji tej metalowej płyty, ale zamiast generować dźwięk, używa ich do określenia, który kolor narysować.
Nathaniel
2
@donbright Możesz także zobaczyć tutaj więcej informacji na temat wizualizacji wibracji metalowych płyt.
Nathaniel
2
@donbright (to bardziej techniczne wytłumaczenie może również trochę Cię stracić, ale to wyjaśnienie działa, ponieważ tryby wibracyjne płyty są również obliczane za pomocą obliczeń wektorowych. Chociaż jest to możliwe, nie jest to takie samo obliczenie, jak mój kod - jestem nie bardzo pewny.)
Nathaniel
25

Python 2 + PIL też, moja pierwsza kolorowanka

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

Zrobiłem to samo, co CarpetPython, z tym wyjątkiem, że wypełniam region „gradientami” i używam innego cyklu kolorów.

Moje najwspanialsze kolory: wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

Czasy obliczeń na moim komputerze:

  • obraz 1 (chiński smok): prawdziwy użytkownik 0m2.862s 0m2.801s sys 0m0.061s

  • obraz 2 (gryffon): użytkownik 0m0,991 s 0m0,963s sys 0m0,029s

  • obraz 3 (jednorożec smok): prawdziwy użytkownik 0m2.260s 0m2.239s sys 0m0.021s

dieter
źródło
Ładne gradienty! Kiedy wsadzasz pętlę for wewnątrz pętli for bez niczego innego w pierwszej, nie musisz dalej wciskać?
OliverGriffin
jasne że tak! to był problem z kopiowaniem / wklejaniem ...
dieter
23

Python 2 i PIL: Psychedelic Worlds

Użyłem prostego algorytmu, aby zalać każdy biały obszar kolorem z palety kolarskiej. Rezultat jest bardzo kolorowy, ale niezbyt realistyczny.

Zwróć uwagę, że „białe” części na tych zdjęciach nie są bardzo białe. Będziesz musiał również przetestować odcienie szarości.

Kod w 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)

Przykładowe zdjęcia:

Kolorowy smok

Dynie na LSD

Logic Knight
źródło
3
Przerażające jest to, że kolory faktycznie działają. Jak długo zajęło ci pokolorowanie każdego obrazu i jak duże były pliki?
OliverGriffin
1
Program pokoloruje każdy obraz w około 2 sekundy. Wymiary obrazu wyjściowego są takie same jak pliki wejściowe. Rozmiary plików są zwykle od 10% do 40% mniejsze niż oryginały (prawdopodobnie dlatego, że stosowane są różne ustawienia kompresji JPEG).
Logic Knight
3
Jestem pod wielkim wrażeniem tego, jak krótki jest ten kod! Podoba mi się również to, jak skutecznie ograniczasz dostępne kolory, a tym samym trzymasz ustawioną paletę. Właściwie to naprawdę mi się podoba, to trochę daje grunge (czy to właściwe słowo? Nie jestem artystą).
OliverGriffin,
@OliverGriffin, cieszę się, że ci się podoba. Dążyłem do palety bez jasnych lub ciemnych kolorów, ale nadal miałem pewien kontrast. Ten zakres kolorów wydawał się być najbardziej przyjemny.
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);

Używamy przestrzeni kolorów HSV i wybieramy każdy region Barwa na podstawie jego względnego rozmiaru między białymi regionami. Największy region będzie niebieski ( Hue = 0.7), a najmniejszy region będzie fioletowy ( Hue = 0.8). Regiony między tymi dwoma rozmiarami mają odcienie w zakresie 0.7 -> 1=0 -> 0.8. Barwa zakresu jest wybierana liniowo względem funkcji area^0.15. Nasycenie i wartość wynosi zawsze 1 dla każdego nie czarnego piksela.

Pokolorowanie obrazu zajmuje mniej niż 1 sekundę.

3 zdjęcia z zamkniętymi regionami, w których algorytm działa przyzwoicie:

smok

inny smok

może inny smok

I reszta zdjęć:

smok

inny smok

może inny smok

Na tych zdjęciach znajdują się duże białe obszary połączone, które powinny być idealnie zabarwione wieloma kolorami (problem ten został dobrze rozwiązany w rozwiązaniu Nathaniela) .

randomra
źródło
Ładny i krótki kod dla niektórych całkiem skoordynowanych kolorów! Podoba mi się, jak wykorzystałeś ten obszar, aby pomóc ustalić odcień. Ile czasu zajęło przetworzenie przeciętnego obrazu i dlaczego nie działało na niektórych bardziej szczegółowych obrazach? Czy obszary były zbyt małe?
OliverGriffin
1
@OliverGriffin Odpowiedziałem w moim poście i dodałem resztę zdjęć.
randomra
7

Python 3 with Pillow

Kod jest nieco długi, aby zawrzeć go w tej odpowiedzi, ale oto jego sedno .

  1. Weź obraz wejściowy i, jeśli ma kanał alfa, skomponuj go na białym tle. (Niezbędne przynajmniej dla obrazu z kurczaka, ponieważ cały obraz był czarny, wyróżniał się tylko przezroczystością, więc po prostu upuszczenie alfa nie było pomocne).
  2. Konwertuj wynik na skalę szarości; nie chcemy, aby artefakty kompresji lub wygładzania, lub szare linie, które nie były całkiem szare, by nas zepsuły.
  3. Utwórz dwupoziomową (czarno-białą) kopię wyniku. Odcienie szarości są konwertowane na czerń lub biel w oparciu o konfigurowalny próg odcięcia między bielą a najciemniejszym odcieniem obrazu.
  4. Wypełnij wszystkie białe obszary obrazu. Kolory są wybierane losowo, przy użyciu wybieranej palety, która uwzględnia lokalizację punktu początkowego operacji zalewania.
  5. Wypełnij czarne linie kolorami najbliższego sąsiada. Pomaga nam to przywrócić wygładzanie krawędzi, zapobiegając granice każdego kolorowego obszaru w postrzępioną czerń.
  6. Weź obraz w skali szarości z kroku 2 i stwórz z niego maskę alfa: najciemniejszy kolor jest całkowicie nieprzezroczysty, najjaśniejszy kolor jest w pełni przezroczysty.
  7. Złóż obraz w skali szarości na kolorowy obraz z kroku 5 przy użyciu tej maski alfa.

Te ostatnie kroki niestety nie wyeliminowały jaśniejszych „aureoli”, które są widoczne w ciemniejszych regionach, ale zrobiły zauważalną różnicę, przynajmniej. Przetwarzanie obrazu nigdy nie było moim obszarem badań, więc wiem, że istnieją bardziej skuteczne i wydajniejsze algorytmy do robienia tego, co próbowałem tutaj zrobić ... ale cóż.

Do tej pory istnieją tylko dwie palety do wyboru dla kroku 4: czysto losowa i bardzo szorstka „naturalna”, która próbuje przypisać kolory nieba do górnych rogów, kolory trawy do dolnych rogów, brązowe (skały lub drewno ) kolory na środku każdej strony i zróżnicowane kolory na środku. Sukces został ... ograniczony.


Stosowanie:

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

Próbki:

paint_by_prog.py -t 0.7 Gryphon-Lines.png Kolorowy gryf

paint_by_prog.py Dragon-Tattoo-Outline.jpg Kolorowy smok kreskówkowy

paint_by_prog.py -t 0.85 -p natural The-Pumpkin-Farm-of-Good-old-Days.jpg Kolorowe sceny rolnicze

paint_by_prog.py -t 0.7 Dragon-OutLine.jpg Kolorowy smok grunge

paint_by_prog.py stejar-arbore-schiţă-natura.png Kolorowe drzewo, bardzo przypominające flagę

Kurczak nie wygląda bardzo dobrze, a mój najnowszy wynik dla obrazu linku nie jest najlepszy; ten, który pochodzi z wcześniejszej wersji kodu, był w większości bladożółty i miał w sobie ciekawy klimat pustyni…


Występ:

Przetwarzanie każdego obrazu zajmuje kilka sekund przy ustawieniach domyślnych, co oznacza, że ​​w kroku 5 wykorzystywany jest algorytm przybliżonego najbliższego sąsiada. Prawdziwy najbliższy sąsiad jest znacznie wolniejszy, może potrwać około pół minuty (właściwie go nie mierzyłem).

Tim Pederick
źródło
Pierwsze zdjęcie wygląda fantastycznie, szczególnie to brązowe oko. Dobra robota. Oklaskuję również za zdobycie zielonej trawy, brązowych pól dyń i purpurowych chmur.
OliverGriffin
3

Jawa

Losowy wybór kolorów z palety.

Ostrzeżenie: wyszukiwanie regionu jest obecnie bardzo wolne, chyba że białe regiony są niezwykle małe.

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

Wymaga dwóch danych wejściowych: nazwy pliku i identyfikatora palety. Zawiera pewną korektę antyaliasingu, ale nie zawiera logiki przezroczystych pikseli.

Obecnie rozpoznawane są następujące palety:

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)

Wyniki:

Dragon, paleta Game Boy:

wprowadź opis zdjęcia tutaj

Drugi smok, niebieska + zielona paleta:

wprowadź opis zdjęcia tutaj

GOL martwa natura Mona Lisa (renderowana przez ten program ), trójkolorowa paleta:

wprowadź opis zdjęcia tutaj

SuperJedi224
źródło
+1 za dostosowanie kolorów! :) jeśli możesz naprawić problem antyaliasingu, wyglądałoby to jeszcze lepiej. Ile czasu zajęło Ci wydrukowanie tych obrazów?
OliverGriffin