Last active
November 2, 2015 07:29
-
-
Save delimitry/d306a5d3edd749c20c57 to your computer and use it in GitHub Desktop.
Spritesheet image cutter
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 -*- | |
#----------------------------------------------------------------------- | |
# 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