geowatch.utils.util_kwimage module

These functions might be added to kwimage

geowatch.utils.util_kwimage.upweight_center_mask(shape)[source]

Example

>>> from geowatch.utils.util_kwimage import *  # NOQA
>>> shapes = [32, 64, 96, 128, 256]
>>> results = {}
>>> for shape in shapes:
>>>     results[str(shape)] = upweight_center_mask(shape)
>>> # xdoc: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> pnum_ = kwplot.PlotNums(nSubplots=len(results))
>>> for k, result in results.items():
>>>     kwplot.imshow(result, pnum=pnum_(), title=k)
>>> kwplot.show_if_requested()
geowatch.utils.util_kwimage.perchannel_colorize(data, channel_colors=None)[source]

Note: this logic semi-exists in kwimage.Heatmap. It would be good to consolidate it.

Parameters:

data (ndarray) – the last dimension should be chanels, and they should be probabilities between zero and one.

Example

>>> from geowatch.utils.util_kwimage import *  # NOQA
>>> import itertools as it
>>> import kwarray
>>> channel_colors = ['tomato', 'gold', 'lime', 'darkturquoise']
>>> c = len(channel_colors)
>>> s = 32
>>> cx_combos = list(ub.flatten(it.combinations(range(c), n) for n in range(0, c + 1)))
>>> w = s // len(cx_combos)
>>> data = np.zeros((s, s, c), dtype=np.float32)
>>> y_slider = kwarray.SlidingWindow((s, s), (w, s,))
>>> x_slider = kwarray.SlidingWindow((s, s), (s, w,))
>>> for idx, cxs in enumerate(cx_combos):
>>>     for cx in cxs:
>>>         data[x_slider[idx] + (cx,)] =  1
>>>         data[y_slider[idx] + (cx,)] =  0.5
>>> canvas = perchannel_colorize(data, channel_colors)[..., 0:3]
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(canvas, docla=1)

Example

>>> from geowatch.utils.util_kwimage import *  # NOQA
>>> import itertools as it
>>> import kwarray
>>> channel_colors = ['blue']
>>> data = np.linspace(0, 1, 512 * 512).reshape(512, 512, 1)
>>> canvas = perchannel_colorize(data, channel_colors)[..., 0:3]
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(canvas, docla=1)
geowatch.utils.util_kwimage.ensure_false_color(canvas, method='ortho')[source]

Given a canvas with more than 3 colors, (or 2 colors) do something to get it into a colorized space.

Todo

  • [ ] I have no idea how well this works. Probably better methods exist. Find them.

Example

>>> import kwimage
>>> import numpy as np
>>> demo_img = kwimage.ensure_float01(kwimage.grab_test_image('astro'))
>>> canvas = demo_img @ np.random.rand(3, 2)
>>> rgb_canvas2 = ensure_false_color(canvas)
>>> canvas = np.tile(demo_img, (1, 1, 10))
>>> rgb_canvas10 = ensure_false_color(canvas)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(rgb_canvas2, pnum=(1, 2, 1))
>>> kwplot.imshow(rgb_canvas10, pnum=(1, 2, 2))
geowatch.utils.util_kwimage.colorize_label_image(labels, with_legend=True, label_mapping=None, label_to_color=None, legend_dpi=200)[source]

Rename to draw_label_image?

Replace an image with integer labels with colors

Parameters:
  • labels (ndarray) – a label image

  • with_legend (bool)

  • legend_mapping (dict) – maps the label used in the label image to what should appear in the legend.

CommandLine

python -X importtime -m xdoctest geowatch.utils.util_kwimage colorize_label_image

Example

>>> from geowatch.utils.util_kwimage import *  # NOQA
>>> labels = (np.random.rand(32, 32) * 10).astype(np.uint8) % 5
>>> label_to_color = {0: 'black'}
>>> label_mapping = {0: 'background'}
>>> with_legend = True
>>> canvas1 = colorize_label_image(labels, with_legend,
>>>     label_mapping=label_mapping, label_to_color=label_to_color)
>>> canvas2 = colorize_label_image(labels, with_legend,
>>>     label_mapping=label_mapping, label_to_color=None)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(canvas1, pnum=(1, 2, 1), fnum=1)
>>> kwplot.imshow(canvas2, pnum=(1, 2, 2), fnum=1)

Example

>>> from geowatch.utils.util_kwimage import *  # NOQA
>>> labels = (np.random.rand(4, 4) * 10) % 5
>>> labels[0:2] = np.nan
>>> label_to_color = {0: 'black'}
>>> label_mapping = {0: 'background'}
>>> with_legend = True
>>> canvas1 = colorize_label_image(labels, with_legend,
>>>     label_mapping=label_mapping, label_to_color=label_to_color)
>>> canvas2 = colorize_label_image(labels, with_legend,
>>>     label_mapping=label_mapping, label_to_color=None)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(canvas1, pnum=(1, 2, 1), fnum=1)
>>> kwplot.imshow(canvas2, pnum=(1, 2, 2), fnum=1)
geowatch.utils.util_kwimage.local_variance(image, kernel, handle_nans=True)[source]

The local variance at each point in the image (take the sqrt to get the local std)

Parameters:
  • image (ndarray)

  • kernel (int | Tuple[int, int]) kernel size (w, h)

Returns:

the image with the variance at each point

Return type:

ndarray

References

https://answers.opencv.org/question/193393/local-mean-and-variance/ https://stackoverflow.com/questions/11456565/opencv-mean-sd-filter

Example

>>> # Test with nans
>>> from geowatch.utils.util_kwimage import *  # NOQA
>>> import kwimage
>>> import kwarray
>>> shape = (512, 512)
>>> dsize = shape[::-1]
>>> image = np.zeros(shape + (3,), dtype=np.uint8)
>>> image = kwimage.ensure_float01(image)
>>> rng = kwarray.ensure_rng(0)
>>> image[:, 256:, :] = rng.rand(512, 256, 3)  # high frequency noise
>>> poly1 = kwimage.Polygon.random(rng=rng).scale(dsize)
>>> poly2 = kwimage.Polygon.random(rng=rng).scale(dsize)
>>> poly3 = kwimage.Polygon.random(rng=rng).scale(dsize)
>>> poly1.draw_on(image, color='kitware_blue')
>>> poly2.draw_on(image, color='pink')
>>> poly3.draw_on(image, color='kitware_green')
>>> image[50:70, :, :] = 1  # a line of ones
>>> image[150:170, :, :] = np.nan  # a line of nans
>>> #image = kwimage.convert_colorspace(image, 'rgb', 'gray')
>>> varimg = local_variance(image, kernel=7)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(kwimage.fill_nans_with_checkers(image), pnum=(1, 2, 1), title='input image')
>>> kwplot.imshow(kwimage.fill_nans_with_checkers(kwarray.normalize(varimg)), pnum=(1, 2, 2), title='variance image')
geowatch.utils.util_kwimage.find_lowvariance_regions(image, kernel=7)[source]

The idea is that we want to detect large region in an image that are filled entirely with the same color.

The approach is that we are going to find the local variance of the image in a KxK window (K is the size of a kernel and corresponds to a minimum size of homogenous region that we care to segment). Then we are going to find all regions with zero variance. The connected components of that binary image should be roughly what we want.

We can postprocess this with floodfills to get nearly exacly what we want.

Example

>>> from geowatch.utils.util_kwimage import *  # NOQA
>>> import kwimage
>>> import kwarray
>>> shape = (512, 512)
>>> dsize = shape[::-1]
>>> image = np.zeros(shape + (3,), dtype=np.uint8)
>>> rng = kwarray.ensure_rng(0)
>>> image[:, 256:, :] = (rng.rand(512, 256, 3) * 255)  # high frequency noise
>>> poly1 = kwimage.Polygon.random(rng=rng).scale(dsize)
>>> poly2 = kwimage.Polygon.random(rng=rng).scale(dsize)
>>> poly3 = kwimage.Polygon.random(rng=rng).scale(dsize)
>>> poly1.draw_on(image, color='kitware_blue')
>>> poly2.draw_on(image, color='pink')
>>> poly3.draw_on(image, color='kitware_green')
>>> image[50:70, :, :] = 255  # a "thin" line
>>> labels = find_lowvariance_regions(image)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> canvas = colorize_label_image(labels)
>>> kwplot.imshow(image, pnum=(1, 2, 1), title='input image')
>>> kwplot.imshow(canvas, pnum=(1, 2, 2), title='labeled regions')

Example

>>> # Test with nans
>>> from geowatch.utils.util_kwimage import *  # NOQA
>>> import kwimage
>>> import kwarray
>>> shape = (512, 512)
>>> dsize = shape[::-1]
>>> image = np.zeros(shape + (3,), dtype=np.uint8)
>>> image = kwimage.ensure_float01(image)
>>> rng = kwarray.ensure_rng(0)
>>> image[:, 256:, :] = rng.rand(512, 256, 3)  # high frequency noise
>>> poly1 = kwimage.Polygon.random(rng=rng).scale(dsize)
>>> poly2 = kwimage.Polygon.random(rng=rng).scale(dsize)
>>> poly3 = kwimage.Polygon.random(rng=rng).scale(dsize)
>>> poly1.draw_on(image, color='kitware_blue')
>>> poly2.draw_on(image, color='pink')
>>> poly3.draw_on(image, color='kitware_green')
>>> image[50:70, :, :] = 1  # a line of ones
>>> image[150:170, :, :] = np.nan  # a line of nans
>>> image = kwimage.convert_colorspace(image, 'rgb', 'gray')
>>> labels = find_lowvariance_regions(image, kernel=7)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> canvas = colorize_label_image(labels)
>>> kwplot.imshow(image, pnum=(1, 2, 1), title='input image')
>>> kwplot.imshow(canvas, pnum=(1, 2, 2), title='labeled regions')
geowatch.utils.util_kwimage.find_samecolor_regions(image, min_region_size=49, seed_method='grid', connectivity=8, scale=1.0, grid_stride='auto', PRINT_STEPS=0, values=None)[source]

Find large spatially connected regions in an image where all pixels have the same value.

Works by selecting a set of initial seed points. A flood fill is run at each seed point to find potential large samedata regions.

More specifically, we find seed points using a regular grid, or finding pixels in regions with low spatial variance. Then for each candidate seed point, we do a flood-fill to see if it in a large samecolor region. Each connected region that satisfies the requested criteria is given an integer label. The return value is a label-mask where each pixel that is part of a region is given a value corresponding to that region’s integer label. Any pixel not part of a region is given a label of zero. For technical reasons, returned labels will start at 1.

Parameters:
  • image (ndarray) – image to find regions of the same color

  • min_region_size (int) – the minimum number of pixels in a region for it to be considered valid.

  • seed_method (str) – can be grid or variance

  • connectivity (int) – cc connectivity. Either 4 or 8.

  • scale (float) – scale at which the computation is done. Should be a value between 0 and 1. The default is 1. Setting to less than 1 will resize the image, perform the computation, and then upsample the output. This can cause a significant speed increase at the cost of some accuracy.

  • values (None | List) – if specified, only finds the samecolor regions with this intensity value, otherwise any value will be considered.

References

https://docs.opencv.org/3.4/d7/d1b/group__imgproc__misc.html#ga366aae45a6c1289b341d140839f18717

Example

>>> from geowatch.utils.util_kwimage import *  # NOQA
>>> import kwimage
>>> import kwarray
>>> shape = (512, 512)
>>> dsize = shape[::-1]
>>> image = np.zeros(shape + (3,), dtype=np.uint8)
>>> rng = kwarray.ensure_rng(0)
>>> image[:, 256:, :] = (rng.rand(512, 256, 3) * 255)  # high frequency noise
>>> poly1 = kwimage.Polygon.random(rng=rng).scale(dsize)
>>> poly2 = kwimage.Polygon.random(rng=rng).scale(dsize)
>>> poly3 = kwimage.Polygon.random(rng=rng).scale(dsize)
>>> poly1.draw_on(image, color='kitware_blue')
>>> poly2.draw_on(image, color='pink')
>>> poly3.draw_on(image, color='kitware_green')
>>> image[50:70, :, :] = 255  # a "thin" line
>>> #labels = find_samecolor_regions(image, seed_method='grid')
>>> labels = find_samecolor_regions(image, seed_method='variance')
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> canvas = colorize_label_image(labels)
>>> kwplot.imshow(image, pnum=(1, 2, 1), title='input image')
>>> kwplot.imshow(canvas, pnum=(1, 2, 2), title='labeled regions')

Example

>>> # Test with nans
>>> from geowatch.utils.util_kwimage import *  # NOQA
>>> import kwimage
>>> import kwarray
>>> shape = (512, 512)
>>> dsize = shape[::-1]
>>> image = np.zeros(shape + (3,), dtype=np.uint8)
>>> image = kwimage.ensure_float01(image).astype(np.float32)
>>> rng = kwarray.ensure_rng(0)
>>> image[:, 256:, :] = rng.rand(512, 256, 3)  # high frequency noise
>>> poly1 = kwimage.Polygon.random(rng=rng).scale(dsize)
>>> poly2 = kwimage.Polygon.random(rng=rng).scale(dsize)
>>> poly3 = kwimage.Polygon.random(rng=rng).scale(dsize)
>>> poly1.draw_on(image, color='kitware_blue')
>>> poly2.draw_on(image, color='pink')
>>> poly3.draw_on(image, color='kitware_green')
>>> image[50:70, :, :] = 1  # a line of ones
>>> image[150:170, :, :] = np.nan  # a line of nans
>>> image = kwimage.convert_colorspace(image, 'rgb', 'gray')
>>> labels = find_samecolor_regions(image, seed_method='grid')
>>> #labels = find_samecolor_regions(image, seed_method='variance')
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> canvas = colorize_label_image(labels)
>>> kwplot.imshow(image, pnum=(1, 2, 1), title='input image')
>>> kwplot.imshow(canvas, pnum=(1, 2, 2), title='labeled regions')

Example

>>> from geowatch.utils.util_kwimage import *  # NOQA
>>> w, h = 5, 4
>>> image = (np.arange(w * h).reshape(h, w)).astype(np.uint8)
>>> image[2, 2] = 0
>>> image[2, 3] = 0
>>> image[3, 4] = 0
>>> min_region_size = 2
>>> seed_method = 'grid'
>>> connectivity = 8
>>> scale = 1.0
>>> grid_stride = 1
>>> labels = find_samecolor_regions(
>>>     image, min_region_size, seed_method, connectivity, scale,
>>>     grid_stride, PRINT_STEPS=0)
>>> print(labels)
>>> print(image)
>>> assert (labels > 0).sum() == 3

Example

>>> # Check dtypes
>>> from geowatch.utils.util_kwimage import *  # NOQA
>>> dtypes = [np.uint8, np.float32, np.float64, int, np.int16, np.uint16]
>>> failed = []
>>> for dtype in dtypes:
...    image = (np.random.rand(32, 32) * 512).astype(dtype)
...    try:
...        find_samecolor_regions(image, min_region_size=10)
...    except Exception:
...        failed.append(dtype)
>>> print(f'failed={failed}')
>>> assert len(failed) == 0

Example

>>> # Check specifying valid values
>>> from geowatch.utils.util_kwimage import *  # NOQA
>>> w, h = 32, 32
>>> image = np.zeros((h, w), dtype=np.uint8)
>>> image[0:8, :] = 1
>>> image[8:16, :] = 2
>>> image[16:32, :] = 3
>>> image[:, 16:32] = 4
>>> image[24:32, :] = 1
>>> image[2, 3] = 5
>>> image[7, 11] = 13
>>> image[17, 19] = 23
>>> image[29, 31] = 37
>>> #image = kwimage.imresize(image, dsize=(256, 256), interpolation='lanczos')
>>> min_region_size = 2
>>> seed_method = 'grid'
>>> connectivity = 8
>>> scale = 1.0
>>> grid_stride = 1
>>> labels1 = find_samecolor_regions(
>>>     image, min_region_size, seed_method, connectivity, scale,
>>>     grid_stride, PRINT_STEPS=0)
>>> labels2 = find_samecolor_regions(
>>>     image, min_region_size, seed_method, connectivity, scale,
>>>     grid_stride, PRINT_STEPS=0, values={1})
>>> #labels = find_samecolor_regions(image, seed_method='variance')
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> import kwarray
>>> kwplot.imshow(image, pnum=(1, 3, 1), title='input image', cmap='viridis')
>>> kwplot.imshow(colorize_label_image(labels1), pnum=(1, 3, 2), title='values=None')
>>> kwplot.imshow(colorize_label_image(labels2), pnum=(1, 3, 3), title='values={1}')
Returns:

a label array where 0 indicates background and a

non-zero label is a samecolor region.

Return type:

ndarray

Todo

  • [ ] Could generalize this to search for values within a tolerence, in

which case we would get a fuzzyfind sort of function.

Note

this is a kwimage candidate

Optimizing:
>>> import xdev
>>> xdev.profile_now(find_lowvariance_regions)(image)
>>> xdev.profile_now(find_samecolor_regions)(image)
>>> find_samecolor_regions(image)
>>> #
>>> import timerit
>>> ti = timerit.Timerit(30, bestof=3, verbose=2)
>>> for timer in ti.reset('find_lowvariance_regions'):
>>>     with timer:
>>>         find_lowvariance_regions(image)
>>> #
>>> ti = timerit.Timerit(30, bestof=3, verbose=2)
>>> for timer in ti.reset('find_samecolor_regions'):
>>>     with timer:
>>>         labels = find_samecolor_regions(image)
>>> #
>>> # Test to see the overhead compared to different levels of downscale / upscale
>>> ti = timerit.Timerit(30, bestof=3, verbose=2)
>>> for timer in ti.reset('find_samecolor_regions + resize'):
>>>     with timer:
>>>         labels = find_samecolor_regions(image, scale=0.5)
>>> #
>>> # Test to see the overhead compared to different levels of downscale / upscale
>>> ti = timerit.Timerit(30, bestof=3, verbose=2)
>>> for timer in ti.reset('find_samecolor_regions + resize'):
>>>     with timer:
>>>         labels = find_samecolor_regions(image, scale=0.25)
>>> from geowatch.utils.util_kwimage import *  # NOQA
>>> import kwimage
>>> image = kwimage.grab_test_image('amazon', dsize=(512, 512))[..., 0:1]
>>> poly = kwimage.Polygon.random().scale(image.shape[0:2][::-1])
>>> image = poly.fill(image, value=0)
>>> mask = (np.random.rand(*image.shape[0:2]) > 0.999)
>>> rs, cs = np.where(mask)
>>> image[rs[0:5], cs[0:5]] = 0
>>> ti = timerit.Timerit(2, bestof=2, verbose=2)
>>> for timer in ti.reset('find_samecolor_regions, with values'):
>>>     labels = find_samecolor_regions(image, values={0})
>>> for timer in ti.reset('find_samecolor_regions, with values, seed_method=values'):
>>>     labels = find_samecolor_regions(image, values={0}, seed_method='values')
>>> for timer in ti.reset('find_samecolor_regions, with values, seed_method=variance'):
>>>     labels = find_samecolor_regions(image, values={0}, seed_method='variance')
>>> for timer in ti.reset('find_samecolor_regions, without values'):
>>>     labels = find_samecolor_regions(image, values=None)
geowatch.utils.util_kwimage.find_high_frequency_values(image, values=None, abs_thresh=0.2, rel_thresh=None)[source]

Values that appear in the image very often, may be indicative of an artifact that we should remove.

Parameters:

values (None | List) – the values of interest to find. if unspecified, any highly frequent value is flagged.

geowatch.utils.util_kwimage.polygon_distance_transform(poly, shape)[source]

The API needs work, but I think the idea could be useful

Parameters:
  • poly (kwimage.Polygon) – polygon to create distance weights for

  • shape (Tuple[int, int]) – size of canvas to draw onto

Returns:

Tuple[ndarray, ndarray] -

dist - pixels inside the polygon contain the distance to the edge of the polygon. poly_mask - a binary mask where 1s indicate where the polygon is.

Example

>>> from geowatch.utils.util_kwimage import *  # NOQA
>>> import cv2
>>> import kwimage
>>> poly = kwimage.Polygon.random().scale(32)
>>> shape = (32, 32)
>>> dist, poly_mask = polygon_distance_transform(poly, shape)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(dist, cmap='viridis', doclf=1, pnum=(1, 2, 1), title='distance weights')
>>> poly.draw(fill=0, border=1)
>>> kwplot.imshow(poly_mask.astype(np.float32), pnum=(1, 2, 2), title='poly-mask')
geowatch.utils.util_kwimage.multiple_polygon_distance_transform_weighting(polys, shape)[source]

Does a distance tranform on multiple polygons independently and then combines their weights such that each pixels uses the maximum distance to a polygon it is contained in.

Parameters:
  • polys (list[kwimage.Polygon]) – polygons to draw.

  • shape (Tuple[int, int]) – size of canvas to draw onto

Returns:

Tuple[ndarray, ndarray] -

dist - pixels inside the polygon contain the distance to the edge of the polygon. poly_mask - a binary mask where 1s indicate where the polygon is.

CommandLine

xdoctest -m geowatch.utils.util_kwimage multiple_polygon_distance_transform_weighting --show

Example

>>> from geowatch.utils.util_kwimage import *  # NOQA
>>> import kwimage
>>> poly1 = kwimage.Polygon.random(rng=0).scale(32)
>>> poly2 = poly1.translate((5, 5))
>>> poly3 = poly2.translate((5, 5))
>>> poly4 = poly3.translate((5, 5))
>>> poly5 = poly4.translate((5, 5))
>>> polys = [poly1, poly2, poly3, poly4, poly5]
>>> shape = (32, 32)
>>> dist, poly_mask = multiple_polygon_distance_transform_weighting(polys, shape)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(dist, cmap='viridis', doclf=1, pnum=(1, 2, 1), title='distance weights')
>>> for poly in polys:
>>>     poly.draw(fill=0, border=1)
>>> kwplot.imshow(poly_mask.astype(np.float32), pnum=(1, 2, 2), title='poly-mask')
>>> kwplot.show_if_requested()
geowatch.utils.util_kwimage.devcheck_frame_poly_weights(poly, shape, dtype=<class 'numpy.uint8'>)[source]

import kwimage import kwplot kwplot.autompl() from geowatch.utils import util_kwimage space_shape = (380, 380) weights1 = util_kwimage.upweight_center_mask(space_shape) weights2 = kwarray.normalize(kwimage.gaussian_patch(space_shape)) sigma3 = 4.8 * ((space_shape[0] - 1) * 0.5 - 1) + 0.8 weights3 = kwarray.normalize(kwimage.gaussian_patch(space_shape, sigma=sigma3))

min_spacetime_weight = 0.5

weights1 = np.maximum(weights1, min_spacetime_weight) weights2 = np.maximum(weights2, min_spacetime_weight) weights3 = np.maximum(weights3, min_spacetime_weight)

# Hack so color bar goes to 0 weights3[0, 0] = 0 weights2[0, 0] = 0 weights1[0, 0] = 0

kwplot.imshow(weights1, pnum=(1, 3, 1), title=’current’, cmap=’viridis’, data_colorbar=1) kwplot.imshow(weights2, pnum=(1, 3, 2), title=’variant1’, cmap=’viridis’, data_colorbar=1) kwplot.imshow(weights3, pnum=(1, 3, 3), title=’variant2’, cmap=’viridis’, data_colorbar=1)

geowatch.utils.util_kwimage.find_low_overlap_covering_boxes(polygons, scale, min_box_dim, max_box_dim, merge_thresh=0.001, max_iters=100)[source]

Given a set of polygons we want to find a small set of boxes that completely cover all of those polygons.

We are going to do some set-cover shenanigans by making a bunch of candidate boxes based on some hueristics and find a set cover of those.

Then we will search for small boxes that can be merged, and iterate.

Parameters:
  • polygons (List[Polygon) – the input shapes that need clustering

  • scale (float) – scale factor for context we want around each polygon.

  • min_box_dim (float) – minimum side length of a returned box

  • max_box_dim (float) – maximum side length of a returned box

Returns:

keep_bbs: The chosen boxes that cover the inputs overlap_idxs: Corresponding list indicating which of the original

inputs overlaps the each covering box.

Return type:

Tuple[Boxes, List[ndarray]]

References

https://aip.scitation.org/doi/pdf/10.1063/1.5090003?cookieSet=1 Mercantile - https://pypi.org/project/mercantile/0.4/ BingMapsTiling - XYZ Tiling for webmap services https://mercantile.readthedocs.io/en/stable/api/mercantile.html#mercantile.bounding_tile https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.713.6709&rep=rep1&type=pdf

Example

>>> # Create random polygons as test data
>>> from geowatch.utils.util_kwimage import *  # NOQA
>>> import kwimage
>>> import kwarray
>>> from kwarray import distributions
>>> rng = kwarray.ensure_rng(934602708841)
>>> num = 200
>>> #
>>> canvas_width = 2000
>>> offset_distri = distributions.Uniform(canvas_width, rng=rng)
>>> scale_distri = distributions.Uniform(10, 150, rng=rng)
>>> #
>>> polygons = []
>>> for _ in range(num):
>>>     poly = kwimage.Polygon.random(rng=rng)
>>>     poly = poly.scale(scale_distri.sample())
>>>     poly = poly.translate(offset_distri.sample(2))
>>>     polygons.append(poly)
>>> polygons = kwimage.PolygonList(polygons)
>>> #
>>> scale = 1.0
>>> min_box_dim = 240
>>> max_box_dim = 500
>>> #
>>> keep_bbs, overlap_idxs = find_low_overlap_covering_boxes(polygons, scale, min_box_dim, max_box_dim)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> plt = kwplot.autoplt()
>>> kwplot.figure(fnum=1, doclf=1)
>>> polygons.draw(color='pink')
>>> keep_bbs.draw(color='orange', setlim=1)
>>> plt.gca().set_title('find_low_overlap_covering_boxes')

Example

>>> # Empty test case
>>> from geowatch.utils.util_kwimage import *  # NOQA
>>> keep_bbs, overlap_idxs = find_low_overlap_covering_boxes([], 1, 0, 1)
>>> assert len(keep_bbs) == 0
>>> assert len(overlap_idxs) == 0
geowatch.utils.util_kwimage.find_low_overlap_covering_boxes_optimize(polygons, scale, min_box_dim, max_box_dim, merge_thresh=0.001, max_iters=100)[source]

A variant of the covering problem that doesn’t work that well, but might in the future with tweaks.

geowatch.utils.util_kwimage.exactly_1channel(image, ndim=2)[source]

Like atleast_3channels, exactly_1channel returns a 2D image as either a 2D or 3D array, depending on if ndim is 2 or 3. For a 3D array the last dimension is always 1. An error is thrown if assumptions are not met.

PORTED TO kwimage in version 0.9.14

Parameters:
  • image (ndarray)

  • ndim (int) – either 2 or 3

Example

>>> assert exactly_1channel(np.empty((3, 3)), ndim=2).shape == (3, 3)
>>> assert exactly_1channel(np.empty((3, 3)), ndim=3).shape == (3, 3, 1)
>>> assert exactly_1channel(np.empty((3, 3, 1)), ndim=2).shape == (3, 3)
>>> assert exactly_1channel(np.empty((3, 3, 1)), ndim=3).shape == (3, 3, 1)
geowatch.utils.util_kwimage.load_image_shape(fpath, backend='auto', include_channels=True)[source]

Version from kwimage dev/0.9.26

Determine the height/width/channels of an image without reading the entire file.

Parameters:
  • fpath (str) – path to an image

  • backend (str | List[str]) – can be “auto”, “pil”, or “gdal”. Can also be a list of which backends to try in which order.

  • include_channels (bool) – if False, only reads the height, width.

Returns:

Tuple[int, int, int] - shape of the image

Recall this library uses the convention that “shape” is refers to height,width,channels array-style ordering and “size” is width,height cv2-style ordering.

Example

>>> from geowatch.utils.util_kwimage import *  # NOQA
>>> import ubelt as ub
>>> import kwimage
>>> dpath = ub.Path.appdir('kwimage/tests', type='cache').ensuredir()
>>> fpath = dpath / 'foo.tif'
>>> kwimage.imwrite(fpath, np.random.rand(64, 64, 3))
>>> shape1 = load_image_shape(fpath, backend=['pil', 'gdal'])
>>> shape2 = load_image_shape(fpath, backend=['gdal', 'pil'])
>>> assert shape1 == shape2 == (64, 64, 3)
geowatch.utils.util_kwimage.draw_multiclass_clf_on_image(im, classes, probs=None, true_ohe=None, top_k=3, border=1)[source]

Draws multiclass classification label on an image.

Works best with image chips sized between 200x200 and 500x500

Parameters:
  • im (ndarray) – the image

  • classes (Sequence[str] | kwcoco.CategoryTree) – list of class names

  • true_ohe (int) – true class indicator vector

  • probs (ndarray) – predicted class probs for each class