Skip to content

Instantly share code, notes, and snippets.

@delimitry
Last active November 2, 2015 07:29
Show Gist options
  • Save delimitry/d306a5d3edd749c20c57 to your computer and use it in GitHub Desktop.
Save delimitry/d306a5d3edd749c20c57 to your computer and use it in GitHub Desktop.
Spritesheet image cutter
#!/usr/bin/env python
#-*- coding: utf-8 -*-
#-----------------------------------------------------------------------
# Author: delimitry
#-----------------------------------------------------------------------
import os
import sys
from PIL import Image
from argparse import ArgumentParser
def find_horizontal_borders(image, border_color):
"""
Find horizontal border lines with defined color
"""
w, h = image.size
pixels = image.load()
horizontals = []
for j in xrange(h):
for i in xrange(w):
if pixels[i, j] != border_color:
break
else:
horizontals.append(j)
return horizontals
def find_vertial_borders(image, border_color):
"""
Find vertical border lines with defined color
"""
w, h = image.size
pixels = image.load()
verticals = []
for i in xrange(w):
for j in xrange(h):
if pixels[i, j] != border_color:
break
else:
verticals.append(i)
return verticals
def find_areas(image, border_color):
"""
Find sprite areas and return a list of rectangles (x, y, w, h).
"""
w, h = image.size
horizontals = find_horizontal_borders(image, border_color)
areas = []
prev_horizontal = 0
for h in horizontals:
if h - prev_horizontal > 1:
verticals = find_vertial_borders(
image.crop((0, prev_horizontal + 1, w, prev_horizontal + (h - prev_horizontal))), border_color)
prev_vertical = 0
for v in verticals:
if v - prev_vertical > 1:
areas.append(
(prev_vertical + 1, prev_horizontal + 1, v - prev_vertical - 1, h - prev_horizontal - 1))
prev_vertical = v
prev_horizontal = h
return areas
def str_to_color(value):
"""
Convert str value to color (tuple of ints)
"""
try:
if ',' not in value or value.count(',') not in (2, 3):
raise
color = tuple([int(x) for x in value.split(',')])
for c in color:
if not 0 <= c <= 255:
raise
return color
except:
raise Exception('Invalid format of color. Must be (R,G,B) or (R,G,B,A), '
'where RGBA channels must be in [0..255]')
def cut_and_save_sprites(image, areas, output_folder):
"""
Cut sprites from image using found areas and save to output folder, grouping them by size
"""
if not os.path.exists(output_folder):
os.makedirs(output_folder)
if not areas:
return
group = 0
index = 0
# get [w, h] from first area (x, y, w, h)
prev_size = areas[0][2:4]
for area in areas:
# assume that a new group starts when a new size is found
if area[2:4] != prev_size:
prev_size = area[2:4]
group += 1
index = 0
image.crop((area[0], area[1], area[0] + area[2] - 0, area[1] + area[3] - 0)).save(
os.path.join(output_folder, 'group_%02d_index_%03d.png' % (group, index)))
index += 1
def main():
# prepare arguments parser
parser = ArgumentParser(usage='%(prog)s [options]', description='Spritesheet image cutter')
parser.add_argument('-i', '--input', dest='input', help='spritesheet image')
parser.add_argument('-c', '--color', dest='color',
help='border RGB/RGBA color (e.g.: 255,0,128 or 0,0,0,255), by default color of top left pixel', default='')
parser.add_argument('-o', '--output', dest='output',
help='output sprites folder, by default current folder', default='./')
parser.add_argument('-v', '--version', action='version', version='Spritesheet image cutter v0.1')
args = parser.parse_args()
if not args.input:
parser.print_help()
sys.exit()
image = Image.open(args.input)
left_top_color = image.getpixel((0, 0))
if args.color:
try:
border_color = str_to_color(args.color)
except Exception as ex:
sys.exit(ex)
else:
# get top left pixel color if not color passed
border_color = left_top_color
# check color channels
if len(border_color) != len(left_top_color):
print 'Warning: Image color channels number is %d, but you have passed %d.' % (
len(left_top_color), len(border_color))
sys.exit()
areas = find_areas(image, border_color)
if not areas:
print 'No sprites were found.'
sys.exit()
print 'Saving %s sprites to "%s"' % (len(areas), os.path.abspath(args.output))
cut_and_save_sprites(image, areas, args.output)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment