Pintar por números (usando programación, no números)

56

Su tarea es crear un programa que tome una imagen contorneada en blanco y negro (a continuación se muestran imágenes de ejemplo) y la rellena de color. Depende de usted cómo secciona cada región y con qué color llenarla (incluso podría usar un RNG).

Por ejemplo:

salida por ejemplo 1

Como puede ver, soy claramente un artista de un calibre superior cuando se trata de MS Paint.


Puntuación

Este es un concurso de popularidad, por lo que gana la respuesta con más votos netos. Se alienta a los votantes a juzgar las respuestas por

  • Criterio de entrada: cualquier imagen que consista en fondo blanco / gris claro y contornos negro / gris oscuro
  • Qué tan bien se hace la coloración; lo que significa que pocas o ninguna áreas son blancas a diferencia de lo anterior (a menos que obviamente pretenda usar blanco, por ejemplo, para nubes)
  • Personalización de los colores utilizados en ciertas secciones.
  • Qué tan bien funciona el sistema en una variedad de imágenes diferentes (de diferentes detalles)
  • Publique cuánto tiempo tarda su programa por imagen. Es posible que no estemos jugando al golf de código, pero un código más corto, más rápido y más eficiente debería considerarse mejor
  • Debe mostrar la nueva imagen en la pantalla o en un archivo (no más grande que 2 MB para que pueda mostrarse en la respuesta)
  • Justifique por qué eligió generar ese tipo de imagen y comente / explique el funcionamiento de su código
  • La aplicabilidad del color utilizado a la forma respectiva a la que está vinculado (esquema de color realista, es decir, el césped es verde, las cercas de madera son marrones, etc.)

    "Podría colorear aleatoriamente cada área, pero si pudiera identificar la" cerca "y hacerla de un color similar, entonces eso es algo que merece votos positivos". - NathanMerrill

Como se trata de un concurso de popularidad, también puede juzgar opcionalmente por:

  • Atractivo general (qué tan bien se ve la imagen)
  • Estilo artístico; si puede programar en sombreado o coloración estilo acuarela, etc.

En general, ganará la imagen de salida más pequeña (tamaño de archivo) de la más alta calidad, con el programa en ayunas y el mayor voto público.

Si tiene otras especificaciones de evaluación que cree que deberían usarse, recomiéndelas en los comentarios de esta publicación.


Ejemplos

No tengo nada; Todas las imágenes de ejemplo son de una licencia Creative Commons.

ejemplo 1 en blanco / negro Fuente: https://pixabay.com/ro/stejar-arbore-schi%C5%A3%C4%83-natura-303890/ ejemplo 2 en blanco / negro Fuente: http://www.freestockphotos.biz/stockphoto/10665 ejemplo 3 en blanco / negro Fuente: http: / /crystal-rose1981.deviantart.com/art/Dragon-Tattoo-Outline-167320011 ejemplo 4 en blanco / negro Fuente: http://jaclynonacloudlines.deviantart.com/art/Gryphon-Lines-PF-273195317 ejemplo 5 en blanco / negro Fuente: http://captaincyprus.deviantart.com / art / Dragon-OutLine-331748686 ejemplo 6 en blanco / negro Fuente: http://electric-meat.deviantart.com/art/A-Heroes-Farewell-280271639 ejemplo 7 en blanco / negro Fuente: http://movillefacepalmplz.deviantart.com/art/Background-The-Pumpkin -Granja de los viejos tiempos-342865938


EDITAR: Debido al suavizado en las líneas que causan píxeles no negros / blancos y algunas imágenes que pueden contener gris en lugar de negro / blanco, como un desafío adicional puede intentar lidiar con él. Debería ser bastante fácil en mi opinión.

OliverGriffin
fuente
44
Para todos: por favor no voten en contra / cierren esto como un "concurso de arte" - hay más que eso
edc65
16
Bienvenido a PPCG! Te aplaudo por tener el coraje de no solo que tu primera publicación sea un desafío, y no solo un desafío pop-con, sino un desafío artístico por encima de todo. Buena suerte, te deseo lo mejor, y si te quedas, creo que llegarás lejos aquí.
AdmBorkBork
44
@OliverGriffin Voto en contra del cierre y, además, agregué las imágenes que vinculó para usted. Puede eliminar los comentarios, si lo desea.
Addison Crump
2
Finalmente encontré un enfoque que probablemente no acumulará desbordamiento, pero ahora se está ejecutando un poco lento.
SuperJedi224
44
He votado para volver a abrir su pregunta y he cambiado mi -1 a un +1. Buen trabajo editando y agregando información adicional. Además, te aplaudo por ser tan receptivo a las críticas de la comunidad. Bienvenido a PPCG! Espero que lo disfrutes.
Zach Gates

Respuestas:

30

Aerografía espectral (Python, PIL, scipy)

Esto utiliza un sofisticado algoritmo matemático para producir tonterías coloridas. El algoritmo está relacionado con el algoritmo PageRank de Google, pero para píxeles en lugar de páginas web.

Tomé este enfoque porque pensé que, a diferencia de los métodos basados ​​en el relleno de inundación, podría hacer frente a imágenes como el pollo y el árbol, donde hay formas que no están completamente encerradas por líneas negras. Como puede ver, funciona, aunque también tiende a colorear en diferentes partes del cielo en diferentes colores.

Para las personas con mentalidad matemática: lo que está haciendo es esencialmente construir el gráfico de adyacencia de los píxeles while en la imagen, luego encontrar los 25 vectores propios principales del gráfico laplaciano. (Excepto que no es exactamente eso, debido a que incluimos los píxeles oscuros, simplemente les damos un menor peso a sus conexiones. Esto ayuda a lidiar con el antialiasing y también parece dar mejores resultados en general). Habiendo encontrado los vectores propios, crea un combinación lineal aleatoria de ellos, ponderada por sus valores propios inversos, para formar los componentes RGB de la imagen de salida.

En aras del tiempo de cálculo, la imagen se reduce antes de hacer todo esto, luego se vuelve a aumentar y luego se multiplica por la imagen original. Aún así, no se ejecuta rápidamente, tarda entre 2 y 10 minutos en mi máquina, dependiendo de la imagen de entrada, aunque por alguna razón el pollo tardó 17 minutos.

En realidad, podría ser posible convertir esta idea en algo útil, haciendo una aplicación interactiva donde pueda controlar el color y la intensidad de cada uno de los vectores propios. De esa manera, podría desvanecer los que dividen el cielo en diferentes secciones, y desvanecerse en los que recogen las características relevantes de la imagen. Pero no tengo planes de hacer esto yo mismo :)

Aquí están las imágenes de salida:

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

(No funcionó tan bien en las calabazas, así que omito esa).

Y aquí está el código:

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
fuente
44
Esto es realmente genial. Probablemente uno de mis favoritos hasta ahora. ¡Hiciste un excelente trabajo al manejar el antialiasing y las áreas abiertas, y finalmente alguien coloreó a Link! ( He estado esperando eso :-P guardar el conjunto en el escritorio ) Me pregunto qué habría dicho mi antiguo profesor de inglés sobre eso como una imagen estática ... "Muestra los dos lados de su corazón, un lado hay paz y en el otra es la lucha necesaria para lograr esa paz ". Suficiente sobre mi amor por los juegos de Legend of Zelda ... Realmente es una pena que lleve tanto tiempo. ¿Qué tan grandes eran los archivos resultantes? Ps Love imágenes 4 y 5
OliverGriffin
2
@donbright un alumno de 3er grado que podría entender los vectores propios sería un niño muy brillante, no estoy seguro de que me sea posible explicar el algoritmo a ese nivel. Pero déjame intentarlo de todos modos: imagina que imprimimos la imagen en una hoja de metal rígida. Luego cortamos cuidadosamente todas las líneas negras y las reemplazamos con algo mucho más flexible, como elástico. Entonces las partes blancas son placas de metal y las partes negras son de tela flexible. A continuación, colgamos todo en el aire de una cuerda, por lo que es libre de moverse. Ahora, si tocamos las placas de metal, vibrarán ...
Nathaniel
2
@donbright (continuación) ... Dependiendo de cómo golpees la placa de metal, vibrará de diferentes maneras. Tal vez a veces solo una de las partes metálicas vibrará y no las otras, pero otras veces (porque están conectadas por elástico), golpear una placa hará que otra se mueva también. Estas diferentes formas de vibración se llaman modos vibracionales . Este programa simula algunos de los modos vibratorios de esta placa de metal, pero en lugar de generar sonido, los utiliza para determinar qué color dibujar.
Nathaniel
2
@donbright También puede ver aquí para obtener más información sobre la visualización de las vibraciones de las placas de metal.
Nathaniel
2
@donbright (esta explicación más técnica también podría perderte un poco, pero esta explicación funciona porque los modos vibracionales de una placa también se calculan usando un cálculo de vector propio. Aunque es posible que no sea el mismo cálculo que mi código, estoy no estoy seguro.)
Nathaniel
25

Python 2 + PIL también, mi primer libro para colorear

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

Hice lo mismo que hizo CarpetPython, excepto que llené la región con 'degradados' y utilicé un ciclo de color diferente.

Mis coloraciones más magníficas: ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

Tiempos de cálculo en mi máquina:

  • imagen 1 (dragón chino): usuario real 0m2.862s 0m2.801s sys 0m0.061s

  • imagen 2 (gryffon): usuario real 0m0.991s 0m0.963s sys 0m0.029s

  • imagen 3 (dragón unicornio): usuario real 0m2.260s 0m2.239s sys 0m0.021s

dieter
fuente
Buenos gradientes! Cuando pegas un bucle for dentro de un bucle for sin nada más dentro del primero, ¿no necesitas sangrar más?
OliverGriffin
seguro lo haces ! fue problema de copiar / pegar ...
dieter
23

Python 2 y PIL: mundos psicodélicos

He usado un algoritmo simple para rellenar cada área blanca con un color de una paleta de ciclismo. El resultado es muy colorido, pero no muy realista.

Tenga en cuenta que las partes "blancas" en estas imágenes no son muy blancas. También necesitará probar tonos de gris.

Código en 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)

Imágenes de ejemplo:

Un dragón colorido

Calabazas en LSD

Caballero Lógico
fuente
3
La parte aterradora es que los colores realmente parecen funcionar. ¿Cuánto tiempo te llevó colorear cada imagen y qué tan grandes eran los archivos?
OliverGriffin
1
El programa colorea cada imagen en aproximadamente 2 segundos. Las dimensiones de la imagen de salida son las mismas que las de los archivos de entrada. Los tamaños de archivo son en su mayoría de 10% a 40% más pequeños que los originales (probablemente porque se utilizan diferentes configuraciones de compresión jpeg).
Logic Knight el
3
¡Estoy completamente impresionado por lo corto que es el código! También me gusta cómo limitas efectivamente los colores disponibles para usar, manteniendo así una paleta fija. De hecho, realmente me gusta, da una especie de ambiente grunge (¿es esa la palabra correcta? No soy un artista).
OliverGriffin
@OliverGriffin, me alegra que te guste. Estaba apuntando a una paleta sin colores brillantes u oscuros, pero que todavía tuviera algo de contraste. Esta gama de colores parecía tener los resultados más agradables.
Logic Knight el
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);

Usamos el espacio de color HSV y elegimos el tono de cada región en función de su tamaño relativo entre las regiones blancas. La región más grande será azul ( Hue = 0.7) y la región más pequeña será violeta ( Hue = 0.8). Las regiones entre estos dos tamaños reciben Hues en el rango 0.7 -> 1=0 -> 0.8. El tono en el rango se selecciona linealmente con respecto a la función area^0.15. Saturación y valor siempre es 1 por cada píxel no negro.

Se tarda menos de 1 segundo en colorear una imagen.

Las 3 imágenes con regiones cerradas donde el algoritmo funciona decentemente:

continuar

otro dragón

tal vez otro dragón

Y el resto de las imágenes:

continuar

otro dragón

tal vez otro dragón

En estas imágenes hay grandes regiones conectadas en blanco que deberían ser idealmente coloreadas por múltiples colores (este problema se resolvió muy bien en la solución de Nathaniel .

randomra
fuente
¡Código agradable y corto para obtener resultados coordinados con colores bonitos! Me gusta cómo usaste el área para ayudar a determinar el tono. ¿Cuánto tiempo llevó procesar la imagen promedio y por qué no funcionó en algunas de las imágenes más detalladas? ¿Las áreas eran demasiado pequeñas?
OliverGriffin
1
@OliverGriffin respondió en mi publicación y agregó el resto de las imágenes.
randomra
7

Python 3 con almohada

El código es un poco largo para incluir en esta respuesta, pero aquí está la esencia del mismo .

  1. Tome la imagen de entrada y, si tiene un canal alfa, compóngala sobre un fondo blanco. (Necesario al menos para la imagen de pollo, porque toda la imagen era negra, distinguida solo por la transparencia, por lo que simplemente no fue útil dejar caer el alfa).
  2. Convierta el resultado a escala de grises; no queremos que los artefactos de compresión o suavizado, o las líneas grises que no son bastante grises, nos confundan.
  3. Cree una copia de dos niveles (blanco y negro) del resultado. Los tonos de gris se convierten en negro o blanco según un umbral de corte configurable entre el blanco y el tono más oscuro de la imagen.
  4. Rellenar cada región blanca de la imagen. Los colores se eligen al azar, utilizando una paleta seleccionable que tiene en cuenta la ubicación del punto de partida para la operación de relleno de inundación.
  5. Rellene las líneas negras con sus colores vecinos más cercanos. Esto nos ayuda a reintroducir el anti-aliasing, al evitar que cada región coloreada se bordee en negro irregular.
  6. Tome la imagen en escala de grises del paso 2 y haga una máscara alfa a partir de ella: el color más oscuro es completamente opaco, el color más claro es completamente transparente.
  7. Componga la imagen en escala de grises en la imagen en color del paso 5 con esta máscara alfa.

Desafortunadamente, esos últimos pasos aún no han eliminado los "halos" más claros que son visibles en las regiones de color más oscuro, pero al menos han marcado una diferencia notable. El procesamiento de imágenes nunca fue mi campo de estudio, por lo que sé que existen algoritmos más exitosos y más eficientes para hacer lo que intenté hacer aquí ... pero bueno.

Hasta ahora, solo hay dos paletas seleccionables para el paso 4: una puramente aleatoria, y una muy "natural", que trata de asignar colores del cielo a las esquinas superiores, colores de hierba a las esquinas inferiores, marrón (rocas o madera) ) colores en el medio de cada lado y colores variados en el centro. El éxito ha sido ... limitado.


Uso:

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

Muestras:

paint_by_prog.py -t 0.7 Gryphon-Lines.png Grifo de color

paint_by_prog.py Dragon-Tattoo-Outline.jpg Dragón de dibujos animados de color

paint_by_prog.py -t 0.85 -p natural The-Pumpkin-Farm-of-Good-old-Days.jpg Escena de granja coloreada

paint_by_prog.py -t 0.7 Dragon-OutLine.jpg Dragón de color grunge

paint_by_prog.py stejar-arbore-schiţă-natura.png Árbol de color, muy parecido a una bandera

El pollo no se ve muy bien, y mi resultado más reciente para la imagen de Link no es el mejor; uno que vino de una versión anterior del código era en gran parte de color amarillo pálido, y tenía un ambiente desértico interesante al respecto ...


Actuación:

Cada imagen tarda un par de segundos en procesarse con la configuración predeterminada, lo que significa que se utiliza un algoritmo de vecino más cercano aproximado para el paso 5. El vecino más cercano verdadero es significativamente más lento, puede tardar medio minuto (en realidad no lo he cronometrado).

Tim Pederick
fuente
La primera imagen se ve fantástica, especialmente ese ojo marrón. Buen trabajo. También te aplaudo por conseguir hierba verde, campos marrones de calabazas y nubes moradas.
OliverGriffin
3

Java

Selección aleatoria de colores de su elección de paleta.

Advertencia: la región se encuentra actualmente muy lenta, a menos que las regiones blancas sean inusualmente pequeñas.

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

Requiere dos entradas: el nombre del archivo y la ID de la paleta. Incluye alguna corrección antialiasing, pero no incluye lógica para píxeles transparentes.

Actualmente se reconocen las siguientes paletas:

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)

Resultados:

Dragon, paleta de Game Boy:

ingrese la descripción de la imagen aquí

El otro dragón, paleta azul + verde:

ingrese la descripción de la imagen aquí

GOL still life mona lisa (tal como se muestra en este programa ), paleta tricolor:

ingrese la descripción de la imagen aquí

SuperJedi224
fuente
¡+1 para su personalización de color! :) si pudiera solucionar el problema de antialiasing, esto se vería aún mejor. ¿Cuánto tiempo tardó en generar estas imágenes?
OliverGriffin