Skip to content

Commit

Permalink
tool to download all the tiles for a given slide
Browse files Browse the repository at this point in the history
  • Loading branch information
lucalianas committed Dec 7, 2016
1 parent 80bdde4 commit 8b6a780
Showing 1 changed file with 147 additions and 0 deletions.
147 changes: 147 additions & 0 deletions tools/image_tiles_downloader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
from cStringIO import StringIO
from PIL import Image
from requests import Session
import xml.etree.ElementTree as ET
import sys
import os
from urlparse import urljoin
import logging
from argparse import ArgumentParser
import math


class ImageTilesDownloader(object):

def __init__(self, image_id, mirax_image, ome_base_url, output_folder=None,
log_level='INFO', log_file=None):
self.logger = self.get_logger(log_level, log_file)
self.image_id = image_id
if mirax_image:
self.get_dzi_url = urljoin(ome_base_url, 'mirax/deepzoom/get/%s.dzi' % self.image_id)
self.get_tile_pattern = urljoin(
ome_base_url, 'mirax/deepzoom/get/%s_files' % self.image_id
)
self.get_tile_pattern = urljoin(self.get_tile_pattern, '%s/%s_%s.%s')
self.logger.info(self.get_tile_pattern)
else:
self.get_dzi_url = urljoin(ome_base_url, 'deepzoom/get/%s.dzi' % self.image_id)
self.get_tile_pattern = urljoin(
ome_base_url, 'deepzoom/get/%s_files/' % self.image_id
)
self.get_tile_pattern = urljoin(self.get_tile_pattern, '%s/%s_%s.%s')
self.logger.info(self.get_tile_pattern)
self.output_folder = output_folder
if self.output_folder:
try:
os.makedirs(self.output_folder)
self.logger.info('Output directory %s created' % self.output_folder)
except OSError:
self.logger.info('Using existing directory %s' % self.output_folder)
self.session = Session()

def get_logger(self, log_level='INFO', log_file=None, mode='a'):
LOG_FORMAT = '%(asctime)s|%(levelname)-8s|%(message)s'
LOG_DATEFMT = '%Y-%m-%d %H:%M:%S'

logger = logging.getLogger('tiler_downloader')
if not isinstance(log_level, int):
try:
log_level = getattr(logging, log_level)
except AttributeError:
raise ValueError('Unsupported literal log level: %s' % log_level)
logger.setLevel(log_level)
logger.handlers = []
if log_file:
handler = logging.FileHandler(log_file, mode=mode)
else:
handler = logging.StreamHandler()
formatter = logging.Formatter(LOG_FORMAT, datefmt=LOG_DATEFMT)
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger

def _get_image_infos(self):
response = self.session.get(self.get_dzi_url)
if response.status_code == 200:
dzi = ET.fromstring(response.text)
self.logger.debug(response.text)
return {
'format': dzi.get('Format'),
'tile_size': int(dzi.get('TileSize')),
'width': int(dzi.getchildren()[0].get('Width')),
'height': int(dzi.getchildren()[0].get('Height'))
}
else:
self.logger.error(response)

def _get_max_zoom_level(self, img_height, img_width):
return int(math.ceil(math.log(max(img_height, img_width), 2)))

def _get_scale_factor(self, level, max_level):
return math.pow(0.5, max_level - level)

def _get_scaled_dimension(self, img_width, img_height, level, max_level):
scale_factor = self._get_scale_factor(level, max_level)
return {
'width': int(math.ceil(img_width * scale_factor)),
'height': int(math.ceil(img_height * scale_factor))
}

def _save_tile(self, image_data, output_folder, file_name):
with open(os.path.join(output_folder, file_name), 'w') as ofile:
img = Image.open(StringIO(image_data))
img.save(ofile)

def _get_tiles(self, level, scaled_width, scaled_height, tile_size, tile_format):
if self.output_folder:
tiles_output_folder = os.path.join(self.output_folder, 'level_%d' % level)
try:
os.makedirs(tiles_output_folder)
except OSError:
pass
for x in xrange(scaled_width / tile_size):
for y in xrange(scaled_height / tile_size):
tr = self.session.get(self.get_tile_pattern % (level, x, y, tile_format))
if self.output_folder:
self._save_tile(tr.content, tiles_output_folder, '%d_%d.%s' % (x, y, tile_format))

def run(self):
image_infos = self._get_image_infos()
max_level = self._get_max_zoom_level(image_infos['height'], image_infos['width'])
self.logger.info('MAX LEVEL: %d' % max_level)
for x in xrange(max_level + 1):
self.logger.info('### Level %d ###' % x)
scaled_dimensions = self._get_scaled_dimension(image_infos['width'], image_infos['height'],
x, max_level)
self.logger.info('Scales dimensions %r' % scaled_dimensions)
self._get_tiles(x, scaled_dimensions['width'], scaled_dimensions['height'],
image_infos['tile_size'], image_infos['format'])



def get_parser():
parser = ArgumentParser('Download tiles for a given image and optionally save them on file system')
parser.add_argument('--image-id', type=str, required=True,
help='The ID of the image that will be downloaded')
parser.add_argument('--mirax', action='store_true',
help='Add this flag to fetch a MIRAX file')
parser.add_argument('--ome-base-url', type=str, required=True,
help='the base URL of the OMERO.web server')
parser.add_argument('--output-dir', type=str, default=None,
help='output folder where the tiles will be saved')
parser.add_argument('--log-level', type=str, default='INFO',
help='log level (default=INFO)')
parser.add_argument('--log-file', type=str, default=None,
help='log file (default=stderr)')
return parser


def main(argv):
parser = get_parser()
args = parser.parse_args(argv)
downloader = ImageTilesDownloader(args.image_id, args.mirax, args.ome_base_url,
args.output_dir, args.log_level, args.log_file)
downloader.run()

if __name__ == '__main__':
main(sys.argv[1:])

0 comments on commit 8b6a780

Please sign in to comment.