Skip to content

Instantly share code, notes, and snippets.

@zhuyifei1999
Created September 21, 2018 01:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zhuyifei1999/0213cfcb369f53e83d3d83da325b876a to your computer and use it in GitHub Desktop.
Save zhuyifei1999/0213cfcb369f53e83d3d83da325b876a to your computer and use it in GitHub Desktop.
#! /usr/bin/python
# -*- coding: UTF-8 -*-
#
# Tile merger
#
# Copyright (c) 2018 Zhuyifei1999
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
import argparse
import collections
import itertools
import logging
import os
from PythonMagick import Image, Geometry, CompositeOperator
logger = logging.getLogger('merge')
Dimensions = collections.namedtuple('Dimensions', 'x y') # width height
def get_tile(source, x, y):
path = os.path.join(source, f'{x}-{y}')
if not os.path.isfile(path) or not os.access(path, os.R_OK):
raise IOError(f"File '{path}' doesn't exist, is not a file, "
'or is unreadable')
return path
def merge(source, dest=None, size=None, tilesize=None):
if dest is None:
dest = os.path.join(source, 'merged.png')
if tilesize is None:
zeroth = Image(get_tile(source, 0, 0))
tilesize = (zeroth.columns(), zeroth.rows())
logger.info('Tile size not given, found '
f'{tilesize[0]} x {tilesize[1]}')
tilesize = Dimensions(*tilesize)
if size is None:
for x in itertools.count():
try:
get_tile(source, x, 0)
except IOError:
break
for y in itertools.count():
try:
get_tile(source, 0, y)
except IOError:
break
numtiles = Dimensions(x, y)
logger.warning('Image size not given, found '
f'{numtiles.x} x {numtiles.y} tiles, will autocrop')
else:
size = Dimensions(*size)
numtiles = Dimensions(-(-size.x//tilesize.x), -(-size.y//tilesize.y))
logger.info(f'Number of tiles: {numtiles.x} x {numtiles.y}')
merged = Image(Geometry(
size.x if size else numtiles.x * tilesize.x,
size.y if size else numtiles.y * tilesize.y,
), 'black')
for i in range(numtiles.x):
for j in range(numtiles.y):
merged.composite(
Image(get_tile(source, i, j)),
i * tilesize.x,
j * tilesize.y,
CompositeOperator.AtopCompositeOp
)
logger.info(f'Tile {i}-{j} merged')
if not size:
merged.trim()
logger.warning(f'Autocropped to {merged.columns()} x {merged.rows()}')
logger.info(f'Saving to {dest}')
merged.write(dest)
def main(*args):
parser = argparse.ArgumentParser(description='Tile merger')
parser.add_argument('source', help='source directory')
parser.add_argument('--dest', metavar='destination',
help='destination file name '
'(default: <source>/merged.png)')
parser.add_argument('--size', nargs=2, type=int, metavar=('X', 'Y'),
help='size of an entire image')
parser.add_argument('--tilesize', nargs=2, type=int, metavar=('X', 'Y'),
help='size of a single tile')
parser.add_argument('--verbose', '-v', action='count',
help='increase verbosity')
args = parser.parse_args(args or None)
args = vars(args)
loglevel = 30 - 10*(args.pop('verbose') or 0)
ch = logging.StreamHandler()
ch.setLevel(loglevel)
logger.setLevel(loglevel)
formatter = logging.Formatter(
'%(asctime)s [%(levelname)s] [%(name)s] %(message)s',
'%FT%TZ')
ch.setFormatter(formatter)
logger.addHandler(ch)
merge(**args)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment