Sprite Sheet自动检测单个Sprite界限
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Sprite Sheet自动检测单个Sprite界限相关的知识,希望对你有一定的参考价值。
给出像这样的精灵表:
我想写一个算法,可以遍历像素数据,并确定每个谨慎精灵的边界矩形。
如果我们假设对于每个像素X,Y我可以拉真(像素不是完全透明)或假(像素是完全透明),我将如何自动生成每个精灵的边界矩形?
结果数据应该是具有{x,y,width,height}的矩形对象数组。
这是相同的图像,但前四个精灵的边界用浅蓝色标记:
如上所述,任何人都可以逐步介绍如何检测这些边界吗?
答案
这个怎么样?唯一的缺点是你需要一个可写的图像版本来标记访问过的像素,否则洪水填充永远不会终止。
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
您不需要处理每个扫描线,您可以每隔10个扫描线或每30个扫描线扫描一次。只要它不超过最小精灵高度。
另一答案
使用Pillow在Python上附加实现:
网址:https://gist.github.com/tuaplicacionpropia/f5bd6b0f69a11141767387eb789f5093
#!/usr/bin/env python
#coding:utf-8
from __future__ import print_function
from PIL import Image
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
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
im = Image.open("test2.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)
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
为了避免留下精灵的一小部分,我们必须添加以下改进:
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, sprit以上是关于Sprite Sheet自动检测单个Sprite界限的主要内容,如果未能解决你的问题,请参考以下文章
Sprite Sheet - 在旧 sprite 之上绘制新 sprite
TextureMerger1.6.6 二:Sprite Sheet的制作和使用
咸鱼教程TextureMerger1.6.6 二:Sprite Sheet的制作和使用