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

URL

#!/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

从 sprite_sheet 坐标创建纹理

TextureMerger1.6.6 二:Sprite Sheet的制作和使用

咸鱼教程TextureMerger1.6.6 二:Sprite Sheet的制作和使用

unity2d sprite sheet 和 sprite packer 有啥区别?

带有 HTML 5 画布的 Sprite-Sheet BLIT?