Created
February 28, 2018 20:55
-
-
Save tuaplicacionpropia/f5bd6b0f69a11141767387eb789f5093 to your computer and use it in GitHub Desktop.
Sprite Sheet Detect Individual Sprite Bounds Automatically
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
#coding:utf-8 | |
from __future__ import print_function | |
from PIL import Image | |
import math | |
#https://stackoverflow.com/questions/13584586/sprite-sheet-detect-individual-sprite-bounds-automatically?rq=1 | |
''' | |
Process each* scan line in turn | |
For each scanline, walk from left to right, until you find a non-transparent pixel P. | |
If the location of P is already inside a known bounded box | |
Continue to the right of the bounded box | |
Else | |
BBox = ExploreBoundedBox(P) | |
Add BBox to the collection of known bounded boxes | |
Function ExploreBoundedBox(pStart) | |
Q = new Queue(pStart) | |
B = new BoundingBox(pStart) | |
While Q is not empty | |
Dequeue the front element as P | |
Expand B to include P | |
For each of the four neighbouring pixels N | |
If N is not transparent and N is not marked | |
Mark N | |
Enqueue N at the back of Q | |
return B | |
''' | |
class Sprite: | |
def __init__(self): | |
self.start_x = -1 | |
self.start_y = -1 | |
self.end_x = -1 | |
self.end_y = -1 | |
def expand (self, point): | |
if (self.start_x < 0 and self.start_y < 0 and self.end_x < 0 and self.end_y < 0): | |
self.start_x = point[0] | |
self.start_y = point[1] | |
self.end_x = point[0] | |
self.end_y = point[1] | |
else: | |
if (point[0] < self.start_x): | |
self.start_x = point[0] | |
if (point[0] > self.end_x): | |
self.end_x = point[0] | |
if (point[1] < self.start_y): | |
self.start_y = point[1] | |
if (point[1] > self.end_y): | |
self.end_y = point[1] | |
def belongs (self, point): | |
result = False | |
result = True | |
result = result and point[0] >= self.start_x and point[0] <= self.end_x | |
result = result and point[1] >= self.start_y and point[1] <= self.end_y | |
return result | |
def __str__(self): | |
result = "" | |
result = result + "(" | |
result = result + str(self.start_x) | |
result = result + ", " | |
result = result + str(self.start_y) | |
result = result + ", " | |
result = result + str(self.end_x) | |
result = result + ", " | |
result = result + str(self.end_y) | |
result = result + ")" | |
return result | |
def loadSprite (pos, sprites): | |
result = None | |
for sprite in sprites: | |
if sprite.belongs(pos): | |
result = sprite | |
break | |
return result | |
''' | |
Function ExploreBoundedBox(pStart) | |
Q = new Queue(pStart) | |
B = new BoundingBox(pStart) | |
While Q is not empty | |
Dequeue the front element as P | |
Expand B to include P | |
For each of the four neighbouring pixels N | |
If N is not transparent and N is not marked | |
Mark N | |
Enqueue N at the back of Q | |
return B | |
''' | |
def exploreBoundedBox (pStart, img): | |
result = None | |
q = [] | |
q.append(pStart) | |
result = Sprite() | |
result.expand(pStart) | |
marks = [] | |
while (len(q) > 0): | |
p = q.pop(0) | |
result.expand(p) | |
neighbouring = loadEightNeighbouringPixels(p, img) | |
for n in neighbouring: | |
if img.getpixel(n)[3] > 0 and not n in marks: | |
marks.append(n) | |
q.append(n) | |
return result | |
def loadFourNeighbouringPixels (point, img): | |
result = None | |
result = [] | |
newPoint = (point[0], point[1] - 1) | |
if (newPoint[0] >= 0 and newPoint[1] >= 0 and newPoint[0] < img.width and newPoint[1] < img.height): | |
result.append(newPoint) | |
newPoint = (point[0] - 1, point[1]) | |
if (newPoint[0] >= 0 and newPoint[1] >= 0 and newPoint[0] < img.width and newPoint[1] < img.height): | |
result.append(newPoint) | |
newPoint = (point[0] + 1, point[1]) | |
if (newPoint[0] >= 0 and newPoint[1] >= 0 and newPoint[0] < img.width and newPoint[1] < img.height): | |
result.append(newPoint) | |
newPoint = (point[0], point[1] + 1) | |
if (newPoint[0] >= 0 and newPoint[1] >= 0 and newPoint[0] < img.width and newPoint[1] < img.height): | |
result.append(newPoint) | |
return result | |
def loadEightNeighbouringPixels (point, img): | |
result = None | |
result = [] | |
newPoint = (point[0], point[1] - 1) | |
if (newPoint[0] >= 0 and newPoint[1] >= 0 and newPoint[0] < img.width and newPoint[1] < img.height): | |
result.append(newPoint) | |
newPoint = (point[0] - 1, point[1]) | |
if (newPoint[0] >= 0 and newPoint[1] >= 0 and newPoint[0] < img.width and newPoint[1] < img.height): | |
result.append(newPoint) | |
newPoint = (point[0] + 1, point[1]) | |
if (newPoint[0] >= 0 and newPoint[1] >= 0 and newPoint[0] < img.width and newPoint[1] < img.height): | |
result.append(newPoint) | |
newPoint = (point[0], point[1] + 1) | |
if (newPoint[0] >= 0 and newPoint[1] >= 0 and newPoint[0] < img.width and newPoint[1] < img.height): | |
result.append(newPoint) | |
newPoint = (point[0] - 1, point[1] - 1) | |
if (newPoint[0] >= 0 and newPoint[1] >= 0 and newPoint[0] < img.width and newPoint[1] < img.height): | |
result.append(newPoint) | |
newPoint = (point[0] + 1, point[1] - 1) | |
if (newPoint[0] >= 0 and newPoint[1] >= 0 and newPoint[0] < img.width and newPoint[1] < img.height): | |
result.append(newPoint) | |
newPoint = (point[0] - 1, point[1] + 1) | |
if (newPoint[0] >= 0 and newPoint[1] >= 0 and newPoint[0] < img.width and newPoint[1] < img.height): | |
result.append(newPoint) | |
newPoint = (point[0] + 1, point[1] + 1) | |
if (newPoint[0] >= 0 and newPoint[1] >= 0 and newPoint[0] < img.width and newPoint[1] < img.height): | |
result.append(newPoint) | |
return result | |
MINIMUM_SPRITE = 8 | |
def firstNonSprites (sprites): | |
result = None | |
for sprite in sprites: | |
if (sprite.end_x - sprite.start_x + 1) < MINIMUM_SPRITE or (sprite.end_y - sprite.start_y + 1) < MINIMUM_SPRITE: | |
result = sprite | |
break | |
return result | |
def mergeSprites (sprite1, sprite2): | |
result = None | |
if (sprite1 != None and sprite2 != None): | |
result = Sprite() | |
result.start_x = min(sprite1.start_x, sprite2.start_x) | |
result.start_y = min(sprite1.start_y, sprite2.start_y) | |
result.end_x = max(sprite1.end_x, sprite2.end_x) | |
result.end_y = max(sprite1.end_y, sprite2.end_y) | |
return result | |
def findNextSprite (pivot, sprites): | |
result = None | |
distance = 99999999 | |
for sprite in sprites: | |
if sprite != pivot: | |
itemDistance = distanceSprites(pivot, sprite) | |
if (itemDistance < distance): | |
distance = itemDistance | |
result = sprite | |
return result | |
#Pitagoras | |
def distancePoints (point1, point2): | |
result = 99999999 | |
if (point1 != None and point2 != None): | |
a = abs(point2[0] - point1[0]) | |
b = abs(point2[1] - point1[1]) | |
result = math.sqrt(math.pow(a, 2) + math.pow(b, 2)) | |
return result | |
def distancePointSprite (point, sprite): | |
result = 99999999 | |
if (point != None and sprite != None): | |
distance = distancePoints(point, (sprite.start_x, sprite.start_y)) | |
if (distance < result): | |
result = distance | |
distance = distancePoints(point, (sprite.end_x, sprite.start_y)) | |
if (distance < result): | |
result = distance | |
distance = distancePoints(point, (sprite.start_x, sprite.end_y)) | |
if (distance < result): | |
result = distance | |
distance = distancePoints(point, (sprite.end_x, sprite.end_y)) | |
if (distance < result): | |
result = distance | |
return result | |
def distanceSprites (sprite1, sprite2): | |
result = 99999999 | |
if (sprite1 != None and sprite2 != None): | |
distance = distancePointSprite((sprite1.start_x, sprite1.start_y), sprite2) | |
if (distance < result): | |
result = distance | |
distance = distancePointSprite((sprite1.end_x, sprite1.start_y), sprite2) | |
if (distance < result): | |
result = distance | |
distance = distancePointSprite((sprite1.start_x, sprite1.end_y), sprite2) | |
if (distance < result): | |
result = distance | |
distance = distancePointSprite((sprite1.end_x, sprite1.end_y), sprite2) | |
if (distance < result): | |
result = distance | |
return result | |
def fixMergeSprites (sprites): | |
result = [] | |
pivotNonSprite = firstNonSprites(sprites) | |
while (pivotNonSprite != None): | |
nextSprite = findNextSprite(pivotNonSprite, sprites) | |
if nextSprite == None: | |
break | |
mergeSprite = mergeSprites(pivotNonSprite, nextSprite) | |
sprites.remove(nextSprite) | |
sprites.remove(pivotNonSprite) | |
sprites.append(mergeSprite) | |
pivotNonSprite = firstNonSprites(sprites) | |
result = sprites | |
return result | |
im = Image.open("test.png") | |
print(im.format, im.size, im.mode) | |
#PNG (640, 252) RGBA | |
#im.show() | |
print("width = " + str(im.width)) | |
print("height = " + str(im.height)) | |
sprites = [] | |
for y in range(im.height): | |
for x in range(im.width): | |
pixel = im.getpixel((x, y)) | |
haycolor = True if pixel[3] > 0 else False | |
if (haycolor): | |
pos = (x, y) | |
#print("(" + str(x) + ", " + str(y) + ") -> " + str(pixel)) | |
pixelP = pixel | |
sprite = loadSprite(pos, sprites) | |
if (sprite != None): | |
x = sprite.end_x | |
else: | |
sprite = exploreBoundedBox(pos, im) | |
sprites.append(sprite) | |
sprites = fixMergeSprites(sprites) | |
print("sprites") | |
print(str(sprites)) | |
idx = 1 | |
for sprite in sprites: | |
print("sprite " + str(idx) + ". -> " + str(sprite)) | |
imSprite = im.crop((sprite.start_x, sprite.start_y, sprite.end_x + 1, sprite.end_y + 1)) | |
#imSprite.show() | |
imSprite.save("sprite" + str(idx) + ".png") | |
idx += 1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment