PySide: Separar una hoja de sprites / Separar una imagen en regiones contiguas de color

Estoy trabajando en un progtwig en el que necesito separar hojas de sprit , o en otras palabras, separar una imagen en regiones contiguas de color.

Nunca he hecho ningún procesamiento de imágenes antes, así que me pregunto cómo haría esto. ¿Qué haría después de probar el color del píxel? ¿Cuál es la mejor manera de determinar qué píxel va con cada sprite?

Todas las imágenes de entrada tienen fondos uniformes, y un canal alfa diferente al del fondo cuenta como color. El orden de las imágenes de salida debe ser de izquierda a derecha, arriba-abajo. Mi proyecto está escrito en PySide, así que espero usarlo también para esta tarea, pero podría importar más bibliotecas si es necesario.

Gracias tus respuestas!

PD: No estoy seguro de si la etiqueta de PySide es apropiada o no, ya que estoy usando PySide, pero la pregunta no involucra los aspectos de la GUI. Si un mod siente que no pertenece, siéntase libre de eliminarlo.


Por ejemplo, tengo un spritesheet que se parece a esto:

Imagen de entrada

Quiero separarlo en estos:

Salida 1Salida 2Salida 3Salida 4Salida 5Salida 6Salida 7Salida 8

Eso suena como algo que debería implementarse en cualquier cosa que se ocupe de sprites, pero aquí implementaremos nuestro propio sprite-spliter.

Lo primero que necesitamos aquí es extraer los objetos individuales. En esta situación, solo es cuestión de decidir si un píxel es uno de fondo o no. Si asumimos que el punto en el origen es un píxel de fondo, entonces hemos terminado:

from PIL import Image def sprite_mask(img, bg_point=(0, 0)): width, height = img.size im = img.load() bg = im[bg_point] mask_img = Image.new('L', img.size) mask = mask_img.load() for x in xrange(width): for y in xrange(height): if im[x, y] != bg: mask[x, y] = 255 return mask_img, bg 

Si guarda la imagen de mask creada arriba y la abre, esto es lo que vería en ella (agregué un rectángulo dentro de su ventana vacía):

introduzca la descripción de la imagen aquí

Con la imagen de arriba, lo siguiente que necesitamos es rellenar sus agujeros si queremos unirnos a los sprites que están dentro de otros (como el rectángulo agregado, ver la figura de arriba). Esta es otra regla simple: si no se puede alcanzar un punto desde el punto en [0, 0], entonces es un agujero y se debe rellenar. Todo lo que queda es separar cada sprite en imágenes individuales. Esto se hace mediante el etiquetado de componentes conectados. Para cada componente obtenemos su caja delimitadora alineada con el eje para definir las dimensiones de la pieza, y luego copiamos de la imagen original los puntos que pertenecen a un componente dado. Para que sea breve, el siguiente código usa scipy para estas tareas:

 import sys import numpy from scipy.ndimage import label, morphology def split_sprite(img, mask, bg, join_interior=True, basename='sprite_%d.png'): im = img.load() m = numpy.array(mask, dtype=numpy.uint8) if join_interior: m = morphology.binary_fill_holes(m) lbl, ncc = label(m, numpy.ones((3, 3))) for i in xrange(1, ncc + 1): px, py = numpy.nonzero(lbl == i) xmin, xmax, ymin, ymax = px.min(), px.max(), py.min(), py.max() sprite = Image.new(img.mode, (ymax - ymin + 1, xmax - xmin + 1), bg) sp = sprite.load() for x, y in zip(px, py): x, y = int(x), int(y) sp[y - int(ymin), x - int(xmin)] = im[y, x] name = basename % i sprite.save(name) print "Wrote %s" % name sprite = Image.open(sys.argv[1]) mask, bg = sprite_mask(sprite) split_sprite(sprite, mask, bg) 

Ahora tiene todas las piezas (sprite_1.png, sprite_2.png, …, sprite_8.png) exactamente como incluyó en la pregunta.