kwimage.structs.polygon module

A class to represent a polygon (and a MultiPolygon)

Defines Polygon and MultiPolygon

Todo

  • [ ] Make function mask -> polygon list

  • [ ] Make function multipolygon -> polygon list

  • [ ] Make function PolygonList -> Boxes

  • [ ] Make function SegmentationList -> Boxes

  • [ ] First class shapely support (format=’shapely’ to mitigate format conversion cost) (or use shapely as the primary format).

class kwimage.structs.polygon._ShapelyMixin[source]

Bases: object

Extends Polygon and MultiPolygon with methods that duck-type shapely objects.

References

Example

>>> from kwimage.structs.polygon import *  # NOQA
>>> import itertools as it
>>> import kwimage
>>> poly1 = kwimage.Polygon.random()
>>> poly2 = kwimage.Polygon.random()
>>> mpoly1 = kwimage.MultiPolygon.random()
>>> mpoly2 = kwimage.MultiPolygon.random().buffer(0)
>>> for self, other in it.combinations([poly1, poly2, mpoly1, mpoly2], 2):
>>>     self.iou(other)
>>>     self.iooa(other)
>>>     self.intersection(other)
>>>     self.union(other)
>>>     self.difference(other)
>>>     self.symmetric_difference(other)
>>>     self.oriented_bounding_box()
oriented_bounding_box()[source]

Example

>>> import kwimage
>>> self = kwimage.Polygon.random().scale(100, 100).round()
>>> obox = self.oriented_bounding_box()
>>> print(f'obox={obox}')
buffer(*args, **kwargs)[source]
simplify(tolerance, preserve_topology=True)[source]
union(other)[source]
intersection(other)[source]
difference(other)[source]
symmetric_difference(other)[source]
iooa(other)[source]

Intersection over other area

iou(other)[source]

Intersection area over union area

property area

Computes area via shapley conversion

Returns:

float

property convex_hull
is_invalid(explain=False)[source]

Return True if the polygon is invalid according to shapely.

Parameters:

explain (bool) – if True, the return value is a string explaining why the polygon is invalid.

Returns:

Always returns False if the polygon is valid.

Returns True or a string if the polygon is invalid according to shapely.

Return type:

bool | str

fix(drop_non_polygons=True)[source]

Attempt to ensure validity

References

class kwimage.structs.polygon._PolyArrayBackend[source]

Bases: object

Extends Polygon and MultiPolygon with methods related to array representations of polygons.

is_numpy()[source]
is_tensor()[source]
tensor(device=NoParam)[source]

Example

>>> # xdoctest: +REQUIRES(module:torch)
>>> from kwimage.structs.polygon import *
>>> self = Polygon.random()
>>> self.tensor()
numpy()[source]

Example

>>> # xdoctest: +REQUIRES(module:torch)
>>> from kwimage.structs.polygon import *
>>> self = Polygon.random()
>>> self.tensor().numpy().tensor().numpy()
class kwimage.structs.polygon._PolyWarpMixin[source]

Bases: object

Extends Polygon and MultiPolygon with methods for warping their geometry.

_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.polygon import *  # NOQA
>>> import imgaug
>>> input_dims = np.array((10, 10))
>>> self = Polygon.random(10, n_holes=1, rng=0).scale(input_dims)
>>> augmenter = imgaug.augmenters.Fliplr(p=1)
>>> new = self._warp_imgaug(augmenter, input_dims)
>>> assert np.allclose(self.data['exterior'].data[:, 1], new.data['exterior'].data[:, 1])
>>> assert np.allclose(input_dims[0] - self.data['exterior'].data[:, 0], new.data['exterior'].data[:, 0])
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.figure(fnum=1, doclf=True)
>>> from matplotlib import pyplot as pl
>>> ax = plt.gca()
>>> ax.set_xlim(0, 10)
>>> ax.set_ylim(0, 10)
>>> self.draw(color='red', alpha=.4)
>>> new.draw(color='blue', alpha=.4)
to_imgaug(shape)[source]
warp(transform, input_dims=None, output_dims=None, inplace=False)[source]

Generalized coordinate transform.

Parameters:
  • transform (SKImageGeometricTransform | ArrayLike | Augmenter | callable) – 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.polygon import *  # NOQA
>>> import skimage
>>> self = Polygon.random()
>>> transform = skimage.transform.AffineTransform(scale=(2, 2))
>>> new = self.warp(transform)

Doctest

>>> # xdoctest: +REQUIRES(module:imgaug)
>>> self = Polygon.random()
>>> import imgaug
>>> augmenter = imgaug.augmenters.Fliplr(p=1)
>>> new = self.warp(augmenter, input_dims=(1, 1))
>>> print('new = {!r}'.format(new.data))
>>> print('self = {!r}'.format(self.data))
>>> #assert np.all(self.warp(np.eye(3)).exterior == self.exterior)
>>> #assert np.all(self.warp(np.eye(2)).exterior == self.exterior)
scale(factor, about=None, output_dims=None, inplace=False)[source]

Scale a polygon by a factor

Parameters:
  • factor (float | Tuple[float, float]) – scale factor as either a scalar or a (sf_x, sf_y) tuple.

  • about (Tuple | None) – if unspecified scales about the origin (0, 0), otherwise the scaling is about this point. Can be “centroid” and will use centroid of polygon Using “ymin,xmin” will be the topmost,leftmost point on the polygon (wrt image coords). See _PolyWarpMixin._rectify_about() for details bout codes.

  • output_dims (Tuple) – unused in non-raster spatial structures

  • inplace (bool) – if True, modifies data inplace

Example

>>> from kwimage.structs.polygon import *  # NOQA
>>> self = Polygon.random(10, rng=0)
>>> new = self.scale(10)

Example

>>> from kwimage.structs.polygon import *  # NOQA
>>> self = Polygon.random(10, rng=0).translate((0.5))
>>> new = self.scale(1.5, about='centroid')
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.figure(fnum=1, doclf=True)
>>> kwplot.autompl()
>>> self.draw(color='red', alpha=0.5)
>>> new.draw(color='blue', alpha=0.5, setlim=True)
_images/fig_kwimage_structs_polygon__PolyWarpMixin_scale_002.jpeg
translate(offset, output_dims=None, inplace=False)[source]

Shift the polygon up/down left/right

Parameters:
  • factor (float | Tuple[float]) – transation amount as either a scalar or a (t_x, t_y) tuple.

  • output_dims (Tuple) – unused in non-raster spatial structures

  • inplace (bool) – if True, modifies data inplace

Example

>>> from kwimage.structs.polygon import *  # NOQA
>>> self = Polygon.random(10, rng=0)
>>> new = self.translate(10)
rotate(theta, about=None, output_dims=None, inplace=False)[source]

Rotate the polygon

Parameters:
  • theta (float) – rotation angle in radians

  • about (Tuple | None | str) – if unspecified rotates about the origin (0, 0). If “center” then rotate around the center of this polygon. Otherwise the rotation is about a custom specified point.

  • output_dims (Tuple) – unused in non-raster spatial structures

Example

>>> from kwimage.structs.polygon import *  # NOQA
>>> self = Polygon.random(10, rng=0)
>>> new = self.rotate(np.pi / 2, about='center')
>>> new2 = self.rotate(np.pi / 2)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.figure(fnum=1, doclf=True)
>>> kwplot.autompl()
>>> self.draw(color='red', alpha=0.5)
>>> new.draw(color='blue', alpha=0.5)
_images/fig_kwimage_structs_polygon__PolyWarpMixin_rotate_002.jpeg
_rectify_about(about)[source]

Ensures that about returns a specified point. Allows for special keys like center to be used.

Parameters:

about (str | Tuple) – either a numeric coordinate or a string code that specifies one. Valid string codes are

  • “origin” - maps to (0, 0)

  • “centroid” - maps to the polygon centroid

  • “center” - alias of centroid

  • “ymin,xmin-bound” - the top-left (img coords) of the polygon bounding box

  • “ymin,xmax-bound” - the top-right (img coords) of the polygon bounding box

  • “ymax,xmin-bound” - the bottom-right (img coords) of the polygon bounding box

  • “ymax,xmax-bound” - the bottom-left (img coords) of the polygon bounding box

  • A comma separated code illustrated in the TextArt adapted from [SO67822179].

Returns:

Tuple[int, int] - the x, y about position

References

TextArt

(0, 0)
+ ---------> +x
|
|    ymin,left ─────────►xxxxxxxxxx◄──────── ymin,xmax
|                     xxxx         xx
V    xmin,ymin ────►xxx             xx
y+                  x                xx◄──── xmax,ymin
                    x                 x
     xmin,ymax ────►xx                x
                     xx              xx◄──── xmax,ymax
                      x             xx
     ymax,left ──────►xxxxxxxxxxxxxxx◄────── ymax,xmax

Example

>>> import kwimage
>>> mask = kwimage.Mask.from_text(ub.codeblock(
>>>     '''
>>>           xxxxxxxxxx
>>>        xxxx         xx
>>>      xxx             xx
>>>      x                xx
>>>      x                 x
>>>      xx                x
>>>       xx              xx
>>>        x             xx
>>>        xxxxxxxxxxxxxxx
>>>    '''), zero_chr=' ')
>>> poly = mask.to_multi_polygon().data[0]
>>> print(poly._rectify_about('ymin,xmin'))
[5 0]
>>> print(poly._rectify_about('xmin,ymin'))
[0 2]
>>> print(poly._rectify_about('xmin,ymax'))
[0 5]
>>> print(poly._rectify_about('ymax,xmin'))
[2 8]
>>> print(poly._rectify_about('ymin,xmax'))
[14  0]
>>> print(poly._rectify_about('xmax,ymin'))
[18  3]
>>> print(poly._rectify_about('xmax,ymax'))
[18  6]
>>> print(poly._rectify_about('ymax,xmax'))
[16  8]

Example

>>> from kwimage.structs.polygon import *  # NOQA
>>> self = Polygon.random(10, rng=0).scale(10).round().astype(np.int32)
>>> print(self._rectify_about('centroid'))
>>> print(self._rectify_about('ymax,xmin'))
>>> print(self._rectify_about('xmin,ymax'))
>>> print(self._rectify_about('ymax,xmin-bounds'))
>>> print(self._rectify_about('xmin,ymax-bounds'))
(4.325, 3.9)
[5 8]
[1 6]
[1 8]
[1 8]
round(decimals=0, inplace=False)[source]

Rounds data to the specified decimal place. This may make the polygon invalid.

Parameters:
  • inplace (bool) – if True, modifies this object

  • decimals (int) – number of decimal places to round to

Returns:

modified polygon

Return type:

Polygon

Example

>>> import kwimage
>>> self = kwimage.Polygon.random(3).scale(10)
>>> new = self.round()
>>> assert np.any(self.exterior.data != new.exterior.data)
>>> assert np.all(self.exterior.data.round() == new.exterior.data)
>>> # demo a case that makes the polygon invalid
>>> self = kwimage.Polygon.random(6).scale(0.1)
>>> new = self.round()
>>> assert np.any(self.exterior.data != new.exterior.data)
>>> assert np.all(self.exterior.data.round() == new.exterior.data)
astype(dtype, inplace=False)[source]

Changes the data type

Parameters:
  • dtype – new type

  • inplace (bool) – if True, modifies this object

Returns:

modified polygon

Return type:

Polygon

Example

>>> import kwimage
>>> self = kwimage.Polygon.random(3, rng=0).scale(10)
>>> new = self.astype(np.int32)
>>> assert np.any(self.exterior.data != new.exterior.data)
>>> assert np.all(self.exterior.data.astype(np.int32) == new.exterior.data)
swap_axes(inplace=False)[source]

Swap the x and y coordinate axes

Parameters:

inplace (bool) – if True, modifies data inplace

Returns:

modified polygon

Return type:

Polygon

class kwimage.structs.polygon.Polygon(data=None, meta=None, datakeys=None, metakeys=None, **kwargs)[source]

Bases: Spatial, _PolyArrayBackend, _PolyWarpMixin, _ShapelyMixin, NiceRepr

Represents a single polygon as set of exterior boundary points and a list of internal polygons representing holes.

By convention exterior boundaries should be counterclockwise and interior holes should be clockwise.

Example

>>> import kwimage
>>> poly1 = kwimage.Polygon(exterior=[[ 5., 10.], [ 1.,  8.], [ 3.,  4.], [ 5.,  3.], [ 8.,  9.], [ 6., 10.]])
>>> poly2 = kwimage.Polygon.random(rng=34214, n_holes=2).scale(10).round()
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.figure(doclf=True)
>>> poly1.draw(setlim=1.4, vertex=0.2, vertexcolor='kw_orange', color='kw_blue', edgecolor='kw_green', alpha=0.5)
>>> poly2.draw(setlim=2.9, vertex=0.2, vertexcolor='kw_red', color='kw_darkgreen', edgecolor='kw_darkblue', alpha=0.5)
>>> kwplot.show_if_requested()
_images/fig_kwimage_structs_polygon_Polygon_002.jpeg

Example

>>> import kwimage
>>> data = {
>>>     'exterior': np.array([[13,  1], [13, 19], [25, 19], [25,  1]]),
>>>     'interiors': [
>>>         np.array([[14, 13], [15, 12], [23, 12], [24, 13], [24, 18],
>>>                   [23, 19], [13, 19], [12, 18]]),
>>>         np.array([[13,  2], [14,  1], [24,  1], [25, 2], [25, 11],
>>>                   [24, 12], [14, 12], [13, 11]])]
>>> }
>>> self = kwimage.Polygon(**data)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> self.draw(setlim=1.4, vertex=0.2, vertexcolor='kw_orange', color='kw_blue', edgecolor='kw_green')
_images/fig_kwimage_structs_polygon_Polygon_003.jpeg

Example

>>> import kwimage
>>> data = {
>>>     'exterior': np.array([[13,  1], [13, 19], [25, 19], [25,  1]]),
>>>     'interiors': [
>>>         np.array([[14, 13], [15, 12], [23, 12], [24, 13], [24, 18],
>>>                   [23, 19], [13, 19], [12, 18]]),
>>>         np.array([[13,  2], [14,  1], [24,  1], [25, 2], [25, 11],
>>>                   [24, 12], [14, 12], [13, 11]])]
>>> }
>>> self = kwimage.Polygon(**data)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> self.draw(setlim=1.4, vertex=0.2, vertexcolor='kw_orange', color='kw_blue', edgecolor='kw_green')
_images/fig_kwimage_structs_polygon_Polygon_004.jpeg

Example

>>> import kwimage
>>> self = kwimage.Polygon.random(
>>>     n=5, n_holes=1, convex=False, rng=0)
>>> print('self = {}'.format(self))
self = <Polygon({
    'exterior': <Coords(data=
                    array([[0.30371392, 0.97195856],
                           [0.24372304, 0.60568445],
                           [0.21408694, 0.34884262],
                           [0.5799477 , 0.44020379],
                           [0.83720288, 0.78367234]]))>,
    'interiors': [<Coords(data=
                     array([[0.50164209, 0.83520279],
                            [0.25835064, 0.40313428],
                            [0.28778562, 0.74758761],
                            [0.30341266, 0.93748088]]))>],
})>
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> self.draw(setlim=True)
_images/fig_kwimage_structs_polygon_Polygon_005.jpeg

Example

>>> # Test empty polygon
>>> import kwimage
>>> data = {
>>>     'exterior': np.array([]),
>>>     'interiors': [],}
>>> self = kwimage.Polygon(**data)
>>> geos = self.to_geojson()
>>> kwimage.Polygon.from_geojson(geos)
>>> geom = self.to_shapely()
>>> kwimage.Polygon.from_shapely(geom)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> self.draw(setlim=True)
_images/fig_kwimage_structs_polygon_Polygon_006.jpeg
property exterior

Returns: kwimage.Coords

property interiors

Returns: List[kwimage.Coords]

classmethod circle(xy=(0, 0), r=1.0, resolution=64)[source]

Create a circular or elliptical polygon.

Might rename to ellipse later?

Parameters:
  • xy (Iterable[Number]) – x and y center coordinate

  • r (float | Number | Tuple[Number, Number]) – circular radius or major and minor elliptical radius

  • resolution (int) – number of sides

Returns:

Polygon

Example

>>> import kwimage
>>> xy = (0.5, 0.5)
>>> r = .3
>>> # Demo with circle
>>> circle = kwimage.Polygon.circle(xy, r, resolution=6)
>>> # Demo with ellipse
>>> xy = (0.5, 0.5)
>>> r = (.4, .7)
>>> ellipse1 = kwimage.Polygon.circle(xy, r, resolution=12)
>>> ellipse2 = kwimage.Polygon.circle(xy, (.7, .4), resolution=12)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> plt = kwplot.autoplt()
>>> kwplot.figure(fnum=1, doclf=True)
>>> circle.draw(setlim=True, border=1, fill=0, color='kitware_orange')
>>> ellipse1.draw(setlim=True, border=1, fill=0, color='kitware_blue')
>>> ellipse2.draw(setlim=True, border=1, fill=0, color='kitware_green')
>>> plt.gca().set_xlim(-0.5, 1.5)
>>> plt.gca().set_ylim(-0.5, 1.5)
>>> plt.gca().set_aspect('equal')
_images/fig_kwimage_structs_polygon_Polygon_circle_002.jpeg

kwimage.Polygon.circle(xy, r, resolution=10).draw()

classmethod regular(num, xy=(0, 0), r=1)[source]

Make a regular polygon with num sides.

Example

>>> import kwimage
>>> n_polys = [
>>>     kwimage.Polygon.regular(n)
>>>     for n in range(3, 11)
>>> ]
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> plt = kwplot.autoplt()
>>> fig =kwplot.figure(fnum=1, doclf=True)
>>> ax = fig.gca()
>>> for i, poly in enumerate(n_polys):
>>>     poly.translate((i * 2.5, 0), inplace=True)
>>>     poly.draw(border=True, fill=False)
>>> ax.set_aspect('equal')
>>> ax.set_xlim(-1, 8 * 2.5)
>>> ax.set_ylim(-1, 1)
_images/fig_kwimage_structs_polygon_Polygon_regular_002.jpeg
classmethod star(xy=(0, 0), r=1)[source]

Make a star polygon

Example

>>> import kwimage
>>> poly = kwimage.Polygon.star()
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> plt = kwplot.autoplt()
>>> fig = kwplot.figure(fnum=1, doclf=True)
>>> ax = fig.gca()
>>> poly.draw()
>>> ax.set_aspect('equal')
>>> ax.set_xlim(-1, 1)
>>> ax.set_ylim(-1, 1)
_images/fig_kwimage_structs_polygon_Polygon_star_002.jpeg
classmethod random(n=6, n_holes=0, convex=True, tight=False, rng=None)[source]
Parameters:
  • n (int) – number of points in the polygon (must be 3 or more)

  • n_holes (int) – number of holes

  • tight (bool) – fits the minimum and maximum points between 0 and 1

  • convex (bool) – force resulting polygon will be convex (may remove exterior points)

Returns:

Polygon

CommandLine

xdoctest -m kwimage.structs.polygon Polygon.random

Example

>>> import kwimage
>>> self = kwimage.Polygon.random(n=4, rng=None, n_holes=2, convex=1)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.figure(fnum=1, doclf=True)
>>> self.draw(color='kw_green', edgecolor='kw_blue', vertexcolor='kw_darkblue', vertex=0.01)
_images/fig_kwimage_structs_polygon_Polygon_random_002.jpeg

References

https://gis.stackexchange.com/questions/207731/random-multipolygon https://stackoverflow.com/questions/8997099/random-polygon https://stackoverflow.com/questions/27548363/from-voronoi-tessellation-to-shapely-polygons https://stackoverflow.com/questions/8997099/algorithm-to-generate-random-2d-polygon

property _impl
to_mask(dims=None, pixels_are='points')[source]

Convert this polygon to a mask

Todo

  • [ ] currently not efficient

Parameters:
  • dims (Tuple) – height and width of the output mask

  • pixels_are (str) – either “points” or “areas”

Returns:

kwimage.Mask

Example

>>> from kwimage.structs.polygon import *  # NOQA
>>> self = Polygon.random(n_holes=1).scale(128)
>>> mask = self.to_mask((128, 128))
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.figure(fnum=1, doclf=True)
>>> mask.draw(color='blue')
>>> mask.to_multi_polygon().draw(color='red', alpha=.5)
_images/fig_kwimage_structs_polygon_Polygon_to_mask_002.jpeg
to_relative_mask(return_offset=False)[source]

Returns a translated mask such the mask dimensions are minimal.

In other words, we move the polygon all the way to the top-left and return a mask just big enough to fit the polygon.

Returns:

kwimage.Mask

Example

>>> from kwimage.structs.polygon import *  # NOQA
>>> self = Polygon.random().scale(8).translate(100, 100)
>>> mask = self.to_relative_mask()
>>> assert mask.shape <= (8, 8)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.figure(fnum=1, doclf=True)
>>> mask.draw(color='blue')
>>> mask.to_multi_polygon().draw(color='red', alpha=.5)
_images/fig_kwimage_structs_polygon_Polygon_to_relative_mask_002.jpeg
_to_cv_countours()[source]

OpenCV polygon representation, which is a list of points. Holes are implicitly represented. When another polygon is drawn over an existing polyon via cv2.fillPoly

Returns:

where each ndarray is of shape [N, 1, 2],

where N is the number of points on the boundary, the middle dimension is always 1, and the trailing dimension represents x and y coordinates respectively.

Return type:

List[ndarray]

classmethod coerce(data)[source]

Routes the input to the proper constructor

Try to autodetermine format of input polygon and coerce it into a kwimage.Polygon.

Parameters:

data (object) – some type of data that can be interpreted as a polygon.

Returns:

kwimage.Polygon

Example

>>> import kwimage
>>> self = kwimage.Polygon.random()
>>> kwimage.Polygon.coerce(self)
>>> kwimage.Polygon.coerce(self.exterior)
>>> kwimage.Polygon.coerce(self.exterior.data)
>>> kwimage.Polygon.coerce(self.data)
>>> kwimage.Polygon.coerce(self.to_geojson())
>>> kwimage.Polygon.coerce('POLYGON ((0.11 0.61, 0.07 0.588, 0.015 0.50, 0.11 0.61))')
classmethod from_shapely(geom)[source]

Convert a shapely polygon to a kwimage.Polygon

Parameters:

geom (shapely.geometry.polygon.Polygon) – a shapely polygon

Returns:

kwimage.Polygon

classmethod from_wkt(data)[source]

Convert a WKT string to a kwimage.Polygon

Parameters:

data (str) – a WKT polygon string

Returns:

kwimage.Polygon

Example

>>> import kwimage
>>> data = 'POLYGON ((0.11 0.61, 0.07 0.588, 0.015 0.50, 0.11 0.61))'
>>> self = kwimage.Polygon.from_wkt(data)
>>> assert len(self.exterior) == 4
classmethod from_geojson(data_geojson)[source]

Convert a geojson polygon to a kwimage.Polygon

Parameters:

data_geojson (dict) – geojson data

Returns:

Polygon

References

https://geojson.org/geojson-spec.html

Example

>>> from kwimage.structs.polygon import *  # NOQA
>>> self = Polygon.random(n_holes=2)
>>> data_geojson = self.to_geojson()
>>> new = Polygon.from_geojson(data_geojson)
to_shapely(fix=False)[source]
Parameters:

fix (bool) – if True, will check for validity and if any simple fixes can be applied, otherwise it returns the data as is.

Returns:

shapely.geometry.polygon.Polygon

Example

>>> # xdoctest: +REQUIRES(module:kwplot)
>>> # xdoctest: +REQUIRES(module:shapely)
>>> from kwimage.structs.polygon import *  # NOQA
>>> self = Polygon.random(n_holes=1)
>>> self = self.scale(100)
>>> geom = self.to_shapely()
>>> print('geom = {!r}'.format(geom))
to_geojson()[source]

Converts polygon to a geojson structure

Returns:

Dict[str, object]

Example

>>> import kwimage
>>> self = kwimage.Polygon.random()
>>> print(self.to_geojson())
to_wkt()[source]

Convert a kwimage.Polygon to WKT string

Returns:

str

Example

>>> import kwimage
>>> self = kwimage.Polygon.random()
>>> print(self.to_wkt())
classmethod from_coco(data, dims=None)[source]

Accepts either new-style or old-style coco polygons

Parameters:
  • data (List[Number] | Dict) – A new or old-style coco polygon

  • dims (None | Tuple[int, …]) – the shape dimensions of the canvas. Unused. Exists for compatibility with masks.

Returns:

Polygon

_to_coco(style='orig')[source]
to_coco(style='orig')[source]
Parameters:

style (str) – can be “orig” or “new”

Returns:

coco-style polygons

Return type:

List | Dict

to_multi_polygon()[source]
Returns:

MultiPolygon

to_boxes()[source]

Deprecated: lossy conversion use ‘bounding_box’ instead

Returns:

kwimage.Boxes

property centroid

Returns: Tuple[Number, Number]

to_box()[source]

## DEPRECATED: Use box() instead. ## Do we deprecate this? Should we stick to the to_ / from_ convention?

Returns:

kwimage.Box

bounding_box()[source]

Returns an axis-aligned bounding box for the segmentation

DEPRECATED: Use singular box() instead.

Returns:

kwimage.Boxes

box()[source]

Returns an axis-aligned bounding box for the segmentation

Returns:

kwimage.Box

Example

>>> import kwimage
>>> poly = kwimage.Polygon.random()
>>> box = poly.box()
>>> print('box = {}'.format(ub.urepr(box, nl=1)))
bounding_box_polygon()[source]

Returns an axis-aligned bounding polygon for the segmentation.

Note

This Polygon will be a Box, not a convex hull! Use shapely for convex hulls.

Returns:

kwimage.Polygon

copy()[source]
Returns:

a copy

Return type:

Polygon

clip(x_min, y_min, x_max, y_max, inplace=False)[source]

Clip polygon to specified boundaries.

Returns:

clipped polygon

Return type:

Polygon

Example

>>> from kwimage.structs.polygon import *
>>> self = Polygon.random().scale(10).translate(-1)
>>> self2 = self.clip(1, 1, 3, 3)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> self2.draw(setlim=True)
_images/fig_kwimage_structs_polygon_Polygon_clip_002.jpeg
fill(image, value=1, pixels_are='points', assert_inplace=False)[source]

Fill in an image based on this polyon.

Parameters:
  • image (ndarray) – image to draw on

  • value (int | Tuple[int]) – value fill in with. Defaults to 1.

  • pixels_are (str) – either points or areas

  • assert_inplace (bool) – if True then the function will error if the modification cannot happen inplace.

Returns:

the image that has been modified in place

Return type:

ndarray

Example

>>> # xdoctest: +REQUIRES(module:rasterio)
>>> import kwimage
>>> mask = kwimage.Mask.random(rng=0)
>>> self = mask.to_multi_polygon(pixels_are='areas').data[0]
>>> image = np.zeros_like(mask.data)
>>> self.fill(image, pixels_are='areas')

Example

>>> # Test case where there are multiple channels
>>> import kwimage
>>> mask = kwimage.Mask.random(shape=(4, 4), rng=0)
>>> self = mask.to_multi_polygon()
>>> image = np.zeros(mask.shape[0:2] + (2,), dtype=np.float32)
>>> fill_v1 = self.fill(image.copy(), value=1)
>>> fill_v2 = self.fill(image.copy(), value=(1, 2))
>>> assert np.all((fill_v1 > 0) == (fill_v2 > 0))

Example

>>> import kwimage
>>> # Test dtype with inplace vs not
>>> mask = kwimage.Mask.random(shape=(32, 32), rng=0)
>>> self = mask.to_multi_polygon()
>>> native_dtypes = []
>>> native_dtypes += [np.uint8, np.uint16]
>>> native_dtypes += [np.int8, np.int16, np.int32]
>>> native_dtypes += [np.float32]
>>> for dtype in native_dtypes:
>>>     image = np.zeros(mask.shape[0:2] + (2,), dtype=dtype)
>>>     image1 = self.fill(image, value=1, assert_inplace=True)
>>>     assert image1.sum() > 0
>>>     assert image.sum() > 0
>>>     print(f'dtype: {dtype} inplace')
>>> needfix_dtypes = [np.uint32, np.uint64, np.int64, np.float16, np.float64]
>>> for dtype in needfix_dtypes:
>>>     image = np.zeros(mask.shape[0:2] + (2,), dtype=dtype)
>>>     image1 = self.fill(image, value=1, assert_inplace=False)
>>>     assert image1.sum() > 0
>>>     assert image.sum() == 0
>>>     print(f'dtype: {dtype} not inplace')
draw_on(image, color='blue', fill=True, border=False, alpha=1.0, edgecolor=None, facecolor=None, copy=False)[source]

Rasterizes a polygon on an image. See draw for a vectorized matplotlib version.

Parameters:
  • image (ndarray) – image to raster polygon on.

  • color (str | tuple) – data coercable to a color

  • fill (bool) – draw the center mass of the polygon. Note: this will be deprecated. Use facecolor instead.

  • border (bool) – draw the border of the polygon Note: this will be deprecated. Use edgecolor instead.

  • alpha (float) – polygon transparency (setting alpha < 1 makes this function much slower). Defaults to 1.0

  • copy (bool) – if False only copies if necessary

  • edgecolor (str | tuple) – color for the border

  • facecolor (str | tuple) – color for the fill

Returns:

np.ndarray

Note

This function will only be inplace if alpha=1.0 and the input has 3 or 4 channels. Otherwise the output canvas is coerced so colors can be drawn on it. In the case where alpha < 1.0,

Example

>>> # xdoctest: +REQUIRES(module:kwplot)
>>> from kwimage.structs.polygon import *  # NOQA
>>> self = Polygon.random(n_holes=1).scale(128)
>>> image_in = np.zeros((128, 128), dtype=np.float32)
>>> image_out = self.draw_on(image_in)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(image_out, fnum=1)
_images/fig_kwimage_structs_polygon_Polygon_draw_on_002.jpeg

Example

>>> # xdoctest: +REQUIRES(module:kwplot)
>>> # Demo drawing on a RGBA canvas
>>> # If you initialize an zero rgba canvas, the alpha values are
>>> # filled correctly.
>>> from kwimage.structs.polygon import *  # NOQA
>>> s = 16
>>> self = Polygon.random(n_holes=1, rng=32).scale(s)
>>> image_in = np.zeros((s, s, 4), dtype=np.float32)
>>> image_out = self.draw_on(image_in, color='black')
>>> assert np.all(image_out[..., 0:3] == 0)
>>> assert not np.all(image_out[..., 3] == 1)
>>> assert not np.all(image_out[..., 3] == 0)

Example

>>> import kwimage
>>> color = 'blue'
>>> self = kwimage.Polygon.random(n_holes=1).scale(128)
>>> image = np.zeros((128, 128), dtype=np.float32)
>>> # Test drawing on all channel + dtype combinations
>>> im3 = np.random.rand(128, 128, 3)
>>> im_chans = {
>>>     'im3': im3,
>>>     'im1': kwimage.convert_colorspace(im3, 'rgb', 'gray'),
>>>     #'im0': im3[..., 0],
>>>     'im4': kwimage.convert_colorspace(im3, 'rgb', 'rgba'),
>>> }
>>> inputs = {}
>>> for k, im in im_chans.items():
>>>     inputs[k + '_f01'] = (kwimage.ensure_float01(im.copy()), {'alpha': None})
>>>     inputs[k + '_u255'] = (kwimage.ensure_uint255(im.copy()), {'alpha': None})
>>>     inputs[k + '_f01_a'] = (kwimage.ensure_float01(im.copy()), {'alpha': 0.5})
>>>     inputs[k + '_u255_a'] = (kwimage.ensure_uint255(im.copy()), {'alpha': 0.5})
>>> # Check cases when image is/isnot written inplace Construct images
>>> # with different dtypes / channels and run a draw_on with different
>>> # keyword args.  For each combination, demo if that results in an
>>> # implace operation or not.
>>> rows = []
>>> outputs = {}
>>> for k, v in inputs.items():
>>>     im, kw = v
>>>     outputs[k] = self.draw_on(im, color=color, **kw)
>>>     inplace = outputs[k] is im
>>>     rows.append({'key': k, 'inplace': inplace})
>>> # xdoctest: +REQUIRES(module:pandas)
>>> import pandas as pd
>>> df = pd.DataFrame(rows).sort_values('inplace')
>>> print(df.to_string())
>>> # xdoctest: +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()

Example

>>> # Test empty polygon draw
>>> from kwimage.structs.polygon import *  # NOQA
>>> self = Polygon.from_coco([])
>>> image_in = np.zeros((128, 128), dtype=np.float32)
>>> image_out = self.draw_on(image_in)

Example

>>> # Test stupid large polygon draw
>>> from kwimage.structs.polygon import *  # NOQA
>>> from kwimage.structs.polygon import _generic
>>> import kwimage
>>> self = kwimage.Polygon.random().scale(2e11)
>>> image = np.zeros((128, 128), dtype=np.float32)
>>> image_out = self.draw_on(image)
draw(color='blue', ax=None, alpha=1.0, radius=1, setlim=False, border=None, linewidth=None, edgecolor=None, facecolor=None, fill=True, vertex=False, vertexcolor=None)[source]

Draws polygon in a matplotlib axes. See draw_on for in-memory image modification.

Parameters:
  • color (str | Tuple) – coercable color. Default color if specific colors are not given.

  • alpha (float) – fill transparency

  • fill (bool) – if True fill the polygon with facecolor, otherwise just draw the border if linewidth > 0

  • setlim (bool | str) – if True, modify the x and y limits of the matplotlib axes such that the polygon is can be seen. Can also be a string “grow”, which only allows growth of the viewport to accomidate the new polyogn.

  • border (bool) – if True, draws an edge border on the polygon. DEPRECATED. Use linewidth instead.

  • linewidth (bool) – width of the border

  • edgecolor (None | Any) – if None, uses the value of color. Otherwise the color of the border when linewidth > 0. Extended types Coercible[kwimage.Color].

  • facecolor (None | Any) – if None, uses the value of color. Otherwise, color of the border when fill=True. Extended types Coercible[kwimage.Color].

  • vertex (float) – if non-zero, draws vertexes on the polygon with this radius.

  • vertexcolor (Any) – color of vertexes Extended types Coercible[kwimage.Color].

Returns:

None for am empty polygon

Return type:

matplotlib.patches.PathPatch | None

Todo

  • [ ] Rework arguments in favor of matplotlib standards

Example

>>> # xdoctest: +REQUIRES(module:kwplot)
>>> from kwimage.structs.polygon import *  # NOQA
>>> self = Polygon.random(n_holes=1)
>>> self = self.scale(100)
>>> # xdoctest: +REQUIRES(--show)
>>> kwargs = dict(edgecolor='orangered', facecolor='dodgerblue', linewidth=10)
>>> self.draw(**kwargs)
>>> import kwplot
>>> kwplot.autompl()
>>> from matplotlib import pyplot as plt
>>> kwplot.figure(fnum=2)
>>> self.draw(setlim=True, **kwargs)
_images/fig_kwimage_structs_polygon_Polygon_draw_002.jpeg _images/fig_kwimage_structs_polygon_Polygon_draw_003.jpeg

Example

>>> # xdoctest: +REQUIRES(module:kwplot)
>>> # xdoctest: +REQUIRES(--show)
>>> from kwimage.structs.polygon import *  # NOQA
>>> self = Polygon.random(n_holes=1, rng=33202)
>>> import textwrap
>>> # Test over a range of parameters
>>> basis = {
>>>     'linewidth': [0, 4],
>>>     'edgecolor': [None, 'gold'],
>>>     'facecolor': ['purple'],
>>>     'fill': [True, False],
>>>     'alpha': [1.0, 0.5],
>>>     'vertex': [0, 0.01],
>>>     'vertexcolor': ['green'],
>>> }
>>> grid = list(ub.named_product(basis))
>>> import kwplot
>>> kwplot.autompl()
>>> pnum_ = kwplot.PlotNums(nSubplots=len(grid))
>>> for kwargs in grid:
>>>     fig = kwplot.figure(fnum=1, pnum=pnum_())
>>>     ax = fig.gca()
>>>     self.draw(ax=ax, **kwargs)
>>>     title = ub.urepr(kwargs, compact=True)
>>>     title = '\n'.join(textwrap.wrap(
>>>         title.replace(',', ' '), break_long_words=False,
>>>         width=60))
>>>     ax.set_title(title, fontdict={'fontsize': 8})
>>>     ax.grid(False)
>>>     ax.set_xticks([])
>>>     ax.set_yticks([])
>>> fig.subplots_adjust(wspace=0.5, hspace=0.3, bottom=0.001, top=0.97)
>>> kwplot.show_if_requested()
_images/fig_kwimage_structs_polygon_Polygon_draw_004.jpeg
_ensure_vertex_order(inplace=False)[source]

Fixes vertex ordering so the exterior ring is CCW and the interior rings are CW.

Example

>>> import kwimage
>>> self = kwimage.Polygon.random(n=3, n_holes=2, rng=0)
>>> print('self = {!r}'.format(self))
>>> new = self._ensure_vertex_order()
>>> print('new = {!r}'.format(new))
>>> self = kwimage.Polygon.random(n=3, n_holes=2, rng=0).swap_axes()
>>> print('self = {!r}'.format(self))
>>> new = self._ensure_vertex_order()
>>> print('new = {!r}'.format(new))
interpolate(other, alpha)[source]
morph(other, alpha)[source]

Perform polygon-to-polygon morphing.

Note

This current algorithm is very basic and does not yet prevent self-intersections in intermediate polygons.

Parameters:
  • other (kwimage.Polygon) – the other polygon to morph into

  • alpha (float | List[float]) – A value between 0 and 1, indicating the fractional position of the new interpolated polygon between self and other. If given as a list multiple interpolations are returned.

Returns:

one ore more interpolated polygons

Return type:

Polygon | List[Polygon]

Todo

  • [ ] Implement level set method [LevelSet] which rasterizes each

polygon, interpolates the raster, and computes the interpolated polygon as contours in that interpolated raster.

References

Example

>>> import kwimage
>>> self = kwimage.Polygon.random(3, convex=0)
>>> other = kwimage.Polygon.random(4, convex=0).translate((2, 2))
>>> results = self.morph(other, np.linspace(0, 1, 5))
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> plt = kwplot.autoplt()
>>> kwplot.figure(doclf=1)
>>> self.draw(setlim='grow', color='kw_blue', alpha=0.5, vertex=0.02)
>>> other.draw(setlim='grow', color='kw_green', alpha=0.5, vertex=0.02)
>>> colors = kwimage.Color('kw_blue').morph(
>>>     'kw_green', np.linspace(0, 1, 5))
>>> for new, c in zip(results, colors):
>>>     pt = new.exterior.data[0]
>>>     new.draw(color=c, alpha=0.5, vertex=0.01)
>>> intepolation_lines = np.array([new.exterior.data for new in results])
>>> for interp_line in intepolation_lines.transpose(1, 0, 2)[::8]:
>>>     plt.plot(*interp_line.T, '--x')
_images/fig_kwimage_structs_polygon_Polygon_morph_002.jpeg
class kwimage.structs.polygon.MultiPolygon(data, meta=None)[source]

Bases: ObjectList, _ShapelyMixin

Data structure for storing multiple polygons (typically related to the same underlying but potentitally disjoing object)

Variables:

data (List[Polygon]) –

classmethod random(n=3, n_holes=0, rng=None, tight=False)[source]

Create a random MultiPolygon

Returns:

MultiPolygon

fill(image, value=1, pixels_are='points', assert_inplace=False)[source]

Inplace fill in an image based on this multi-polyon.

Parameters:
  • image (ndarray) – image to draw on (inplace)

  • value (int | Tuple[int, …]) – value fill in with. Defaults to 1.0

Returns:

the image that has been modified in place

Return type:

ndarray

to_multi_polygon()[source]
Returns:

MultiPolygon

to_boxes()[source]

Deprecated: lossy conversion use ‘bounding_box’ instead

Returns:

kwimage.Boxes

to_box()[source]
Returns:

kwimage.Box

bounding_box()[source]

Return the bounding box of the multi polygon

DEPRECATED: Use singular box() instead.

Returns:

a Boxes object with one box that encloses all

polygons

Return type:

kwimage.Boxes

Example

>>> from kwimage.structs.polygon import *  # NOQA
>>> self = MultiPolygon.random(rng=0, n=10)
>>> boxes = self.to_boxes()
>>> sub_boxes = [d.to_boxes() for d in self.data]
>>> areas1 = np.array([s.intersection(boxes).area[0] for s in sub_boxes])
>>> areas2 = np.array([s.area[0] for s in sub_boxes])
>>> assert np.allclose(areas1, areas2)
box()[source]

Returns an axis-aligned bounding box for the segmentation

Returns:

kwimage.Box

Example

>>> from kwimage.structs.polygon import *  # NOQA
>>> self = MultiPolygon.random(rng=0, n=10)
>>> boxes = self.box()
>>> sub_boxes = [d.box() for d in self.data]
>>> areas1 = np.array([s.intersection(boxes).area for s in sub_boxes])
>>> areas2 = np.array([s.area for s in sub_boxes])
>>> assert np.allclose(areas1, areas2)
to_mask(dims=None, pixels_are='points')[source]

Returns a mask object indication regions occupied by this multipolygon

Returns:

kwimage.Mask

Example

>>> from kwimage.structs.polygon import *  # NOQA
>>> s = 100
>>> self = MultiPolygon.random(rng=0).scale(s)
>>> dims = (s, s)
>>> mask = self.to_mask(dims)
>>> # xdoctest: +REQUIRES(--show)
>>> # xdoctest: +REQUIRES(module:kwplot)
>>> import kwplot
>>> plt = kwplot.autoplt()
>>> kwplot.figure(fnum=1, doclf=True)
>>> ax = plt.gca()
>>> ax.set_xlim(0, s)
>>> ax.set_ylim(0, s)
>>> self.draw(color='red', alpha=.4)
>>> mask.draw(color='blue', alpha=.4)
_images/fig_kwimage_structs_polygon_MultiPolygon_to_mask_002.jpeg
to_relative_mask(return_offset=False)[source]

Returns a translated mask such the mask dimensions are minimal.

In other words, we move the polygon all the way to the top-left and return a mask just big enough to fit the polygon.

Returns:

kwimage.Mask

classmethod coerce(data, dims=None)[source]

Attempts to construct a MultiPolygon instance from the input data

See Segmentation.coerce

Returns:

None | MultiPolygon

Example

>>> import kwimage
>>> dims = (32, 32)
>>> kw_poly = kwimage.Polygon.random().scale(dims)
>>> kw_multi_poly = kwimage.MultiPolygon.random().scale(dims)
>>> forms = [kw_poly, kw_multi_poly]
>>> forms.append(kw_poly.to_shapely())
>>> forms.append(kw_poly.to_mask((32, 32)))
>>> forms.append(kw_poly.to_geojson())
>>> forms.append(kw_poly.to_coco(style='orig'))
>>> forms.append(kw_poly.to_coco(style='new'))
>>> forms.append(kw_multi_poly.to_shapely())
>>> forms.append(kw_multi_poly.to_mask((32, 32)))
>>> forms.append(kw_multi_poly.to_geojson())
>>> forms.append(kw_multi_poly.to_coco(style='orig'))
>>> forms.append(kw_multi_poly.to_coco(style='new'))
>>> for data in forms:
>>>     result = kwimage.MultiPolygon.coerce(data, dims=dims)
>>>     assert isinstance(result, kwimage.MultiPolygon)
to_shapely(fix=False)[source]
Parameters:

fix (bool) – if True, will check for validity and if any simple fixes can be applied, otherwise it returns the data as is.

Returns:

shapely.geometry.MultiPolygon

Example

>>> # xdoctest: +REQUIRES(module:kwplot)
>>> # xdoctest: +REQUIRES(module:shapely)
>>> from kwimage.structs.polygon import *  # NOQA
>>> self = MultiPolygon.random(rng=0)
>>> geom = self.to_shapely()
>>> print('geom = {!r}'.format(geom))
classmethod from_shapely(geom)[source]

Convert a shapely polygon or multipolygon to a kwimage.MultiPolygon

Parameters:

geom (shapely.geometry.MultiPolygon | shapely.geometry.Polygon)

Returns:

MultiPolygon

Example

>>> import kwimage
>>> sh_poly = kwimage.Polygon.random().to_shapely()
>>> sh_multi_poly = kwimage.MultiPolygon.random().to_shapely()
>>> kwimage.MultiPolygon.from_shapely(sh_poly)
>>> kwimage.MultiPolygon.from_shapely(sh_multi_poly)
classmethod from_geojson(data_geojson)[source]

Convert a geojson polygon or multipolygon to a kwimage.MultiPolygon

Parameters:

data_geojson (Dict) – geojson data

Returns:

MultiPolygon

Example

>>> import kwimage
>>> orig = kwimage.MultiPolygon.random()
>>> data_geojson = orig.to_geojson()
>>> self = kwimage.MultiPolygon.from_geojson(data_geojson)
to_geojson()[source]

Converts polygon to a geojson structure

Returns:

Dict

classmethod from_coco(data, dims=None)[source]

Accepts either new-style or old-style coco multi-polygons

Parameters:
  • data (List[List[Number] | Dict]) – a new or old style coco multi polygon

  • dims (None | Tuple[int, …]) – the shape dimensions of the canvas. Unused. Exists for compatibility with masks.

Returns:

MultiPolygon

_to_coco(style='orig')[source]
to_coco(style='orig')[source]
Parameters:

style (str) – can be “orig” or “new”

Example

>>> from kwimage.structs.polygon import *  # NOQA
>>> self = MultiPolygon.random(1, rng=0)
>>> self.to_coco()
swap_axes(inplace=False)[source]

Swap x and y axis

Parameters:

inplace (bool)

Returns:

MultiPolygon

draw_on(image, **kwargs)[source]
class kwimage.structs.polygon.PolygonList(data, meta=None)[source]

Bases: ObjectList

Stores and allows manipluation of multiple polygons, usually within the same image.

to_mask_list(dims=None, pixels_are='points')[source]

Converts all items to masks

Returns:

kwimage.MaskList

to_polygon_list()[source]
Returns:

PolygonList

to_segmentation_list()[source]

Converts all items to segmentation objects

Returns:

kwimage.SegmentationList

swap_axes(inplace=False)[source]
Returns:

PolygonList

to_geojson(as_collection=False)[source]

Converts a list of polygons/multipolygons to a geojson structure

Parameters:

as_collection (bool) – if True, wraps the polygon geojson items in a geojson feature collection, otherwise just return a list of items.

Returns:

items or geojson data

Return type:

List[Dict] | Dict

Example

>>> import kwimage
>>> data = [kwimage.Polygon.random(),
>>>         kwimage.Polygon.random(n_holes=1),
>>>         kwimage.MultiPolygon.random(n_holes=1),
>>>         kwimage.MultiPolygon.random()]
>>> self = kwimage.PolygonList(data)
>>> geojson = self.to_geojson(as_collection=True)
>>> items = self.to_geojson(as_collection=False)
>>> print('geojson = {}'.format(ub.urepr(geojson, nl=-2, precision=1)))
>>> print('items = {}'.format(ub.urepr(items, nl=-2, precision=1)))
fill(image, value=1, pixels_are='points', assert_inplace=False)[source]

Inplace fill in an image based on these polygons

Parameters:
  • image (ndarray) – image to draw on (inplace)

  • value (int | Tuple[int, …]) – value fill in with

Returns:

the image that has been modified in place

Return type:

ndarray

draw_on(*args, **kw)[source]
unary_union()[source]
kwimage.structs.polygon._kwimage_from_shapely(geom)[source]
Parameters:

geom (shapely.geometry.base.BaseGeometry)

Returns:

Polygon | MultiPolygon

kwimage.structs.polygon._is_clockwise(verts)[source]

Test if points are in clockwise order [SO1165647].

Parameters:

verts (ndarray)

Returns:

bool

References

kwimage.structs.polygon._order_vertices(verts)[source]

Reorder vertices to be clockwise [SO1709283].

Parameters:

verts (ndarray)

Returns:

ndarray

References