geowatch.utils.kwcoco_extensions module

Adds fields needed by ndsampler to correctly “watch” a region.

Some of this is done hueristically. We assume images come from certain sensors. We assume input is orthorectified. We assume some GSD “target” gsd for video and image processing. Note a video GSD will typically be much higher (i.e. lower resolution) than an image GSD.

geowatch.utils.kwcoco_extensions.filter_image_ids(coco_dset, gids=None, include_sensors=None, exclude_sensors=None, select_images=None, select_videos=None)[source]

Filters to a specific set of images given query parameters

geowatch.utils.kwcoco_extensions.populate_watch_fields(coco_dset, target_gsd=10.0, vidids=None, overwrite=False, default_gsd=None, conform=True, enable_video_stats=True, enable_valid_region=False, enable_intensity_stats=False, workers=0, mode='thread', remove_broken=False, skip_populate_errors=False)[source]

Aggregate populate function for fields useful to GeoWATCH.

Parameters:
  • coco_dset (Dataset) – dataset to work with

  • target_gsd (float) – target gsd in meters

  • overwrite (bool | List[str]) – if True or False overwrites everything or nothing. Otherwise it can be a list of strings indicating what is overwritten. Valid keys are warp, band, and channels.

  • default_gsd (None | float) – if specified, assumed any images without geo-metadata have this GSD’

Example

>>> from geowatch.utils.kwcoco_extensions import *  # NOQA
>>> import kwcoco
>>> # TODO: make a demo dataset with some sort of gsd metadata
>>> coco_dset = kwcoco.CocoDataset.demo('vidshapes8-multispectral')
>>> print('coco_dset = {!r}'.format(coco_dset))
>>> target_gsd = 13.0
>>> populate_watch_fields(coco_dset, target_gsd, default_gsd=1)
>>> print('coco_dset.index.imgs[1] = ' + ub.urepr(coco_dset.index.imgs[1], nl=2))
>>> print('coco_dset.index.videos = {}'.format(ub.urepr(coco_dset.index.videos, nl=1)))
>>> # TODO: make a demo dataset with some sort of gsd metadata
>>> coco_dset = kwcoco.CocoDataset.demo('vidshapes8')
>>> print('coco_dset = {!r}'.format(coco_dset))
>>> target_gsd = 13.0
>>> populate_watch_fields(coco_dset, target_gsd, default_gsd=1)
>>> print('coco_dset.index.imgs[1] = ' + ub.urepr(coco_dset.index.imgs[1], nl=2))
>>> print('coco_dset.index.videos = {}'.format(ub.urepr(coco_dset.index.videos, nl=1)))
geowatch.utils.kwcoco_extensions.coco_populate_geo_heuristics(coco_dset: CocoDataset, gids=None, overwrite=False, default_gsd=None, workers=0, mode='thread', remove_broken=False, **kw)[source]

Example

>>> # xdoctest: +REQUIRES(env:DVC_DPATH)
>>> from geowatch.utils.kwcoco_extensions import *  # NOQA
>>> from geowatch.utils.util_data import find_dvc_dpath
>>> import kwcoco
>>> dvc_dpath = find_dvc_dpath()
>>> coco_fpath = dvc_dpath / 'drop1-S2-L8-aligned/data.kwcoco.json'
>>> coco_dset = kwcoco.CocoDataset(coco_fpath)
>>> coco_populate_geo_heuristics(coco_dset, overwrite=True, workers=12,
>>>                              keep_geotiff_metadata=False,
>>>                              mode='process')

Example

>>> # xdoctest: +REQUIRES(env:DVC_DPATH)
>>> from geowatch.utils.kwcoco_extensions import *  # NOQA
>>> from geowatch.utils.util_data import find_dvc_dpath
>>> import kwcoco
>>> dvc_dpath = find_dvc_dpath()
>>> coco_fpath = dvc_dpath / 'drop1-S2-L8-aligned/data.kwcoco.json'
>>> coco_dset = kwcoco.CocoDataset(coco_fpath)
>>> coco_populate_geo_heuristics(coco_dset, overwrite=True, workers=12,
>>>                              keep_geotiff_metadata=False,
>>>                              mode='process')
geowatch.utils.kwcoco_extensions.coco_populate_geo_img_heuristics2(coco_img, overwrite=False, default_gsd=None, keep_geotiff_metadata=False, enable_intensity_stats=False, enable_valid_region=False, skip_populate_errors=False)[source]

Note: this will not overwrite existing channel info unless specified

Commandline

xdoctest -m ~/code/watch/geowatch/utils/kwcoco_extensions.py –profile

Todo

  • [ ] Use logic in the align demo classmethod to make an example

    that uses a real L8 / S2 image.

Example

>>> from geowatch.utils.kwcoco_extensions import *  # NOQA
>>> import geowatch
>>> import json
>>> coco_dset = geowatch.coerce_kwcoco('geowatch-msi-geodata-dates-heatmap-videos1-frames2-gsize64')
>>> gid = 1
>>> overwrite = {'warp', 'band'}
>>> default_gsd = None
>>> kw = {}
>>> coco_img = coco_dset.coco_image(gid)
>>> before_img_attrs = list(coco_img.img.keys())
>>> before_aux_attr_hist = ub.dict_hist(ub.flatten([list(aux) for aux in coco_img.img['auxiliary']]))
>>> print('before_img_attrs = {!r}'.format(before_img_attrs))
>>> print('before_aux_attr_hist = {}'.format(ub.urepr(before_aux_attr_hist, nl=1)))
>>> coco_populate_geo_img_heuristics2(coco_img)
>>> img = coco_dset.index.imgs[gid]
>>> after_img_attrs = list(coco_img.img.keys())
>>> after_aux_attr_hist = ub.dict_hist(ub.flatten([list(aux) for aux in coco_img.img['auxiliary']]))
>>> new_img_attrs = set(after_img_attrs) - set(before_img_attrs)
>>> new_aux_attrs = {k: after_aux_attr_hist[k] - before_aux_attr_hist.get(k, 0) for k in after_aux_attr_hist}
>>> new_aux_attrs = {k: v for k, v in new_aux_attrs.items() if v > 0}
>>> print('new_img_attrs = {}'.format(ub.urepr(new_img_attrs, nl=1)))
>>> print('new_aux_attrs = {}'.format(ub.urepr(new_aux_attrs, nl=1)))
>>> #print('after_img_attrs = {}'.format(ub.urepr(after_img_attrs, nl=1)))
>>> #print('after_aux_attr_hist = {}'.format(ub.urepr(after_aux_attr_hist, nl=1)))
>>> assert 'geos_corners' in img
>>> #assert 'default_nodata' in img
>>> #assert 'default_nodata' in new_aux_attrs
>>> print(ub.varied_values(list(map(lambda x: ub.map_vals(json.dumps, x), coco_img.img['auxiliary'])), default=None))

Example

>>> from geowatch.utils.kwcoco_extensions import *  # NOQA
>>> import kwcoco
>>> ###
>>> gid = 1
>>> dset = kwcoco.CocoDataset.demo('vidshapes8-multispectral')
>>> coco_img = dset.coco_image(gid)
>>> coco_populate_geo_img_heuristics2(coco_img, overwrite=True)
>>> ###
>>> gid = 1
>>> dset2 = kwcoco.CocoDataset.demo('shapes8')
>>> coco_img = dset2.coco_image(gid)
>>> coco_populate_geo_img_heuristics2(coco_img, overwrite=True)
geowatch.utils.kwcoco_extensions.coco_populate_geo_video_stats(coco_dset, video_id, target_gsd='max-resolution')[source]

Create a “video-space” for all images in a video sequence at a specified resolution.

For this video, this chooses the “best” image as the “video canvas / region” and registers everything to that canvas/region. This creates the “video-space” for this image sequence. Currently the “best” image is the one that has the GSD closest to the target-gsd. This hueristic works well in most cases, but no all.

Notes

  • Currently the “best image” exactly define the video canvas / region.

  • Areas where other images do not overlap the vieo canvas are effectively lost when sampling in video space, because anything outside the video canvas is cropped out.

  • Auxilary / asset images are required to have an “approx_meter_gsd” and a “warp_to_wld” attribute to use this function atm.

Todo

  • [ ] Allow choosing of a custom “video-canvas” not based on any one image.

  • [ ] Allow choosing a “video-canvas” that encompases all images

  • [ ] Allow the base image to contain “approx_meter_gsd” /

    “warp_to_wld” instead of the auxiliary image

  • [ ] Is computing the scale factor based on approx_meter_gsd safe?

Parameters:
  • coco_dset (CocoDataset) – coco dataset to be modified inplace

  • video_id (int) – video_id to modify

  • target_gsd (float | str) – string code, or float target gsd

Example

>>> # xdoctest: +REQUIRES(env:DVC_DPATH)
>>> from geowatch.utils.kwcoco_extensions import *  # NOQA
>>> from geowatch.utils.util_data import find_dvc_dpath
>>> import kwcoco
>>> dvc_dpath = find_dvc_dpath()
>>> coco_fpath = dvc_dpath / 'Drop2-Aligned-TA1-2022-02-15/data.kwcoco.json'
>>> video_id = 2
>>> coco_fpath = dvc_dpath / 'Aligned-Drop2-TA1-2022-03-07/data.kwcoco.json'
>>> coco_dset = kwcoco.CocoDataset(coco_fpath)
>>> target_gsd = 10.0
>>> video_id = 1
>>> # We can check transforms before we apply this function
>>> coco_dset.images(video_id=video_id).lookup('warp_img_to_vid', None)
>>> # Apply the function
>>> coco_populate_geo_video_stats(coco_dset, video_id, target_gsd)
>>> # Check these transforms to make sure they look right
>>> popualted_video = coco_dset.index.videos[video_id]
>>> popualted_video = ub.dict_isect(popualted_video, ['width', 'height', 'warp_wld_to_vid', 'target_gsd'])
>>> print('popualted_video = {}'.format(ub.urepr(popualted_video, nl=-1)))
>>> coco_dset.images(video_id=video_id).lookup('warp_img_to_vid')

# TODO: make a demo dataset with some sort of gsd metadata coco_dset = kwcoco.CocoDataset.demo(‘vidshapes8-multispectral’) print(‘coco_dset = {!r}’.format(coco_dset))

coco_fpath = ub.expandpath(‘~/data/dvc-repos/smart_watch_dvc/drop0_aligned/data.kwcoco.json’) coco_fpath = ‘/home/joncrall/data/dvc-repos/smart_watch_dvc/drop1-S2-L8-aligned/combo_data.kwcoco.json’ coco_dset = kwcoco.CocoDataset(coco_fpath) video_id = 1

target_gsd = 2.8

# Check drawing the valid region on the image frac = 1

valid_regions = coco_dset.images().lookup(‘valid_region’) cands = [] for idx, valid_region in enumerate(valid_regions):

valid_region_img = kwimage.MultiPolygon.coerce(valid_region) frac = valid_region_img.to_shapely().area / valid_region_img.bounding_box().area if frac < 0.6:

cands.append(idx)

gid = coco_dset.images().take(cands).lookup(‘id’)[0]

coco_img = coco_dset.coco_image(gid) imdata = coco_img.imdelay(‘blue’).finalize(nodata=’float’) valid_region_img = kwimage.MultiPolygon.coerce(coco_img.img[‘valid_region’]) frac = valid_region_img.to_shapely().area / valid_region_img.bounding_box().area print(‘frac = {!r}’.format(frac))

canvas_imgspace = kwimage.normalize_intensity(imdata) kwplot.autompl() kwplot.imshow(canvas_imgspace, doclf=1)

valid_region_img = kwimage.MultiPolygon.coerce(coco_img.img[‘valid_region’]) canvas_imgspace = valid_region_img.draw_on(canvas_imgspace, fill=0, color=’green’) kwplot.imshow(canvas_imgspace)

# Check the nodata polygon returned raw pixel methods primary_data = kwimage.imread(primary_fpath, nodata=’float’) valid_mask = ~np.isnan(primary_data) kw_poly = kwimage.Mask(valid_mask.astype(np.uint8), ‘c_mask’).to_multi_polygon() print(‘kwimage kw_poly.data = {!r}’.format(kw_poly.data))

# CHeck the one returned by util_raster primary_fpath = coco_img.primary_image_filepath() sh_poly = util_raster.mask(

primary_fpath, tolerance=None, convex_hull=0)

kw_poly = kwimage.MultiPolygon.from_shapely(sh_poly) print(‘rasterio kw_poly.data = {!r}’.format(kw_poly.data))

geowatch.utils.kwcoco_extensions.check_kwcoco_spatial_transforms(coco_dset)[source]

import kwplot kwplot.plt.ion() import kwcoco dset = kwcoco.CocoDataset(‘/home/joncrall/remote/toothbrush/data/dvc-repos/smart_data_dvc-ssd/Drop6_MeanYear/imgonly-KR_R001.kwcoco.zip’)

import geowatch data_dvc_dpath = geowatch.find_dvc_dpath(tags=’phase2_data’, hardware=’auto’) dset = kwcoco.CocoDataset(data_dvc_dpath / ‘Drop6-MeanYear10GSD/imganns-NZ_R001.kwcoco.zip’)

dset = kwcoco.CocoDataset(‘/home/joncrall/quicklinks/toothbrush_smart_expt_dvc/_debug/pred.kwcoco.zip’)

geowatch.utils.kwcoco_extensions.check_geo_transform_consistency(coco_dset)[source]

Checks the consistency of transforms between world, video, image, and asset space in a coco dataset.

geowatch.utils.kwcoco_extensions.check_unique_channel_names(coco_dset, gids=None, verbose=0)[source]

Check each image has unique channel names

Todo

  • [ ] move to kwcoco proper

Example

>>> from geowatch.utils.kwcoco_extensions import *  # NOQA
>>> import kwcoco
>>> # TODO: make a demo dataset with some sort of gsd metadata
>>> coco_dset = kwcoco.CocoDataset.demo('vidshapes8-multispectral')
>>> check_unique_channel_names(coco_dset)
>>> # Make some duplicate channels to test
>>> obj = coco_dset.images().objs[0]
>>> obj['auxiliary'][0]['channels'] = 'B1|B1'
>>> obj = coco_dset.images().objs[1]
>>> obj['auxiliary'][0]['channels'] = 'B1|B1'
>>> obj = coco_dset.images().objs[2]
>>> obj['auxiliary'][1]['channels'] = 'B1'
>>> import pytest
>>> with pytest.raises(AssertionError):
>>>     check_unique_channel_names(coco_dset)
geowatch.utils.kwcoco_extensions.coco_list_asset_infos(coco_dset)[source]

Get a list of filename and channels for each coco image

geowatch.utils.kwcoco_extensions.check_geotiff_formats(coco_dset)[source]
geowatch.utils.kwcoco_extensions.rewrite_geotiffs(coco_dset)[source]
geowatch.utils.kwcoco_extensions.geotiff_format_info(fpath)[source]
geowatch.utils.kwcoco_extensions.ensure_transfered_geo_data(coco_dset, gids=None)[source]
geowatch.utils.kwcoco_extensions.transfer_geo_metadata(coco_dset, gid)[source]

Transfer geo-metadata from source geotiffs to predicted feature images

THIS FUNCITON MODIFIES THE IMAGE DATA ON DISK! BE CAREFUL!

ASSUMES THAT EVERYTHING IS ALREADY ALIGNED

Example

# xdoctest: +REQUIRES(env:DVC_DPATH) from geowatch.utils.kwcoco_extensions import * # NOQA from geowatch.utils.util_data import find_dvc_dpath import kwcoco dvc_dpath = find_dvc_dpath() coco_fpath = dvc_dpath / ‘drop1-S2-L8-aligned/combo_data.kwcoco.json’ coco_dset = kwcoco.CocoDataset(coco_fpath) gid = coco_dset.images().peek()[‘id’]

Example

>>> from geowatch.utils.kwcoco_extensions import *  # NOQA
>>> import kwcoco
>>> from geowatch.demo.smart_kwcoco_demodata import hack_seed_geometadata_in_dset
>>> coco_dset = kwcoco.CocoDataset.demo('vidshapes8-multispectral')
>>> hack_seed_geometadata_in_dset(coco_dset, force=True, rng=0)
>>> gid = 2
>>> transfer_geo_metadata(coco_dset, gid)
>>> fpath = join(coco_dset.bundle_dpath, coco_dset.coco_image(gid).primary_asset()['file_name'])
>>> _ = ub.cmd('gdalinfo ' + fpath, verbose=1)
geowatch.utils.kwcoco_extensions.transfer_geo_metadata2(coco_img, dry=0)[source]

Second version of this function to work in process mode

Transfer geo-metadata from source geotiffs to predicted feature images

THIS FUNCITON MODIFIES THE IMAGE DATA ON DISK! BE CAREFUL!

ASSUMES THAT EVERYTHING IS ALREADY ALIGNED

geowatch.utils.kwcoco_extensions.coco_channel_stats(coco_dset)[source]

Return information about what channels are available in the dataset

Example

>>> import kwcoco
>>> import ubelt as ub
>>> import geowatch
>>> coco_dset = geowatch.coerce_kwcoco('vidshapes-geowatch')
>>> from geowatch.utils import kwcoco_extensions
>>> info = kwcoco_extensions.coco_channel_stats(coco_dset)
>>> print(ub.urepr(info, nl=3))
class geowatch.utils.kwcoco_extensions.TrackidGenerator(coco_dset=None)[source]

Bases: object

Keep track of which trackids have been used and generate new ones on demand

TODO merge this into kwcoco as something like CocoDataset.next_trackid()? Or expose whatever mechanism is already generating new aids, gids, etc

update_generator()[source]
exclude_trackids(trackids)[source]
geowatch.utils.kwcoco_extensions.coco_img_wld_info(coco_img)[source]

TODO: candidate for kwcoco.CocoImage method

geowatch.utils.kwcoco_extensions.warp_annot_segmentations_from_geos(coco_dset)[source]

Uses the segmentation_geos property (which should be crs84) and warps it into image space based on available geo data.

Parameters:

coco_dset (kwcoco.CocoDataset) – a CocoDataset where annotations contain a “segmentation_geos” attribute. The “segmentation” attribute will be modified in-place.

Example

>>> from geowatch.utils.kwcoco_extensions import *  # NOQA
>>> import geowatch
>>> # creating demodata also uses warp_annot_segmentations_to_geos
>>> orig_dset = geowatch.coerce_kwcoco('geowatch-msi', geodata=True)
>>> coco_dset = orig_dset.copy()
>>> for ann in coco_dset.annots().objs:
...     ann.pop('segmentation', None)
>>> warp_annot_segmentations_from_geos(coco_dset)
>>> errors = []
>>> for aid in coco_dset.annots():
>>>     ann1 = orig_dset.index.anns[aid]
>>>     ann2 = coco_dset.index.anns[aid]
>>>     poly1 = kwimage.MultiPolygon.coerce(ann1['segmentation'])
>>>     poly2 = kwimage.MultiPolygon.coerce(ann2['segmentation'])
>>>     worked = (poly1.is_invalid() and poly2.is_invalid()) or poly1.iou(poly2) > 0.99
>>>     errors.append(not worked)
>>> if sum(errors) > 0:
>>>     # FIXME: THERE SHOULD BE NO ERRORS HERE. PUNTING TO MAKE
>>>     # THE DASHBOARDS GREEN, BUT THIS SHOULD BE REVISITED
>>>     #raise AssertionError('transforms should have cyclic consistency')
>>>     warnings.warn('Transforms should have cyclic consistency, but some dont. This should be an error, but we will allow it for now')
>>>     assert (sum(errors) / len(errors)) < 0.5, 'more than half of the data does not have cyclic consistency'
geowatch.utils.kwcoco_extensions.warp_annot_segmentations_to_geos(coco_dset)[source]

Example

>>> from geowatch.utils.kwcoco_extensions import *  # NOQA
>>> import geowatch
>>> orig_dset = geowatch.coerce_kwcoco('geowatch-msi', geodata=True)
>>> coco_dset = orig_dset.copy()
>>> for ann in coco_dset.annots().objs:
...     ann.pop('segmentation_geos', None)
>>> warp_annot_segmentations_to_geos(coco_dset)
>>> errors = []
>>> for aid in ub.ProgIter(coco_dset.annots()):
>>>     ann1 = orig_dset.index.anns[aid]
>>>     ann2 = coco_dset.index.anns[aid]
>>>     poly1 = kwimage.MultiPolygon.from_geojson(ann1['segmentation_geos'])
>>>     poly2 = kwimage.MultiPolygon.from_geojson(ann2['segmentation_geos'])
>>>     worked = (poly1.is_invalid() and poly2.is_invalid()) or poly1.iou(poly2) > 0.99
>>>     errors.append(not worked)
>>> if sum(errors) > 0:
>>>     # FIXME: THERE SHOULD BE NO ERRORS HERE. PUNTING TO MAKE
>>>     # THE DASHBOARDS GREEN, BUT THIS SHOULD BE REVISITED
>>>     #raise AssertionError('transforms should have cyclic consistency')
>>>     warnings.warn('Transforms should have cyclic consistency, but some dont. This should be an error, but we will allow it for now')
>>>     assert (sum(errors) / len(errors)) < 0.5, 'more than half of the data does not have cyclic consistency'
geowatch.utils.kwcoco_extensions.visualize_rois(coco_dset, zoom=None)[source]

Matplotlib visualization of image and annotation regions on a world map

Example

>>> # xdoctest: +REQUIRES(--slow)
>>> from geowatch.utils.kwcoco_extensions import *  # NOQA
>>> from geowatch.demo.smart_kwcoco_demodata import demo_kwcoco_with_heatmaps
>>> coco_dset = demo_kwcoco_with_heatmaps(num_videos=1)
>>> coco_populate_geo_heuristics(coco_dset, overwrite=True)
>>> visualize_rois(coco_dset, zoom=0)

Example

>>> # xdoctest: +REQUIRES(env:DVC_DPATH)
>>> from geowatch.utils.kwcoco_extensions import *  # NOQA
>>> from geowatch.utils.util_data import find_dvc_dpath
>>> import kwcoco
>>> dvc_dpath = find_dvc_dpath()
>>> coco_fpath = dvc_dpath / 'drop1-S2-L8-aligned/combo_data.kwcoco.json'
>>> coco_dset = kwcoco.CocoDataset(coco_fpath)
>>> coco_populate_geo_heuristics(coco_dset, overwrite=True, workers=4)
>>> visualize_rois(coco_dset)
geowatch.utils.kwcoco_extensions.covered_image_geo_regions(coco_dset, merge=False)[source]

Find the intersection of all image bounding boxes in world space to see what spatial regions are covered by the imagery.

Returns:

gpd.GeoDataFrame

Example

>>> from geowatch.utils.kwcoco_extensions import *  # NOQA
>>> from geowatch.demo.smart_kwcoco_demodata import demo_kwcoco_with_heatmaps
>>> coco_dset = demo_kwcoco_with_heatmaps(num_frames=1, num_videos=1)
>>> coco_populate_geo_heuristics(coco_dset, overwrite=True)
>>> img = coco_dset.index.imgs[1]
>>> cov_image_gdf = covered_image_geo_regions(coco_dset)

Example

>>> # Check it works with empty data frame
>>> from geowatch.utils.kwcoco_extensions import *  # NOQA
>>> coco_dset = kwcoco.CocoDataset()
>>> cov_image_gdf1 = covered_image_geo_regions(coco_dset, merge=False)
>>> cov_image_gdf2 = covered_image_geo_regions(coco_dset, merge=True)
>>> assert len(cov_image_gdf1) == 0
>>> assert len(cov_image_gdf2) == 0
geowatch.utils.kwcoco_extensions.covered_video_geo_regions(coco_dset)[source]

Compute CRS84 bounds for each video in the coco dataset.

Returns:

gpd.GeoDataFrame

Example

>>> from geowatch.utils.kwcoco_extensions import *  # NOQA
>>> from geowatch.demo.smart_kwcoco_demodata import demo_kwcoco_with_heatmaps
>>> coco_dset = demo_kwcoco_with_heatmaps(num_frames=1, num_videos=1)
>>> # coco_populate_geo_heuristics(coco_dset, overwrite=True)
>>> # video_gdf = covered_video_geo_regions(coco_dset)
geowatch.utils.kwcoco_extensions.covered_annot_geo_regions(coco_dset, merge=False)[source]

Given a dataset find spatial regions of interest that contain annotations

geowatch.utils.kwcoco_extensions.associate_images(dset1, dset2, key_fallback=None)[source]

Builds an association between image-ids in two datasets.

One use for this is if dset1 is a truth dataset and dset2 is a prediction dataset, and you need the to know which images are in common so they can be scored.

Parameters:
  • dset1 (kwcoco.CocoDataset) – a kwcoco datset.

  • dset2 (kwcoco.CocoDataset) – another kwcoco dataset

  • key_fallback (str) – The fallback key to use if the image “name” is not specified. This can either be “file_name” or “id” or None.

Todo

  • [ ] port to kwcoco proper

  • [ ] use in kwcoco eval as a robust image/video association method

Example

>>> from geowatch.utils.kwcoco_extensions import *  # NOQA
>>> import kwcoco
>>> from kwcoco.demo.perterb import perterb_coco
>>> dset1 = kwcoco.CocoDataset.demo('shapes2')
>>> kwargs = {
>>>     'box_noise': 0.5,
>>>     'n_fp': (0, 10),
>>>     'n_fn': (0, 10),
>>> }
>>> dset2 = perterb_coco(dset1, **kwargs)
>>> matches = associate_images(dset1, dset2, key_fallback='file_name')
>>> assert len(matches['image']['match_gids1'])
>>> assert len(matches['image']['match_gids2'])
>>> assert not len(matches['video'])

Example

>>> from geowatch.utils.kwcoco_extensions import *  # NOQA
>>> import kwcoco
>>> from kwcoco.demo.perterb import perterb_coco
>>> dset1 = kwcoco.CocoDataset.demo('vidshapes2')
>>> kwargs = {
>>>     'box_noise': 0.5,
>>>     'n_fp': (0, 10),
>>>     'n_fn': (0, 10),
>>> }
>>> dset2 = perterb_coco(dset1, **kwargs)
>>> matches = associate_images(dset1, dset2, key_fallback='file_name')
>>> assert not len(matches['image']['match_gids1'])
>>> assert not len(matches['image']['match_gids2'])
>>> assert len(matches['video'])
geowatch.utils.kwcoco_extensions.reorder_video_frames(dset)[source]

Reorder the image indexes in each video to ensure temporal ordering

geowatch.utils.kwcoco_extensions.pick_channels(coco_img, choices)[source]

Choose the set of channels in choices that all exist in this image.

Todo

  • [ ] Add to CocoIamge as a method

Parameters:
  • coco_img (CocoImage) – an image with channels

  • choices (List[FusedChannelSpec | str]) – a list of fused channels in priority order to choose from.

Returns:

The first channel group in choices where all of those channels exist in the image.

Return type:

None | FusedChannelSpec

CommandLine

xdoctest -m geowatch.utils.kwcoco_extensions pick_channels

Example

>>> from geowatch.utils import kwcoco_extensions
>>> import kwcoco
>>> choices = ['blue|green|red', 'pan']
>>> # Make different demo CocoImages that contain different bands
>>> coco_img1 = kwcoco.CocoImage({
>>>     'channels': 'red|green|blue', 'file_name': 'dummy'})
>>> coco_img2 = kwcoco.CocoImage({
>>>     'channels': 'green|blue', 'file_name': 'dummy'})
>>> coco_img3 = kwcoco.CocoImage({
>>>     'channels': 'blue|green|red', 'file_name': 'dummy'})
>>> coco_img4 = kwcoco.CocoImage({
>>>     'channels': 'pan', 'file_name': 'dummy'})
>>> # Channels are only found if all bands in a choices item are given
>>> found1 = kwcoco_extensions.pick_channels(coco_img1, choices)
>>> found2 = kwcoco_extensions.pick_channels(coco_img2, choices)
>>> found3 = kwcoco_extensions.pick_channels(coco_img3, choices)
>>> found4 = kwcoco_extensions.pick_channels(coco_img4, choices)
>>> print(f'found1={found1}')
>>> print(f'found2={found2}')
>>> print(f'found3={found3}')
>>> print(f'found4={found4}')
found1=<FusedChannelSpec(blue|green|red)>
found2=None
found3=<FusedChannelSpec(blue|green|red)>
found4=<FusedChannelSpec(pan)>

Example

>>> # Test case with different choices orders
>>> from geowatch.utils import kwcoco_extensions
>>> channel_priority1 = ['blue|green|red', 'pan']
>>> channel_priority2 = ['pan', 'blue|green|red']
>>> coco_img1 = kwcoco.CocoImage({
>>>     'channels': 'blue|green|red|pan', 'file_name': 'dummy'})
>>> coco_img2 = kwcoco.CocoImage({
>>>     'channels': 'pan|blue|green|red', 'file_name': 'dummy'})
>>> found1 = kwcoco_extensions.pick_channels(coco_img1, channel_priority1)
>>> found2 = kwcoco_extensions.pick_channels(coco_img1, channel_priority2)
>>> found3 = kwcoco_extensions.pick_channels(coco_img2, channel_priority1)
>>> found4 = kwcoco_extensions.pick_channels(coco_img2, channel_priority2)
>>> # The first found band in choices is returned when
>>> # the image contains both, regardless of order in the image.
>>> print(f'found1={found1}')
>>> print(f'found2={found2}')
>>> print(f'found3={found3}')
>>> print(f'found4={found4}')
found1=<FusedChannelSpec(blue|green|red)>
found2=<FusedChannelSpec(pan)>
found3=<FusedChannelSpec(blue|green|red)>
found4=<FusedChannelSpec(pan)>