def __init__(self, size, *args, **kwargs): super().__init__() self._width = size[0] self._height = size[1] self.pix_format = kwargs.get('pix_format', 'bgra32') shape = (self._height, self._width, 4) buffer = np.zeros(shape, dtype=np.uint8) canvas_klass = pix_format_canvases[self.pix_format] self.gc = canvas_klass(buffer, bottom_up=True) self.marker_gc = MarkerRenderer( buffer, pix_format=self.pix_format, bottom_up=True ) # init the state variables clip = agg.Rect(0, 0, self._width, self._height) self.canvas_state = agg.GraphicsState(clip_box=clip) self.stroke_paint = agg.SolidPaint(0.0, 0.0, 0.0) self.fill_paint = agg.SolidPaint(0.0, 0.0, 0.0) self.path = CompiledPath() self.text_transform = agg.Transform() self.text_pos = (0.0, 0.0) self.transform = agg.Transform() self.font = None self.__state_stack = [] # For HiDPI support self.base_scale = kwargs.pop('base_pixel_scale', 1) self.transform.scale(self.base_scale, self.base_scale) # Make this look like a kiva.agg GC self.bmp_array = buffer
def interpret_color(value, context): color = parse_color(value) if color is not None: return agg.SolidPaint(*color) elif value.lower() == 'none': return None elif value.startswith('url'): match = re.match(r'url\(#(?P<label>\w+)\)', value) if match is None: return None obj = context.get(match.group('label')) if obj is None or 'type' not in obj: return None if obj['type'] == 'linearGradient': grad = agg.LinearGradientPaint( obj['x1'], obj['y1'], obj['x2'], obj['y2'], obj['stops'], obj.get('spreadMethod', agg.GradientSpread.SpreadPad), obj.get('gradientUnits', agg.GradientUnits.ObjectBoundingBox)) grad.transform = obj.get('gradientTransform', agg.Transform()) return grad if obj['type'] == 'radialGradient': grad = agg.RadialGradientPaint( obj['cx'], obj['cy'], obj['r'], obj['fx'], obj['fy'], obj['stops'], obj.get('spreadMethod', agg.GradientSpread.SpreadPad), obj.get('gradientUnits', agg.GradientUnits.ObjectBoundingBox)) grad.transform = obj.get('gradientTransform', agg.Transform()) return grad
def parse_transform(value): def rotate(deg): t = agg.Transform() t.rotate(deg * np.pi / 180) return t def scale(*s): sx = s[0] sy = sx if len(s) == 1 else s[1] return agg.Transform(sx, 0.0, 0.0, sy, 0.0, 0.0) def translate(*t): tx = t[0] ty = 0.0 if len(t) == 1 else t[1] return agg.Transform(1.0, 0.0, 0.0, 1.0, tx, ty) FUNCTIONS = { 'matrix': agg.Transform, 'translate': translate, 'rotate': rotate, 'scale': scale, 'skewX': lambda s: agg.Transform(1.0, 0.0, s, 1.0, 0.0, 0.0), 'skewY': lambda s: agg.Transform(1.0, s, 0.0, 1.0, 0.0, 0.0), } match = re.match(r'(?P<func>\w+)\((?P<args>[0-9.,-e]+)\)', value) if match is None: return None func = FUNCTIONS.get(match.group('func'), lambda *a: None) args = match.group('args') args = [float(a) for a in args.split(',')] return func(*args)
def show_text(self, text, point=None): """ Draw text on the device at current text position. This is also used for showing text at a particular point specified by x and y. """ if self.font is None: raise RuntimeError("show_text called before setting a font!") if point is None: pos = tuple(self.text_pos) else: pos = tuple(point) transform = agg.Transform() transform.multiply(self.transform) transform.translate(*pos) self.gc.draw_text( text, self.font, transform, self.canvas_state, fill=self.fill_paint, stroke=self.stroke_paint, )
def sky(width, height, canvas, gs): def rcolor(): return hsv2rgb(.55 + randneghalf2half() * .25, .6 + randneghalf2half() * .125, .25 + randneghalf2half() * .125) transform = agg.Transform() starpaint = agg.SolidPaint(*hsv2rgb(.165, .96, .99)) starshape = agg.Path() angles = np.linspace(0, 2 * np.pi, num=5, endpoint=False) pts = np.stack([np.cos(angles), np.sin(angles)]).T * 7 starshape.lines([pts[i] for i in (0, 2, 4, 1, 3)]) starshape.close() bgshape = agg.Path() bgshape.rect(0, 0, width, height) bgpaint = agg.LinearGradientPaint(0, 0, 0, height, randgradstops(2, rcolor), agg.GradientSpread.SpreadReflect, agg.GradientUnits.UserSpace) canvas.draw_shape(bgshape, transform, gs, fill=bgpaint) star_count = 40 xs = np.random.rand(star_count) * width ys = np.random.rand(star_count) * height for x, y in zip(xs, ys): transform.reset() transform.translate(x, y) canvas.draw_shape(starshape, transform, gs, fill=starpaint)
def concat_ctm(self, transform): """ Concatenate the transform to current coordinate transform matrix. transform:affine_matrix -- the transform matrix to concatenate with the current coordinate matrix. """ self.transform.premultiply(agg.Transform(*transform))
def spiral(size, hue, sat, val): canvas = agg.CanvasRGB24(np.zeros((size[1], size[0], 3), dtype=np.uint8)) gs = agg.GraphicsState(drawing_mode=agg.DrawingMode.DrawFill) transform = agg.Transform() circle = agg.Path() circle.ellipse(0, 0, CIRCLE_SIZE, CIRCLE_SIZE) divisions = np.linspace(0, 2*np.pi, CIRCLE_COUNT, endpoint=False) centers = np.stack((np.cos(divisions), np.sin(divisions)), axis=1) offsets = compute_offsets(np.sqrt(size[0]**2 + size[1]**2) / 2) color_count = len(offsets) hsv = np.ones((color_count, 1, 3)) hsv[:, 0, 0] = np.linspace(hue[0], hue[1], color_count, endpoint=False) hsv[:, 0, 1] = np.linspace(sat[0], sat[1], color_count, endpoint=False) hsv[:, 0, 2] = np.linspace(val[0], val[1], color_count, endpoint=False) spectrum = hsv2rgb(hsv).reshape(color_count, 3) for idx, offset in enumerate(offsets): paint = agg.SolidPaint(*spectrum[idx]) radius = np.pi * offset / CIRCLE_COUNT scale = radius / CIRCLE_SIZE for i in range(CIRCLE_COUNT): if ((idx + i) % 2) == 0: continue transform.reset() transform.translate(size[0]/2 + offset*centers[i, 0], size[1]/2 + offset*centers[i, 1]) transform.scale(scale, scale) canvas.draw_shape(circle, transform, gs, fill=paint) imsave('spiral.png', canvas.array)
def draw_rect(self, rect, mode=constants.FILL_STROKE): """ Draw a rect. """ # XXX: kiva::graphics_context<>::_draw_rect_simple() does a VERY # specific optimization for drawing rectangles in certain circumstances # which results in chaco plot borders which are sharp. # This implements that same special case. - JW 2018/09/01 transform = self.transform if (not self.canvas_state.anti_aliased and self.canvas_state.line_width in (0.0, 1.0) and fabs(self.transform.shx) < 1e-3 and fabs(self.transform.shy) < 1e-3): scale_x = self.transform.sx scale_y = self.transform.sy tx = self.transform.tx ty = self.transform.ty x1 = int(rect[0] * scale_x + tx) y1 = int(rect[1] * scale_y + ty) x2 = int((rect[0] + rect[2]) * scale_x + tx) y2 = int((rect[1] + rect[3]) * scale_y + ty) rect = (x1, y1, abs(x2 - x1), abs(y2 - y1)) # XXX: The base transform is a half-pixel translate transform = agg.Transform(tx=0.5, ty=0.5) path = agg.Path() path.rect(*rect) self.canvas_state.drawing_mode = draw_modes[mode] self.gc.draw_shape(path, transform, self.canvas_state, stroke=self.stroke_paint, fill=self.fill_paint)
def test_bad_method_args(): canvas = agg.CanvasG8(np.zeros((1, 1), dtype=np.uint8)) pix_format = agg.PixelFormat.Gray8 gs = agg.GraphicsState() path = agg.Path() transform = agg.Transform() with pytest.raises(TypeError): canvas.draw_image(None, pix_format, transform, gs) with pytest.raises(TypeError): canvas.draw_image(canvas.array, None, transform, gs) with pytest.raises(TypeError): canvas.draw_image(canvas.array, pix_format, None, gs) with pytest.raises(TypeError): canvas.draw_image(canvas.array, pix_format, transform, None) # But this version should work canvas.draw_image(canvas.image, None, transform, gs) with pytest.raises(TypeError): canvas.draw_shape(None, transform, gs) with pytest.raises(TypeError): canvas.draw_shape(path, None, gs) with pytest.raises(TypeError): canvas.draw_shape(path, transform, None) text = "Hello!" font = agg.Font("Times New Roman", 12.0, agg.FontCacheType.RasterFontCache) with pytest.raises(TypeError): canvas.draw_text(text, None, transform, gs) with pytest.raises(TypeError): canvas.draw_text(text, font, None, gs) with pytest.raises(TypeError): canvas.draw_text(text, font, transform, None)
def draw_mask(image_shape, geometry, antialias=False): """Draw a mask (0-255) from a given celiagg path. Requires celiagg installed. Note: to produce a True/False mask from the output, simply do the following: mask = draw_mask(image_shape, geometry) bool_mask = mask > 0 Parameters: image_shape: shape of the resulting image geometry: celiagg VertexSource class, such as celiagg.Path or celiagg.BSpline, containing geometry to draw antialias: if False (default), output contains only 0 and 255. If True, output will be antialiased (better for visualization). Returns: mask array of dtype numpy.uint8 """ import celiagg image = numpy.zeros(image_shape, dtype=numpy.uint8, order='F') # NB celiagg uses (h, w) C-order convention for image shapes, so give it the transpose canvas = celiagg.CanvasG8(image.T) state = celiagg.GraphicsState(drawing_mode=celiagg.DrawingMode.DrawFill, anti_aliased=antialias) fill = celiagg.SolidPaint(1,1,1) transform = celiagg.Transform() canvas.draw_shape(geometry, transform, state, fill=fill) return image
def test_stencil_size_mismatch(): canvas = agg.CanvasRGB24(np.zeros((4, 5, 3), dtype=np.uint8)) stencil_canvas = agg.CanvasG8(np.zeros((1, 2), dtype=np.uint8)) gs = agg.GraphicsState(stencil=stencil_canvas.image) path = agg.Path() transform = agg.Transform() with pytest.raises(agg.AggError): canvas.draw_shape(path, transform, gs)
def test_no_text_draw_text_failure(): if agg.HAS_TEXT: return buffer = np.zeros((1, 1), dtype=np.uint8) canvas = agg.CanvasG8(buffer) transform = agg.Transform() line_paint = agg.SolidPaint(1.0, 1.0, 1.0) gs = agg.GraphicsState() with pytest.raises(RuntimeError): canvas.draw_text("", None, transform, line_paint, line_paint, gs)
def __init__(self, size, *args, **kwargs): super(GraphicsContext, self).__init__() self._width = size[0] self._height = size[1] self.pix_format = kwargs.get('pix_format', 'rgba32') shape = (self._height, self._width, 4) canvas_klass = pix_format_canvases[self.pix_format] self.gc = canvas_klass(np.zeros(shape, dtype=np.uint8), bottom_up=True) # init the state variables clip = agg.Rect(0, 0, self._width, self._height) self.canvas_state = agg.GraphicsState(clip_box=clip) self.stroke_paint = agg.SolidPaint(0.0, 0.0, 0.0) self.fill_paint = agg.SolidPaint(0.0, 0.0, 0.0) self.path = CompiledPath() self.text_transform = agg.Transform() self.text_pos = (0.0, 0.0) self.transform = agg.Transform() self.font = None self.__state_stack = []
def draw_image(self, img, rect=None): """ img is either a N*M*3 or N*M*4 numpy array, or a PIL Image rect - a tuple (x, y, w, h) """ from PIL import Image def normalize_image(img): if not img.mode.startswith('RGB'): img = img.convert('RGB') if img.mode == 'RGB': return img, agg.PixelFormat.RGB24 elif img.mode == 'RGBA': return img, agg.PixelFormat.RGBA32 img_format = agg.PixelFormat.RGB24 if isinstance(img, np.ndarray): # Numeric array img = Image.fromarray(img) img, img_format = normalize_image(img) img_array = np.array(img) elif isinstance(img, Image.Image): img, img_format = normalize_image(img) img_array = np.array(img) elif isinstance(img, GraphicsContext): img_array = img.gc.array img_format = pix_formats[img.pix_format] elif hasattr(img, 'bmp_array'): # An offscreen kiva.agg context # XXX: Use a copy to kill the read-only flag which plays havoc # with the Cython memoryviews used by celiagg img = Image.fromarray(img.bmp_array) img, img_format = normalize_image(img) img_array = np.array(img) else: msg = "Cannot render image of type '{}' into celiagg context." warnings.warn(msg.format(type(img))) return x, y, w, h = rect img_height, img_width = img_array.shape[:2] sx, sy = w / img_width, h / img_height transform = agg.Transform() transform.multiply(self.transform) transform.translate(x, y) transform.scale(sx, sy) self.gc.draw_image( img_array, img_format, transform, self.canvas_state, bottom_up=True )
def render(context, scale=1.0): docattrs = context['__document__'] width = parse_united(docattrs.get('width', '100')) * scale height = parse_united(docattrs.get('height', '100')) * scale canvas = agg.CanvasRGB24( np.empty((int(height), int(width), 3), dtype=np.uint8)) canvas.clear(1, 1, 1) for group in context['graphics']: for elem in group.values(): transform = elem.get('transform') or agg.Transform() shape = elem['data'] gs, stroke, fill = interpret_style(elem['style'], context) transform.scale(scale, scale) canvas.draw_shape(shape, transform, gs, stroke=stroke, fill=fill) return canvas.array
def draw_image(self, img, rect=None): """ img is either a N*M*3 or N*M*4 numpy array, or a Kiva image rect - a tuple (x, y, w, h) """ def get_format(array): if array.shape[2] == 3: return agg.PixelFormat.RGB24 elif array.shape[2] == 4: return agg.PixelFormat.RGBA32 img_format = agg.PixelFormat.RGB24 if isinstance(img, np.ndarray): # Numeric array img_array = img.astype(np.uint8) img_format = get_format(img_array) elif isinstance(img, GraphicsContext): img_array = img.gc.array img_format = pix_formats[img.pix_format] elif hasattr(img, 'bmp_array'): # An offscreen kiva context # XXX: Use a copy to kill the read-only flag which plays havoc # with the Cython memoryviews used by celiagg img_array = img.bmp_array.copy() img_format = get_format(img_array) else: msg = "Cannot render image of type '{}' into celiagg context." warnings.warn(msg.format(type(img))) return x, y, w, h = rect img_height, img_width = img_array.shape[:2] sx, sy = w / img_width, h / img_height transform = agg.Transform() transform.multiply(self.transform) transform.translate(x, y) transform.scale(sx, sy) self.gc.draw_image(img_array, img_format, transform, self.canvas_state, bottom_up=True)
def draw_horizon(xs, ys, canvas, gs): def rcolor(): return hsv2rgb(.125 + randneghalf2half() * .25, .6 + randneghalf2half() * .4, .6 + randneghalf2half() * .4) width = xs[-1] path = agg.Path() path.move_to(0, 0) path.line_to(xs[0], ys[0]) for x, y in zip(xs[1:], ys[1:]): path.line_to(x, y) path.line_to(width, 0) path.close() transform = agg.Transform() fill = agg.LinearGradientPaint(0, 0, width, 0, randgradstops(2, rcolor), agg.GradientSpread.SpreadReflect, agg.GradientUnits.UserSpace) canvas.draw_shape(path, transform, gs, fill=fill)
import pathlib import pickle import numpy import celiagg import freeimage from zplib.curve import spline_geometry from zplib.curve import interpolate from zplib.image import resample AGG_STATE = celiagg.GraphicsState(anti_aliased=False) AGG_PAINT = celiagg.SolidPaint(1, 1, 1) AGG_TRANSFORM = celiagg.Transform() AGG_TRANSPARENT = celiagg.SolidPaint(0, 0, 0, 0) def write_xy_positions(filename, positions): position_lines = ['{}\t{}'.format(x, y) for x, y in positions] with open(filename, 'w') as f: f.write('\n'.join(position_lines)) def save_centerline(metadata, centerline_file): spine_tck = metadata['spine_tck'] positions = interpolate.spline_interpolate(spine_tck, 50) write_xy_positions(centerline_file, positions) def save_landmarks(metadata, landmark_file): spine_tck = metadata['spine_tck'] vulva_t = metadata['vulva_t']
import numpy as np from skimage.io import imsave import celiagg as agg SIZE = 1000 order = (2, 4, 1, 3, 0) angles = np.linspace(0, 2 * np.pi, num=5, endpoint=False) pts = np.stack([np.cos(angles), np.sin(angles)]).T * SIZE / 2 + SIZE / 2 star_shape = agg.Path() star_shape.lines([pts[i] for i in (0, 2, 4, 1, 3)]) star_shape.close() star_transform = agg.Transform() star_transform.translate(SIZE / 2, SIZE / 2) star_transform.rotate(-np.pi / 2) star_transform.translate(-SIZE / 2, -SIZE / 2) big_box = agg.Path() big_box.rect(0, 0, SIZE, SIZE) small_box = agg.Path() small_box.rect(0, 0, SIZE / 2, SIZE / 2) stencil_canvas = agg.CanvasG8(np.zeros((SIZE, SIZE), dtype=np.uint8)) canvas = agg.CanvasRGB24(np.zeros((SIZE, SIZE, 3), dtype=np.uint8)) gs = agg.GraphicsState(drawing_mode=agg.DrawingMode.DrawFill, line_width=6.0) transform = agg.Transform() blue_paint = agg.SolidPaint(0.1, 0.1, 1.0) white_paint = agg.SolidPaint(1.0, 1.0, 1.0) stops = ((0.0, 0.0, 0.0, 0.0, 1.0), (1.0, 0.3, 0.3, 0.75, 1.0)) bw_grad = agg.LinearGradientPaint(0, 0, 2, 5, stops,
def transform(): return agg.Transform()
import celiagg as agg import numpy as np from skimage.io import imsave canvas = agg.CanvasRGB24(np.ones((400, 400, 3), dtype=np.uint8)) state = agg.GraphicsState(drawing_mode=agg.DrawingMode.DrawStroke, line_width=10.0) transform = agg.Transform() red_paint = agg.SolidPaint(1.0, 0.0, 0.0) black_paint = agg.SolidPaint(0.0, 0.0, 0.0) font = agg.Font("/Library/Fonts/Verdana.ttf", 96.0, agg.FontCacheType.RasterFontCache) path = agg.Path() path.ellipse(200, 200, 190, 190) canvas.clear(1.0, 1.0, 1.0) canvas.draw_shape(path, transform, state, stroke=red_paint) transform.translate(30.0, 220.0) canvas.draw_text("celiagg", font, transform, state, stroke=black_paint) imsave("example.png", canvas.array)
def translate(*t): tx = t[0] ty = 0.0 if len(t) == 1 else t[1] return agg.Transform(1.0, 0.0, 0.0, 1.0, tx, ty)
def scale(*s): sx = s[0] sy = sx if len(s) == 1 else s[1] return agg.Transform(sx, 0.0, 0.0, sy, 0.0, 0.0)
def rotate(deg): t = agg.Transform() t.rotate(deg * np.pi / 180) return t