geowatch.heuristics module

In the interest of development speed we can’t always be perfect about correctly passing the right metadata to functions that need it. This module serves as a place to store hard-coded heuristics so we are explicit about where we are cutting corners or using magic numbers. The idea is that this will make it easier for us to go back and make the code robust.

References

https://gitlab.kitware.com/smart/annotations-wiki/-/blob/main/Annotation-Status-Types.md

geowatch.heuristics.DEFAULT_QA_ENCODING = 'ARA-4'

Status Data Info

The following is a long-form table, where list-items are rows and keys are columns to describe how to interpret IARPA annotation status labels in different cases. More information on status labels can be found in [TEAnnotStatus] and in our internal fork [KWAnnotStatus].

Column Info:
  • status:

    the name of the site status label

  • color:

    what color to use for this status in visualizations. These are mostly taken from [EvalMetricColors], but in cases where they are undefined we make them up.

  • kwcoco_catname:

    what category to map this status to in the kwcoco dataset (and thus what will be learned), Note: a status is different than a phase label, and annotations with phase labels may overwrite the kwcoco category defined here. This is used in geowatch reproject.

  • positive_match_confusion

    This is the label the truth is given when it has some match in our set of positive predictions. Denote what type of confusion a truth status incurs when it is matched.

The following code prints a concice version of this table and shows a legend with colors.

from geowatch.heuristics import *  # NOQA
import pandas as pd
import rich
df = pd.DataFrame(HUERISTIC_STATUS_DATA)
rich.print(df.to_string())

import kwplot
kwplot.autompl()
status_to_color = {r['status']: r['color'] for r in HUERISTIC_STATUS_DATA}
img = kwplot.make_legend_img(status_to_color, dpi=300)
kwplot.imshow(img, fnum=1)

References

geowatch.heuristics.IARPA_REAL_STATUS = {'ignore': 'ignore', 'negative': ['positive_excluded', 'negative', 'negative_unbounded'], 'positive': ['positive_annotated', 'positive_annotated_static', 'positive_partial', 'positive_pending']}

For official color definitions see:

iarpa_smart_metrics.evaluation.Evaluation.get_sm_color() and iarpa_smart_metrics.evaluation.Evaluation.get_gt_color()

geowatch.heuristics.iarpa_assign_truth_confusion(truth_status, has_positive_match)[source]

Example

>>> from geowatch.heuristics import *  # NOQA
>>> import pandas as pd
>>> rows = []
>>> for truth_status in IARPA_STATUS_TO_INFO.keys():
>>>     for has_positive_match in [0, 1]:
>>>         gt_cfsn = iarpa_assign_truth_confusion(truth_status, has_positive_match)
>>>         rows.append({
>>>             'truth_status': truth_status,
>>>             'has_positive_match': has_positive_match,
>>>             'confusion': gt_cfsn,
>>>         })
>>> print(pd.DataFrame(rows).to_string())
geowatch.heuristics.iarpa_assign_pred_confusion(truth_match_statuses)[source]

Example

>>> from geowatch.heuristics import *  # NOQA
>>> import itertools as it
>>> truth_match_statuses = {'positive_partial', 'positive_excluded'}
>>> for combo in it.combinations(IARPA_STATUS_TO_INFO, 2):
>>>     truth_match_statuses = combo
>>>     pred_cfsn = iarpa_assign_pred_confusion(truth_match_statuses)
>>>     print(f'{pred_cfsn=} for {truth_match_statuses}')
geowatch.heuristics.TAG_IF(tag, condition)[source]
geowatch.heuristics.CONDITION(op, args)[source]
geowatch.heuristics.ALL(*args)[source]
geowatch.heuristics.hack_track_categories(track_catnames, task)[source]
Returns:

Modified categories

Return type:

List[str]

Example

>>> from geowatch.heuristics import *  # NOQA
>>> basis = {
>>>     #'task': ['class', 'saliency'],
>>>     'task': ['class'],
>>>     'track_catnames': [
>>>         ['No Activity'],
>>>         ['Post Construction'],
>>>         ['Post Construction', 'Post Construction', ],
>>>         ['Post Construction', 'Post Construction', 'Post Construction', ],
>>>         ['No Activity', 'ignore', 'ignore'],
>>>         ['No Activity', 'Post Construction'],
>>>         ['No Activity', 'Site Preparation', 'Post Construction'],
>>>     ],
>>> }
>>> for kw in ub.named_product(basis):
>>>     task = kw['task']
>>>     track_catnames = kw['track_catnames']
>>>     kw['new_catnames'] = hack_track_categories(track_catnames, task)
>>>     print('kw = {}'.format(ub.urepr(kw, nl=1)))

Example

>>> from geowatch.heuristics import *  # NOQA
>>> track_catnames = ['negative', 'negative']
>>> task = 'saliency'
>>> result = hack_track_categories(track_catnames, task)
>>> print(result)
['negative', 'negative']
geowatch.heuristics.ensure_heuristic_coco_colors(coco_dset, force=False)[source]
Parameters:
  • coco_dset (kwcoco.CocoDataset) – object to modify

  • force (bool) – if True, overwrites existing colors if needed

Todo

  • [ ] Move this non-heuristic functionality to

    kwcoco.CocoDataset.ensure_class_colors()

Example

>>> from geowatch.heuristics import *  # NOQA
>>> import kwcoco
>>> coco_dset = kwcoco.CocoDataset.demo()
>>> ensure_heuristic_coco_colors(coco_dset)
>>> assert all(c['color'] for c in coco_dset.cats.values())
geowatch.heuristics.ensure_heuristic_category_tree_colors(classes, force=False)[source]
Parameters:
  • classes (kwcoco.CategoryTree) – object to modify

  • force (bool) – if True, overwrites existing colors if needed

Todo

  • [ ] Move this non-heuristic functionality to

    kwcoco.CategoryTree.ensure_colors()

  • [ ] Consolidate with ~/code/watch/geowatch/heuristics.py :: category_tree_ensure_color

  • [ ] Consolidate with ~/code/watch/geowatch/heuristics.py :: category_category_colors

  • [ ] Consolidate with ~/code/watch/geowatch/heuristics.py :: ensure_heuristic_category_tree_colors

  • [ ] Consolidate with ~/code/watch/geowatch/heuristics.py :: ensure_heuristic_coco_colors

Example

>>> from geowatch import heuristics
>>> import kwcoco
>>> classes = kwcoco.CategoryTree.coerce(['ignore', 'positive', 'Active Construction', 'foobar', 'Unknown', 'baz'])
>>> heuristics.ensure_heuristic_category_tree_colors(classes)
>>> assert all(d['color'] for n, d in classes.graph.nodes(data=True))
geowatch.heuristics.category_tree_ensure_color(classes)[source]

Ensures that each category in a CategoryTree has a color

Todo

  • [ ] Add to CategoryTree

  • [ ] TODO: better function

  • [ ] Consolidate with ~/code/watch/geowatch/heuristics.py :: category_tree_ensure_color

  • [ ] Consolidate with ~/code/watch/geowatch/heuristics.py :: category_category_colors

  • [ ] Consolidate with ~/code/watch/geowatch/heuristics.py :: ensure_heuristic_category_tree_colors

  • [ ] Consolidate with ~/code/watch/geowatch/heuristics.py :: ensure_heuristic_coco_colors

Example

>>> import kwcoco
>>> classes = kwcoco.CategoryTree.demo()
>>> assert not any('color' in data for data in classes.graph.nodes.values())
>>> category_tree_ensure_color(classes)
>>> assert all('color' in data for data in classes.graph.nodes.values())
geowatch.heuristics.category_category_colors(coco_dset)[source]

Ensures that each category in a CategoryTree has a color

Todo

  • [ ] Add to CategoryTree

  • [ ] Consolidate with ~/code/watch/geowatch/heuristics.py :: category_tree_ensure_color

  • [ ] Consolidate with ~/code/watch/geowatch/heuristics.py :: category_category_colors

  • [ ] Consolidate with ~/code/watch/geowatch/heuristics.py :: ensure_heuristic_category_tree_colors

  • [ ] Consolidate with ~/code/watch/geowatch/heuristics.py :: ensure_heuristic_coco_colors

geowatch.heuristics.dummy_legend()[source]

from geowatch.heuristics import * # NOQA dummy_legend()

geowatch.heuristics.build_image_header_text(**kwargs)[source]

A heuristic for what sort of info is useful to plot on the header of an image.

Kwargs:

img coco_dset vidname, _header_extra

gid, frame_index, dset_idstr, name, sensor_coarse, date_captured

Example

>>> from geowatch.heuristics import *  # NOQA
>>> img = {
>>>     'id': 1,
>>>     'frame_index': 0,
>>>     'date_captured': '2020-01-01',
>>>     'name': 'BLARG',
>>>     'sensor_coarse': 'Sensor1',
>>> }
>>> kwargs = {
>>>     'img': img,
>>>     'dset_idstr': '',
>>>     'name': '',
>>>     '_header_extra': None,
>>> }
>>> header_lines = build_image_header_text(**kwargs)
>>> print('header_lines = {}'.format(ub.urepr(header_lines, nl=1)))

Example

>>> from geowatch.heuristics import *  # NOQA
>>> img = {
>>>     'id': 1,
>>>     'frame_index': 0,
>>>     'timestamp': '2020-01-01',
>>>     'name': 'BLARG',
>>> }
>>> kwargs = {
>>>     'img': img,
>>>     'dset_idstr': '',
>>>     'name': '',
>>>     '_header_extra': None,
>>> }
>>> header_lines = build_image_header_text(**kwargs)
>>> print('header_lines = {}'.format(ub.urepr(header_lines, nl=1)))
geowatch.heuristics.auto_expt_dvc()[source]
geowatch.heuristics.auto_data_dvc()[source]
geowatch.heuristics.normalize_sensors(coco_dset, sensor_warnings=True, format='te')[source]

Convert to / from internal representations or IAPRA sensor standards

geowatch.heuristics.extract_region_id(fname)[source]

Example

>>> fname = 'foobar_KR_R001_otherstuff'
>>> extract_region_id(fname)
geowatch.heuristics.register_known_fsspec_s3_buckets()[source]

A workaround to handle requester pays information for particular s3 endpoints. Ideally the user would be able to specify this mapping via the CLI, but for now lets just hack it in.

We are not specifying the profile here, assuming that instead the user will use the AWS_DEFAULT_PROFILE environ.

Note: the AWS_REQUEST_PAYER environ is only repsected by gdal, and this function does not impact gdal at all, so this environ needs to be set as well as calling this workaround.