kwimage.structs.heatmap module

Todo

  • [ ] Remove doctest dependency on ndsampler?

  • [ ] Remove the datakeys that tries to define what heatmap should represent

    (e.g. class_probs, keypoints, etc…) and instead just focus on a data structure that stores a [C, H, W] or [H, W] tensor?

CommandLine

xdoctest -m ~/code/kwimage/kwimage/structs/heatmap.py __doc__

Example

>>> # xdoctest: +REQUIRES(module:ndsampler)
>>> # xdoctest: +REQUIRES(--mask)
>>> from kwimage.structs.heatmap import *  # NOQA
>>> import kwimage
>>> import ndsampler
>>> sampler = ndsampler.CocoSampler.demo('shapes')
>>> iminfo, anns = sampler.load_image_with_annots(1)
>>> image = iminfo['imdata']
>>> input_dims = image.shape[0:2]
>>> kp_classes = sampler.dset.keypoint_categories()
>>> dets = kwimage.Detections.from_coco_annots(
>>>     anns, sampler.dset.dataset['categories'],
>>>     sampler.catgraph, kp_classes, shape=input_dims)
>>> bg_size = [100, 100]
>>> heatmap = dets.rasterize(bg_size, input_dims, soften=2)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.figure(fnum=1, doclf=True)
>>> kwplot.imshow(image)
>>> heatmap.draw(invert=True, kpts=[0, 1, 2, 3, 4])

Example

>>> # xdoctest: +REQUIRES(module:ndsampler)
>>> # xdoctest: +REQUIRES(--mask)
>>> from kwimage.structs.heatmap import *  # NOQA
>>> from kwimage.structs.detections import _dets_to_fcmaps
>>> import kwimage
>>> import ndsampler
>>> sampler = ndsampler.CocoSampler.demo('shapes')
>>> iminfo, anns = sampler.load_image_with_annots(1)
>>> image = iminfo['imdata']
>>> input_dims = image.shape[0:2]
>>> kp_classes = sampler.dset.keypoint_categories()
>>> dets = kwimage.Detections.from_coco_annots(
>>>     anns, sampler.dset.dataset['categories'],
>>>     sampler.catgraph, kp_classes, shape=input_dims)
>>> bg_size = [100, 100]
>>> bg_idxs = sampler.catgraph.index('background')
>>> fcn_target = _dets_to_fcmaps(dets, bg_size, input_dims, bg_idxs)
>>> fcn_target.keys()
>>> print('fcn_target: ' + ub.urepr(ub.map_vals(lambda x: x.shape, fcn_target), nl=1))
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> size_mask = fcn_target['size']
>>> dxdy_mask = fcn_target['dxdy']
>>> cidx_mask = fcn_target['cidx']
>>> kpts_mask = fcn_target['kpts']
>>> def _vizmask(dxdy_mask):
>>>     dx, dy = dxdy_mask
>>>     mag = np.sqrt(dx ** 2 + dy ** 2)
>>>     mag /= (mag.max() + 1e-9)
>>>     mask = (cidx_mask != 0).astype(np.float32)
>>>     angle = np.arctan2(dy, dx)
>>>     orimask = kwplot.make_orimask(angle, mask, alpha=mag)
>>>     vecmask = kwplot.make_vector_field(
>>>         dx, dy, stride=4, scale=0.1, thickness=1, tipLength=.2,
>>>         line_type=16)
>>>     return [vecmask, orimask]
>>> vecmask, orimask = _vizmask(dxdy_mask)
>>> raster = kwimage.overlay_alpha_layers(
>>>     [vecmask, orimask, image], keepalpha=False)
>>> raster = dets.draw_on((raster * 255).astype(np.uint8),
>>>                       labels=True, alpha=None)
>>> kwplot.imshow(raster)
>>> kwplot.show_if_requested()
class kwimage.structs.heatmap._HeatmapDrawMixin[source]

Bases: object

mixin methods for drawing heatmap details

_colorize_class_idx()[source]
colorize(channel=None, invert=False, with_alpha=1.0, interpolation='linear', imgspace=False, cmap=None)[source]

Creates a colorized version of a heatmap channel suitable for visualization

Parameters:
  • channel (int | str) – index of category to visualize, or a special code indicating how to visualize multiple classes. Can be class_idx, class_probs, or class_energy.

  • imgspace (bool) – colorize the image after warping into the image space.

CommandLine

xdoctest -m ~/code/kwimage/kwimage/structs/heatmap.py _HeatmapDrawMixin.colorize --show

Example

>>> # xdoctest: +REQUIRES(module:kwplot)
>>> self = Heatmap.random(rng=0, dims=(32, 32))
>>> colormask1 = self.colorize(0, imgspace=False)
>>> colormask2 = self.colorize(0, imgspace=True)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(colormask1, pnum=(1, 2, 1), fnum=1, title='output space')
>>> kwplot.imshow(colormask2, pnum=(1, 2, 2), fnum=1, title='image space')
>>> kwplot.show_if_requested()
_images/fig_kwimage_structs_heatmap__HeatmapDrawMixin_colorize_002.jpeg

Example

>>> # xdoctest: +REQUIRES(module:kwplot)
>>> self = Heatmap.random(rng=0, dims=(32, 32))
>>> colormask1 = self.colorize('diameter', imgspace=False)
>>> colormask2 = self.colorize('diameter', imgspace=True)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(colormask1, pnum=(1, 2, 1), fnum=1, title='output space')
>>> kwplot.imshow(colormask2, pnum=(1, 2, 2), fnum=1, title='image space')
>>> kwplot.show_if_requested()
_images/fig_kwimage_structs_heatmap__HeatmapDrawMixin_colorize_003.jpeg
draw_stacked(image=None, dsize=(224, 224), ignore_class_idxs={}, top=None, chosen_cxs=None)[source]

Draws per-class probabilities and stacks them into a single image

Example

>>> # xdoctest: +REQUIRES(module:kwplot)
>>> self = Heatmap.random(rng=0, dims=(32, 32))
>>> stacked = self.draw_stacked()
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(stacked)
_images/fig_kwimage_structs_heatmap__HeatmapDrawMixin_draw_stacked_002.jpeg
draw(channel=None, image=None, imgspace=None, **kwargs)[source]

Accepts same args as draw_on, but uses maplotlib

Parameters:

channel (int | str) – category index to visualize, or special key

draw_on(image=None, channel=None, invert=False, with_alpha=1.0, interpolation='linear', vecs=False, kpts=None, imgspace=None)[source]

Overlays a heatmap channel on top of an image

Parameters:
  • image (ndarray) – image to draw on, if unspecified one is created.

  • channel (int | str) – category index to visualize, or special key. special keys are: class_idx, class_probs, class_idx

  • imgspace (bool) – colorize the image after warping into the image space.

Todo

  • [ ] Find a way to visualize offset, diameter, and class_probs

    either individually or all at the same time

CommandLine:

xdoctest -m /home/joncrall/code/kwimage/kwimage/structs/heatmap.py

Example

>>> # xdoctest: +REQUIRES(module:kwplot)
>>> import kwarray
>>> import kwimage
>>> image = kwimage.grab_test_image('astro')
>>> probs = kwimage.gaussian_patch(image.shape[0:2])[None, :]
>>> probs = probs / probs.max()
>>> class_probs = kwarray.ArrayAPI.cat([probs, 1 - probs], axis=0)
>>> self = kwimage.Heatmap(class_probs=class_probs, offset=5 * np.random.randn(2, *probs.shape[1:]))
>>> toshow = self.draw_on(image, 0, vecs=True, with_alpha=0.85)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(toshow)
_images/fig_kwimage_structs_heatmap__HeatmapDrawMixin_draw_on_002.jpeg

Example

>>> # xdoctest: +REQUIRES(module:kwplot)
>>> # xdoctest: +REQUIRES(module:ndsampler)
>>> import kwimage
>>> self = kwimage.Heatmap.random(dims=(200, 200), dets='coco', keypoints=True)
>>> image = kwimage.grab_test_image('astro')
>>> toshow = self.draw_on(image, 0, vecs=False, with_alpha=0.85)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(toshow)

Example

>>> # xdoctest: +REQUIRES(module:kwplot)
>>> # xdoctest: +REQUIRES(module:ndsampler)
>>> import kwimage
>>> self = kwimage.Heatmap.random(dims=(200, 200), dets='coco', keypoints=True)
>>> kpts = [6]
>>> self = self.warp(self.tf_data_to_img.params)
>>> image = kwimage.grab_test_image('astro')
>>> image = kwimage.ensure_alpha_channel(image)
>>> toshow = self.draw_on(image, 0, with_alpha=0.85, kpts=kpts)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(toshow)

Example

>>> # xdoctest: +REQUIRES(module:kwplot)
>>> # xdoctest: +REQUIRES(module:ndsampler)
>>> import kwimage
>>> mask = np.random.rand(32, 32)
>>> self = kwimage.Heatmap(
>>>     class_probs=mask,
>>>     img_dims=mask.shape[0:2],
>>>     tf_data_to_img=np.eye(3),
>>> )
>>> canvas = self.draw_on()
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(canvas)
class kwimage.structs.heatmap._HeatmapWarpMixin[source]

Bases: object

mixin method having to do with warping and aligning heatmaps

_align_other(other)[source]

Warp another Heatmap (with the same underlying imgdims) into the same space as this heatmap. This lets us perform elementwise operations on the two heatmaps (like geometric mean).

Parameters:

other (Heatmap) – the heatmap to align with self

Returns:

warped version of other that aligns with self.

Return type:

Heatmap

Example

>>> # xdoctest: +REQUIRES(module:torch)
>>> from kwimage.structs.heatmap import *  # NOQA
>>> self = Heatmap.random((120, 130), img_dims=(200, 210), classes=2, nblips=10, rng=0)
>>> other = Heatmap.random((60, 70), img_dims=(200, 210), classes=2, nblips=10, rng=1)
>>> other2 = self._align_other(other)
>>> assert self.shape != other.shape
>>> assert self.shape == other2.shape
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(self.colorize(0, imgspace=False), fnum=1, pnum=(2, 2, 1))
>>> kwplot.imshow(self.colorize(1, imgspace=False), fnum=1, pnum=(2, 2, 2))
>>> kwplot.imshow(other.colorize(0, imgspace=False), fnum=1, pnum=(2, 2, 3))
>>> kwplot.imshow(other.colorize(1, imgspace=False), fnum=1, pnum=(2, 2, 4))
_images/fig_kwimage_structs_heatmap__HeatmapWarpMixin__align_other_002.jpeg
_align(mask, interpolation='linear')[source]

Align a linear combination of heatmap channels with the original image

DEPRICATE

_warp_imgspace(chw, interpolation='linear')[source]
upscale(channel=None, interpolation='linear')[source]

Warp the heatmap with the image dimensions

Parameters:

channel (ndarray | None) – if None, use class probs, else chw data.

Todo

  • [ ] Needs refactor

Example

>>> # xdoctest: +REQUIRES(module:torch)
>>> self = Heatmap.random(rng=0, dims=(32, 32))
>>> colormask = self.upscale()
warp(mat=None, input_dims=None, output_dims=None, interpolation='linear', modify_spatial_coords=True, int_interpolation='nearest', mat_is_xy=True, version=None)[source]

Warp all spatial maps. If the map contains spatial data, that data is also warped (ignoring the translation component).

Parameters:
  • mat (ArrayLike) – transformation matrix

  • input_dims (tuple) – unused, only exists for compatibility

  • output_dims (tuple) – size of the output heatmap

  • interpolation (str) – see kwimage.warp_tensor

  • int_interpolation (str) – interpolation used for interger types (should be nearest)

  • mat_is_xy (bool) – set to false if the matrix is in yx space instead of xy space

Returns:

this heatmap warped into a new spatial dimension

Return type:

Heatmap

Example

>>> # xdoctest: +REQUIRES(module:torch)
>>> from kwimage.structs.heatmap import *  # NOQA
>>> self = Heatmap.random(rng=0, keypoints=True)
>>> S = 3.0
>>> mat = np.eye(3) * S
>>> mat[-1, -1] = 1
>>> newself = self.warp(mat, np.array(self.dims) * S).numpy()
>>> assert newself.offset.shape[0] == 2
>>> assert newself.diameter.shape[0] == 2
>>> f1 = newself.offset.max() / self.offset.max()
>>> assert f1 == S
>>> f2 = newself.diameter.max() / self.diameter.max()
>>> assert f2 == S

Example

>>> import kwimage
>>> # xdoctest: +REQUIRES(module:ndsampler)
>>> self = kwimage.Heatmap.random(dims=(100, 100), dets='coco', keypoints=True)
>>> image = np.zeros(self.img_dims)
>>> # xdoctest: +REQUIRES(module:kwplot)
>>> toshow = self.draw_on(image, 1, vecs=True, with_alpha=0.85)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.figure(fnum=1, doclf=True)
>>> kwplot.imshow(toshow)
scale(factor, output_dims=None, interpolation='linear')[source]

Scale the heatmap

translate(offset, output_dims=None, interpolation='linear')[source]
class kwimage.structs.heatmap._HeatmapAlgoMixin[source]

Bases: object

Algorithmic operations on heatmaps

classmethod combine(heatmaps, root_index=None, dtype=<class 'numpy.float32'>)[source]

Combine multiple heatmaps into a single heatmap.

Parameters:
  • heatmaps (Sequence[Heatmap]) – multiple heatmaps to combine into one

  • root_index (int) – which heatmap in the sequence to align other heatmaps with

Returns:

the combined heatmap

Return type:

Heatmap

Example

>>> # xdoctest: +REQUIRES(module:torch)
>>> from kwimage.structs.heatmap import *  # NOQA
>>> a = Heatmap.random((120, 130), img_dims=(200, 210), classes=2, nblips=10, rng=0)
>>> b = Heatmap.random((60, 70), img_dims=(200, 210), classes=2, nblips=10, rng=1)
>>> c = Heatmap.random((40, 30), img_dims=(200, 210), classes=2, nblips=10, rng=1)
>>> heatmaps = [a, b, c]
>>> newself = Heatmap.combine(heatmaps, root_index=2)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(a.colorize(0, imgspace=1), fnum=1, pnum=(4, 2, 1))
>>> kwplot.imshow(a.colorize(1, imgspace=1), fnum=1, pnum=(4, 2, 2))
>>> kwplot.imshow(b.colorize(0, imgspace=1), fnum=1, pnum=(4, 2, 3))
>>> kwplot.imshow(b.colorize(1, imgspace=1), fnum=1, pnum=(4, 2, 4))
>>> kwplot.imshow(c.colorize(0, imgspace=1), fnum=1, pnum=(4, 2, 5))
>>> kwplot.imshow(c.colorize(1, imgspace=1), fnum=1, pnum=(4, 2, 6))
>>> kwplot.imshow(newself.colorize(0, imgspace=1), fnum=1, pnum=(4, 2, 7))
>>> kwplot.imshow(newself.colorize(1, imgspace=1), fnum=1, pnum=(4, 2, 8))
>>> # xdoctest: +REQUIRES(--show)
>>> kwplot.imshow(a.colorize('offset', imgspace=1), fnum=2, pnum=(4, 1, 1))
>>> kwplot.imshow(b.colorize('offset', imgspace=1), fnum=2, pnum=(4, 1, 2))
>>> kwplot.imshow(c.colorize('offset', imgspace=1), fnum=2, pnum=(4, 1, 3))
>>> kwplot.imshow(newself.colorize('offset', imgspace=1), fnum=2, pnum=(4, 1, 4))
>>> # xdoctest: +REQUIRES(--show)
>>> kwplot.imshow(a.colorize('diameter', imgspace=1), fnum=3, pnum=(4, 1, 1))
>>> kwplot.imshow(b.colorize('diameter', imgspace=1), fnum=3, pnum=(4, 1, 2))
>>> kwplot.imshow(c.colorize('diameter', imgspace=1), fnum=3, pnum=(4, 1, 3))
>>> kwplot.imshow(newself.colorize('diameter', imgspace=1), fnum=3, pnum=(4, 1, 4))
_images/fig_kwimage_structs_heatmap__HeatmapAlgoMixin_combine_002.jpeg _images/fig_kwimage_structs_heatmap__HeatmapAlgoMixin_combine_003.jpeg _images/fig_kwimage_structs_heatmap__HeatmapAlgoMixin_combine_004.jpeg
detect(channel, invert=False, min_score=0.01, num_min=10, max_dims=None, min_dims=None, dim_thresh_space='image')[source]

Lossy conversion from a Heatmap to a Detections object.

For efficiency, the detections are returned in the same space as the heatmap, which usually some downsampled version of the image space. This is because it is more efficient to transform the detections into image-space after non-max supression is applied.

Parameters:
  • channel (int | ArrayLike) – class index to detect objects in. Alternatively, channel can be a custom probability map as long as its dimension agree with the heatmap.

  • invert (bool) – if True, inverts the probabilities in the chosen channel. (Useful if you have a background channel but want to detect foreground objects).

  • min_score (float) – probability threshold required for a pixel to be converted into a detection. Defaults to 0.1

  • num_min (int) – always return at least nmin of the highest scoring detections even if they aren’t above the min_score threshold. Defaults to 10.

  • max_dims (Tuple[int, int]) – maximum height / width of detections By default these are expected to be in image-space.

  • min_dims (Tuple[int, int]) – minimum height / width of detections By default these are expected to be in image-space.

  • dim_thresh_space (str) – When dim_thresh_space==’native’, dimension thresholds (e.g. min_dims and max_dims) are specified in the native heatmap space (i.e. usually a downsampled space). If dim_thresh_space==’image’, then dimension thresholds are interpreted in the original image space. Defaults to ‘image’

Returns:

raw detections.

Note that these detections will not have class_idx populated

It is the users responsbility to run non-max suppression on these results to remove duplicate detections.

Return type:

kwimage.Detections

SeeAlso:

Detections.rasterize

Example

>>> # xdoctest: +REQUIRES(module:ndsampler)
>>> from kwimage.structs.heatmap import *  # NOQA
>>> import ndsampler
>>> self = Heatmap.random(rng=2, dims=(32, 32))
>>> dets = self.detect(channel=0, max_dims=7, num_min=None)
>>> img_dets = dets.warp(self.tf_data_to_img)
>>> assert img_dets.boxes.to_xywh().width.max() <= 7
>>> assert img_dets.boxes.to_xywh().height.max() <= 7
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> dets1 = dets.sort().take(range(30))
>>> colormask1 = self.colorize(0, imgspace=False)
>>> kwplot.imshow(colormask1, pnum=(1, 2, 1), fnum=1, title='output space')
>>> dets1.draw()
>>> # Transform heatmap and detections into image space.
>>> dets2 = dets1.warp(self.tf_data_to_img)
>>> colormask2 = self.colorize(0, imgspace=True)
>>> kwplot.imshow(colormask2, pnum=(1, 2, 2), fnum=1, title='image space')
>>> dets2.draw()

Example

>>> # xdoctest: +REQUIRES(module:ndsampler)
>>> from kwimage.structs.heatmap import *  # NOQA
>>> import torch
>>> import ndsampler
>>> catgraph = ndsampler.CategoryTree.demo()
>>> class_energy = torch.rand(len(catgraph), 32, 32)
>>> class_probs = catgraph.hierarchical_softmax(class_energy, dim=0)
>>> self = Heatmap.random(rng=0, dims=(32, 32), classes=catgraph, keypoints=True)
>>> print(ub.urepr(ub.map_vals(lambda x: x.shape, self.data), nl=1))
>>> self.data['class_probs'] = class_probs.numpy()
>>> channel = catgraph.index('background')
>>> dets = self.detect(channel, invert=True)
>>> class_idx, scores = catgraph.decision(dets.probs, dim=1)
>>> dets.data['class_idx'] = class_idx
>>> dets.data['scores'] = scores
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> dets1 = dets.sort().take(range(10))
>>> colormask1 = self.colorize(0, imgspace=False)
>>> kwplot.imshow(colormask1, pnum=(1, 2, 1), fnum=1, title='output space')
>>> dets1.draw(radius=1.0)
>>> # Transform heatmap and detections into image space.
>>> colormask2 = self.colorize(0, imgspace=True)
>>> dets2 = dets1.warp(self.tf_data_to_img)
>>> kwplot.imshow(colormask2, pnum=(1, 2, 2), fnum=1, title='image space')
>>> dets2.draw(radius=1.0)
class kwimage.structs.heatmap.Heatmap(data=None, meta=None, **kwargs)[source]

Bases: Spatial, _HeatmapDrawMixin, _HeatmapWarpMixin, _HeatmapAlgoMixin

Keeps track of a downscaled heatmap and how to transform it to overlay the original input image. Heatmaps generally are used to estimate class probabilites at each pixel. This data struction additionally contains logic to augment pixel with offset (dydx) and scale (diamter) information.

Variables:
  • data (Dict[str, ArrayLike]) –

    dictionary containing spatially aligned heatmap data. Valid keys are as follows.

    class_probs (ArrayLike[C, H, W] | ArrayLike[C, D, H, W]):

    A probability map for each class. C is the number of classes.

    offset (ArrayLike[2, H, W] | ArrayLike[3, D, H, W], optional):

    object center position offset in y,x / t,y,x coordinates

    diamter (ArrayLike[2, H, W] | ArrayLike[3, D, H, W], optional):

    object bounding box sizes in h,w / d,h,w coordinates

    keypoints (ArrayLike[2, K, H, W] | ArrayLike[3, K, D, H, W], optional):

    y/x offsets for K different keypoint classes

  • meta (Dict[str, object]) –

    dictionary containing miscellanious metadata about the heatmap data. Valid keys are as follows.

    img_dims (Tuple[H, W] | Tuple[D, H, W]):

    original image dimension

    tf_data_to_image (SKImageGeometricTransform):

    transformation matrix (typically similarity or affine) that projects the given, heatmap onto the image dimensions such that the image and heatmap are spatially aligned.

    classes (List[str] | ndsampler.CategoryTree):

    information about which index in data['class_probs'] corresponds to which semantic class.

  • dims (Tuple) – dimensions of the heatmap (See image_dims) for the original image dimensions.

  • **kwargs – any key that is accepted by the data or meta dictionaries can be specified as a keyword argument to this class and it will be properly placed in the appropriate internal dictionary.

CommandLine

xdoctest -m ~/code/kwimage/kwimage/structs/heatmap.py Heatmap --show

Example

>>> # xdoctest: +REQUIRES(module:torch)
>>> from kwimage.structs.heatmap import *  # NOQA
>>> import skimage
>>> import kwimage
>>> class_probs = kwimage.grab_test_image(dsize=(32, 32), space='gray')[None, ..., 0] / 255.0
>>> img_dims = (220, 220)
>>> tf_data_to_img = skimage.transform.AffineTransform(translation=(-18, -18), scale=(8, 8))
>>> self = Heatmap(class_probs=class_probs, img_dims=img_dims,
>>>                tf_data_to_img=tf_data_to_img)
>>> aligned = self.upscale()
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(aligned[0])
>>> kwplot.show_if_requested()
_images/fig_kwimage_structs_heatmap_Heatmap_002.jpeg

Example

>>> # xdoctest: +REQUIRES(module:torch)
>>> import kwimage
>>> self = Heatmap.random()
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> self.draw()
_images/fig_kwimage_structs_heatmap_Heatmap_003.jpeg
property shape
property bounds
property dims

space-time dimensions of this heatmap

is_numpy()[source]
is_tensor()[source]
property _impl

Returns the internal tensor/numpy ArrayAPI implementation

Returns:

kwarray.ArrayAPI

classmethod random(dims=(10, 10), classes=3, diameter=True, offset=True, keypoints=False, img_dims=None, dets=None, nblips=10, noise=0.0, smooth_k=3, rng=None, ensure_background=True)[source]

Creates dummy data, suitable for use in tests and benchmarks

Parameters:
  • dims (Tuple[int, int]) – dimensions of the heatmap

  • classes (int | List[str] | kwcoco.CategoryTree) – foreground classes

  • diameter (bool) – if True, include a “diameter” heatmap

  • offset (bool) – if True, include an “offset” heatmap

  • keypoints (bool)

  • smooth_k (int) – kernel size for gaussian blur to smooth out the heatmaps.

  • img_dims (Tuple) – dimensions of an upscaled image the heatmap corresponds to. (This should be removed and simply handled with a transform

    in the future).

Returns:

Heatmap

Example

>>> from kwimage.structs.heatmap import *  # NOQA
>>> self = Heatmap.random((128, 128), img_dims=(200, 200),
>>>     classes=3, nblips=10, rng=0, noise=0.1)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(self.colorize(0, imgspace=0), fnum=1, pnum=(1, 4, 1), doclf=1)
>>> kwplot.imshow(self.colorize(1, imgspace=0), fnum=1, pnum=(1, 4, 2))
>>> kwplot.imshow(self.colorize(2, imgspace=0), fnum=1, pnum=(1, 4, 3))
>>> kwplot.imshow(self.colorize(3, imgspace=0), fnum=1, pnum=(1, 4, 4))
_images/fig_kwimage_structs_heatmap_Heatmap_random_002.jpeg

Example

>>> # xdoctest: +REQUIRES(module:ndsampler)
>>> import kwimage
>>> self = kwimage.Heatmap.random(dims=(50, 200), dets='coco',
>>>                               keypoints=True)
>>> image = np.zeros(self.img_dims)
>>> # xdoctest: +REQUIRES(module:kwplot)
>>> toshow = self.draw_on(image, 1, vecs=True, kpts=0, with_alpha=0.85)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.figure(fnum=1, doclf=True)
>>> kwplot.imshow(toshow)
property class_probs
property offset
property diameter
property img_dims
property tf_data_to_img
property classes
numpy()[source]

Converts underlying data to numpy arrays

tensor(device=NoParam)[source]

Converts underlying data to torch tensors

kwimage.structs.heatmap._prob_to_dets(probs, diameter=None, offset=None, class_probs=None, keypoints=None, min_score=0.01, num_min=10, max_dims=None, min_dims=None)[source]

Directly convert a one-channel probability map into a Detections object.

Helper for Heatmap.detect

It does this by converting each pixel above a threshold in a probability map to a detection with a specified diameter.

Parameters:
  • probs (ArrayLike[H, W]) – liklihood that each particular pixel should be detected as an object.

  • diameter (ArrayLike[2, H, W] | Tuple) – H, W sizes for the bounding box at each pixel location. If passed as a tuple, then all boxes receive that diameter.

  • offset (Tuple | ArrayLike[2, H, W]) – Y, X offsets from the pixel location to the bounding box center. If passed as a tuple, then all boxes receive that offset.

  • class_probs (ArrayLike[C, H, W], optional) – probabilities for each class at each pixel location. If specified, this will populate the probs attribute of the returned Detections object.

  • keypoints (ArrayLike[2, K, H, W], optional) – Keypoint predictions for all keypoint classes

  • min_score (float) – probability threshold required for a pixel to be converted into a detection. Defaults to 0.1

  • num_min (int) – always return at least nmin of the highest scoring detections even if they aren’t above the min_score threshold. Defaults to 10

Returns:

raw detections. It is the users responsbility to

run non-max suppression on these results to remove duplicate detections.

Return type:

kwimage.Detections

Example

>>> # xdoctest: +REQUIRES(module:torch)
>>> import torch
>>> rng = np.random.RandomState(0)
>>> probs = rng.rand(3, 3).astype(np.float32)
>>> min_score = .5
>>> diameter = [10, 10]
>>> dets = _prob_to_dets(probs, diameter, min_score=min_score)
>>> assert dets.boxes.data.dtype.kind == 'f'
>>> assert len(dets) == 9
>>> dets = _prob_to_dets(torch.FloatTensor(probs), diameter, min_score=min_score)
>>> assert dets.boxes.data.dtype.is_floating_point
>>> assert len(dets) == 9

Example

>>> # xdoctest: +REQUIRES(module:torch)
>>> import kwimage
>>> from kwimage.structs.heatmap import *
>>> from kwimage.structs.heatmap import _prob_to_dets
>>> heatmap = kwimage.Heatmap.random(rng=0, dims=(3, 3), keypoints=True)
>>> # Try with numpy
>>> min_score = .5
>>> dets = _prob_to_dets(heatmap.class_probs[0], heatmap.diameter,
>>>                            heatmap.offset, heatmap.class_probs,
>>>                            heatmap.data['keypoints'],
>>>                            min_score)
>>> assert dets.boxes.data.dtype.kind == 'f'
>>> assert 'keypoints' in dets.data
>>> dets_np = dets
>>> # Try with torch
>>> heatmap = heatmap.tensor()
>>> dets = _prob_to_dets(heatmap.class_probs[0], heatmap.diameter,
>>>                            heatmap.offset, heatmap.class_probs,
>>>                            heatmap.data['keypoints'],
>>>                            min_score)
>>> assert dets.boxes.data.dtype.is_floating_point
>>> assert len(dets) == len(dets_np)
>>> dets_torch = dets
>>> assert np.all(dets_torch.numpy().boxes.data == dets_np.boxes.data)

Example

>>> heatmap = Heatmap.random(rng=0, dims=(3, 3), diameter=1)
>>> probs = heatmap.class_probs[0]
>>> diameter = heatmap.diameter
>>> offset = heatmap.offset
>>> class_probs = heatmap.class_probs
>>> min_score = 0.5
>>> dets = _prob_to_dets(probs, diameter, offset, class_probs, None, min_score)
kwimage.structs.heatmap.smooth_prob(prob, k=3, inplace=False, eps=1e-09)[source]

Smooths the probability map, but preserves the magnitude of the peaks.

Note

even if inplace is true, we still need to make a copy of the input array, however, we do ensure that it is cleaned up before we leave the function scope.

sigma=0.8 @ k=3, sigma=1.1 @ k=5, sigma=1.4 @ k=7

kwimage.structs.heatmap._remove_translation(tf)[source]

Removes the translation component of a transform

Todo

  • [ ] Is this possible in more general cases? E.g. projective transforms?

kwimage.structs.heatmap._gmean(a, axis=0, clobber=False)[source]

Compute the geometric mean along the specified axis.

Modification of the scipy.mstats method to be more memory efficient

Example
>>> rng = np.random.RandomState(0)
>>> C, H, W = 8, 32, 32
>>> axis = 0
>>> a = rng.rand(2, C, H, W)
>>> _gmean(a)