def _get_transformed(self, query, format): dst_srs = query.srs src_srs = self._best_supported_srs(dst_srs) dst_bbox = query.bbox src_bbox = dst_srs.transform_bbox_to(src_srs, dst_bbox) src_width, src_height = src_bbox[2]-src_bbox[0], src_bbox[3]-src_bbox[1] ratio = src_width/src_height dst_size = query.size xres, yres = src_width/dst_size[0], src_height/dst_size[1] if xres < yres: src_size = dst_size[0], int(dst_size[0]/ratio + 0.5) else: src_size = int(dst_size[1]*ratio +0.5), dst_size[1] src_query = MapQuery(src_bbox, src_size, src_srs, format) if self.coverage and not self.coverage.contains(src_bbox, src_srs): img = self._get_sub_query(src_query, format) else: resp = self.client.retrieve(src_query, format) img = ImageSource(resp, size=src_size, image_opts=self.image_opts) img = ImageTransformer(src_srs, dst_srs).transform(img, src_bbox, query.size, dst_bbox, self.image_opts) img.format = format return img
def test_output_formats_png24(self): img = Image.new('RGBA', (100, 100)) image_opts = PNG_FORMAT.copy() image_opts.colors = 0 # TODO image_opts ir = ImageSource(img, image_opts=image_opts) img = Image.open(ir.as_buffer()) eq_(img.mode, 'RGBA') assert img.getpixel((0, 0)) == (0, 0, 0, 0)
def test_save_with_unsupported_transparency(self): # check if encoding of non-RGB image with tuple as transparency # works. workaround for Pillow #2633 img = Image.new('P', (100, 100)) img.info['transparency'] = (0, 0, 0) image_opts = PNG_FORMAT.copy() ir = ImageSource(img, image_opts=image_opts) img = Image.open(ir.as_buffer()) eq_(img.mode, 'P')
def test_from_non_seekable_file(self): with open(self.tmp_filename, 'rb') as tmp_file: data = tmp_file.read() class FileLikeDummy(object): # "file" without seek, like urlopen response def read(self): return data ir = ImageSource(FileLikeDummy(), 'png') assert ir.as_buffer(seekable=True).read() == data assert ir.as_image().size == (100, 100) assert ir.as_buffer().read() == data
class TestTransform(object): def setup(self): self.src_img = ImageSource(create_debug_img((200, 200), transparent=False)) self.src_srs = SRS(31467) self.dst_size = (100, 150) self.dst_srs = SRS(4326) self.dst_bbox = (0.2, 45.1, 8.3, 53.2) self.src_bbox = self.dst_srs.transform_bbox_to(self.src_srs, self.dst_bbox) def test_transform(self, mesh_div=4): transformer = ImageTransformer(self.src_srs, self.dst_srs, mesh_div=mesh_div) result = transformer.transform(self.src_img, self.src_bbox, self.dst_size, self.dst_bbox, image_opts=ImageOptions(resampling='nearest')) assert isinstance(result, ImageSource) assert result.as_image() != self.src_img.as_image() assert result.size == (100, 150) def _test_compare_mesh_div(self): """ Create transformations with different div values. """ for div in [1, 2, 4, 6, 8, 12, 16]: transformer = ImageTransformer(self.src_srs, self.dst_srs, mesh_div=div) result = transformer.transform(self.src_img, self.src_bbox, self.dst_size, self.dst_bbox) result.as_image().save('/tmp/transform-%d.png' % (div,))
def setup(self): self.src_img = ImageSource(create_debug_img((200, 200), transparent=False)) self.src_srs = SRS(31467) self.dst_size = (100, 150) self.dst_srs = SRS(4326) self.dst_bbox = (0.2, 45.1, 8.3, 53.2) self.src_bbox = self.dst_srs.transform_bbox_to(self.src_srs, self.dst_bbox)
def test_converted_output(self): ir = ImageSource(self.tmp_filename, (100, 100), PNG_FORMAT) assert is_png(ir.as_buffer()) assert is_jpeg(ir.as_buffer(JPEG_FORMAT)) assert is_jpeg(ir.as_buffer()) assert is_tiff(ir.as_buffer(TIFF_FORMAT)) assert is_tiff(ir.as_buffer())
def test_paletted_merge(self): # generate RGBA images with a transparent rectangle in the lower right img1 = ImageSource(Image.new("RGBA", (50, 50), (0, 255, 0, 255))).as_image() draw = ImageDraw.Draw(img1) draw.rectangle((25, 25, 49, 49), fill=(0, 0, 0, 0)) paletted_img = quantize(img1, alpha=True) assert img_has_transparency(paletted_img) assert paletted_img.mode == "P" rgba_img = Image.new("RGBA", (50, 50), (255, 0, 0, 255)) draw = ImageDraw.Draw(rgba_img) draw.rectangle((25, 25, 49, 49), fill=(0, 0, 0, 0)) img1 = ImageSource(paletted_img) img2 = ImageSource(rgba_img) # generate base image and merge the others above img3 = ImageSource(Image.new("RGBA", (50, 50), (0, 0, 255, 255))) result = merge_images([img3, img1, img2], ImageOptions(transparent=True)) img = result.as_image() assert img.mode == "RGBA" assert img.getpixel((49, 49)) == (0, 0, 255, 255) assert img.getpixel((0, 0)) == (255, 0, 0, 255)
def test_invalid_tile(self): self.cleanup_tiles = [ create_tmp_image_file((100, 100)) for _ in range(9) ] self.tiles = [ImageSource(tile) for tile in self.cleanup_tiles] invalid_tile = self.tiles[0].source with open(invalid_tile, "wb") as tmp: tmp.write(b"invalid") m = TileMerger(tile_grid=(3, 3), tile_size=(100, 100)) img_opts = ImageOptions(bgcolor=(200, 0, 50)) result = m.merge(self.tiles, img_opts) img = result.as_image() assert img.size == (300, 300) assert img.getcolors() == [(10000, (200, 0, 50)), (80000, (0, 0, 0))] assert not os.path.isfile(invalid_tile)
def test_args(self): def callback(img_src, service, layers, environ, query_extent, **kw): assert isinstance(img_src, ImageSource) eq_('wms.map', service) assert isinstance(layers, list) assert isinstance(environ, dict) assert len(query_extent) == 2 assert len(query_extent[1]) == 4 return img_src img_src1 = ImageSource(Image.new('RGBA', (100, 100))) env = make_wsgi_env('', extra_environ={'mapproxy.decorate_img': callback}) img_src2 = self.tile_server.decorate_img( img_src1, 'wms.map', ['layer1'], env, self.query_extent )
def load_tile(self, tile, with_metadata=False): # bulk loading with load_tiles is not implemented, because # CouchDB's /all_docs? does not include attachments if tile.source or tile.coord is None: return True url = self.document_url(tile.coord) + '?attachments=true' resp = requests.get(url, headers={'Accept': 'application/json'}) if resp.status_code == 200: doc = json.loads(resp.content) tile_data = StringIO(doc['_attachments']['tile']['data'].decode('base64')) tile.source = ImageSource(tile_data) tile.timestamp = doc.get(self.md_template.timestamp_key) return True return False
def load_tiles(self, tiles, with_metadata=False, dimensions=None): #associate the right tiles with the cursor tile_dict = {} coords = [] for tile in tiles: if tile.source or tile.coord is None: continue x, y, level = tile.coord coords.append(x) coords.append(y) coords.append(level) tile_dict[(x, y)] = tile if not tile_dict: # all tiles loaded or coords are None return True if self.supports_timestamp: stmt_base = "SELECT tile_column, tile_row, tile_data, last_modified FROM tiles WHERE " else: stmt_base = "SELECT tile_column, tile_row, tile_data FROM tiles WHERE " loaded_tiles = 0 # SQLite is limited to 1000 args -> split into multiple requests if more arguments are needed while coords: cur_coords = coords[:999] stmt = stmt_base + ' OR '.join( ['(tile_column = ? AND tile_row = ? AND zoom_level = ?)'] * (len(cur_coords) // 3)) cursor = self.db.cursor() cursor.execute(stmt, cur_coords) for row in cursor: loaded_tiles += 1 tile = tile_dict[(row[0], row[1])] data = row[2] tile.size = len(data) tile.source = ImageSource(BytesIO(data)) if self.supports_timestamp: tile.timestamp = sqlite_datetime_to_timestamp(row[3]) cursor.close() coords = coords[999:] return loaded_tiles == len(tile_dict)
def load_tile(self, tile, with_metadata=False, dimensions=None): if tile.timestamp is None: tile.timestamp = 0 if tile.source or tile.coord is None: return True res = self._get_object(tile.coord) if res.exists: tile_data = BytesIO(res.encoded_data) tile.source = ImageSource(tile_data) if with_metadata: tile.timestamp = self._get_timestamp(res) tile.size = len(res.encoded_data) return True return False
def load_tile(self, tile, with_metadata=False): if tile.source or tile.coord is None: return True cur = self.db.cursor() cur.execute("""SELECT tile_data FROM [{0}] WHERE tile_column = ? AND tile_row = ? AND zoom_level = ?""".format(self.table_name), tile.coord) content = cur.fetchone() if content: tile.source = ImageSource(BytesIO(content[0])) return True else: return False
def test_background_larger_crop_with_transparent(self): img = ImageSource(Image.new('RGBA', (356, 266), (130, 140, 120, 255))) img_opts = ImageOptions('RGBA', transparent=True) splitter = TileSplitter(img, img_opts) tile = splitter.get_tile((0, 0), (256, 256)) eq_(tile.size, (256, 256)) colors = tile.as_image().getcolors() eq_(colors, [(256*256, (130, 140, 120, 255))]) tile = splitter.get_tile((256, 256), (256, 256)) eq_(tile.size, (256, 256)) colors = tile.as_image().getcolors() eq_(sorted(colors), [(10*100, (130, 140, 120, 255)), (256*256-10*100, (255, 255, 255, 0))])
def load_tile(self, tile, with_metadata=True): if not tile.is_missing(): return True key = self.tile_key(tile) log.debug('AzureBlob:load_tile, key: %s' % key) try: r = self.conn.download_blob(key) tile.timestamp = calendar.timegm(r.properties.last_modified.timetuple()) tile.size = r.properties.size tile.source = ImageSource(BytesIO(r.readall())) except Exception as e: return False return True
def test_defragmentation_empty_bundle(self): cache = self.cache_class(self.cache_dir) t = Tile((5000, 1000, 12), ImageSource(BytesIO(b'a' * 60 * 1024), image_opts=ImageOptions(format='image/png'))) cache.store_tile(t) cache.remove_tile(t) fname = os.path.join(self.cache_dir, 'L12', 'R0380C1380.bundle') assert os.path.exists(fname) logger = mockProgressLog() defrag_compact_cache(cache, min_bytes=50000, log_progress=logger) assert not os.path.exists(fname)
def load_tile(self, tile, with_metadata=False): """ Fills the `Tile.source` of the `tile` if it is cached. If it is not cached or if the ``.coord`` is ``None``, nothing happens. """ if not tile.is_missing(): return True location = self.tile_location(tile) if os.path.exists(location): if with_metadata: self.load_tile_metadata(tile) tile.source = ImageSource(location) return True return False
def test_scaled_tiles(self, name, file_cache, tile_locker, rescale_tiles, tiles, store, expected_load, output): res = [ 1.40625, # 0 0.703125, # 1 0.3515625, # 2 0.17578125, # 3 0.087890625, # 4 0.0439453125, # 5 0.02197265625, # 6 0.010986328125, # 7 0.007, # 8 additional resolution to test unregular grids 0.0054931640625, # 9 0.00274658203125, # 10 ] grid = TileGrid(SRS(4326), origin='sw', bbox=[-180, -90, 180, 90], res=res) image_opts = ImageOptions(format='image/png', resampling='nearest') tm = TileManager( grid, file_cache, [], 'png', locker=tile_locker, image_opts=image_opts, rescale_tiles=rescale_tiles, ) if store: colors = set() if output == "partial": colors.add((255, 255, 255)) for i, t in enumerate(store): color = (150+i*35, 5+i*35, 5+i*35) colors.add(color) tile = Tile(t, ImageSource(create_tmp_image_buf((256, 256), color=color))) file_cache.store_tile(tile) loaded_tiles = tm.load_tile_coords(tiles) assert not is_blank(loaded_tiles) assert len(loaded_tiles) == len(tiles) got_colors = set() for t in loaded_tiles: got_colors.update([c for _, c in t.source.as_image().getcolors()]) assert got_colors == colors else: loaded_tiles = tm.load_tile_coords(tiles) assert is_blank(loaded_tiles) == (output == "blank") assert len(loaded_tiles.tiles) == len(tiles) assert file_cache.stored_tiles == set(store) assert file_cache.loaded_tiles == counting_set(expected_load)
def test_filter_with_alpha(self): img = Image.new('RGBA', (200, 200), (10, 15, 20, 0)) orig_source = ImageSource(img) self.tile.source = orig_source filtered_tile = self.filter(self.tile) assert self.tile is filtered_tile assert orig_source != filtered_tile.source pil_img = filtered_tile.source.as_image() eq_(pil_img.getpixel((0, 0)), (10, 15, 20, 0)) colors = pil_img.getcolors() colors.sort() # most but not all parts are bg color assert 39950 > colors[-1][0] > 39000 eq_(colors[-1][1], (10, 15, 20, 0))
def load_tile(self, tile, with_metadata=False): if tile.source or tile.coord is None: return True idx = BundleIndex(self.base_filename + BUNDLEX_EXT) x, y = self._rel_tile_coord(tile.coord) offset = idx.tile_offset(x, y) if offset == 0: return False bundle = BundleData(self.base_filename + BUNDLE_EXT, self.offset) data = bundle.read_tile(offset) if not data: return False tile.source = ImageSource(BytesIO(data)) return True
def test_wkt_mask_partial_image_transparent(self): img = ImageSource( Image.new("RGB", (100, 100), color=(100, 0, 200)), image_opts=ImageOptions(transparent=True), ) # polygon with hole geom = "POLYGON((2 2, 2 8, 8 8, 8 2, 2 2), (4 4, 4 6, 6 6, 6 4, 4 4))" result = mask_image_source_from_coverage( img, [0, 0, 10, 10], SRS(4326), coverage(geom) ) # 60*60 - 20*20 = 3200 assert_img_colors_eq( result.as_image().getcolors(), [(10000 - 3200, (255, 255, 255, 0)), (3200, (100, 0, 200, 255))], )
def test_shapely_mask_with_transform_partial_image_transparent(self): img = ImageSource( Image.new("RGB", (100, 100), color=(100, 0, 200)), image_opts=ImageOptions(transparent=True), ) p = Polygon( [(0, 0), (222000, 0), (222000, 222000), (0, 222000)] ) # ~ 2x2 degres result = mask_image_source_from_coverage( img, [0, 0, 10, 10], SRS(4326), coverage(p, "EPSG:3857") ) # 20*20 = 400 assert_img_colors_eq( result.as_image().getcolors(), [(10000 - 400, (255, 255, 255, 0)), (400, (100, 0, 200, 255))], )
def load_tile(self, tile, with_metadata=True): if not tile.is_missing(): return True key = self.tile_key(tile) log.debug('S3:load_tile, key: %s' % key) try: r = self.conn().get_object(Bucket=self.bucket_name, Key=key) self._set_metadata(r, tile) tile.source = ImageSource(r['Body']) except botocore.exceptions.ClientError as e: error = e.response.get('Errors', e.response)['Error'] # moto get_object can return Error wrapped in Errors... if error['Code'] in ('404', 'NoSuchKey'): return False raise return True
def _get_map(self, query): format = self.image_opts.format if not format: format = query.format if self.supported_formats and format not in self.supported_formats: format = self.supported_formats[0] if self.supported_srs: if query.srs not in self.supported_srs: return self._get_transformed(query, format) # some srs are equal but not the same (e.g. 900913/3857) # use only supported srs so we use the right srs code. idx = self.supported_srs.index(query.srs) if self.supported_srs[idx] is not query.srs: query.srs = self.supported_srs[idx] if self.extent and not self.extent.contains(MapExtent(query.bbox, query.srs)): return self._get_sub_query(query, format) resp = self.client.retrieve(query, format) return ImageSource(resp, size=query.size, image_opts=self.image_opts)
def load_tile(self, tile, with_metadata=False, dimensions=None): # bulk loading with load_tiles is not implemented, because # CouchDB's /all_docs? does not include attachments if tile.source or tile.coord is None: return True url = self.document_url(tile.coord) + '?attachments=true' self.init_db() resp = self.req_session.get(url, headers={'Accept': 'application/json'}) if resp.status_code == 200: doc = json.loads(codecs.decode(resp.content, 'utf-8')) tile_data = BytesIO( base64.b64decode(doc['_attachments']['tile']['data'])) tile.source = ImageSource(tile_data) tile.timestamp = doc.get(self.md_template.timestamp_key) return True return False
def load_tiles(self, tiles, with_metadata=False): #associate the right tiles with the cursor tile_dict = {} coords = [] for tile in tiles: if tile.source or tile.coord is None: continue x, y, level = tile.coord coords.append(x) coords.append(y) coords.append(level) tile_dict[(x, y)] = tile if not tile_dict: # all tiles loaded or coords are None return True if len(coords) > 1000: # SQLite is limited to 1000 args raise CacheBackendError( 'cannot query SQLite for more than 333 tiles') if self.supports_timestamp: stmt = "SELECT tile_column, tile_row, tile_data, last_modified FROM tiles WHERE " else: stmt = "SELECT tile_column, tile_row, tile_data FROM tiles WHERE " stmt += ' OR '.join( ['(tile_column = ? AND tile_row = ? AND zoom_level = ?)'] * (len(coords) // 3)) cursor = self.db.cursor() cursor.execute(stmt, coords) loaded_tiles = 0 for row in cursor: loaded_tiles += 1 tile = tile_dict[(row[0], row[1])] data = row[2] tile.size = len(data) tile.source = ImageSource(BytesIO(data)) if self.supports_timestamp: tile.timestamp = sqlite_datetime_to_timestamp(row[3]) cursor.close() return loaded_tiles == len(tile_dict)
def merge(self, ordered_tiles, image_opts): """ Merge all tiles into one image. :param ordered_tiles: list of tiles, sorted row-wise (top to bottom) :rtype: `ImageSource` """ if self.tile_grid == (1, 1): assert len(ordered_tiles) == 1 if ordered_tiles[0] is not None: tile = ordered_tiles.pop() return tile src_size = self._src_size() result = create_image(src_size, image_opts) cacheable = True for i, source in enumerate(ordered_tiles): if source is None: continue try: if not source.cacheable: cacheable = False tile = source.as_image() pos = self._tile_offset(i) tile.draft(image_opts.mode, self.tile_size) result.paste(tile, pos) source.close_buffers() except IOError as e: if e.errno is None: # PIL error log.warning( 'unable to load tile %s, removing it (reason was: %s)' % (source, str(e))) if getattr(source, 'filename'): if os.path.exists(source.filename): os.remove(source.filename) else: raise return ImageSource(result, size=src_size, image_opts=image_opts, cacheable=cacheable)
def test_background_larger_crop_with_transparent(self): img = ImageSource(Image.new("RGBA", (356, 266), (130, 140, 120, 255))) img_opts = ImageOptions("RGBA", transparent=True) splitter = TileSplitter(img, img_opts) tile = splitter.get_tile((0, 0), (256, 256)) assert tile.size == (256, 256) colors = tile.as_image().getcolors() assert colors == [(256 * 256, (130, 140, 120, 255))] tile = splitter.get_tile((256, 256), (256, 256)) assert tile.size == (256, 256) colors = tile.as_image().getcolors() assert sorted(colors) == [ (10 * 100, (130, 140, 120, 255)), (256 * 256 - 10 * 100, (255, 255, 255, 0)), ]
def concat_legends(legends, format='png', size=None, bgcolor='#ffffff', transparent=True): """ Merge multiple legends into one :param images: list of `ImageSource`, bottom image first :param format: the format of the output `ImageSource` :param size: size of the merged image, if ``None`` the size will be calculated :rtype: `ImageSource` """ if not legends: return BlankImageSource(size=(1,1), image_opts=ImageOptions(bgcolor=bgcolor, transparent=transparent)) if len(legends) == 1: return legends[0] legends = legends[:] legends.reverse() if size is None: legend_width = 0 legend_height = 0 legend_position_y = [] #iterate through all legends, last to first, calc img size and remember the y-position for legend in legends: legend_position_y.append(legend_height) tmp_img = legend.as_image() legend_width = max(legend_width, tmp_img.size[0]) legend_height += tmp_img.size[1] #images shall not overlap themselfs size = [legend_width, legend_height] bgcolor = ImageColor.getrgb(bgcolor) if transparent: img = Image.new('RGBA', size, bgcolor+(0,)) else: img = Image.new('RGB', size, bgcolor) for i in range(len(legends)): legend_img = legends[i].as_image() if legend_img.mode == 'RGBA': # paste w transparency mask from layer img.paste(legend_img, (0, legend_position_y[i]), legend_img) else: img.paste(legend_img, (0, legend_position_y[i])) return ImageSource(img, image_opts=ImageOptions(format=format))
def _transform(self, src_img, src_bbox, dst_size, dst_bbox, image_opts): """ Do a 'real' transformation with a transformed mesh (see above). """ src_bbox = self.src_srs.align_bbox(src_bbox) dst_bbox = self.dst_srs.align_bbox(dst_bbox) src_size = src_img.size src_quad = (0, 0, src_size[0], src_size[1]) dst_quad = (0, 0, dst_size[0], dst_size[1]) to_src_px = make_lin_transf(src_bbox, src_quad) to_dst_w = make_lin_transf(dst_quad, dst_bbox) meshes = [] # more recent versions of Pillow use center coordinates for # transformations, we manually need to add half a pixel otherwise if transform_uses_center(): px_offset = 0.0 else: px_offset = 0.5 def dst_quad_to_src(quad): src_quad = [] for dst_px in [(quad[0], quad[1]), (quad[0], quad[3]), (quad[2], quad[3]), (quad[2], quad[1])]: dst_w = to_dst_w( (dst_px[0] + px_offset, dst_px[1] + px_offset)) src_w = self.dst_srs.transform_to(self.src_srs, dst_w) src_px = to_src_px(src_w) src_quad.extend(src_px) return quad, src_quad mesh_div = self.mesh_div while mesh_div > 1 and (dst_size[0] / mesh_div < 10 or dst_size[1] / mesh_div < 10): mesh_div -= 1 for quad in griddify(dst_quad, mesh_div): meshes.append(dst_quad_to_src(quad)) img = img_for_resampling(src_img.as_image(), image_opts.resampling) result = img.transform(dst_size, Image.MESH, meshes, image_filter[image_opts.resampling]) return ImageSource(result, size=dst_size, image_opts=image_opts)
def test_from_mixed_merge(self): """ Check merge RGBA bands from image without alpha (mixed) """ merger = BandMerger(mode="RGBA") merger.add_ops(dst_band=0, src_img=0, src_band=2) merger.add_ops(dst_band=1, src_img=0, src_band=1) merger.add_ops(dst_band=2, src_img=0, src_band=0) merger.add_ops(dst_band=3, src_img=0, src_band=3) img = Image.new("RGB", (10, 10), (0, 100, 200)) src_img = ImageSource(img) img_opts = ImageOptions("RGBA") result = merger.merge([src_img], img_opts) img = result.as_image() assert img.mode == "RGBA" assert img.getpixel((0, 0)) == (200, 100, 0, 255)
def _render_mapfile(self, mapfile, query): start_time = time.time() m = self.map_obj(mapfile) m.resize(query.size[0], query.size[1]) m.srs = '+init=%s' % str(query.srs.srs_code.lower()) envelope = mapnik.Box2d(*query.bbox) m.zoom_to_box(envelope) data = None try: if self.layers: i = 0 for layer in m.layers[:]: if layer.name != 'Unkown' and layer.name not in self.layers: del m.layers[i] else: i += 1 img = mapnik.Image(query.size[0], query.size[1]) if self.scale_factor: mapnik.render(m, img, self.scale_factor) else: mapnik.render(m, img) data = img.tostring(str(query.format)) finally: size = None if data: size = len(data) log_request('%s:%s:%s:%s' % (mapfile, query.bbox, query.srs.srs_code, query.size), status='200' if data else '500', size=size, method='API', duration=time.time() - start_time) return ImageSource(BytesIO(data), size=query.size, image_opts=ImageOptions( transparent=self.transparent, format=query.format))
def test_from_p_merge(self): """ Check merge bands from paletted image """ merger = BandMerger(mode="RGB") merger.add_ops(dst_band=0, src_img=0, src_band=2) merger.add_ops(dst_band=1, src_img=0, src_band=1) merger.add_ops(dst_band=2, src_img=0, src_band=0) img = Image.new("RGB", (10, 10), (0, 100, 200)).quantize(256) assert img.mode == "P" # src img is P but we can still access RGB bands src_img = ImageSource(img) img_opts = ImageOptions("RGB") result = merger.merge([src_img], img_opts) img = result.as_image() assert img.mode == "RGB" assert img.getpixel((0, 0)) == (200, 100, 0)
class TestTransform(object): def setup(self): self.src_img = ImageSource( create_debug_img((200, 200), transparent=False)) self.src_srs = SRS(31467) self.dst_size = (100, 150) self.dst_srs = SRS(4326) self.dst_bbox = (0.2, 45.1, 8.3, 53.2) self.src_bbox = self.dst_srs.transform_bbox_to(self.src_srs, self.dst_bbox) def test_transform(self): transformer = ImageTransformer(self.src_srs, self.dst_srs) result = transformer.transform( self.src_img, self.src_bbox, self.dst_size, self.dst_bbox, image_opts=ImageOptions(resampling="nearest"), ) assert isinstance(result, ImageSource) assert result.as_image() != self.src_img.as_image() assert result.size == (100, 150) def _test_compare_max_px_err(self): """ Create transformations with different div values. """ for err in [0.2, 0.5, 1, 2, 4, 6, 8, 12, 16]: transformer = ImageTransformer(self.src_srs, self.dst_srs, max_px_err=err) result = transformer.transform( self.src_img, self.src_bbox, self.dst_size, self.dst_bbox, image_opts=ImageOptions(resampling="nearest"), ) result.as_image().save("/tmp/transform-%03d.png" % (err * 10, ))
def load_tiles(self, tiles, with_metadata=False): missing = False with self.index().readonly() as idx: if not idx: return False with self.data().readonly() as bundle: for t in tiles: if t.source or t.coord is None: continue x, y = self._rel_tile_coord(t.coord) offset = idx.tile_offset(x, y) if offset == 0: missing = True continue data = bundle.read_tile(offset) if not data: missing = True continue t.source = ImageSource(BytesIO(data)) return not missing
def get_tile(self, crop_coord, tile_size): """ Return the cropped tile. :param crop_coord: the upper left pixel coord to start :param tile_size: width and height of the new tile :rtype: `ImageSource` """ minx, miny = crop_coord maxx = minx + tile_size[0] maxy = miny + tile_size[1] if (minx < 0 or miny < 0 or maxx > self.meta_img.size[0] or maxy > self.meta_img.size[1]): crop = self.meta_img.crop( (max(minx, 0), max(miny, 0), min(maxx, self.meta_img.size[0]), min(maxy, self.meta_img.size[1]))) result = create_image(tile_size, self.image_opts) result.paste(crop, (abs(min(minx, 0)), abs(min(miny, 0)))) crop = result else: crop = self.meta_img.crop((minx, miny, maxx, maxy)) return ImageSource(crop, size=tile_size, image_opts=self.image_opts)
def test_output_formats_png8(self): img = Image.new('RGBA', (100, 100)) ir = ImageSource(img, image_opts=PNG_FORMAT) img = Image.open(ir.as_buffer(ImageOptions(colors=256, transparent=True, format='image/png'))) assert img.mode == 'P' assert img.getpixel((0, 0)) == 255
def test_from_filename(self): ir = ImageSource(self.tmp_filename, PNG_FORMAT) assert is_png(ir.as_buffer()) assert ir.as_image().size == (100, 100)
def test_output_formats(self): img = Image.new('RGB', (100, 100)) for format in ['png', 'gif', 'tiff', 'jpeg', 'GeoTIFF', 'bmp']: ir = ImageSource(img, (100, 100), image_opts=ImageOptions(format=format)) yield check_format, ir.as_buffer(), format
def test_from_file(self): with open(self.tmp_filename, 'rb') as tmp_file: ir = ImageSource(tmp_file, 'png') assert ir.as_buffer() == tmp_file assert ir.as_image().size == (100, 100)
def test_from_image(self): img = Image.new('RGBA', (100, 100)) ir = ImageSource(img, (100, 100), PNG_FORMAT) assert ir.as_image() == img assert is_png(ir.as_buffer())