def main(): if (sys.version_info < (2, 6)): sys.exit("This program requires python version 2.6 or later") # Options and arguments p = optparse.OptionParser( description='IIIF Image API static file generator', usage='usage: %prog [options] file|path (-h for help)', version='%prog ' + __version__) p.add_option('--dst', '-d', action='store', default='/tmp', help="destination directory [/tmp default]") p.add_option('--tilesize', '-t', action='store', type='int', default=512, help="tilesize in pixels [512 default]") p.add_option('--api-version', '--api', '-a', action='store', default='1.1', help="API version, may be 1.1 [default] or 2.0") p.add_option('--dryrun', '-n', action='store_true', help="do not write anything, say what would be done") p.add_option('--verbose', '-v', action='store_true', help="verbose") (args, sources) = p.parse_args() logging.basicConfig(format='%(name)s: %(message)s', level=(logging.INFO if (args.verbose) else logging.WARNING)) logger = logging.getLogger(os.path.basename(__file__)) if (len(sources) == 0): logger.warn("no sources specified, nothing to do, bye! (-h for help)") else: for source in sources: # File or directory (or neither)? sg = IIIFStatic(dst=args.dst, tilesize=args.tilesize, api_version=args.api_version, dryrun=args.dryrun) if (os.path.isfile(source)): logger.info("source file: %s" % (source)) sg.generate(source) elif (os.path.isdir(source)): raise Exception("source dir: %s - FIXME: not yet supported" % (source)) else: logger.warn("ignoring source '%s': neither file nor path" % (source))
def test02_generate(self): tmp = tempfile.mkdtemp() try: s=IIIFStatic( src='testimages/starfish_1500x2000.png', dst=tmp, tilesize=1024, api_version='1', dryrun=True ) with capture_stdout() as capturer: s.generate(identifier='a') self.assertTrue( re.search(' / a/1024,1024,476,976/476,976/0/native.jpg', capturer.result )) self.assertTrue( re.search(' / a/full/1,1/0/native.jpg', capturer.result )) finally: shutil.rmtree(tmp)
def generate_deep_zoom(self, jekyll_site_dir): self.log_status('Downloading deep zoom images') imagedir = os.path.join(jekyll_site_dir, self.image_dir) # If exporting to GitHub, add the repo name to the path. _prefix = os.path.join(os.sep, self.github_repo, self.image_dir) if self.github_repo else os.path.join(os.sep, self.image_dir) staticgen = IIIFStatic(dst=imagedir, prefix=_prefix) for teipage in self.tei.page_list: for graphic in teipage.graphics: if graphic.rend == 'full': imgsrc = os.path.join(jekyll_site_dir, graphic.url) iiif_img = IIIFImageClient.init_from_url(graphic.url) staticgen.generate(imgsrc, identifier=iiif_img.image_id)
def handler(event, context): for record in event['Records']: bucket = record['s3']['bucket']['name'] key = record['s3']['object']['key'] inbound = '/tmp/inbound/%s' % key outbound = '/tmp/outbound/%s' % key s3_client.download_file(bucket, key, inbound) # TODO: Add prefix sg = IIIFStatic(dst=outbound, tilesize=512, api_version='2.0') sg.generate(inbound) for dirpath, dirnames, files in os.walk(outbound): for f in files: s3_client.upload_file(os.path.join(dirpath, f), destbucket, f)
def generate_deep_zoom(self, jekyll_site_dir): self.log_status('Downloading deep zoom images') imagedir = os.path.join(jekyll_site_dir, self.image_dir) # If exporting to GitHub, add the repo name to the path. _prefix = os.path.join( os.sep, self.github_repo, self.image_dir) if self.github_repo else os.path.join( os.sep, self.image_dir) staticgen = IIIFStatic(dst=imagedir, prefix=_prefix) for teipage in self.tei.page_list: for graphic in teipage.graphics: if graphic.rend == 'full': imgsrc = os.path.join(jekyll_site_dir, graphic.url) iiif_img = IIIFImageClient.init_from_url(graphic.url) staticgen.generate(imgsrc, identifier=iiif_img.image_id)
def test02_generate(self): tmp = tempfile.mkdtemp() try: s = IIIFStatic(src='testimages/starfish_1500x2000.png', dst=tmp, tilesize=1024, api_version='1', dryrun=True) with capture_stdout() as capturer: s.generate(identifier='a') self.assertTrue( re.search(' / a/1024,1024,476,976/476,976/0/native.jpg', capturer.result)) self.assertTrue( re.search(' / a/full/1,1/0/native.jpg', capturer.result)) finally: shutil.rmtree(tmp)
def main(): if (sys.version_info < (2,6)): sys.exit("This program requires python version 2.6 or later") # Options and arguments p = optparse.OptionParser(description='IIIF Image API static file generator', usage='usage: %prog [options] file|path (-h for help)', version='%prog '+__version__ ) p.add_option('--dst', '-d', action='store', default='/tmp', help="destination directory [/tmp default]") p.add_option('--tilesize', '-t', action='store', type='int', default=512, help="tilesize in pixels [512 default]") p.add_option('--api-version', '--api','-a', action='store', default='1.1', help="API version, may be 1.1 [default] or 2.0") p.add_option('--dryrun', '-n', action='store_true', help="do not write anything, say what would be done") p.add_option('--verbose', '-v', action='store_true', help="verbose") (args, sources) = p.parse_args() logging.basicConfig( format='%(name)s: %(message)s', level=( logging.INFO if (args.verbose) else logging.WARNING ) ) logger = logging.getLogger(os.path.basename(__file__)) if (len(sources)==0): logger.warn("no sources specified, nothing to do, bye! (-h for help)") else: for source in sources: # File or directory (or neither)? sg = IIIFStatic( dst=args.dst, tilesize=args.tilesize, api_version=args.api_version, dryrun=args.dryrun ) if (os.path.isfile(source)): logger.info("source file: %s" % (source)) sg.generate(source) elif (os.path.isdir(source)): raise Exception("source dir: %s - FIXME: not yet supported" % (source)) else: logger.warn("ignoring source '%s': neither file nor path" % (source))
def test03_generate(self): """Test generation of a static image.""" # dryrun covers most tmp1 = tempfile.mkdtemp() os.mkdir(os.path.join(tmp1, 'a')) try: # no canonical syntax with osd_version='1.0.0' s = IIIFStatic(dst=tmp1, tilesize=512, api_version='1.1', osd_version='1.0.0', dryrun=True) with MyLogCapture('iiif.static') as lc: s.generate(src='testimages/starfish_1500x2000.png', identifier='a') self.assertTrue(re.search(' / a/info.json', lc.all_msgs)) self.assertTrue( re.search(' / a/1024,1536,476,464/476,464/0/native.jpg', lc.all_msgs)) # largest full region (and symlink from w,h) self.assertTrue( re.search(' / a/full/375,500/0/native.jpg', lc.all_msgs)) # smallest full region self.assertTrue( re.search(' / a/full/1,1/0/native.jpg', lc.all_msgs)) # v2.0 s = IIIFStatic(dst=tmp1, tilesize=512, api_version='2.0', dryrun=True) with MyLogCapture('iiif.static') as lc: s.generate(src='testimages/starfish_1500x2000.png', identifier='a') self.assertTrue(re.search(' / a/info.json', lc.all_msgs)) self.assertTrue( re.search(' / a/1024,1536,476,464/476,/0/default.jpg', lc.all_msgs)) # largest full region (and symlink from w,h) self.assertTrue( re.search(' / a/full/375,/0/default.jpg', lc.all_msgs)) self.assertTrue( re.search(' / a/full/375,500 -> a/full/375,', lc.all_msgs)) # smallest full region self.assertTrue( re.search(' / a/full/1,/0/default.jpg', lc.all_msgs)) self.assertTrue( re.search(' / a/full/1,1 -> a/full/1,', lc.all_msgs)) finally: shutil.rmtree(tmp1) # real write tmp2 = tempfile.mkdtemp() try: s = IIIFStatic(dst=tmp2, tilesize=1024, api_version='2.0') with MyLogCapture('iiif.static') as lc: s.generate(src='testimages/starfish_1500x2000.png', identifier='b') self.assertTrue(os.path.isfile(os.path.join(tmp2, 'b/info.json'))) self.assertTrue(os.path.isfile(os.path.join( tmp2, 'b/1024,1024,476,976/476,/0/default.jpg'))) self.assertTrue(os.path.isfile( os.path.join(tmp2, 'b/full/1,/0/default.jpg'))) finally: shutil.rmtree(tmp2)
def test02_generate(self): # dryrun covers most tmp1 = tempfile.mkdtemp() os.mkdir( os.path.join(tmp1,'a') ) try: s=IIIFStatic( dst=tmp1, tilesize=512, api_version='1.1', dryrun=True ) with capture_stdout() as capturer: s.generate( src='testimages/starfish_1500x2000.png', identifier='a' ) self.assertTrue( re.search(' / a/info.json', capturer.result )) self.assertTrue( re.search(' / a/1024,1536,476,464/476,/0/native.jpg', capturer.result )) self.assertTrue( re.search(' / a/full/1,/0/native.jpg', capturer.result )) finally: shutil.rmtree(tmp1) # real write tmp2 = tempfile.mkdtemp() try: s=IIIFStatic( dst=tmp2, tilesize=1024, api_version='2.0' ) with capture_stdout() as capturer: s.generate( src='testimages/starfish_1500x2000.png', identifier='b' ) self.assertTrue( os.path.isfile(os.path.join(tmp2,'b/info.json')) ) self.assertTrue( os.path.isfile(os.path.join(tmp2,'b/1024,1024,476,976/476,/0/default.jpg')) ) self.assertTrue( os.path.isfile(os.path.join(tmp2,'b/full/1,/0/default.jpg')) ) finally: shutil.rmtree(tmp2)
def main(): """Parse arguments, instantiate IIIFStatic, run.""" if (sys.version_info < (2, 6)): sys.exit("This program requires python version 2.6 or later") # Options and arguments p = optparse.OptionParser( description='IIIF Image API static file generator', usage='usage: %prog [options] file [[file2..]] (-h for help)', version='%prog ' + __version__) p.add_option('--dst', '-d', action='store', default='/tmp', help="Destination directory for images [default '%default']") p.add_option('--tilesize', '-t', action='store', type='int', default=512, help="Tilesize in pixels [default %default]") p.add_option('--api-version', '--api', '-a', action='store', default='2.1', help="API version, may be 1.1, 2.0 or 2.1 [default %default]") p.add_option( '--prefix', '-p', action='store', default=None, help= "URI prefix for where the images will be served from (default '%default'). " "An empty prefix may be OK if the HTML page including the image shares the " "the same root on the same server as the images, otherwise a full URL should " "be specified. This is used to construct the @id in the info.json") p.add_option( '--identifier', '-i', action='store', default=None, help= "Identifier for the image that will be used in place of the filename " "(minus extension). Notes that this option cannot be used if more than " "one image file is to be processed") p.add_option( '--write-html', action='store', default=None, help= "Write HTML page to the specified directory using the 'identifier.html' " "as the filename. HTML will launch OpenSeadragon for this image and to " "display some of information about info.json and tile locations. HTML will " "assume OpenSeadragon at relative path openseadragonVVV/openseadragon.min.js " "and user-interface icons in openseadragonVVV/images, where VVV are the " "three parts of the version number. The --include-osd flag is also specified " "then OpenSeadragon will be copied to these locations") p.add_option('--include-osd', action='store_true', help="Include OpenSeadragon files with --write-html flag") p.add_option( '--osd-version', action='store', default='2.0.0', help= "Generate static images for older versions of OpenSeadragon. Use of versions " "prior to 1.2.1 will force use of /w,h/ for size parameter instead of /w,/. " "Likely useful only in combination with --api-version=1.1 " "[default %default]") p.add_option( '--osd-width', action='store', type='int', default='500', help="Width of OpenSeadragon pane in pixels. Applies only with " "--write-html [default %default]") p.add_option( '--osd-height', action='store', type='int', default='500', help="Height of OpenSeadragon pane in pixels. Applies only with " "--write-html [default %default]") p.add_option( '--generator', action='store_true', default=False, help="Use named generator modules in iiif.generators package instead " "of a starting image [default %default]") p.add_option( '--max-image-pixels', action='store', type='int', default=0, help="Set the maximum number of pixels in an image. A non-zero value " "will set a hard limit on the image size. If left unset then the " "default configuration of the Python Image Libary (PIL) will give " "a DecompressionBombWarning if the image size exceeds a default " "maximum, but otherwise continue as normal") p.add_option('--dryrun', '-n', action='store_true', help="Do not write anything, say what would be done") p.add_option('--quiet', '-q', action='store_true', help="Quite (no output unless there is a warning/error)") p.add_option('--verbose', '-v', action='store_true', help="Verbose") (opt, sources) = p.parse_args() level = logging.DEBUG if (opt.verbose) else \ logging.WARNING if (opt.quiet) else logging.INFO logging.basicConfig(format='%(name)s: %(message)s', level=level) logger = logging.getLogger(os.path.basename(__file__)) if (not opt.write_html and opt.include_osd): logger.warn( "--include-osd has no effect without --write-html, ignoring") if (len(sources) == 0): logger.warn("No sources specified, nothing to do, bye! (-h for help)") elif (len(sources) > 1 and opt.identifier): logger.error( "Cannot use --identifier/-i option with multiple sources, aborting." ) else: try: sg = IIIFStatic(dst=opt.dst, tilesize=opt.tilesize, api_version=opt.api_version, dryrun=opt.dryrun, prefix=opt.prefix, osd_version=opt.osd_version, generator=opt.generator, max_image_pixels=opt.max_image_pixels) for source in sources: # File or directory (or neither)? if (os.path.isfile(source) or opt.generator): logger.info("source file: %s" % (source)) sg.generate(source, identifier=opt.identifier) if (opt.write_html): sg.write_html(html_dir=opt.write_html, include_osd=opt.include_osd, osd_width=opt.osd_width, osd_height=opt.osd_height) elif (os.path.isdir(source)): logger.warn( "Ignoring source '%s': directory coversion not supported" % (source)) else: logger.warn("Ignoring source '%s': neither file nor path" % (source)) except (IIIFStaticError, IIIFError) as e: # catch known errors and report nicely... logger.error("Error: " + str(e))
def main(): """Parse arguments, instantiate IIIFStatic, run.""" if (sys.version_info < (2, 6)): sys.exit("This program requires python version 2.6 or later") # Options and arguments p = optparse.OptionParser(description='IIIF Image API static file generator', usage='usage: %prog [options] file [[file2..]] (-h for help)', version='%prog ' + __version__) p.add_option('--dst', '-d', action='store', default='/tmp', help="Destination directory for images [default '%default']") p.add_option('--tilesize', '-t', action='store', type='int', default=512, help="Tilesize in pixels [default %default]") p.add_option('--api-version', '--api', '-a', action='store', default='2.1', help="API version, may be 1.1, 2.0 or 2.1 [default %default]") p.add_option('--prefix', '-p', action='store', default=None, help="URI prefix for where the images will be served from (default '%default'). " "An empty prefix may be OK if the HTML page including the image shares the " "the same root on the same server as the images, otherwise a full URL should " "be specified. This is used to construct the @id in the info.json") p.add_option('--identifier', '-i', action='store', default=None, help="Identifier for the image that will be used in place of the filename " "(minus extension). Notes that this option cannot be used if more than " "one image file is to be processed") p.add_option('--write-html', action='store', default=None, help="Write HTML page to the specified directory using the 'identifier.html' " "as the filename. HTML will launch OpenSeadragon for this image and to " "display some of information about info.json and tile locations. HTML will " "assume OpenSeadragon at relative path openseadragonVVV/openseadragon.min.js " "and user-interface icons in openseadragonVVV/images, where VVV are the " "three parts of the version number. The --include-osd flag is also specified " "then OpenSeadragon will be copied to these locations") p.add_option('--include-osd', action='store_true', help="Include OpenSeadragon files with --write-html flag") p.add_option('--osd-version', action='store', default='2.0.0', help="Generate static images for older versions of OpenSeadragon. Use of versions " "prior to 1.2.1 will force use of /w,h/ for size parameter instead of /w,/. " "Likely useful only in combination with --api-version=1.1 " "[default %default]") p.add_option('--osd-width', action='store', type='int', default='500', help="Width of OpenSeadragon pane in pixels. Applies only with " "--write-html [default %default]") p.add_option('--osd-height', action='store', type='int', default='500', help="Height of OpenSeadragon pane in pixels. Applies only with " "--write-html [default %default]") p.add_option('--generator', action='store_true', default=False, help="Use named generator modules in iiif.generators package instead " "of a starting image [default %default]") p.add_option('--max-image-pixels', action='store', type='int', default=0, help="Set the maximum number of pixels in an image. A non-zero value " "will set a hard limit on the image size. If left unset then the " "default configuration of the Python Image Libary (PIL) will give " "a DecompressionBombWarning if the image size exceeds a default " "maximum, but otherwise continue as normal") p.add_option('--dryrun', '-n', action='store_true', help="Do not write anything, say what would be done") p.add_option('--quiet', '-q', action='store_true', help="Quite (no output unless there is a warning/error)") p.add_option('--verbose', '-v', action='store_true', help="Verbose") (opt, sources) = p.parse_args() level = logging.DEBUG if (opt.verbose) else \ logging.WARNING if (opt.quiet) else logging.INFO logging.basicConfig(format='%(name)s: %(message)s', level=level) logger = logging.getLogger(os.path.basename(__file__)) if (not opt.write_html and opt.include_osd): logger.warn( "--include-osd has no effect without --write-html, ignoring") if (len(sources) == 0): logger.warn("No sources specified, nothing to do, bye! (-h for help)") elif (len(sources) > 1 and opt.identifier): logger.error( "Cannot use --identifier/-i option with multiple sources, aborting.") else: try: sg = IIIFStatic(dst=opt.dst, tilesize=opt.tilesize, api_version=opt.api_version, dryrun=opt.dryrun, prefix=opt.prefix, osd_version=opt.osd_version, generator=opt.generator, max_image_pixels=opt.max_image_pixels) for source in sources: # File or directory (or neither)? if (os.path.isfile(source) or opt.generator): logger.info("source file: %s" % (source)) sg.generate(source, identifier=opt.identifier) if (opt.write_html): sg.write_html(html_dir=opt.write_html, include_osd=opt.include_osd, osd_width=opt.osd_width, osd_height=opt.osd_height) elif (os.path.isdir(source)): logger.warn( "Ignoring source '%s': directory coversion not supported" % (source)) else: logger.warn( "Ignoring source '%s': neither file nor path" % (source)) except (IIIFStaticError, IIIFError) as e: # catch known errors and report nicely... logger.error("Error: " + str(e))
def main(): """Parse arguments, instantiate IIIFStatic, run.""" if sys.version_info < (2, 6): sys.exit("This program requires python version 2.6 or later") # Options and arguments p = optparse.OptionParser( description="IIIF Image API static file generator", usage="usage: %prog [options] file [[file2..]] (-h for help)", version="%prog " + __version__, ) p.add_option("--dst", "-d", action="store", default="/tmp", help="destination directory [default '%default']") p.add_option( "--tilesize", "-t", action="store", type="int", default=512, help="tilesize in pixels [default %default]" ) p.add_option( "--api-version", "--api", "-a", action="store", default="2.0", help="API version, may be 1.1 or 2.0 [default %default]", ) p.add_option( "--prefix", "-p", action="store", default=None, help="URI prefix for where the images will be served from (default '%default'). " "An empty prefix may be OK if the HTML page including the image shares the " "the same root on the same server as the images, otherwise a full URL should " "be specified. This is used to construct the @id in the info.json", ) p.add_option( "--identifier", "-i", action="store", default=None, help="Identifier for the image that will be used in place of the filename " "(minus extension). Notes that this option cannot be used if more than " "one image file is to be processed", ) p.add_option( "--write-html", action="store", default=None, help="Write HTML page to the specified directory using the 'identifier.html' " "as the filename. HTML will launch OpenSeadragon for this image and to " "display some of information about info.json and tile locations. HTML will " "assume OpenSeadragon at relative path osd/openseadragon.min.js and interface " "icons in osd/images. The --include-osd flag is also specified then " "OpenSeadragon will be copied to these locations", ) p.add_option("--include-osd", action="store_true", help="Include OpenSeadragon files with --write-html flag") p.add_option("--dryrun", "-n", action="store_true", help="do not write anything, say what would be done") p.add_option("--verbose", "-v", action="store_true", help="verbose") (opt, sources) = p.parse_args() logging.basicConfig(format="%(name)s: %(message)s", level=(logging.INFO if (opt.verbose) else logging.WARNING)) logger = logging.getLogger(os.path.basename(__file__)) if len(sources) == 0: logger.warn("No sources specified, nothing to do, bye! (-h for help)") elif len(sources) > 1 and opt.identifier: logger.warn("Cannot use --identifier/-i option with multiple sources, aborting.") else: sg = IIIFStatic( dst=opt.dst, tilesize=opt.tilesize, api_version=opt.api_version, dryrun=opt.dryrun, prefix=opt.prefix ) for source in sources: # File or directory (or neither)? if os.path.isfile(source): logger.info("source file: %s" % (source)) sg.generate(source, identifier=opt.identifier) if opt.write_html: sg.write_html(opt.write_html, opt.include_osd) elif os.path.isdir(source): logger.warn("Ignoring source '%s': directory coversion not supported" % (source)) else: logger.warn("Ignoring source '%s': neither file nor path" % (source))
class ImageProcessor: """Manage generation of IIIF resources from manuscript dictionary.""" _failed = [] def __init__(self, generate_image_pyramid: bool = True): """Inits ImageProcessor and configures manifest factory. :param generate_image_pyramid:if True, pyramid map will be generated. Else, only manifest is generated. """ if not os.path.exists(MANIFEST_OUTPUT_DIR): os.makedirs(MANIFEST_OUTPUT_DIR) self._manifest_factory = ManifestFactory() self._manifest_factory.set_base_prezi_uri(MANIFEST_BASE_URL) self._manifest_factory.set_base_prezi_dir(MANIFEST_OUTPUT_DIR) self._manifest_factory.set_base_image_uri(IMAGE_BASE_URL) self._manifest_factory.set_iiif_image_info(2.0, 1) # Version, ComplianceLevel self._image_reader = ImageReader(IMAGE_SOURCE_DIR) if generate_image_pyramid: self._tile_generator = IIIFStatic(dst=IMAGE_FILE_OUTPUT_DIR, prefix=IMAGE_BASE_URL) self._generate_images = generate_image_pyramid def generate_iiif_resources(self, manuscript_data: Iterable[ManuscriptRow]) -> None: """Generate static IIIF resources for every manuscript record. IIIF resources include image pyramid and manifests. :param manuscript_data:list of dictionaries containing manuscript metadata. :return:None """ for manuscript in manuscript_data: self._process_manuscript(manuscript) if self._failed: print("Errors encountered processing following manuscripts: ") print(*self._failed, sep=", ") def _process_manuscript(self, manuscript: ManuscriptRow) -> None: mhs_number = manuscript.get(ColumnKeys.MHS_NUMBER) # noinspection PyBroadException try: if Path(os.path.join(MANIFEST_OUTPUT_DIR, f"{mhs_number}.json")).is_file(): print(f"{mhs_number} already processed. Skipping") return manifest = self._create_manifest(manuscript) self._add_canvases(manuscript, manifest) manifest.toFile(compact=False) except Exception as e: print(f"**Error processing {mhs_number}. {e}") self._failed.append(mhs_number) def _create_manifest(self, manuscript: ManuscriptRow) -> Manifest: mhs_number = manuscript.get(ColumnKeys.MHS_NUMBER) alternative_name = manuscript.get(ColumnKeys.ALTERNATIVE_NAME) print(f"creating manifest for {mhs_number}") manifest = self._manifest_factory.manifest(label=f"{mhs_number} - {alternative_name}", ident=mhs_number) # add all non-empty fields as metadata (excluding "No" field as this is just internal id) manifest.set_metadata({k: v for (k, v) in manuscript.items() if v and k != ColumnKeys.NO and k != ColumnKeys.COMMENTS}) manifest.description = manuscript.get(ColumnKeys.COMMENTS) return manifest def _add_canvases(self, manuscript: ManuscriptRow, manifest: Manifest) -> None: manuscript_images = self._image_reader.get_files_for_manuscript(manuscript) mhs_number = manuscript.get(ColumnKeys.MHS_NUMBER) image_count = len(manuscript_images) print(f"creating {image_count} canvases for {mhs_number}..") seq = manifest.sequence() for p in range(image_count): image_id = f"{mhs_number}-{p}" start = time.time() print(f"processing {image_id}..") cvs = seq.canvas(ident=image_id, label=f"Page {p}") # Create an annotation on the Canvas annotation = cvs.annotation(ident=f"page-{p}") # set source of image data img = annotation.image(image_id, iiif=True) # Set image height and width, and canvas to same dimensions image_file = manuscript_images[p] img.set_hw_from_file(image_file) cvs.height = img.height cvs.width = img.width self._generate_image_pyramid(image_file, image_id, img.width, img.height) end = time.time() print(f"processed {image_id} in {end - start} secs") def _generate_image_pyramid(self, image_file: str, image_id: str, width: int, height: int) -> None: if not self._generate_images: return self._tile_generator.generate(src=image_file, identifier=image_id) # generate a 90-wide thumb for UV (see: https://github.com/UniversalViewer/universalviewer/issues/102) self._tile_generator.generate_tile("full", [90, None]) # generate a 1000-wide /full/ image for UV download h = 1000 / (width / height) self._tile_generator.generate_tile("full", [1000, h]) # generate a max-width /full/ image for UV download self._tile_generator.generate_tile("full", [width, None])