def make_orimask(radians, mag=None, alpha=1.0): """ Makes a colormap in HSV space where the orientation changes color and mag changes the saturation/value. Args: radians (ndarray): orientation in radians mag (ndarray): magnitude (must be normalized between 0 and 1) alpha (float | ndarray): if False or None, then the image is returned without alpha if a float, then mag is scaled by this and used as the alpha channel if an ndarray, then this is explicilty set as the alpha channel Returns: ndarray[float32]: an rgb / rgba image in 01 space SeeAlso: kwimage.overlay_alpha_images Example: >>> # xdoc: +REQUIRES(module:matplotlib) >>> x, y = np.meshgrid(np.arange(64), np.arange(64)) >>> dx, dy = x - 32, y - 32 >>> radians = np.arctan2(dx, dy) >>> mag = np.sqrt(dx ** 2 + dy ** 2) >>> orimask = make_orimask(radians, mag) >>> # xdoc: +REQUIRES(--show) >>> import kwplot >>> kwplot.imshow(orimask, fnum=1, doclf=True, colorspace='rgb') >>> kwplot.show_if_requested() """ import matplotlib as mpl import matplotlib.cm # NOQA TAU = np.pi * 2 # Map radians to 0 to 1 ori01 = (radians % TAU) / TAU cmap_ = mpl.cm.get_cmap('hsv') color_rgb = cmap_(ori01)[..., 0:3].astype(np.float32) if mag is not None: import kwimage if mag.max() > 1: mag = mag / mag.max() color_hsv = kwimage.convert_colorspace(color_rgb, 'rgb', 'hsv') color_hsv[..., 1:3] = mag[..., None] color_rgb = kwimage.convert_colorspace(color_hsv, 'hsv', 'rgb') else: mag = 1 orimask = np.array(color_rgb, dtype=np.float32) if isinstance(alpha, np.ndarray): # Alpha specified as explicit numpy array orimask = kwimage.ensure_alpha_channel(orimask) orimask[:, :, 3] = alpha elif alpha is not False and alpha is not None: orimask = kwimage.ensure_alpha_channel(orimask) orimask[:, :, 3] = mag * alpha return orimask
def _draw_batch_preds(harn, batch, outputs, lim=16): """ Example: >>> # xdoctest: +REQUIRES(--slow) >>> kw = {'workers': 0, 'xpu': 'cpu', 'batch_size': 8} >>> harn = setup_harn(cmdline=False, **kw).initialize() >>> batch = harn._demo_batch(tag='train') >>> outputs, loss_parts = harn.run_batch(batch) >>> toshow = harn._draw_batch_preds(batch, outputs) >>> # xdoctest: +REQUIRES(--show) >>> import kwplot >>> kwplot.autompl() >>> kwplot.imshow(toshow) """ import cv2 im = batch['im'].data.cpu().numpy() class_true = batch['class_idxs'].data.cpu().numpy() class_pred = outputs['class_probs'].data.cpu().numpy().argmax(axis=1) batch_imgs = [] for bx in range(min(len(class_true), lim)): orig_img = im[bx].transpose(1, 2, 0) out_size = class_pred[bx].shape[::-1] orig_img = cv2.resize(orig_img, tuple(map(int, out_size))) orig_img = kwimage.ensure_alpha_channel(orig_img) pred_heatmap = kwimage.Heatmap( class_idx=class_pred[bx], classes=harn.classes ) true_heatmap = kwimage.Heatmap( class_idx=class_true[bx], classes=harn.classes ) # TODO: scale up to original image size pred_img = pred_heatmap.draw_on(orig_img, channel='idx', with_alpha=.5) true_img = true_heatmap.draw_on(orig_img, channel='idx', with_alpha=.5) true_img = kwimage.ensure_uint255(true_img) pred_img = kwimage.ensure_uint255(pred_img) true_img = kwimage.draw_text_on_image( true_img, 'true', org=(0, 0), valign='top', color='blue') pred_img = kwimage.draw_text_on_image( pred_img, 'pred', org=(0, 0), valign='top', color='blue') item_img = kwimage.stack_images([pred_img, true_img], axis=1) batch_imgs.append(item_img) toshow = kwimage.stack_images_grid(batch_imgs, chunksize=2, overlap=-32) return toshow
def _from_elem(self, cname, chip, size=None): """ Example: >>> # hack to allow chip to be None >>> chip = None >>> size = (32, 32) >>> cname = 'superstar' >>> self = CategoryPatterns.coerce() >>> self._from_elem(cname, chip, size) """ elem_func = self._category_to_elemfunc[cname] if chip is None: assert size is not None x = max(size) else: size = tuple(map(int, chip.shape[0:2][::-1])) x = max(chip.shape[0:2]) # x = int(2 ** np.floor(np.log2(x))) elem, kpts_yx = elem_func(x) if kpts_yx is not None: kp_catnames = list(kpts_yx.keys()) xy = np.array([yx[::-1] for yx in kpts_yx.values()]) kpts = kwimage.Points(xy=xy, class_idxs=np.arange(len(xy)), classes=kp_catnames) sf = np.array(size) / np.array(elem.shape[0:2][::-1]) kpts = kpts.scale(sf) else: kpts = None # center kpts = kwimage.Points(xy=np.array([])) # kpts = kwimage.Points(xy=np.array([[.5, .5]])) kpts = kpts.scale(size) template = cv2.resize(elem, size).astype(np.float32) fg_intensity = np.float32(self.fg_intensity) fg_scale = np.float32(self.fg_scale) if chip is not None: fgdata = kwarray.standard_normal(chip.shape, std=fg_scale, mean=fg_intensity, rng=self.rng, dtype=np.float32) fgdata = np.clip(fgdata, 0, 1, out=fgdata) fga = kwimage.ensure_alpha_channel(fgdata, alpha=template) data = kwimage.overlay_alpha_images(fga, chip, keepalpha=False) else: data = None mask = (template > 0.05).astype(np.uint8) return data, mask, kpts
def overlay_colorized(colorized, orig, alpha=.6, keepcolors=False): """ Overlays a color segmentation mask on an original image Args: colorized (ndarray): the color mask to be overlayed on top of the original image orig (ndarray): the original image to superimpose on alpha (float): blend level to use if colorized is not an alpha image """ import kwimage color_mask = kwimage.ensure_alpha_channel(colorized, alpha=alpha) if not keepcolors: orig = ensure_grayscale(orig) color_blend = kwimage.overlay_alpha_images(color_mask, orig) color_blend = (color_blend * 255).astype(np.uint8) return color_blend
def draw_on(self, image, color='blue', fill=True, border=False, alpha=1.0, copy=False): """ Rasterizes a polygon on an image. See `draw` for a vectorized matplotlib version. Args: image (ndarray): image to raster polygon on. color (str | tuple): data coercable to a color fill (bool, default=True): draw the center mass of the polygon border (bool, default=False): draw the border of the polygon alpha (float, default=1.0): polygon transparency (setting alpha < 1 makes this function much slower). copy (bool, default=False): if False only copies if necessary Example: >>> # xdoc: +REQUIRES(module:kwplot) >>> from kwimage.structs.polygon import * # NOQA >>> self = Polygon.random(n_holes=1).scale(128) >>> image = np.zeros((128, 128), dtype=np.float32) >>> image = self.draw_on(image) >>> # xdoc: +REQUIRES(--show) >>> import kwplot >>> kwplot.autompl() >>> kwplot.imshow(image, fnum=1) Example: >>> import kwimage >>> color = 'blue' >>> self = kwimage.Polygon.random(n_holes=1).scale(128) >>> image = np.zeros((128, 128), dtype=np.float32) >>> # Test drawong on all channel + dtype combinations >>> im3 = np.random.rand(128, 128, 3) >>> im_chans = { >>> 'im3': im3, >>> 'im1': kwimage.convert_colorspace(im3, 'rgb', 'gray'), >>> 'im4': kwimage.convert_colorspace(im3, 'rgb', 'rgba'), >>> } >>> inputs = {} >>> for k, im in im_chans.items(): >>> inputs[k + '_01'] = (kwimage.ensure_float01(im.copy()), {'alpha': None}) >>> inputs[k + '_255'] = (kwimage.ensure_uint255(im.copy()), {'alpha': None}) >>> inputs[k + '_01_a'] = (kwimage.ensure_float01(im.copy()), {'alpha': 0.5}) >>> inputs[k + '_255_a'] = (kwimage.ensure_uint255(im.copy()), {'alpha': 0.5}) >>> outputs = {} >>> for k, v in inputs.items(): >>> im, kw = v >>> outputs[k] = self.draw_on(im, color=color, **kw) >>> # xdoc: +REQUIRES(--show) >>> import kwplot >>> kwplot.figure(fnum=2, doclf=True) >>> kwplot.autompl() >>> pnum_ = kwplot.PlotNums(nCols=2, nRows=len(inputs)) >>> for k in inputs.keys(): >>> kwplot.imshow(inputs[k][0], fnum=2, pnum=pnum_(), title=k) >>> kwplot.imshow(outputs[k], fnum=2, pnum=pnum_(), title=k) >>> kwplot.show_if_requested() """ import kwimage # return shape of contours to openCV contours dtype_fixer = _generic._consistent_dtype_fixer(image) # print('--- A') # print('image.dtype = {!r}'.format(image.dtype)) # print('image.max() = {!r}'.format(image.max())) # line_type = cv2.LINE_AA line_type = cv2.LINE_8 cv_contours = self._to_cv_countours() if alpha is None or alpha == 1.0: # image = kwimage.ensure_uint255(image) image = kwimage.atleast_3channels(image, copy=copy) rgba = kwimage.Color(color)._forimage(image) else: image = kwimage.ensure_float01(image) image = kwimage.ensure_alpha_channel(image) rgba = kwimage.Color(color, alpha=alpha)._forimage(image) # print('--- B') # print('image.dtype = {!r}'.format(image.dtype)) # print('image.max() = {!r}'.format(image.max())) # print('rgba = {!r}'.format(rgba)) if fill: if alpha is None or alpha == 1.0: # Modification happens inplace image = cv2.fillPoly(image, cv_contours, rgba, line_type, shift=0) else: orig = image.copy() mask = np.zeros_like(orig) mask = cv2.fillPoly(mask, cv_contours, rgba, line_type, shift=0) # TODO: could use add weighted image = kwimage.overlay_alpha_images(mask, orig) rgba = kwimage.Color(rgba)._forimage(image) # print('--- C') # print('image.dtype = {!r}'.format(image.dtype)) # print('image.max() = {!r}'.format(image.max())) # print('rgba = {!r}'.format(rgba)) if border or True: thickness = 4 contour_idx = -1 image = cv2.drawContours(image, cv_contours, contour_idx, rgba, thickness, line_type) # image = kwimage.ensure_float01(image)[..., 0:3] # print('--- D') # print('image.dtype = {!r}'.format(image.dtype)) # print('image.max() = {!r}'.format(image.max())) image = dtype_fixer(image, copy=False) return image
def draw_vector_field(image, dx, dy, stride=0.02, thresh=0.0, scale=1.0, alpha=1.0, color='red', thickness=1, tipLength=0.1, line_type='aa'): """ Create an image representing a 2D vector field. Args: image (ndarray): image to draw on dx (ndarray): grid of vector x components dy (ndarray): grid of vector y components stride (int | float): sparsity of vectors, int specifies stride step in pixels, a float specifies it as a percentage. thresh (float): only plot vectors with magnitude greater than thres scale (float): multiply magnitude for easier visualization alpha (float): alpha value for vectors. Non-vector regions receive 0 alpha (if False, no alpha channel is used) color (str | tuple | kwimage.Color): RGB color of the vectors thickness (int, default=1): thickness of arrows tipLength (float, default=0.1): fraction of line length line_type (int): either cv2.LINE_4, cv2.LINE_8, or cv2.LINE_AA Returns: ndarray[float32]: The image with vectors overlaid. If image=None, then an rgb/a image is created and returned. Example: >>> import kwimage >>> width, height = 512, 512 >>> image = kwimage.grab_test_image(dsize=(width, height)) >>> x, y = np.meshgrid(np.arange(height), np.arange(width)) >>> dx, dy = x - width / 2, y - height / 2 >>> radians = np.arctan2(dx, dy) >>> mag = np.sqrt(dx ** 2 + dy ** 2) + 1e-3 >>> dx, dy = dx / mag, dy / mag >>> img = kwimage.draw_vector_field(image, dx, dy, scale=10, alpha=False) >>> # xdoctest: +REQUIRES(--show) >>> import kwplot >>> kwplot.autompl() >>> kwplot.imshow(img) >>> kwplot.show_if_requested() """ import cv2 import kwimage if image is None: # Create a default image image = np.zeros(dx.shape + (3, ), dtype=np.uint8) # image = kwimage.atleast_3channels(image) color = kwimage.Color(color)._forimage(image) line_type_lookup = {'aa': cv2.LINE_AA} line_type = line_type_lookup.get(line_type, line_type) height, width = dx.shape[0:2] x_grid = np.arange(0, width, 1) y_grid = np.arange(0, height, 1) # Vector locations and directions X, Y = np.meshgrid(x_grid, y_grid) U, V = dx, dy XYUV = [X, Y, U, V] if isinstance(stride, float): if stride < 0 or stride > 1: raise ValueError('Floating point strides must be between 0 and 1') stride = int(np.ceil(stride * min(width, height))) # stride the points if stride is not None and stride > 1: XYUV = [a[::stride, ::stride] for a in XYUV] # flatten the points XYUV = [a.ravel() for a in XYUV] # Filter out points with low magnitudes if thresh is not None and thresh > 0: M = np.sqrt((XYUV[2]**2) + (XYUV[3]**2)).ravel() XYUV = np.array(XYUV) flags = M > thresh XYUV = [a[flags] for a in XYUV] # Adjust vector magnitude for visibility if scale is not None: XYUV[2] *= scale XYUV[3] *= scale if alpha is not None and alpha is not False and alpha != 1: raise NotImplementedError for (x, y, u, v) in zip(*XYUV): pt1 = (int(x), int(y)) pt2 = tuple(map(int, map(np.round, (x + u, y + v)))) cv2.arrowedLine(image, pt1, pt2, color=color, thickness=thickness, tipLength=tipLength, line_type=line_type) if isinstance(alpha, np.ndarray): # Alpha specified as explicit numpy array image = kwimage.ensure_float01(image) image = kwimage.ensure_alpha_channel(image) image[:, :, 3] = alpha elif alpha is not False and alpha is not None: # Alpha specified as a scale factor image = kwimage.ensure_float01(image) image = kwimage.ensure_alpha_channel(image) # image[:, :, 3] = (image[:, :, 0:3].sum(axis=2) > 0) * alpha image[:, :, 3] = image[:, :, 0:3].sum(axis=2) * alpha return image
def make_vector_field(dx, dy, stride=0.02, thresh=0.0, scale=1.0, alpha=1.0, color='red', thickness=1, tipLength=0.1, line_type='aa'): """ Create an image representing a 2D vector field. Args: dx (ndarray): grid of vector x components dy (ndarray): grid of vector y components stride (int | float): sparsity of vectors, int specifies stride step in pixels, a float specifies it as a percentage. thresh (float): only plot vectors with magnitude greater than thres scale (float): multiply magnitude for easier visualization alpha (float): alpha value for vectors. Non-vector regions receive 0 alpha (if False, no alpha channel is used) color (str | tuple | kwimage.Color): RGB color of the vectors thickness (int, default=1): thickness of arrows tipLength (float, default=0.1): fraction of line length line_type (int): either cv2.LINE_4, cv2.LINE_8, or cv2.LINE_AA Returns: ndarray[float32]: vec_img: an rgb/rgba image in 0-1 space SeeAlso: kwimage.overlay_alpha_images DEPRECATED USE: draw_vector_field instead Example: >>> x, y = np.meshgrid(np.arange(512), np.arange(512)) >>> dx, dy = x - 256.01, y - 256.01 >>> radians = np.arctan2(dx, dy) >>> mag = np.sqrt(dx ** 2 + dy ** 2) >>> dx, dy = dx / mag, dy / mag >>> img = make_vector_field(dx, dy, scale=10, alpha=False) >>> # xdoctest: +REQUIRES(--show) >>> import kwplot >>> kwplot.autompl() >>> kwplot.imshow(img) >>> kwplot.show_if_requested() """ import warnings warnings.warn('Deprecated, use draw_vector_field instead', DeprecationWarning) import cv2 import kwimage color = kwimage.Color(color).as255('rgb') vecmask = np.zeros(dx.shape + (3, ), dtype=np.uint8) line_type_lookup = {'aa': cv2.LINE_AA} line_type = line_type_lookup.get(line_type, line_type) width = dx.shape[1] height = dy.shape[0] x_grid = np.arange(0, width, 1) y_grid = np.arange(0, height, 1) # Vector locations and directions X, Y = np.meshgrid(x_grid, y_grid) U, V = dx, dy XYUV = [X, Y, U, V] if isinstance(stride, float): if stride < 0 or stride > 1: raise ValueError('Floating point strides must be between 0 and 1') stride = int(np.ceil(stride * min(width, height))) # stride the points if stride is not None and stride > 1: XYUV = [a[::stride, ::stride] for a in XYUV] # flatten the points XYUV = [a.ravel() for a in XYUV] # Filter out points with low magnitudes if thresh is not None and thresh > 0: M = np.sqrt((XYUV[2]**2) + (XYUV[3]**2)).ravel() XYUV = np.array(XYUV) flags = M > thresh XYUV = [a[flags] for a in XYUV] # Adjust vector magnitude for visibility if scale is not None: XYUV[2] *= scale XYUV[3] *= scale for (x, y, u, v) in zip(*XYUV): pt1 = (int(x), int(y)) pt2 = tuple(map(int, map(np.round, (x + u, y + v)))) cv2.arrowedLine(vecmask, pt1, pt2, color=color, thickness=thickness, tipLength=tipLength, line_type=line_type) vecmask = kwimage.ensure_float01(vecmask) if isinstance(alpha, np.ndarray): # Alpha specified as explicit numpy array vecmask = kwimage.ensure_alpha_channel(vecmask) vecmask[:, :, 3] = alpha elif alpha is not False and alpha is not None: # Alpha specified as a scale factor vecmask = kwimage.ensure_alpha_channel(vecmask) # vecmask[:, :, 3] = (vecmask[:, :, 0:3].sum(axis=2) > 0) * alpha vecmask[:, :, 3] = vecmask[:, :, 0:3].sum(axis=2) * alpha return vecmask