As patch-level data and results are not really useful for our purposes, here we run the predictions for larger scenes. Each plot is tiled to 512x512px patches, possibly with 256px overlap and afterwards the predictions are collated and optionally cleaned so that the amount of overlapping predictions is lower.
1 Hiidenportti test set
As Hiidenportti test set is so small, we can run predictions here if needed.
1.1 No overlap, no post-processing
Code
from drone_detector.engines.detectron2.predict import predict_instance_masksraw_path = Path('../../data/raw/hiidenportti/virtual_plots/buffered_test/images')test_rasters = [raw_path/f for f in os.listdir(raw_path) if f.endswith('tif')]
Where raster_tiles and vector_tiles are symbolic links pointing to corresponding data directories, and predicted_vectors and raw_preds are empty folders for predictions.
for t in test_rasters: outfile_name = pred_outpath/f'raw_preds/{str(t).split("/")[-1][:-4]}.geojson' predict_instance_masks(path_to_model_files='../models/hiidenportti/mask_rcnn_R_101_FPN_3x/', path_to_image=str(t), outfile=str(outfile_name), processing_dir='temp', tile_size=512, tile_overlap=0, smooth_preds=False, use_tta=True, coco_set='../../data/processed/hiidenportti/hiidenportti_valid.json', postproc_results=False)
Code
raw_res_path = pred_outpathtruth_shps =sorted([raw_res_path/'vector_tiles'/f for f in os.listdir(raw_res_path/'vector_tiles')])raw_shps =sorted([raw_res_path/'raw_preds'/f for f in os.listdir(raw_res_path/'raw_preds')])rasters =sorted([raw_res_path/'raster_tiles'/f for f in os.listdir(raw_res_path/'raster_tiles')])
“Raw” predictions are modified as such: 1. Invalid polygons are fixed to be valid polygons. MultiPolygon masks are replaced with the largest single polygon of the multipoly. 2. Extent is clipped to be same as the corresponding ground truth data 3. Label numbering is adjusted 4. Polygons with area less than 16² pixels are discarded
loading annotations into memory...
Done (t=0.03s)
creating index...
index created!
Loading and preparing results...
DONE (t=0.03s)
creating index...
index created!
As the scenes can contain more than 1000 annotations, set maxDets to larger values than default.
Evaluating for category uprightwood
Running per image evaluation...
Evaluate annotation type *segm*
DONE (t=0.81s).
Accumulating evaluation results...
DONE (t=0.01s).
Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=10000 ] = 0.348
Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=10000 ] = 0.677
Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=10000 ] = 0.325
Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=10000 ] = 0.217
Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=10000 ] = 0.396
Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=10000 ] = 1.000
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.466
Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.387
Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.497
Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 1.000
Evaluating for category groundwood
Running per image evaluation...
Evaluate annotation type *segm*
DONE (t=11.59s).
Accumulating evaluation results...
DONE (t=0.02s).
Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=10000 ] = 0.202
Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=10000 ] = 0.525
Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=10000 ] = 0.104
Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=10000 ] = 0.204
Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=10000 ] = 0.160
Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=10000 ] = -1.000
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.342
Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.347
Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.200
Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = -1.000
Evaluating for full data...
Running per image evaluation...
Evaluate annotation type *segm*
DONE (t=12.39s).
Accumulating evaluation results...
DONE (t=0.03s).
Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=10000 ] = 0.275
Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=10000 ] = 0.601
Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=10000 ] = 0.214
Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=10000 ] = 0.210
Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=10000 ] = 0.278
Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=10000 ] = 1.000
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.404
Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.367
Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.349
Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 1.000
Compared to the patch-level results, the AP50 score is around 0.2 lower than patch-level results. However, edges are not handled in any way in this processing level.
Get the number of false positives (FP), true positives (TP) and false negatives (FN). Object detection has infinite number of true negatives so we are not interested in them.
From these we can get both precision and recall: \(Precision = \frac{tp}{tp+fp}, Recall = \frac{tp}{tp+fn}\)
Code
print(f'Precision for fallen deadwood with IoU threshold of 0.5 is {(953/2790):.2f}')print(f'Recall for fallen deadwood with IoU threshold of 0.5 is {(953/1401):.2f}')
Precision for fallen deadwood with IoU threshold of 0.5 is 0.34
Recall for fallen deadwood with IoU threshold of 0.5 is 0.68
Code
print(f'Precision for standing deadwood with IoU threshold of 0.5 is {(272/654):.2f}')print(f'Recall for standing deadwood with IoU threshold of 0.5 is {(212/340):.2f}')
Precision for standing deadwood with IoU threshold of 0.5 is 0.42
Recall for standing deadwood with IoU threshold of 0.5 is 0.62
Code
print(f'Overall precision with IoU threshold of 0.5 is {(1225/3444):.2f}')print(f'Overall recall with IoU threshold of 0.5 is {(1225/1741):.2f}')
Overall precision with IoU threshold of 0.5 is 0.36
Overall recall with IoU threshold of 0.5 is 0.70
1.2 Half patch overlap and edge filtering
For this postprocessing method, mosaics are tiled so that the sliding window moves half tile lenght. For example, when moving row-wise, the first bottom-left coordinates are (0,0), and next ones (256,0), (512,0)… and same is done column-wise. We discard all predicted polygons whose centroid point is not within the half-overlap area. For instance, for first tile (bottom left (0,0)), the x-coordinate must be between 128 and 384, for second tile (256,0) between 384 and 640, and likewise for y-coordinates. This method discards almost 75% of all predictions in the scenes as they are either overlapping or cut in half in the patch borders.
The images used for predictions are buffered so that the whole area is covered, considering the discarding process.
raw_path = Path('../../data/raw/hiidenportti/virtual_plots/buffered_test/images')test_rasters = [raw_path/f for f in os.listdir(raw_path) if f.endswith('tif')]for t in test_rasters: outfile_name = pred_outpath/f'raw_preds/{str(t).split("/")[-1][:-4]}.geojson' predict_instance_masks(path_to_model_files='../models/hiidenportti/mask_rcnn_R_101_FPN_3x/', path_to_image=str(t), outfile=str(outfile_name), processing_dir='temp', tile_size=512, tile_overlap=256, smooth_preds=False, use_tta=True, coco_set='../../data/processed/hiidenportti/hiidenportti_valid.json', postproc_results=True)
Modify as previously.
Code
hp_res_path = pred_outpathtruth_shps =sorted([hp_res_path/'vector_tiles'/f for f in os.listdir(hp_res_path/'vector_tiles')])hp_raw_shps =sorted([hp_res_path/'raw_preds'/f for f in os.listdir(hp_res_path/'raw_preds')])rasters =sorted([hp_res_path/'raster_tiles'/f for f in os.listdir(hp_res_path/'raster_tiles')])
loading annotations into memory...
Done (t=0.03s)
creating index...
index created!
Loading and preparing results...
DONE (t=0.03s)
creating index...
index created!
print(f'Precision for fallen deadwood with IoU threshold of 0.5 is {(1062/2698):.2f}')print(f'Recall for fallen deadwood with IoU threshold of 0.5 is {(1062/1401):.2f}')
Precision for fallen deadwood with IoU threshold of 0.5 is 0.39
Recall for fallen deadwood with IoU threshold of 0.5 is 0.76
Code
print(f'Precision for standing deadwood with IoU threshold of 0.5 is {(301/619):.2f}')print(f'Recall for standing deadwood with IoU threshold of 0.5 is {(301/340):.2f}')
Precision for standing deadwood with IoU threshold of 0.5 is 0.49
Recall for standing deadwood with IoU threshold of 0.5 is 0.89
Code
print(f'Overall precision with IoU threshold of 0.5 is {(1363/3317):.2f}')print(f'Overall recall with IoU threshold of 0.5 is {(1363/1741):.2f}')
Overall precision with IoU threshold of 0.5 is 0.41
Overall recall with IoU threshold of 0.5 is 0.78
1.3 Overlap, edge filtering and mask merging
Mask merging is built on previous predictions. In this step, for each polygon we check whether the ratio between intersection with any other polygon of the same class and the area of the polygon is more than 0.2. If yes, the polygon is merged to the other polygon with which it had intersection-over-area ratio.
loading annotations into memory...
Done (t=0.03s)
creating index...
index created!
Loading and preparing results...
DONE (t=0.02s)
creating index...
index created!
Evaluating for category uprightwood
Running per image evaluation...
Evaluate annotation type *segm*
DONE (t=0.68s).
Accumulating evaluation results...
DONE (t=0.01s).
Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.436
Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=1000 ] = 0.761
Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=1000 ] = 0.465
Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.288
Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.495
Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 1.000
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.399
Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.246
Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.460
Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 1.000
Evaluating for category groundwood
Running per image evaluation...
Evaluate annotation type *segm*
DONE (t=9.05s).
Accumulating evaluation results...
DONE (t=0.01s).
Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.246
Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=1000 ] = 0.605
Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=1000 ] = 0.130
Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.248
Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.207
Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = -1.000
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.151
Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.153
Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.082
Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = -1.000
Evaluating for full data...
Running per image evaluation...
Evaluate annotation type *segm*
DONE (t=9.90s).
Accumulating evaluation results...
DONE (t=0.02s).
Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=1000 ] = 0.341
Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=1000 ] = 0.683
Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=1000 ] = 0.297
Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.268
Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.351
Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 1.000
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.275
Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.200
Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.271
Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 1.000
This usually worsens the results a bit, but the produced results are better suited for deriving forest charasteristics, as the number of overlapping detected instances decrease significantly.
print(f'Precision for fallen deadwood with IoU threshold of 0.5 is {(967/2003):.2f}')print(f'Recall for fallen deadwood with IoU threshold of 0.5 is {(967/1401):.2f}')
Precision for fallen deadwood with IoU threshold of 0.5 is 0.48
Recall for fallen deadwood with IoU threshold of 0.5 is 0.69
Code
print(f'Precision for standing deadwood with IoU threshold of 0.5 is {(293/564):.2f}')print(f'Recall for standing deadwood with IoU threshold of 0.5 is {(293/340):.2f}')
Precision for standing deadwood with IoU threshold of 0.5 is 0.52
Recall for standing deadwood with IoU threshold of 0.5 is 0.86
Code
print(f'Overall precision with IoU threshold of 0.5 is {(1260/2567):.2f}')print(f'Overall recall with IoU threshold of 0.5 is {(1260/1741):.2f}')
Overall precision with IoU threshold of 0.5 is 0.49
Overall recall with IoU threshold of 0.5 is 0.72
As the postprocessing merges data instead of dropping less certain predictions, total area and IoU remain the same as in previous step.
Running predictions for Evo dataset takes so much time that it has been done separately.
2.1 No overlap, no post-processing
Code
spk_raw_res_path = Path('../results/spk_benchmark/r101_nobuf/')truth_shps =sorted([spk_raw_res_path/'vector_tiles'/f for f in os.listdir(spk_raw_res_path/'vector_tiles')])spk_raw_shps =sorted([spk_raw_res_path/'raw_preds'/f for f in os.listdir(spk_raw_res_path/'raw_preds')])rasters =sorted([spk_raw_res_path/'raster_tiles'/f for f in os.listdir(spk_raw_res_path/'raster_tiles')])
print(f'Precision for fallen deadwood with IoU threshold of 0.5 is {(2055/5481):.2f}')print(f'Recall for fallen deadwood with IoU threshold of 0.5 is {(2055/3915):.2f}')
Precision for fallen deadwood with IoU threshold of 0.5 is 0.37
Recall for fallen deadwood with IoU threshold of 0.5 is 0.52
Code
print(f'Precision for standing deadwood with IoU threshold of 0.5 is {(883/1412):.2f}')print(f'Recall for standing deadwood with IoU threshold of 0.5 is {(883/1419):.2f}')
Precision for standing deadwood with IoU threshold of 0.5 is 0.63
Recall for standing deadwood with IoU threshold of 0.5 is 0.62
Code
print(f'Overall precision with IoU threshold of 0.5 is {(2938/6893):.2f}')print(f'Overall recall with IoU threshold of 0.5 is {(2938/5334):.2f}')
Overall precision with IoU threshold of 0.5 is 0.43
Overall recall with IoU threshold of 0.5 is 0.55
2.2 Half patch overlap and edge filtering
Code
spk_res_path = Path('../results/spk_benchmark/r101/')truth_shps =sorted([spk_res_path/'vector_tiles'/f for f in os.listdir(spk_res_path/'vector_tiles')])spk_buf_raw_shps =sorted([spk_res_path/'raw_preds'/f for f in os.listdir(spk_res_path/'raw_preds')])rasters =sorted([spk_res_path/'raster_tiles'/f for f in os.listdir(spk_res_path/'raster_tiles')])
print(f'Precision for fallen deadwood with IoU threshold of 0.5 is {(2170/5204):.2f}')print(f'Recall for fallen deadwood with IoU threshold of 0.5 is {(2170/3915):.2f}')
Precision for fallen deadwood with IoU threshold of 0.5 is 0.42
Recall for fallen deadwood with IoU threshold of 0.5 is 0.55
Code
print(f'Precision for standing deadwood with IoU threshold of 0.5 is {(929/1307):.2f}')print(f'Recall for standing deadwood with IoU threshold of 0.5 is {(930/1419):.2f}')
Precision for standing deadwood with IoU threshold of 0.5 is 0.71
Recall for standing deadwood with IoU threshold of 0.5 is 0.66
Code
print(f'Overall precision with IoU threshold of 0.5 is {(3099/6511):.2f}')print(f'Overall recall with IoU threshold of 0.5 is {(3100/5334):.2f}')
Overall precision with IoU threshold of 0.5 is 0.48
Overall recall with IoU threshold of 0.5 is 0.58
print(f'Precision for fallen deadwood with IoU threshold of 0.5 is {(2116/4130):.2f}')print(f'Recall for fallen deadwood with IoU threshold of 0.5 is {(2115/3915):.2f}')
Precision for fallen deadwood with IoU threshold of 0.5 is 0.51
Recall for fallen deadwood with IoU threshold of 0.5 is 0.54
Code
print(f'Precision for standing deadwood with IoU threshold of 0.5 is {(884/1190):.2f}')print(f'Recall for standing deadwood with IoU threshold of 0.5 is {(885/1419):.2f}')
Precision for standing deadwood with IoU threshold of 0.5 is 0.74
Recall for standing deadwood with IoU threshold of 0.5 is 0.62
Code
print(f'Overall precision with IoU threshold of 0.5 is {(3000/5320):.2f}')print(f'Overall recall with IoU threshold of 0.5 is {(3000/5334):.2f}')
Overall precision with IoU threshold of 0.5 is 0.56
Overall recall with IoU threshold of 0.5 is 0.56