Skip to content

Instantly share code, notes, and snippets.

@tuaplicacionpropia
Created February 28, 2018 20:55
Show Gist options
  • Save tuaplicacionpropia/f5bd6b0f69a11141767387eb789f5093 to your computer and use it in GitHub Desktop.
Save tuaplicacionpropia/f5bd6b0f69a11141767387eb789f5093 to your computer and use it in GitHub Desktop.
Sprite Sheet Detect Individual Sprite Bounds Automatically
#!/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