kwimage.structs.polygon
¶
Module Contents¶
Classes¶
Represents a single polygon as set of exterior boundary points and a list |
|
Data structure for storing multiple polygons (typically related to the same |
|
Stores and allows manipluation of multiple polygons, usually within the |
Functions¶
|
References |
|
References |
- class kwimage.structs.polygon._PolyArrayBackend[source]¶
- class kwimage.structs.polygon._PolyWarpMixin[source]¶
- _warp_imgaug(self, 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, default=False) – 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])
>>> # xdoc: +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)
- warp(self, transform, input_dims=None, output_dims=None, inplace=False)[source]¶
Generalized coordinate transform.
- Parameters
transform (GeometricTransform | 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, default=False) – if True, modifies data inplace
Example
>>> from kwimage.structs.polygon import * # NOQA >>> 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(self, factor, about=None, output_dims=None, inplace=False)[source]¶
Scale a polygon by a factor
- Parameters
factor (float or 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 rotation is about this point.
output_dims (Tuple) – unused in non-raster spatial structures
inplace (bool, default=False) – 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='center') >>> # xdoc: +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)
- translate(self, offset, output_dims=None, inplace=False)[source]¶
Shift the polygon up/down left/right
- Parameters
factor (float or 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, default=False) – if True, modifies data inplace
Example
>>> from kwimage.structs.polygon import * # NOQA >>> self = Polygon.random(10, rng=0) >>> new = self.translate(10)
- rotate(self, 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) >>> # xdoc: +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)
- class kwimage.structs.polygon.Polygon(data=None, meta=None, datakeys=None, metakeys=None, **kwargs)[source]¶
Bases:
kwimage.structs._generic.Spatial
,_PolyArrayBackend
,_PolyWarpMixin
,ubelt.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 >>> data = { >>> 'exterior': np.array([[13, 1], [13, 19], [25, 19], [25, 1]]), >>> 'interiors': [ >>> np.array([[13, 13], [14, 12], [24, 12], [25, 13], [25, 18], >>> [24, 19], [14, 19], [13, 18]]), >>> np.array([[13, 2], [14, 1], [24, 1], [25, 2], [25, 11], >>> [24, 12], [14, 12], [13, 11]])] >>> } >>> self = kwimage.Polygon(**data) >>> # xdoc: +REQUIRES(--show) >>> import kwplot >>> kwplot.autompl() >>> self.draw(setlim=True)
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]]))>], })> >>> # xdoc: +REQUIRES(--show) >>> import kwplot >>> kwplot.autompl() >>> self.draw(setlim=True)
- classmethod circle(cls, xy, r, resolution=64)[source]¶
Create a circular polygon
Example
>>> xy = (0.5, 0.5) >>> r = .3 >>> poly = Polygon.circle(xy, r)
- classmethod random(cls, 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, default=False) – fits the minimum and maximum points between 0 and 1
convex (bool, default=True) – force resulting polygon will be convex (may remove exterior points)
- CommandLine:
xdoctest -m kwimage.structs.polygon Polygon.random
Example
>>> rng = None >>> n = 4 >>> n_holes = 1 >>> cls = Polygon >>> self = Polygon.random(n=n, rng=rng, n_holes=n_holes, convex=1) >>> # xdoc: +REQUIRES(--show) >>> import kwplot >>> kwplot.figure(fnum=1, doclf=True) >>> kwplot.autompl() >>> self.draw()
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
- to_mask(self, dims=None)[source]¶
Convert this polygon to a mask
Todo
[ ] currently not efficient
- Parameters
dims (Tuple) – height and width of the output mask
- Returns
kwimage.Mask
Example
>>> from kwimage.structs.polygon import * # NOQA >>> self = Polygon.random(n_holes=1).scale(128) >>> mask = self.to_mask((128, 128)) >>> # xdoc: +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)
- to_relative_mask(self)[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) >>> # xdoc: +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)
- fill(self, image, value=1)[source]¶
Inplace fill in an image based on this polyon.
- Parameters
image (ndarray) – image to draw on
value (int | Tuple[int], default=1) – value fill in with
- Returns
the image that has been modified in place
- Return type
ndarray
- _to_cv_countours(self)[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(Polygon, data)[source]¶
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() >>> self.coerce(self) >>> self.coerce(self.exterior) >>> self.coerce(self.exterior.data) >>> self.coerce(self.data) >>> self.coerce(self.to_geojson())
- classmethod from_shapely(Polygon, 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(Polygon, 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(Polygon, data_geojson)[source]¶
Convert a geojson polygon to a kwimage.Polygon
- Parameters
data_geojson (dict) – geojson data
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(self)[source]¶
Example
>>> # xdoc: +REQUIRES(module:kwplot) >>> # xdoc: +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(self)[source]¶
Converts polygon to a geojson structure
- Returns
Dict[str, object]
Example
>>> import kwimage >>> self = kwimage.Polygon.random() >>> print(self.to_geojson())
- to_wkt(self)[source]¶
Convert a kwimage.Polygon to WKT string
Example
>>> import kwimage >>> self = kwimage.Polygon.random() >>> print(self.to_wkt())
- classmethod from_coco(cls, data, dims=None)[source]¶
Accepts either new-style or old-style coco polygons
- bounding_box(self)[source]¶
Returns an axis-aligned bounding box for the segmentation
- Returns
kwimage.Boxes
- bounding_box_polygon(self)[source]¶
Returns an axis-aligned bounding polygon for the segmentation.
Notes
This Polygon will be a Box, not a convex hull! Use shapely for convex hulls.
- Returns
kwimage.Polygon
- clip(self, x_min, y_min, x_max, y_max, inplace=False)[source]¶
Clip polygon to image boundaries.
Example
>>> from kwimage.structs.polygon import * >>> self = Polygon.random().scale(10).translate(-1) >>> self2 = self.clip(1, 1, 3, 3) >>> # xdoc: +REQUIRES(--show) >>> import kwplot >>> kwplot.autompl() >>> self2.draw(setlim=True)
- draw_on(self, image, color='blue', fill=True, border=False, alpha=1.0, 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, 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()
- draw(self, color='blue', ax=None, alpha=1.0, radius=1, setlim=False, border=False, linewidth=2)[source]¶
Draws polygon in a matplotlib axes. See draw_on for in-memory image modification.
- Parameters
setlim (bool) – if True ensures the limits of the axes contains the polygon
color (str | Tuple) – coercable color
alpha (float) – fill transparency
setlim (bool) – if True, modify the x and y limits of the matplotlib axes such that the polygon is can be seen.
border (bool, default=False) – if True, draws an edge border on the polygon.
linewidth (bool) – width of the border
Todo
[ ] Rework arguments in favor of matplotlib standards
Example
>>> # xdoc: +REQUIRES(module:kwplot) >>> from kwimage.structs.polygon import * # NOQA >>> self = Polygon.random(n_holes=1) >>> self = self.scale(100) >>> # xdoc: +REQUIRES(--show) >>> self.draw() >>> import kwplot >>> kwplot.autompl() >>> from matplotlib import pyplot as plt >>> kwplot.figure(fnum=2) >>> self.draw(setlim=True)
- _ensure_vertex_order(self, 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))
- kwimage.structs.polygon._is_clockwise(verts)[source]¶
References
- Ignore:
verts = poly.data[‘exterior’].data[::-1]
- kwimage.structs.polygon._order_vertices(verts)[source]¶
References
- Ignore:
verts = poly.data[‘exterior’].data[::-1]
- class kwimage.structs.polygon.MultiPolygon[source]¶
Bases:
kwimage.structs._generic.ObjectList
Data structure for storing multiple polygons (typically related to the same underlying but potentitally disjoing object)
- Variables
data (List[Polygon]) –
- classmethod random(self, n=3, n_holes=0, rng=None, tight=False)[source]¶
Create a random MultiPolygon
- Returns
MultiPolygon
- fill(self, image, value=1)[source]¶
Inplace fill in an image based on this multi-polyon.
- Parameters
image (ndarray) – image to draw on (inplace)
value (int | Tuple[int], default=1) – value fill in with
- Returns
the image that has been modified in place
- Return type
ndarray
- bounding_box(self)[source]¶
Return the bounding box of the multi polygon
- Returns
- a Boxes object with one box that encloses all
polygons
- Return type
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)
- to_mask(self, dims=None)[source]¶
Returns a mask object indication regions occupied by this multipolygon
Example
>>> from kwimage.structs.polygon import * # NOQA >>> s = 100 >>> self = MultiPolygon.random(rng=0).scale(s) >>> dims = (s, s) >>> mask = self.to_mask(dims)
>>> # xdoc: +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, s) >>> ax.set_ylim(0, s) >>> self.draw(color='red', alpha=.4) >>> mask.draw(color='blue', alpha=.4)
- to_relative_mask(self)[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
Mask
- classmethod coerce(cls, data, dims=None)[source]¶
Attempts to construct a MultiPolygon instance from the input data
See Mask.coerce
- to_shapely(self)[source]¶
Example
>>> # xdoc: +REQUIRES(module:kwplot) >>> # xdoc: +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(MultiPolygon, geom)[source]¶
Convert a shapely polygon or multipolygon to a kwimage.MultiPolygon
- classmethod from_geojson(MultiPolygon, data_geojson)[source]¶
Convert a geojson polygon or multipolygon to a kwimage.MultiPolygon
Example
>>> import kwimage >>> orig = kwimage.MultiPolygon.random() >>> data_geojson = orig.to_geojson() >>> self = kwimage.MultiPolygon.from_geojson(data_geojson)
- classmethod from_coco(cls, data, dims=None)[source]¶
Accepts either new-style or old-style coco multi-polygons
- class kwimage.structs.polygon.PolygonList[source]¶
Bases:
kwimage.structs._generic.ObjectList
Stores and allows manipluation of multiple polygons, usually within the same image.
- to_geojson(self, 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.repr2(geojson, nl=-2, precision=1))) >>> print('items = {}'.format(ub.repr2(items, nl=-2, precision=1)))