Malen nach Zahlen (mit Programmieren, nicht mit Zahlen)

56

Ihre Aufgabe ist es, ein Programm zu erstellen, das ein schwarz-weiß umrandetes Bild (Beispielbilder unten) aufnimmt und es mit Farbe füllt. Es liegt an Ihnen, wie Sie die einzelnen Regionen trennen und mit welcher Farbe sie gefüllt werden sollen (Sie können sogar ein RNG verwenden).

Zum Beispiel:

Ausgabe zum Beispiel 1

Wie Sie sehen, bin ich eindeutig ein Künstler von überlegenem Kaliber, wenn es um MS Paint geht.


Wertung

Dies ist ein Beliebtheitswettbewerb, daher gewinnt die Antwort mit den meisten Netto-Stimmen. Die Wähler werden aufgefordert, die Antworten nach zu beurteilen

  • Eingabekriterium: Jedes Bild, das aus weißem / hellgrauem Hintergrund und schwarz / dunkelgrauen Konturen besteht
  • Wie gut ist die Färbung gemacht; Dies bedeutet, dass nur wenige oder gar keine Bereiche im Gegensatz zu den oben genannten weiß sind (es sei denn, Sie beabsichtigen offensichtlich, Weiß zu verwenden, z. B. für Wolken).
  • Anpassbarkeit der in bestimmten Abschnitten verwendeten Farben
  • Wie gut funktioniert das System mit einer Reihe verschiedener Bilder (mit unterschiedlichen Details)?
  • Geben Sie an, wie lange Ihr Programm pro Bild dauert. Wir spielen vielleicht kein Codegolf, aber kürzerer, schnellerer und effizienterer Code sollte als besser angesehen werden
  • Soll das neue Bild entweder auf dem Bildschirm oder in einer Datei ausgeben (nicht größer als 2 MB, damit es in der Antwort angezeigt werden kann)
  • Bitte begründen Sie, warum Sie sich für die Ausgabe auf diesen Bildtyp entschieden haben, und kommentieren / erläutern Sie die Funktionsweise Ihres Codes
  • Die Anwendbarkeit der verwendeten Farbe auf die jeweilige Form, an die sie gebunden ist (realistisches Farbschema, dh Gras ist grün, Holzzäune sind braun usw.)

    "Ich könnte jeden Bereich zufällig färben, aber wenn ich den" Zaun "identifizieren und ihn ähnlich färben könnte, dann ist das etwas, das Aufwertungen verdient." - Nathan Merrill

Sehen Sie, da dies ist ein Beliebtheitswettbewerb, können Sie auch durch gegebenenfalls beurteilen:

  • Gesamtattraktivität (wie gut das Bild aussieht)
  • Künstlerisches Flair; wenn Sie Schattierungen, Aquarellfarben usw. programmieren können

Im Allgemeinen gewinnt das kleinste ausgegebene Bild (Dateigröße) von höchster Qualität mit dem schnellsten Programm und der höchsten öffentlichen Bewertung.

Wenn Sie andere Richtspezifikationen haben, die Ihrer Meinung nach verwendet werden sollten, empfehlen Sie diese bitte in den Kommentaren dieses Beitrags.


Beispiele

Ich besitze nichts; Alle Beispielbilder sind von einer Creative-Commons-Lizenz.

Beispiel 1 in schwarz / weiß Quelle: https://pixabay.com/ro/stejar-arbore-schi%C5%A3%C4%83-natura-303890/ Beispiel 2 in schwarz / weiß Quelle: http://www.freestockphotos.biz/stockphoto/10665 Beispiel 3 in schwarz / weiß Quelle: http: / /crystal-rose1981.deviantart.com/art/Dragon-Tattoo-Outline-167320011 Beispiel 4 in schwarz / weiß Quelle: http://jaclynonacloudlines.deviantart.com/art/Gryphon-Lines-PF-273195317 Beispiel 5 in schwarz / weiß Quelle: http://captaincyprus.deviantart.com / art / Dragon-OutLine-331748686 Beispiel 6 in schwarz / weiß Quelle: http://electric-meat.deviantart.com/art/A-Heroes-Farewell-280271639 Beispiel 7 in schwarz / weiß Quelle: http://movillefacepalmplz.deviantart.com/art/Background-The-Pumpkin -Farm-of-Good-Old-Days-342865938


BEARBEITEN: Aufgrund von Anti-Aliasing auf Linien, die nicht schwarz / weiße Pixel verursachen, und einigen Bildern, die möglicherweise grau statt schwarz / weiß enthalten, können Sie als Bonus-Herausforderung versuchen, damit umzugehen. Es sollte meiner Meinung nach einfach genug sein.

OliverGriffin
quelle
4
An alle: Bitte nicht als "Kunstwettbewerb" abstimmen / schließen - da
steckt
16
Willkommen bei PPCG! Ich begrüße Sie dafür, dass Sie den Mut haben, Ihren ersten Beitrag nicht nur als Herausforderung, sondern auch als künstlerische Herausforderung zu betrachten. Viel Glück, ich wünsche Ihnen alles Gute, und wenn Sie hier bleiben, werden Sie wahrscheinlich weit kommen.
AdmBorkBork
4
@OliverGriffin Ich stimme gegen das Schließen und habe auch die Bilder hinzugefügt, die Sie für Sie verlinkt haben. Sie können die Kommentare entfernen, wenn Sie möchten.
Addison Crump
2
Endlich habe ich einen Ansatz gefunden, der wahrscheinlich nicht überläuft, aber jetzt läuft er langsam.
SuperJedi224
4
Ich habe dafür gestimmt, Ihre Frage erneut zu öffnen und meine -1 in eine +1 geändert. Gute Jobbearbeitung und Hinzufügen zusätzlicher Informationen. Ich begrüße Sie auch, dass Sie so empfänglich für Community-Kritik sind. Willkommen bei PPCG! Hoffe es gefällt euch.
Zach Gates

Antworten:

30

Spektrales Airbrushing (Python, PIL, scipy)

Hierbei wird ein ausgefeilter mathematischer Algorithmus verwendet, um bunten Unsinn zu erzeugen. Der Algorithmus ist mit dem PageRank-Algorithmus von Google verwandt, jedoch für Pixel anstelle von Webseiten.

Ich habe diesen Ansatz gewählt, weil ich dachte, dass es im Gegensatz zu auf Flood-Fill basierenden Methoden möglich ist, Bilder wie das Huhn und den Baum zu verarbeiten, bei denen es Formen gibt, die nicht vollständig von schwarzen Linien umschlossen sind. Wie Sie sehen können, funktioniert es, obwohl es auch dazu neigt, verschiedene Teile des Himmels in verschiedenen Farben zu färben

Für Mathematiker bedeutet dies, dass sie im Wesentlichen den Adjazenzgraphen der while-Pixel im Bild erstellen und dann die 25 besten Eigenvektoren des Laplace-Graphen finden. (Außer es ist nicht ganz so, weil wir die dunklen Pixel einschließen, geben wir ihren Verbindungen nur eine geringere Gewichtung. Dies hilft beim Umgang mit Antialiasing und scheint auch im Allgemeinen bessere Ergebnisse zu liefern.) Nachdem die Eigenvektoren gefunden wurden, erzeugt es eine zufällige lineare Kombination von ihnen, gewichtet mit ihren inversen Eigenwerten, um die RGB-Komponenten des Ausgabebildes zu bilden.

Im Interesse der Rechenzeit wird das Bild zuvor verkleinert, dann wieder vergrößert und dann mit dem Originalbild multipliziert. Es läuft immer noch nicht schnell und dauert auf meinem Computer zwischen 2 und 10 Minuten, abhängig vom Eingabebild, obwohl das Huhn aus irgendeinem Grund 17 Minuten gebraucht hat.

Es könnte tatsächlich möglich sein, diese Idee in etwas Nützliches zu verwandeln, indem Sie eine interaktive App erstellen, in der Sie die Farbe und Intensität jedes Eigenvektors steuern können. Auf diese Weise können Sie diejenigen ausblenden, die den Himmel in verschiedene Abschnitte unterteilen, und diejenigen, die relevante Merkmale des Bildes erkennen. Aber ich habe nicht vor, das selbst zu machen :)

Hier sind die Ausgabebilder:

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

(Bei den Kürbissen hat es nicht so gut geklappt, also lasse ich das weg.)

Und hier ist der Code:

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))
Nathaniel
quelle
4
Das ist wirklich cool. Wahrscheinlich einer meiner Favoriten. Sie haben das Antialiasing und die offenen Bereiche hervorragend gemeistert, und endlich hat jemand Link eingefärbt! (Seit diesem :-P warten speichern auf Desktop - Set ) Ich frage mich , was meine alten englischen Lehrer gesagt hätte darüber als statisches Bild ... „Es zeigt die beiden Seiten seines Herzens, da eine Seite ist Frieden und auf die anderen gibt es die notwendigen Kämpfe, um diesen Frieden zu erreichen ". Genug von meiner Liebe zu den Legend of Zelda-Spielen ... Es ist wirklich eine Schande, dass es so lange dauert. Wie groß waren die resultierenden Dateien? Ps Love Bilder 4 & 5
OliverGriffin
2
@donbright, ein Drittklässler, der Eigenvektoren verstehen könnte, wäre in der Tat ein sehr kluges Kind - ich bin mir nicht sicher, ob es mir möglich ist, den Algorithmus auf dieser Ebene zu erklären. Aber lassen Sie es mich trotzdem versuchen: Stellen Sie sich vor, wir drucken das Bild auf ein steifes Blech. Dann schneiden wir sorgfältig alle schwarzen Linien weg und ersetzen sie durch etwas viel flexibleres, wie elastisches. So sind die weißen Teile Metallplatten und die schwarzen Teile sind flexibler Stoff. Als nächstes hängen wir das Ganze an einer Schnur in die Luft, damit es sich frei bewegen kann. Wenn wir nun auf die Metallplatten klopfen, vibrieren sie ...
Nathaniel
2
@donbright (Fortsetzung) ... Je nachdem, wie Sie auf die Metallplatte schlagen, vibriert diese auf unterschiedliche Weise. Vielleicht vibriert manchmal nur eines der Metallteile und nicht die anderen, aber ein anderes Mal (weil sie durch Gummibänder verbunden sind), wenn eine Platte angeschlagen wird, bewegt sich auch eine andere. Diese verschiedenen Arten des Vibrierens werden Vibrationsmoden genannt . Dieses Programm simuliert einige der Schwingungsmodi dieser Metallplatte, generiert jedoch keinen Ton, sondern ermittelt anhand dieser Modi, welche Farbe gezeichnet werden soll.
Nathaniel
2
@donbright Sie können auch sehen hier für weitere Informationen über die Schwingungen von Metallplatten zu visualisieren.
Nathaniel
2
@donbright (diese technischere Erklärung könnte Sie auch ein wenig verlieren, aber diese Erklärung funktioniert, weil die Schwingungsmodi einer Platte auch mit einer Eigenvektorberechnung berechnet werden. Obwohl es möglich ist, ist es nicht ganz die gleiche Berechnung, die mein Code ausführt - ich bin es nicht wirklich sicher.)
Nathaniel
25

Python 2 + PIL auch, mein erstes Malbuch

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

Ich habe genau das gleiche getan wie CarpetPython, nur dass ich die Region mit "Verläufen" fülle und einen anderen Farbzyklus verwende.

Meine schönsten Färbungen: Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Berechnungszeiten auf meiner Maschine:

  • bild 1 (chinesischer drache): real 0m2.862s benutzer 0m2.801s sys 0m0.061s

  • image 2 (gryffon): real 0m0.991s user 0m0.963s sys 0m0.029s

  • bild 3 (einhorniger drache): real 0m2.260s benutzer 0m2.239s sys 0m0.021s

Dieter
quelle
Schöne Farbverläufe! Wenn Sie eine for-Schleife in eine for-Schleife stecken, in der sich nichts anderes befindet, müssen Sie dann nicht weiter einrücken?
OliverGriffin
natürlich tust du das ! Es war Copy / Paste-Problem ...
Dieter
23

Python 2 und PIL: Psychedelische Welten

Ich habe einen einfachen Algorithmus verwendet, um jeden weißlichen Bereich mit einer Farbe aus einer Fahrradpalette zu füllen. Das Ergebnis ist sehr farbenfroh, aber nicht sehr naturgetreu.

Beachten Sie, dass die "weißen" Teile in diesen Bildern nicht sehr weiß sind. Sie müssen auch auf Graustufen testen.

Code in 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)

Beispielbilder:

Ein bunter Drache

Kürbisse auf LSD

Logik-Ritter
quelle
3
Der beängstigende Teil ist, dass die Farben tatsächlich zu funktionieren scheinen. Wie lange haben Sie in jedem Bild zum Ausmalen gebraucht und wie groß waren die Dateien?
OliverGriffin
1
Das Programm färbt jedes Bild in ca. 2 Sekunden. Die Abmessungen des Ausgabebilds entsprechen denen der Eingabedateien. Die Dateigrößen sind meistens 10% bis 40% kleiner als die Originaldateien (wahrscheinlich, weil unterschiedliche JPEG-Komprimierungseinstellungen verwendet werden).
Logic Knight
3
Ich bin sehr beeindruckt, wie kurz der Code ist! Mir gefällt auch, wie Sie die verfügbaren Farben effektiv begrenzen und so eine festgelegte Palette einhalten. Ich mag es wirklich, es gibt eine Art Grunge-Stimmung (ist das das richtige Wort? Ich bin kein Künstler).
OliverGriffin
@ OliverGriffin, ich bin froh, dass es dir gefällt. Ich strebte eine Palette ohne helle oder dunkle Farben an, die aber immer noch einen gewissen Kontrast aufwies. Dieser Farbbereich schien die angenehmsten Ergebnisse zu haben.
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);

Wir verwenden den HSV-Farbraum und wählen den Farbton jeder Region basierend auf ihrer relativen Größe zwischen den weißen Regionen aus. Die größte Region ist blau ( Hue = 0.7) und die kleinste Region violett ( Hue = 0.8). Die Bereiche zwischen diesen beiden Größen erhalten Farbtöne im Bereich 0.7 -> 1=0 -> 0.8. Der Farbton des Bereichs wird in Bezug auf die Funktion linear ausgewählt area^0.15. Sättigung und Wert ist immer 1 für jedes nicht schwarze Pixel.

Es dauert weniger als 1 Sekunde, um ein Bild einzufärben.

Die 3 Bilder mit geschlossenen Bereichen, in denen der Algorithmus anständig funktioniert:

Drachen

ein anderer Drache

vielleicht ein anderer Drache

Und der Rest der Bilder:

Drachen

ein anderer Drache

vielleicht ein anderer Drache

Auf diesen Bildern gibt es große weiße zusammenhängende Bereiche, die idealerweise durch mehrere Farben gefärbt werden sollten (dieses Problem wurde in Nathaniels Lösung gut gelöst) .

randomra
quelle
Netter und kurzer Code für einige hübsche farblich abgestimmte Ergebnisse! Mir gefällt, wie Sie den Bereich zur Bestimmung des Farbtons verwendet haben. Wie lange hat es gedauert, das durchschnittliche Bild zu verarbeiten, und warum hat es bei einigen der detaillierteren Bilder nicht funktioniert? Waren die Flächen zu klein?
OliverGriffin
1
@OliverGriffin Beantwortete meinen Beitrag und fügte den Rest der Bilder hinzu.
Randomra
7

Python 3 mit Kissen

Der Code ist ein bisschen lang, um ihn in diese Antwort aufzunehmen, aber hier ist der Kern davon .

  1. Nehmen Sie das Eingabebild und setzen Sie es auf einen weißen Hintergrund, wenn es einen Alphakanal hat. (Zumindest für das Hühnerbild erforderlich, da das gesamte Bild schwarz war und sich nur durch Transparenz auszeichnete. Daher war es nicht hilfreich, das Alpha einfach fallen zu lassen.)
  2. Konvertieren Sie das Ergebnis in Graustufen. Wir wollen keine Komprimierungs- oder Anti-Aliasing-Artefakte oder graue Linien, die nicht ganz grau sind, um uns durcheinander zu bringen.
  3. Erstellen Sie eine Kopie des Ergebnisses auf zwei Ebenen (Schwarzweiß). Graustufen werden basierend auf einem konfigurierbaren Grenzwert zwischen Weiß und der dunkelsten Farbe im Bild in Schwarz oder Weiß konvertiert.
  4. Fülle jeden weißen Bereich des Bildes mit Farbe. Die Farben werden nach dem Zufallsprinzip mithilfe einer auswählbaren Palette ausgewählt, die die Position des Startpunkts für die Überflutungsoperation berücksichtigt.
  5. Füllen Sie die schwarzen Linien mit den Farben des nächsten Nachbarn aus. Dies hilft uns, Anti-Aliasing wieder einzuführen, indem verhindert wird, dass jede farbige Region in zackigem Schwarz umrandet wird.
  6. Nehmen Sie das Graustufenbild aus Schritt 2 und erstellen Sie daraus eine Alphamaske: Die dunkelste Farbe ist vollständig undurchsichtig, die hellste Farbe ist vollständig transparent.
  7. Kombinieren Sie das Graustufenbild mit dieser Alphamaske mit dem Farbbild aus Schritt 5.

Leider haben diese letzten Schritte immer noch keine helleren "Lichthöfe" beseitigt, die in dunkleren Bereichen sichtbar sind, aber sie haben zumindest einen spürbaren Unterschied bewirkt. Bildverarbeitung war nie mein Fachgebiet, daher gibt es meines Wissens erfolgreichere und effizientere Algorithmen, um das zu tun, was ich hier versucht habe ... aber na ja.

Bisher gibt es nur zwei wählbare Paletten für Schritt 4: eine rein zufällige und eine sehr raue "natürliche", bei der versucht wird, den oberen Ecken Himmelsfarben zuzuweisen, den unteren Ecken Grasfarben, braun (Felsen oder Holz) ) Farben in der Mitte jeder Seite und abwechslungsreiche Farben in der Mitte. Der Erfolg war ... begrenzt.


Verwendungszweck:

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

Proben:

paint_by_prog.py -t 0.7 Gryphon-Lines.png Farbiger Greif

paint_by_prog.py Dragon-Tattoo-Outline.jpg Farbiger Cartoony-Drache

paint_by_prog.py -t 0.85 -p natural The-Pumpkin-Farm-of-Good-old-Days.jpg Farbige Bauernhofszene

paint_by_prog.py -t 0.7 Dragon-OutLine.jpg Farbiger grunge Drache

paint_by_prog.py stejar-arbore-schiţă-natura.png Farbiger Baum, der sehr fahnenartig aussieht

Das Huhn sieht nicht sehr gut aus und mein letztes Ergebnis für das Link-Image ist nicht das beste. Eines, das aus einer früheren Version des Codes stammt, war größtenteils blassgelb und hatte eine interessante Wüstenstimmung ...


Performance:

Es dauert einige Sekunden, bis jedes Bild mit den Standardeinstellungen verarbeitet ist. Dies bedeutet, dass für Schritt 5 ein Algorithmus für den nächsten Nachbarn verwendet wird. Der echte nächste Nachbar ist bedeutend langsamer und dauert möglicherweise eine halbe Minute (ich habe ihn nicht zeitlich festgelegt).

Tim Pederick
quelle
Das erste Bild sieht fantastisch aus, besonders das braune Auge. Gut gemacht. Ich begrüße Sie auch, dass Sie grünes Gras, braune Kürbisfelder und lila Wolken bekommen.
OliverGriffin
3

Java

Zufällige Farbauswahl aus einer Palette Ihrer Wahl.

Warnung: Die Suche nach Regionen ist derzeit sehr langsam, es sei denn, die weißen Regionen sind ungewöhnlich klein.

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

Erfordert zwei Eingaben: den Dateinamen und die Paletten-ID. Enthält einige Antialiasing-Korrekturen, jedoch keine Logik für transparente Pixel.

Die folgenden Paletten werden derzeit erkannt:

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)

Ergebnisse:

Dragon, Game Boy-Palette:

Bildbeschreibung hier eingeben

Der andere Drache, blau + grüne Palette:

Bildbeschreibung hier eingeben

GOL Stillleben mona lisa (wie von diesem Programm gerendert ), dreifarbige Palette:

Bildbeschreibung hier eingeben

SuperJedi224
quelle
+1 für Ihre Farbanpassbarkeit! :) Wenn Sie das Antialiasing-Problem beheben könnten, würde dies noch besser aussehen. Wie lange haben Sie für die Ausgabe dieser Bilder gebraucht?
OliverGriffin