r""" Uses the building detector to validate a construction event. Given a site model: * search for the K highest quality images at the start of the sequences. * search for the K highest quality images at the end of the sequence. * run the building detector on all of the chosen images. * test if the site boundary intersects detections in the start images. * test if the site boundary intersects detections in the end images. .. code:: StartTest | EndTest | Result ----------+---------+------- T | T | Reject ----------+---------+------- T | F | Reject ----------+---------+------- F | T | Accept ----------+---------+------- F | F | Reject ----------+---------+------- ? | T | Accept ----------+---------+------- T | ? | Reject ----------+---------+------- ? | F | Reject ----------+---------+------- F | ? | Accept ----------+---------+------- Dataflow: * BAS outputs a region model with candidate site summaries * We should be given a kwcoco path that indexes all of the data we could look at. In MLOPs this will be a region cropped kwcoco path that indexes existant images on disk. In smartflow this will be a virtual kwcoco file that requires network access. """ #!/usr/bin/env python3 import scriptconfig as scfg import ubelt as ub class BuildingValidatorConfig(scfg.DataConfig): input_kwcoco = scfg.Value(None, help='input') input_region = scfg.Value(None) input_sites = scfg.Value(None) output_region_fpath = scfg.Value(None) output_sites_dpath = scfg.Value(None) output_site_manifest_fpath = scfg.Value(None) box_isect_threshold = scfg.Value(0.1, help='This fraction of the a detected building box must intersect the proposed polygon') box_score_threshold = scfg.Value(0.01, help='The detected building boxes must have a score higher than this') start_max_score = scfg.Value(1.0, help='The max building score needed in the start sequence') end_min_score = scfg.Value(0.1, help='The min building score needed in the end sequence') IGNORE_CLASS_NAMES = ub.codeblock( ''' Fixed-wing Aircraft Small Aircraft Cargo Plane Helicopter Passenger Vehicle Small Car Bus Pickup Truck Utility Truck Truck Cargo Truck Truck w/Box Truck Tractor Trailer Truck w/Flatbed Truck w/Liquid Crane Truck Railway Vehicle Passenger Car Cargo Car Flat Car Tank car Locomotive Maritime Vessel Motorboat Sailboat Tugboat Barge Fishing Vessel Ferry Yacht Container Ship Oil Tanker Engineering Vehicle Tower crane Container Crane Reach Stacker Straddle Carrier Mobile Crane Dump Truck Haul Truck Scraper/Tractor Front loader/Bulldozer Excavator Cement Mixer Ground Grader ''').split('\n') DONT_IGNORE_CLASSNAMES = ub.codeblock( ''' Aircraft Hangar Helipad Storage Tank Hut/Tent Shipping container lot Shipping Container Pylon Tower Shed Building Damaged Building Facility Construction Site Vehicle Lot ''').split('\n') def main(cmdline=1, **kwargs): """ Ignore: from geowatch.tasks.dino_detector.building_validator import * # NOQA NODE_DPATH = '/home/joncrall/remote/toothbrush/data/dvc-repos/smart_expt_dvc/_mlops_eval10_baseline/pred/flat/buildings/buildings_id_fd298dba' from geowatch.utils.partial_format import fsubtemplate coco_fpath = fsubtemplate('$NODE_DPATH/pred_boxes.kwcoco.zip') from kwutil.util_path import coerce_patterned_paths site_summary = ub.Path(fsubtemplate('$NODE_DPATH/.pred/sv_crop/*/.pred/bas_poly/*/site_summaries_manifest.json')) site_summary = ub.Path(fsubtemplate('$NODE_DPATH/.pred/valicrop/*/.pred/bas_poly/*/site_summaries_manifest.json')) site_summary = coerce_patterned_paths(site_summary)[0] output_region_fpath = ub.Path(fsubtemplate('$NODE_DPATH/filtered_site_summaries.geojson')) kwargs = { 'input_kwcoco': coco_fpath, 'input_region': site_summary, 'input_sites': (site_summary.parent / 'sites'), 'output_region_fpath': output_region_fpath, } cmdline = 0 Ignore: >>> import geowatch >>> dvc_data_dpath = geowatch.find_dvc_dpath(tags='phase2_data', hardware='auto') >>> coco_fpath = dvc_data_dpath / 'Drop6/imgonly-KR_R001.kwcoco.json' >>> region_fpath = dvc_data_dpath / 'annotations/drop6_hard_v1/region_models/KR_R001.geojson' Example: >>> # xdoctest: +SKIP >>> cmdline = 0 >>> kwargs = dict() >>> main(cmdline=cmdline, **kwargs) """ config = BuildingValidatorConfig.cli(cmdline=cmdline, data=kwargs, strict=True) import rich rich.print('config = ' + ub.urepr(config, nl=1)) from geowatch.geoannots import geomodels from geowatch.utils import util_gis import kwcoco from geowatch.cli import reproject_annotations from kwcoco.util import util_json from geowatch.utils import process_context import os import safer import json # Args will be serailized in kwcoco, so make sure it can be coerced to json jsonified_config = util_json.ensure_json_serializable(config.asdict()) walker = ub.IndexableWalker(jsonified_config) for problem in util_json.find_json_unserializable(jsonified_config): bad_data = problem['data'] walker[problem['loc']] = str(bad_data) filter_output = { 'type': 'tracking_result', 'info': [], 'files': [], } # Track process info proc_context = process_context.ProcessContext( name='geowatch.tasks.dino_detector.building_validator', type='process', config=jsonified_config, track_emissions=False, ) proc_context.start() filter_output['info'].append(proc_context.obj) input_coco = kwcoco.CocoDataset(config.input_kwcoco) region_model = geomodels.RegionModel.coerce(config.input_region) output_region_fpath = ub.Path(config.output_region_fpath) input_site_fpaths = util_gis.coerce_geojson_paths(config.input_sites) # set(input_coco.annots().lookup('track_id', None)) site_to_site_fpath = ub.udict({ p.stem: p for p in input_site_fpaths }) input_sites = list(geomodels.SiteModel.coerce_multiple(site_to_site_fpath.values())) site_id_to_summary = ub.udict() for summary in region_model.site_summaries(): assert summary.site_id not in site_id_to_summary site_id_to_summary[summary.site_id] = summary ## # Ensure caches for summary in site_id_to_summary.values(): summary['properties'].setdefault('cache', {}) for site in input_sites: site.header['properties'].setdefault('cache', {}) output_kwcoco = reproject_annotations.main( cmdline=0, src=input_coco.copy(), dst='return', region_models=config.input_region, status_to_catname={'system_confirmed': 'positive'}, role='pred_poly', validate_checks=False, clear_existing=False, ) if 0: from geowatch.utils import util_framework region_models = [region_model] site_models = input_sites util_framework.check_region_and_site_models_agree(region_models, site_models) site_to_decisions = make_decisions(output_kwcoco, site_id_to_summary, config) num_accept = sum(d['accept'] for s, d in site_to_decisions.items()) print(f'Filter to {num_accept} / {len(site_id_to_summary)} sites') # Enrich each site summary with the decision reason and update status for site_id, decision in site_to_decisions.items(): sitesum = site_id_to_summary[site_id] # Change the status of sites to "system_rejected" instead of droping them if not decision['accept']: sitesum['properties']['status'] = 'system_rejected' if 'cache' not in sitesum['properties']: sitesum['properties']['cache'] = {} sitesum['properties']['cache']['dino_decision'] = decision # Copy the site models and update their header with new summary # information. output_sites_dpath = ub.Path(config.output_sites_dpath) output_sites_dpath.ensuredir() out_site_fpaths = [] for old_site in input_sites: site_id = old_site.site_id old_fpath = site_to_site_fpath[site_id] new_fpath = output_sites_dpath / old_fpath.name new_summary = site_id_to_summary[site_id] old_site.header['properties']['status'] = new_summary['properties']['status'] if 'cache' not in old_site.header['properties']: old_site.header['properties']['cache'] = {} old_site.header['properties']['cache'].update(new_summary['properties']['cache']) new_fpath.write_text(old_site.dumps()) out_site_fpaths.append(new_fpath) # Write the updated site summaries in a new region model new_summaries = list(site_id_to_summary.values()) new_region_model = geomodels.RegionModel.from_features( [region_model.header] + list(new_summaries)) output_region_fpath.parent.ensuredir() if 1: from geowatch.utils import util_framework region_models = [region_model] site_models = input_sites util_framework.check_region_and_site_models_agree([new_region_model], site_models) print(f'Write filtered region model to: {output_region_fpath}') with safer.open(output_region_fpath, 'w', temp_file=not ub.WIN32) as file: json.dump(new_region_model, file, indent=4) # from kwutil.util_json import debug_json_unserializable # debug_json_unserializable(new_region_model) proc_context.stop() if config.output_site_manifest_fpath is not None: filter_output['files'] = [os.fspath(p) for p in out_site_fpaths] print(f'Write filtered site result to {config.output_site_manifest_fpath}') with safer.open(config.output_site_manifest_fpath, 'w', temp_file=not ub.WIN32) as file: json.dump(filter_output, file, indent=4) def make_decisions(output_kwcoco, site_id_to_summary, config): """ Make yes/no decision about sites with dino predictions in the kwcoco file. Returns: Dict: mapping from site-ids to the decision made for it """ from kwcoco.util import util_json from kwutil import util_time # Enrich all sites with features (evidence) and decisions. site_to_decisions = { s: { 'type': 'dino_decision', 'accept': True, 'why': None, 'features': None, } for s in site_id_to_summary.keys() } for video_id in ub.ProgIter(output_kwcoco.videos(), desc='validate sites'): video = output_kwcoco.index.videos[video_id] video_name = video['name'] # the vide name should be the site id summary = site_id_to_summary[video_name] site_id = summary.site_id video_images = output_kwcoco.images(video_id=video_id) start_date = summary.start_date end_date = summary.end_date # Get the starting and ending observations start_images = [] end_images = [] for coco_img in video_images.coco_images: img_time = util_time.coerce_datetime(coco_img['date_captured']) dist_start = abs(img_time - start_date) dist_end = abs(img_time - end_date) if dist_end < dist_start: end_images.append(coco_img) else: start_images.append(coco_img) start_features = [] end_features = [] try: for coco_img in start_images: feat = building_in_image_features(coco_img, site_id, config) feat['datetime'] = coco_img['date_captured'] feat['type'] = 'start_feature' start_features.append(feat) for coco_img in end_images: feat = building_in_image_features(coco_img, site_id, config) feat['datetime'] = coco_img['date_captured'] feat['type'] = 'end_feature' end_features.append(feat) if len(start_features) and len(end_features): max_start_score = max(f['max_score'] for f in start_features) max_end_score = max(f['max_score'] for f in end_features) accept = bool( max_start_score <= config.start_max_score and max_end_score >= config.end_min_score ) why = 'threshold' else: # Unobservable case, automatically accept accept = True why = 'unobservable' except CouldNotValidate: accept = True why = 'CouldNotValidate' decision = site_to_decisions[site_id] decision['accept'] = accept decision['why'] = why decision['features'] = start_features + end_features decision = util_json.ensure_json_serializable(decision) site_to_decisions[site_id] = decision return site_to_decisions def building_in_image_features(coco_img, site_id, config): import numpy as np import kwimage import geopandas as gpd import warnings annots = coco_img.annots() flags = np.array([r == 'pred_poly' for r in annots.lookup('role', None)], dtype=bool) box_annots = annots.compress(~flags) poly_annots = annots.compress(flags) is_main_poly = [t == site_id for t in poly_annots.lookup('track_id')] main_poly_annots = poly_annots.compress(is_main_poly) if len(main_poly_annots) > 1: warnings.warn('FIXME: Len of "main-poly-annots" is not 1.') elif len(main_poly_annots) == 0: raise CouldNotValidate('We dont expect to be here') flags = [cname not in IGNORE_CLASS_NAMES for cname in box_annots.category_names] box_annots = box_annots.compress(flags) iooa_thresh = config.box_isect_threshold score_thresh = config.box_score_threshold box_gdf = gpd.GeoDataFrame({ 'geometry': box_annots.boxes.to_shapely(), 'class': box_annots.category_names, 'score': box_annots.lookup('score'), }) box_gdf = box_gdf[box_gdf.score > score_thresh] proposal_gdf = gpd.GeoDataFrame({ 'geometry': [ kwimage.MultiPolygon.coerce(p).to_shapely() for p in main_poly_annots.lookup('segmentation') ], }) proposal_geom = proposal_gdf.iloc[0]['geometry'] with warnings.catch_warnings(): warnings.filterwarnings('ignore', 'invalid value encountered in intersection') intersections = box_gdf.intersection(proposal_geom) bad_flags = (intersections.isnull() | intersections.is_empty) isect_flags = (~bad_flags) & (intersections.is_valid) feat = { 'max_iou': 0, 'max_iooa': 0, 'max_score': 0, 'num_cands': 0, } if isect_flags.any(): cand_boxes = box_gdf[isect_flags] cand_isects = intersections[isect_flags] cand_iooa1 = cand_isects.area / cand_boxes.area ioaa_flags = cand_iooa1 > iooa_thresh if ioaa_flags.any(): cand_boxes = cand_boxes[ioaa_flags] cand_unions = cand_boxes.union(proposal_geom) cand_ious = cand_isects.area / cand_unions.area feat['max_iou'] = cand_ious.max() feat['max_iooa'] = cand_iooa1.max() feat['max_score'] = cand_boxes['score'].max() feat['num_cands'] = len(cand_boxes) return feat class CouldNotValidate(Exception): ... if __name__ == '__main__': """ CommandLine: python ~/code/watch/geowatch/tasks/dino_detector/building_validator.py python -m geowatch.tasks.dino_detector.building_validator """ main() """ Ignore: DVC_DATA_DPATH=$(geowatch_dvc --tags='phase2_data' --hardware=auto) DVC_EXPT_DPATH=$(geowatch_dvc --tags='phase2_expt' --hardware=auto) geowatch schedule --params=" matrix: bas_pxl.package_fpath: - $DVC_EXPT_DPATH/models/fusion/Drop6-MeanYear10GSD/packages/Drop6_TCombo1Year_BAS_10GSD_split6_V42_cont2/Drop6_TCombo1Year_BAS_10GSD_split6_V42_cont2_epoch3_step941.pt bas_pxl.test_dataset: - $DVC_DATA_DPATH/Drop6-MeanYear10GSD-V2/imganns-KR_R002.kwcoco.zip - $DVC_DATA_DPATH/Drop6-MeanYear10GSD-V2/imganns-BR_R002.kwcoco.zip - $DVC_DATA_DPATH/Drop6-MeanYear10GSD-V2/imganns-CH_R001.kwcoco.zip - $DVC_DATA_DPATH/Drop6-MeanYear10GSD-V2/imganns-NZ_R001.kwcoco.zip - $DVC_DATA_DPATH/Drop6-MeanYear10GSD-V2/imganns-KR_R001.kwcoco.zip - $DVC_DATA_DPATH/Drop6-MeanYear10GSD-V2/imganns-AE_R001.kwcoco.zip bas_pxl.chip_overlap: 0.3 bas_pxl.chip_dims: - auto bas_pxl.time_span: - auto bas_pxl.time_sampling: # - auto # - soft5 - soft4 bas_poly.thresh: - 0.33 #- 0.38 #- 0.4 bas_poly.inner_window_size: - 1y #- null bas_poly.inner_agg_fn: - mean bas_poly.norm_ord: #- 1 - inf bas_poly.polygon_simplify_tolerance: - 1 bas_poly.agg_fn: - probs bas_poly.resolution: - 10GSD bas_poly.moving_window_size: - null #- 1 bas_poly.poly_merge_method: - 'v2' #- 'v1' bas_poly.min_area_square_meters: - 7200 bas_poly.max_area_square_meters: - 8000000 bas_poly.boundary_region: $DVC_DATA_DPATH/annotations/drop6/region_models bas_poly_eval.true_site_dpath: $DVC_DATA_DPATH/annotations/drop6/site_models bas_poly_eval.true_region_dpath: $DVC_DATA_DPATH/annotations/drop6/region_models bas_pxl.enabled: 1 bas_pxl_eval.enabled: 1 bas_poly.enabled: 1 bas_poly_eval.enabled: 1 bas_poly_viz.enabled: 0 sv_crop.enabled: 1 sv_crop.minimum_size: "256x256@2GSD" sv_crop.num_start_frames: 3 sv_crop.num_end_frames: 3 sv_crop.context_factor: 1.5 sv_dino_boxes.enabled: 1 sv_dino_boxes.package_fpath: $DVC_EXPT_DPATH/models/kitware/xview_dino.pt sv_dino_boxes.window_dims: - 256 - 512 - 768 - 1024 # - 1536 sv_dino_boxes.window_overlap: - "0.5" sv_dino_boxes.fixed_resolution: - "1GSD" - "2GSD" - "2.5GSD" - "3GSD" - "3.3GSD" sv_dino_filter.box_isect_threshold: - 0.1 sv_dino_filter.box_score_threshold: - 0.01 sv_dino_filter.start_max_score: - 1.0 - 0.8 # - 0.5 sv_dino_filter.end_min_score: - 0.0 # - 0.05 - 0.1 - 0.15 - 0.2 - 0.25 - 0.3 # - 0.4 - 0.5 submatrices: - bas_pxl.test_dataset: $DVC_DATA_DPATH/Drop6-MeanYear10GSD-V2/imganns-KR_R001.kwcoco.zip sv_crop.crop_src_fpath: $DVC_DATA_DPATH/Drop6/imgonly-KR_R001.kwcoco.json - bas_pxl.test_dataset: $DVC_DATA_DPATH/Drop6-MeanYear10GSD-V2/imganns-KR_R002.kwcoco.zip sv_crop.crop_src_fpath: $DVC_DATA_DPATH/Drop6/imgonly-KR_R002.kwcoco.json - bas_pxl.test_dataset: $DVC_DATA_DPATH/Drop6-MeanYear10GSD-V2/imganns-BR_R002.kwcoco.zip sv_crop.crop_src_fpath: $DVC_DATA_DPATH/Drop6/imgonly-BR_R002.kwcoco.json - bas_pxl.test_dataset: $DVC_DATA_DPATH/Drop6-MeanYear10GSD-V2/imganns-CH_R001.kwcoco.zip sv_crop.crop_src_fpath: $DVC_DATA_DPATH/Drop6/imgonly-CH_R001.kwcoco.json - bas_pxl.test_dataset: $DVC_DATA_DPATH/Drop6-MeanYear10GSD-V2/imganns-NZ_R001.kwcoco.zip sv_crop.crop_src_fpath: $DVC_DATA_DPATH/Drop6/imgonly-NZ_R001.kwcoco.json - bas_pxl.test_dataset: $DVC_DATA_DPATH/Drop6-MeanYear10GSD-V2/imganns-AE_R001.kwcoco.zip sv_crop.crop_src_fpath: $DVC_DATA_DPATH/Drop6/imgonly-AE_R001.kwcoco.json " \ --root_dpath="$DVC_EXPT_DPATH/_mlops_eval10_baseline" \ --devices="0," --tmux_workers=8 \ --backend=tmux --queue_name "_mlops_eval10_baseline" \ --pipeline=bas_building_vali --skip_existing=1 \ --run=1 DVC_EXPT_DPATH=$(geowatch_dvc --tags='phase2_expt' --hardware=auto) gwmlops aggregate \ --pipeline=bas_building_vali \ --target \ "$DVC_EXPT_DPATH/_mlops_eval10_baseline" \ --resource_report=0 \ --rois='[KR_R002,KR_R001,BR_R002,CH_R001,NZ_R001,AE_R001]' \ --stdout_report=" top_k: 5 per_group: 1 macro_analysis: 0 analyze: 0 reference_region: final shorten: 1 " --eval_nodes="[sv_poly_eval]" \ --plot_params=" enabled: True " --rois='[NZ_R001,KR_R001]' \ print_models: True geowatch align \ --src "/home/joncrall/remote/toothbrush/data/dvc-repos/smart_data_dvc-ssd/Drop6/imgonly-KR_R001.kwcoco.json" \ --dst "/home/joncrall/remote/toothbrush/data/dvc-repos/smart_expt_dvc/_mlops_eval10_baseline/pred/flat/sv_crop/valicrop_id_2e8c8dc3/sv_crop.kwcoco.zip" \ --regions="/home/joncrall/remote/toothbrush/data/dvc-repos/smart_expt_dvc/_mlops_eval10_baseline/pred/flat/bas_poly/bas_poly_id_dc32b2a6/site_summaries_manifest.json" \ --site_summary=True \ --verbose="1" \ --workers="32" \ --aux_workers="4" \ --debug_valid_regions="False" \ --visualize="False" \ --keep="img" \ --geo_preprop="auto" \ --minimum_size="256x256@2GSD" \ --num_start_frames="3" \ --num_end_frames="3" \ --context_factor="1.5" \ --include_sensors="WV" \ --force_nodata="-9999" \ --rpc_align_method="orthorectify" \ --target_gsd="2" \ --force_min_gsd="2" \ --convexify_regions="True" geowatch visualize /home/joncrall/remote/toothbrush/data/dvc-repos/smart_expt_dvc/_mlops_eval10_baseline/pred/flat/sv_crop/valicrop_id_2e8c8dc3/sv_crop.kwcoco.zip --smart python ~/code/watch/dev/wip/grid_sitevali_crops.py /home/joncrall/remote/toothbrush/data/dvc-repos/smart_expt_dvc/_mlops_eval10_baseline/pred/flat/sv_crop/valicrop_id_2e8c8dc3/_viz_* python -m geowatch.tasks.dino_detector.predict \ --package_fpath="/home/joncrall/remote/toothbrush/data/dvc-repos/smart_expt_dvc/models/kitware/xview_dino.pt" \ --coco_fpath="/home/joncrall/remote/toothbrush/data/dvc-repos/smart_expt_dvc/_mlops_eval10_baseline/pred/flat/sv_crop/valicrop_id_2e8c8dc3/sv_crop.kwcoco.zip" \ --out_coco_fpath="/home/joncrall/remote/toothbrush/data/dvc-repos/smart_expt_dvc/_mlops_eval10_baseline/pred/flat/buildings/buildings_id_61b8c2c7/pred_boxes.kwcoco.zip" \ --device="0" \ --data_workers="2" \ --fixed_resolution="1.0GSD" \ --window_dims="2048" \ --batch_size="1" geowatch visualize /home/joncrall/remote/toothbrush/data/dvc-repos/smart_expt_dvc/_mlops_eval10_baseline/pred/flat/buildings/buildings_id_61b8c2c7/pred_and_truth.kwcoco.zip \ --resolution=2GSD \ --smart \ --ann_score_thresh=0.3 \ --viz_dpath /home/joncrall/remote/toothbrush/data/dvc-repos/smart_expt_dvc/_mlops_eval10_baseline/pred/flat/buildings/buildings_id_61b8c2c7/_vizme python ~/code/watch/dev/wip/grid_sitevali_crops.py --sub=_anns \ /home/joncrall/remote/toothbrush/data/dvc-repos/smart_expt_dvc/_mlops_eval10_baseline/pred/flat/buildings/buildings_id_61b8c2c7/_vizme NODE_DPATH=/home/joncrall/remote/toothbrush/data/dvc-repos/smart_expt_dvc/_mlops_eval10_baseline/pred/flat/buildings/buildings_id_fd298dba DVC_DATA_DPATH=$(geowatch_dvc --tags='phase2_data' --hardware=auto) geowatch reproject \ --src "$NODE_DPATH/pred_boxes.kwcoco.zip" \ --dst "$NODE_DPATH/pred_boxes_with_polys.kwcoco.zip" \ --region_models "$NODE_DPATH/.pred/sv_crop/*/.pred/bas_poly/*/site_summaries_manifest.json" \ --status_to_catname="{system_confirmed: positive}" \ --role=pred_poly \ --validate_checks=False \ --clear_existing=False geowatch reproject \ --src "$NODE_DPATH/pred_boxes_with_polys.kwcoco.zip" \ --dst "$NODE_DPATH/pred_and_truth.kwcoco.zip" \ --region_models="$DVC_DATA_DPATH/annotations/drop6/region_models/*.geojson" \ --site_models="$DVC_DATA_DPATH/annotations/drop6/site_models/*.geojson" \ --status_to_catname="{system_confirmed: positive}" \ --role=truth \ --clear_existing=False gw visualize --smart 1 \ --ann_score_thresh 0.5 \ --draw_labels False \ --alpha 0.5 \ --src $NODE_DPATH/pred_and_truth.kwcoco.zip \ --viz_dpath $NODE_DPATH/_vizme \ python ~/code/watch/dev/wip/grid_sitevali_crops.py \ --sub=_anns \ $NODE_DPATH/_vizme python -m geowatch.tasks.dino_detector.building_validator \ --input_kwcoco "$NODE_DPATH/pred_boxes.kwcoco.zip" \ --input_region $NODE_DPATH/.pred/sv_crop/*/.pred/bas_poly/*/site_summaries_manifest.json \ --output_region_fpath "$NODE_DPATH/filtered_summaries.json" \ --box_isect_threshold 0.1 \ --box_score_threshold 0.1 \ --start_max_score 0.0 \ --end_min_score 0.3 python -m geowatch.tasks.dino_detector.building_validator \ --input_kwcoco="/home/joncrall/remote/toothbrush/data/dvc-repos/smart_expt_dvc/_mlops_eval10_baseline/pred/flat/buildings/buildings_id_663bd461/pred_boxes.kwcoco.zip" \ --input_region="/home/joncrall/remote/toothbrush/data/dvc-repos/smart_expt_dvc/_mlops_eval10_baseline/pred/flat/bas_poly/bas_poly_id_f8061df6/site_summaries_manifest.json" \ --input_sites="/home/joncrall/remote/toothbrush/data/dvc-repos/smart_expt_dvc/_mlops_eval10_baseline/pred/flat/bas_poly/bas_poly_id_f8061df6/sites_manifest.json" \ --output_region_fpath="/home/joncrall/remote/toothbrush/data/dvc-repos/smart_expt_dvc/_mlops_eval10_baseline/pred/flat/building_validate/building_validate_id_60a56eed/out_region.geojson" \ --output_sites_dpath="/home/joncrall/remote/toothbrush/data/dvc-repos/smart_expt_dvc/_mlops_eval10_baseline/pred/flat/building_validate/building_validate_id_60a56eed/out_sites" \ --output_site_manifest_fpath="/home/joncrall/remote/toothbrush/data/dvc-repos/smart_expt_dvc/_mlops_eval10_baseline/pred/flat/building_validate/building_validate_id_60a56eed/out_site_manifest.json" \ --box_isect_threshold="0.1" \ --box_score_threshold="0.1" \ --start_max_score="0.1" \ --end_min_score="0.3" """