kwimage.structs.points module¶
Data structures to represent and manipulate 2D Points
- class kwimage.structs.points._PointsWarpMixin[source]¶
Bases:
object
- _warp_imgaug(augmenter, input_dims, inplace=False)[source]¶
Warps by applying an augmenter from the imgaug library
- Parameters:
augmenter (imgaug.augmenters.Augmenter)
input_dims (Tuple) – h/w of the input image
inplace (bool) – if True, modifies data inplace
Example
>>> # xdoctest: +REQUIRES(module:imgaug) >>> from kwimage.structs.points import * # NOQA >>> import imgaug >>> input_dims = (10, 10) >>> self = Points.random(10).scale(input_dims) >>> augmenter = imgaug.augmenters.Fliplr(p=1) >>> new = self._warp_imgaug(augmenter, input_dims)
>>> self = Points(xy=(np.random.rand(10, 2) * 10).astype(int)) >>> augmenter = imgaug.augmenters.Fliplr(p=1) >>> new = self._warp_imgaug(augmenter, input_dims)
>>> # xdoctest: +REQUIRES(--show) >>> import kwplot >>> plt = kwplot.autoplt() >>> kwplot.figure(fnum=1, doclf=True) >>> ax = plt.gca() >>> ax.set_xlim(0, 10) >>> ax.set_ylim(0, 10) >>> self.draw(color='red', alpha=.4, radius=0.1) >>> new.draw(color='blue', alpha=.4, radius=0.1)
- to_imgaug(input_dims)[source]¶
Example
>>> # xdoctest: +REQUIRES(module:imgaug) >>> from kwimage.structs.points import * # NOQA >>> pts = Points.random(10) >>> input_dims = (10, 10) >>> kpoi = pts.to_imgaug(input_dims)
- property dtype¶
- warp(transform, input_dims=None, output_dims=None, inplace=False)[source]¶
Generalized coordinate transform.
- Parameters:
transform (ArrayLike | Callable | kwimage.Affine | SKImageGeometricTransform | Augmenter) – scikit-image tranform, a 3x3 transformation matrix, an imgaug Augmenter, or generic callable which transforms an NxD ndarray.
input_dims (Tuple) – shape of the image these objects correspond to (only needed / used when transform is an imgaug augmenter)
output_dims (Tuple) – unused, only exists for compatibility
inplace (bool) – if True, modifies data inplace
Example
>>> from kwimage.structs.points import * # NOQA >>> import skimage >>> self = Points.random(10, rng=0) >>> transform = skimage.transform.AffineTransform(scale=(2, 2)) >>> new = self.warp(transform) >>> assert np.all(new.xy == self.scale(2).xy)
Doctest
>>> self = Points.random(10, rng=0) >>> assert np.all(self.warp(np.eye(3)).xy == self.xy) >>> assert np.all(self.warp(np.eye(2)).xy == self.xy)
- scale(factor, output_dims=None, inplace=False)[source]¶
Scale a points by a factor
- Parameters:
factor (float | Tuple[float, float]) – scale factor as either a scalar or a (sf_x, sf_y) tuple.
output_dims (Tuple) – unused in non-raster spatial structures
Example
>>> from kwimage.structs.points import * # NOQA >>> self = Points.random(10, rng=0) >>> new = self.scale(10) >>> assert new.xy.max() <= 10
- translate(offset, output_dims=None, inplace=False)[source]¶
Shift the points
- Parameters:
factor (float | Tuple[float, float]) – transation amount as either a scalar or a (t_x, t_y) tuple.
output_dims (Tuple) – unused in non-raster spatial structures
Example
>>> from kwimage.structs.points import * # NOQA >>> self = Points.random(10, rng=0) >>> new = self.translate(10) >>> assert new.xy.min() >= 10 >>> assert new.xy.max() <= 11
- class kwimage.structs.points.Points(data=None, meta=None, datakeys=None, metakeys=None, **kwargs)[source]¶
Bases:
Spatial
,_PointsWarpMixin
Stores multiple keypoints for a single object.
This stores both the geometry and the class metadata if available
Example
>>> from kwimage.structs.points import * # NOQA >>> xy = np.random.rand(10, 2) >>> pts = Points(xy=xy) >>> print('pts = {!r}'.format(pts))
- property shape¶
- property xy¶
- classmethod random(num=1, classes=None, rng=None)[source]¶
Makes random points; typically for testing purposes
Example
>>> import kwimage >>> self = kwimage.Points.random(classes=[1, 2, 3]) >>> self.data >>> print('self.data = {!r}'.format(self.data))
- property _impl¶
- tensor(device=NoParam)[source]¶
Example
>>> # xdoctest: +REQUIRES(module:torch) >>> from kwimage.structs.points import * # NOQA >>> self = Points.random(10) >>> self.tensor()
- round(inplace=False)[source]¶
Rounds data to the nearest integer
- Parameters:
inplace (bool) – if True, modifies this object
Example
>>> import kwimage >>> self = kwimage.Points.random(3).scale(10) >>> self.round()
- numpy()[source]¶
Example
>>> # xdoctest: +REQUIRES(module:torch) >>> from kwimage.structs.points import * # NOQA >>> self = Points.random(10) >>> self.tensor().numpy().tensor().numpy()
- draw_on(image=None, color='white', radius=None, copy=False)[source]¶
- Parameters:
image (ndarray) – image to draw points on.
color (str | Any | List[Any]) – one color for all boxes or a list of colors for each box Can be any type accepted by kwimage.Color.coerce. Extended types: str | ColorLike | List[ColorLike]
radius (None | int) – if an integer, an circle is drawn at each xy point with this radius. if None, attempts to fill a single point with subpixel accuracy, which generally means 4 pixels will be given some weight. Note: color can only be a single value for all points in this case.
copy (bool) – if True, force a copy of the image, otherwise try to draw inplace (may not work depending on dtype).
CommandLine
xdoctest -m ~/code/kwimage/kwimage/structs/points.py Points.draw_on --show
Example
>>> # xdoctest: +REQUIRES(module:kwplot) >>> from kwimage.structs.points import * # NOQA >>> s = 128 >>> image = np.zeros((s, s)) >>> self = Points.random(10).scale(s) >>> image = self.draw_on(image) >>> # xdoctest: +REQUIRES(--show) >>> import kwplot >>> kwplot.figure(fnum=1, doclf=True) >>> kwplot.autompl() >>> kwplot.imshow(image) >>> self.draw(radius=3, alpha=.5) >>> kwplot.show_if_requested()
Example
>>> # xdoctest: +REQUIRES(module:kwplot) >>> from kwimage.structs.points import * # NOQA >>> s = 128 >>> image = np.zeros((s, s)) >>> self = Points.random(10).scale(s) >>> image = self.draw_on(image, radius=3, color='distinct') >>> # xdoctest: +REQUIRES(--show) >>> import kwplot >>> kwplot.figure(fnum=1, doclf=True) >>> kwplot.autompl() >>> kwplot.imshow(image) >>> #self.draw(radius=3, alpha=.5, color='classes') >>> kwplot.show_if_requested()
Example
>>> import kwimage >>> s = 32 >>> self = kwimage.Points.random(10).scale(s) >>> color = 'kitware_green' >>> # Test drawing on all channel + dtype combinations >>> im3 = np.zeros((s, s, 3), dtype=np.float32) >>> 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()), {'radius': None}) >>> inputs[k + '_255'] = (kwimage.ensure_uint255(im.copy()), {'radius': None}) >>> outputs = {} >>> for k, v in inputs.items(): >>> im, kw = v >>> outputs[k] = self.draw_on(im, color=color, **kw) >>> # xdoctest: +REQUIRES(--show) >>> import kwplot >>> kwplot.figure(fnum=2, doclf=True) >>> plt = kwplot.autoplt() >>> pnum_ = kwplot.PlotNums(nRows=2, nSubplots=len(inputs)) >>> for k in inputs.keys(): >>> kwplot.imshow(outputs[k], fnum=2, pnum=pnum_(), title=k) >>> plt.gcf().suptitle('Test draw points on channel + dtype combos') >>> kwplot.show_if_requested()
Example
>>> # xdoctest: +REQUIRES(module:kwplot) >>> from kwimage.structs.points import * # NOQA >>> self = Points.random(10).scale(32) >>> image = self.draw_on(radius=3, color='distinct') >>> # xdoctest: +REQUIRES(--show) >>> import kwplot >>> kwplot.autompl() >>> kwplot.imshow(image) >>> kwplot.show_if_requested()
Example
>>> # xdoctest: +REQUIRES(module:kwplot) >>> # Test cases where single and multiple colors are given >>> # with radius=None and radius=scalar >>> from kwimage.structs.points import * # NOQA >>> import kwimage >>> self = kwimage.Points.random(10).scale(32) >>> image1 = self.draw_on(radius=2, color='blue') >>> image2 = self.draw_on(radius=None, color='blue') >>> image3 = self.draw_on(radius=2, color='distinct') >>> image4 = self.draw_on(radius=None, color='distinct') >>> # xdoctest: +REQUIRES(--show) >>> import kwplot >>> canvas = kwimage.stack_images_grid( >>> [image1, image2, image3, image4], >>> pad=3, bg_value=(1, 1, 1)) >>> kwplot.autompl() >>> kwplot.imshow(canvas) >>> kwplot.show_if_requested()
- draw(color='blue', ax=None, alpha=None, radius=1, setlim=False, **kwargs)[source]¶
TODO: can use kwplot.draw_points
Example
>>> # xdoctest: +REQUIRES(module:kwplot) >>> from kwimage.structs.points import * # NOQA >>> pts = Points.random(10) >>> # xdoctest: +REQUIRES(--show) >>> import kwplot >>> kwplot.figure(doclf=1) >>> pts.draw(radius=0.01) >>> from kwimage.structs.points import * # NOQA >>> self = Points.random(10, classes=['a', 'b', 'c']) >>> self.draw(radius=0.01, color='classes')
- compress(flags, axis=0, inplace=False)[source]¶
Filters items based on a boolean criterion
Example
>>> from kwimage.structs.points import * # NOQA >>> self = Points.random(4) >>> flags = [1, 0, 1, 1] >>> other = self.compress(flags) >>> assert len(self) == 4 >>> assert len(other) == 3
>>> # xdoctest: +REQUIRES(module:torch) >>> other = self.tensor().compress(flags) >>> assert len(other) == 3
- take(indices, axis=0, inplace=False)[source]¶
Takes a subset of items at specific indices
Example
>>> from kwimage.structs.points import * # NOQA >>> self = Points.random(4) >>> indices = [1, 3] >>> other = self.take(indices) >>> assert len(self) == 4 >>> assert len(other) == 2
>>> # xdoctest: +REQUIRES(module:torch) >>> other = self.tensor().take(indices) >>> assert len(other) == 2
- to_coco(style='orig')[source]¶
Converts to an mscoco-like representation
Note
items that are usually id-references to other objects may need to be rectified.
- Parameters:
style (str) – either orig, new, new-id, or new-name
- Returns:
mscoco-like representation
- Return type:
Dict
Example
>>> from kwimage.structs.points import * # NOQA >>> self = Points.random(4, classes=['a', 'b']) >>> orig = self._to_coco(style='orig') >>> print('orig = {!r}'.format(orig)) >>> new_name = self._to_coco(style='new-name') >>> print('new_name = {}'.format(ub.urepr(new_name, nl=-1))) >>> # xdoctest: +REQUIRES(module:kwcoco) >>> import kwcoco >>> self.meta['classes'] = kwcoco.CategoryTree.coerce(self.meta['classes']) >>> new_id = self._to_coco(style='new-id') >>> print('new_id = {}'.format(ub.urepr(new_id, nl=-1)))
- classmethod from_coco(coco_kpts, class_idxs=None, classes=None, warn=False)[source]¶
- Parameters:
coco_kpts (list | dict) – either the original list keypoint encoding or the new dict keypoint encoding.
class_idxs (list) – only needed if using old style
classes (list | kwcoco.CategoryTree) – list of all keypoint category names
warn (bool) – if True raise warnings
Example
>>> ## >>> classes = ['mouth', 'left-hand', 'right-hand'] >>> coco_kpts = [ >>> {'xy': (0, 0), 'visible': 2, 'keypoint_category': 'left-hand'}, >>> {'xy': (1, 2), 'visible': 2, 'keypoint_category': 'mouth'}, >>> ] >>> Points.from_coco(coco_kpts, classes=classes) >>> # Test without classes >>> Points.from_coco(coco_kpts) >>> # Test without any category info >>> coco_kpts2 = [ub.dict_diff(d, {'keypoint_category'}) for d in coco_kpts] >>> Points.from_coco(coco_kpts2) >>> # Test without category instead of keypoint_category >>> coco_kpts3 = [ub.map_keys(lambda x: x.replace('keypoint_', ''), d) for d in coco_kpts] >>> Points.from_coco(coco_kpts3) >>> # >>> # Old style >>> coco_kpts = [0, 0, 2, 0, 1, 2] >>> Points.from_coco(coco_kpts) >>> # Fail case >>> coco_kpts4 = [{'xy': [4686.5, 1341.5], 'category': 'dot'}] >>> Points.from_coco(coco_kpts4, classes=[])
Example
>>> # xdoctest: +REQUIRES(module:kwcoco) >>> import kwcoco >>> classes = kwcoco.CategoryTree.from_coco([ >>> {'name': 'mouth', 'id': 2}, {'name': 'left-hand', 'id': 3}, {'name': 'right-hand', 'id': 5} >>> ]) >>> coco_kpts = [ >>> {'xy': (0, 0), 'visible': 2, 'keypoint_category_id': 5}, >>> {'xy': (1, 2), 'visible': 2, 'keypoint_category_id': 2}, >>> ] >>> pts = Points.from_coco(coco_kpts, classes=classes) >>> assert pts.data['class_idxs'].tolist() == [2, 0]
- class kwimage.structs.points.PointsList(data, meta=None)[source]¶
Bases:
ObjectList
Stores a list of Points, each item usually corresponds to a different object.
Note
# TODO: when the data is homogenous we can use a more efficient # representation, otherwise we have to use heterogenous storage.