geowatch.cli.run_tracker module¶
This file contains logic to convert a kwcoco file into an IARPA Site Model.
At a glance the IARPA Site Model is a GeoJSON FeatureCollection with the following informal schema:
For official documentation about the KWCOCO json format see [kwcoco]. A formal
json-schema can be found in kwcoco.coco_schema
For official documentation about the IARPA json format see [2, 3]_. A formal
json-schema can be found in ../../geowatch/rc/site-model.schema.json
.
References
- SeeAlso:
../tasks/tracking/from_heatmap.py
../tasks/tracking/old_polygon_extraction.py
../tasks/tracking/polygon_extraction.py
../tasks/tracking/utils.py
../../tests/test_tracker.py
- class geowatch.cli.run_tracker.KWCocoToGeoJSONConfig(*args, **kwargs)[source]¶
Bases:
DataConfig
Convert KWCOCO to IARPA GeoJSON
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- default = {'append_mode': <Value(False)>, 'boundary_region': <Value(None)>, 'clear_annots': <Value(False)>, 'default_track_fn': <Value(None)>, 'in_file': <Value(None)>, 'in_file_gt': <Value(None)>, 'out_kwcoco': <Value(None)>, 'out_site_summaries_dir': <Value(None)>, 'out_site_summaries_fpath': <Value(None)>, 'out_sites_dir': <Value(None)>, 'out_sites_fpath': <Value(None)>, 'region_id': <Value(None)>, 'sensor_warnings': <Value(True)>, 'site_score_thresh': <Value(None)>, 'site_summary': <Value(None)>, 'smoothing': <Value(None)>, 'time_pad_after': <Value(None)>, 'time_pad_before': <Value(None)>, 'track_fn': <Value(None)>, 'track_kwargs': <Value('{}')>, 'viz_out_dir': <Value(None)>}¶
- geowatch.cli.run_tracker.coco_create_observation(coco_dset, anns)[source]¶
Group kwcoco annotations in the same track (site) and image into one Feature in an IARPA site model
- geowatch.cli.run_tracker.predict_phase_changes(site_id, observations)[source]¶
Set predicted_phase_transition and predicted_phase_transition_date.
This should only kick in when the site does not end before the current day (latest available image). See tracking.normalize.normalize_phases for what happens if the site has ended.
- Parameters:
site_id (str) – site identifier
features (List[Dict]) – observation feature dictionaries for the site
- Returns:
dict
References
https://smartgitlab.com/TE/standards/-/wikis/Site-Model-Specification https://gitlab.kitware.com/smart/standards-wiki/-/blob/main/Site-Model-Specification.md
Example
>>> from geowatch.geoannots import geomodels >>> site = geomodels.SiteModel.random(rng=0, num_observations=20) >>> site_id = site.site_id >>> observations = list(site.body_features()) >>> observations[-1]['properties']['cache'] = {'phase_transition_days': [100]} >>> predict_phase_changes(site_id, observations)
- geowatch.cli.run_tracker.smooth_observation_scores(observations, smoothing=0.5, smooth_mode='ewma')[source]¶
Add smoothed scores inplace
Example
>>> from geowatch.cli.run_tracker import * # NOQA >>> from geowatch.geoannots import geomodels >>> site = geomodels.SiteModel.random(num_observations=15) >>> observations = list(site.observations()) >>> # Add random scores for tests >>> import kwarray >>> rng = kwarray.ensure_rng() >>> for obs in observations: >>> obs['properties']['cache'] = {'raw_multi_scores': [{ >>> 'No Activity': rng.rand(), >>> 'Site Preparation': rng.rand(), >>> 'Post Construction': rng.rand(), >>> 'Active Construction': rng.rand(), >>> }]} >>> data1 = [obs['properties']['cache']['raw_multi_scores'][0] for obs in observations] >>> smooth_observation_scores(observations, smooth_mode='ewma') >>> data2 = [obs['properties']['cache']['smooth_scores'].copy() for obs in observations] >>> smooth_observation_scores(observations, smooth_mode='conv3') >>> data3 = [obs['properties']['cache']['smooth_scores'].copy() for obs in observations] >>> import pandas as pd >>> df1 = pd.DataFrame(data1) >>> df2 = pd.DataFrame(data2) >>> df3 = pd.DataFrame(data3) >>> print(df1) >>> print(df2) >>> print(df3)
- geowatch.cli.run_tracker.classify_site(site, config)[source]¶
Modify a site inplace with classifications.
Given a site with extracted and scored observations, postprocess the raw observation scores and make site-level predictions.
- geowatch.cli.run_tracker.coco_create_site_header(region_id, site_id, trackid, observations)[source]¶
Feature containing metadata about the site
- Returns:
geomodels.SiteSummary | geomodels.SiteHeader
- geowatch.cli.run_tracker.convert_kwcoco_to_iarpa(coco_dset, default_region_id=None)[source]¶
Convert a kwcoco coco_dset to the IARPA JSON format
- Parameters:
coco_dset (kwcoco.CocoDataset) – a coco dataset, but requires images are geotiffs as well as certain special fields.
- Returns:
- sites
dictionary of json-style data in IARPA site format
- Return type:
Example
>>> import geowatch >>> from geowatch.cli.run_tracker import * # NOQA >>> from geowatch.tasks.tracking.normalize import run_tracking_pipeline >>> from geowatch.tasks.tracking.from_polygon import MonoTrack >>> import ubelt as ub >>> coco_dset = geowatch.coerce_kwcoco('geowatch-msi', heatmap=True, geodata=True, dates=True) >>> coco_dset = run_tracking_pipeline( >>> coco_dset, track_fn=MonoTrack, overwrite=False, >>> sensor_warnings=False) >>> videos = coco_dset.videos() >>> videos.set('name', ['DM_R{:03d}'.format(vidid) for vidid in videos]) >>> sites = convert_kwcoco_to_iarpa(coco_dset) >>> print(f'{len(sites)} sites') >>> if 0: # validation fails >>> import jsonschema >>> SITE_SCHEMA = geowatch.rc.load_site_model_schema() >>> for site in sites: >>> jsonschema.validate(site, schema=SITE_SCHEMA) >>> elif 0: # but this works if metrics are available >>> import tempfile >>> import json >>> from iarpa_smart_metrics.evaluation import SiteStack >>> for site in sites: >>> with tempfile.NamedTemporaryFile() as f: >>> json.dump(site, open(f.name, 'w')) >>> SiteStack(f.name)
- geowatch.cli.run_tracker.coco_track_to_site(coco_dset, trackid, region_id, site_idx=None)[source]¶
Turn a kwcoco track into an IARPA site model or site summary
- geowatch.cli.run_tracker.assign_sites_to_videos(coco_dset, site_summaries, viz_out_dir=None)[source]¶
Compute assignments between which sites summaries should be projected onto which videos for scoring.
- geowatch.cli.run_tracker.add_site_summary_to_kwcoco(possible_summaries, coco_dset, default_region_id=None, viz_out_dir=None)[source]¶
Add a site summary(s) to a kwcoco dataset as a set of polygon annotations. These annotations will have category “Site Boundary”, 1 track per summary.
This function is mainly for SC. The “possible_summaries” indicate regions flagged by BAS (which could also be truth data if we are evaluating SC independently) that need SC processing. We need to associate these and place them in the correct videos so we can process those areas.
- geowatch.cli.run_tracker.main(argv=None, **kwargs)[source]¶
Example
>>> # test BAS and default (SC) modes >>> from geowatch.cli.run_tracker import * # NOQA >>> from geowatch.cli.run_tracker import main >>> from geowatch.demo import smart_kwcoco_demodata >>> from kwgis.utils import util_gis >>> import json >>> import kwcoco >>> import ubelt as ub >>> # run BAS on demodata in a new place >>> import geowatch >>> coco_dset = geowatch.coerce_kwcoco('geowatch-msi', heatmap=True, geodata=True, dates=True) >>> dpath = ub.Path.appdir('geowatch', 'test', 'tracking', 'main0').ensuredir() >>> coco_dset.reroot(absolute=True) >>> coco_dset.fpath = dpath / 'bas_input.kwcoco.json' >>> coco_dset.clear_annotations() >>> coco_dset.dump(coco_dset.fpath, indent=2) >>> region_id = 'dummy_region' >>> regions_dir = dpath / 'regions/' >>> bas_coco_fpath = dpath / 'bas_output.kwcoco.json' >>> sc_coco_fpath = dpath / 'sc_output.kwcoco.json' >>> bas_fpath = dpath / 'bas_sites.json' >>> sc_fpath = dpath / 'sc_sites.json' >>> # Run BAS >>> argv = bas_args = [ >>> '--in_file', coco_dset.fpath, >>> '--out_site_summaries_dir', str(regions_dir), >>> '--out_site_summaries_fpath', str(bas_fpath), >>> '--out_kwcoco', str(bas_coco_fpath), >>> '--track_fn', 'saliency_heatmaps', >>> '--sensor_warnings', 'False', >>> '--track_kwargs', json.dumps({ >>> 'thresh': 1e-9, 'min_area_square_meters': None, >>> 'max_area_square_meters': None, >>> 'polygon_simplify_tolerance': 1}), >>> ] >>> main(argv) >>> # Run SC on the same dset, but with BAS pred sites removed >>> sites_dir = dpath / 'sites' >>> argv = sc_args = [ >>> '--in_file', coco_dset.fpath, >>> '--out_sites_dir', str(sites_dir), >>> '--out_sites_fpath', str(sc_fpath), >>> '--out_kwcoco', str(sc_coco_fpath), >>> '--track_fn', 'class_heatmaps', >>> '--site_summary', str(bas_fpath), >>> '--sensor_warnings', 'False', >>> '--track_kwargs', json.dumps( >>> {'thresh': 1e-9, 'min_area_square_meters': None, 'max_area_square_meters': None, >>> 'polygon_simplify_tolerance': 1, 'key': 'salient'}), >>> ] >>> main(argv) >>> # Check expected results >>> bas_coco_dset = kwcoco.CocoDataset(bas_coco_fpath) >>> sc_coco_dset = kwcoco.CocoDataset(sc_coco_fpath) >>> bas_trackids = bas_coco_dset.annots().lookup('track_id', None) >>> sc_trackids = sc_coco_dset.annots().lookup('track_id', None) >>> print('bas_trackids = {}'.format(ub.urepr(bas_trackids, nl=1))) >>> print('sc_trackids = {}'.format(ub.urepr(sc_trackids, nl=1))) >>> assert len(bas_trackids) and None not in bas_trackids >>> assert len(sc_trackids) and None not in sc_trackids >>> summaries = list(util_gis.coerce_geojson_datas(bas_fpath, format='dataframe')) >>> sites = list(util_gis.coerce_geojson_datas(sc_fpath, format='dataframe')) >>> import pandas as pd >>> sc_df = pd.concat([d['data'] for d in sites]) >>> bas_df = pd.concat([d['data'] for d in summaries]) >>> ssum_rows = bas_df[bas_df['type'] == 'site_summary'] >>> site_rows = sc_df[sc_df['type'] == 'site'] >>> obs_rows = sc_df[sc_df['type'] == 'observation'] >>> assert len(site_rows) > 0 >>> assert len(ssum_rows) > 0 >>> assert len(ssum_rows) == len(site_rows) >>> assert len(obs_rows) > len(site_rows) >>> # Cleanup >>> #dpath.delete()
Example
>>> # test resolution >>> from geowatch.cli.run_tracker import * # NOQA >>> from geowatch.cli.run_tracker import main >>> import geowatch >>> dset = geowatch.coerce_kwcoco('geowatch-msi', heatmap=True, geodata=True, dates=True) >>> dpath = ub.Path.appdir('geowatch', 'test', 'tracking', 'main1').ensuredir() >>> out_fpath = dpath / 'resolution_test.kwcoco.json' >>> regions_dir = dpath / 'regions' >>> bas_fpath = dpath / 'bas_sites.json' >>> import json >>> track_kwargs = json.dumps({ >>> 'resolution': '10GSD', >>> 'min_area_square_meters': 1000000, # high area threshold filters results >>> 'max_area_square_meters': None, >>> 'thresh': 1e-9, >>> }) >>> kwargs = { >>> 'in_file': str(dset.fpath), >>> 'out_site_summaries_dir': str(regions_dir), >>> 'out_site_summaries_fpath': str(bas_fpath), >>> 'out_kwcoco': str(out_fpath), >>> 'track_fn': 'saliency_heatmaps', >>> 'track_kwargs': track_kwargs, >>> 'sensor_warnings': False, >>> } >>> argv = [] >>> # Test case for no results >>> main(argv=argv, **kwargs) >>> from kwgis.utils import util_gis >>> assert len(list(util_gis.coerce_geojson_datas(bas_fpath))) == 0 >>> # Try to get results here >>> track_kwargs = json.dumps({ >>> 'resolution': '10GSD', >>> 'min_area_square_meters': None, >>> 'max_area_square_meters': None, >>> 'thresh': 1e-9, >>> }) >>> kwargs = { >>> 'in_file': str(dset.fpath), >>> 'out_site_summaries_dir': str(regions_dir), >>> 'out_site_summaries_fpath': str(bas_fpath), >>> 'out_kwcoco': str(out_fpath), >>> 'track_fn': 'saliency_heatmaps', >>> 'track_kwargs': track_kwargs, >>> 'sensor_warnings': False, >>> } >>> argv = [] >>> main(argv=argv, **kwargs) >>> assert len(list(util_gis.coerce_geojson_datas(bas_fpath))) > 0
Example
>>> # xdoctest: +REQUIRES(--slow) >>> # test a more complicated track function >>> import geowatch >>> from geowatch.cli.run_tracker import demo >>> import kwcoco >>> import geowatch >>> import ubelt as ub >>> # make a new BAS dataset >>> coco_dset = geowatch.coerce_kwcoco('geowatch-msi', heatmap=True, geodata=True) >>> #coco_dset.images().set('sensor_coarse', 'S2') >>> for img in coco_dset.imgs.values(): >>> img['sensor_coarse'] = 'S2' >>> coco_dset.remove_categories(coco_dset.cats.keys()) >>> coco_dset.fpath = 'bas.kwcoco.json' >>> # TODO make serializable, check set() and main() >>> coco_dset.dump(coco_dset.fpath, indent=2) >>> # make a new SC dataset >>> coco_dset_sc = smart_kwcoco_demodata.demo_kwcoco_with_heatmaps( >>> num_videos=2) >>> for img in coco_dset_sc.imgs.values(): >>> img['sensor_coarse'] = 'S2' >>> coco_dset_sc.remove_categories(coco_dset_sc.cats.keys()) >>> for img in coco_dset_sc.imgs.values(): >>> for aux, key in zip(img['auxiliary'], >>> ['Site Preparation', 'Active Construction', >>> 'Post Construction', 'No Activity']): >>> aux['channels'] = key >>> coco_dset_sc.fpath = 'sc.kwcoco.json' >>> coco_dset_sc.dump(coco_dset_sc.fpath, indent=2) >>> regions_dir = 'regions/' >>> sites_dir = 'sites/' >>> # moved this to a separate function for length >>> demo(coco_dset, regions_dir, coco_dset_sc, sites_dir, cleanup=True)
- geowatch.cli.run_tracker.assign_videos_to_regions(video_gdf, boundary_regions_gdf)[source]¶
Assign each video to a region (usually for BAS)