PySide: Splitting a Sprite / Splitting an Image into Contiguous Color Areas

I am working on a program where I need to split sprites , or in other words split an image into contiguous regions of color.

I've never done image processing before, so I'm wondering how I would do it. What would I do after checking the pixel color? What's the best way to determine which pixel goes with each sprite?

All input images have the same backgrounds, and the alpha channel differs from the alpha channel as a color. The order of the output images should be left to right, up and down. My project is written in PySide, so I hope to use it for this task too, but I could import more libraries if needed.

Thank you for your responses!

PS: I'm not sure if the PySide tag is appropriate or not as I am using PySide, but the question is not related to its GUI aspects. If a mod thinks it doesn't belong, feel free to delete it.


For example, I have a sprite that looks like this:

Input image

I want to split it into the following:

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

+3


source to share


1 answer


This is similar to what should be implemented in all sprites, but here we will be implementing our own sprite-spliter.

The first thing we need to do is extract individual objects. In this situation, it is only a matter of whether the pixel is in the background or not. Assuming that the point at the origin is the background pixel, we do:

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

      

If you saved the above image mask

and opened it, this is what you will see on it (I added a rectangle inside your empty window):



enter image description here

With the image above, the next thing we need is to fill its holes if we want to attach to sprites that are inside others (for example, the added rectangle, see the image above). This is another simple rule: if a point cannot be reached from a point at [0, 0], then this hole and it must be filled. All that's left then splits each sprite into separate images. This is done by marking the connected components. For each component, we get its axis-aligned bounding box to determine the size of the shape, and then copy the points belonging to that component from the original image. To save it, the following code uses scipy

for these tasks:

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)

      

You now have all the parts (sprite_1.png, sprite_2.png, ..., sprite_8.png) exactly as you included in the question.

+4


source







All Articles