megadetector 5.0.29__py3-none-any.whl → 10.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of megadetector might be problematic. Click here for more details.
- megadetector/classification/efficientnet/model.py +8 -8
- megadetector/classification/efficientnet/utils.py +6 -5
- megadetector/classification/prepare_classification_script_mc.py +3 -3
- megadetector/data_management/annotations/annotation_constants.py +0 -1
- megadetector/data_management/camtrap_dp_to_coco.py +34 -1
- megadetector/data_management/cct_json_utils.py +2 -2
- megadetector/data_management/coco_to_yolo.py +22 -5
- megadetector/data_management/databases/add_width_and_height_to_db.py +85 -12
- megadetector/data_management/databases/combine_coco_camera_traps_files.py +2 -2
- megadetector/data_management/databases/integrity_check_json_db.py +29 -15
- megadetector/data_management/generate_crops_from_cct.py +50 -1
- megadetector/data_management/labelme_to_coco.py +4 -2
- megadetector/data_management/labelme_to_yolo.py +82 -2
- megadetector/data_management/lila/generate_lila_per_image_labels.py +276 -18
- megadetector/data_management/lila/get_lila_annotation_counts.py +5 -3
- megadetector/data_management/lila/lila_common.py +3 -0
- megadetector/data_management/lila/test_lila_metadata_urls.py +15 -5
- megadetector/data_management/mewc_to_md.py +5 -0
- megadetector/data_management/ocr_tools.py +4 -3
- megadetector/data_management/read_exif.py +20 -5
- megadetector/data_management/remap_coco_categories.py +66 -4
- megadetector/data_management/remove_exif.py +50 -1
- megadetector/data_management/rename_images.py +3 -3
- megadetector/data_management/resize_coco_dataset.py +563 -95
- megadetector/data_management/yolo_output_to_md_output.py +131 -2
- megadetector/data_management/yolo_to_coco.py +140 -5
- megadetector/detection/change_detection.py +4 -3
- megadetector/detection/pytorch_detector.py +60 -22
- megadetector/detection/run_detector.py +225 -25
- megadetector/detection/run_detector_batch.py +42 -16
- megadetector/detection/run_inference_with_yolov5_val.py +12 -2
- megadetector/detection/run_tiled_inference.py +1 -0
- megadetector/detection/video_utils.py +53 -24
- megadetector/postprocessing/add_max_conf.py +4 -0
- megadetector/postprocessing/categorize_detections_by_size.py +1 -1
- megadetector/postprocessing/classification_postprocessing.py +55 -20
- megadetector/postprocessing/combine_batch_outputs.py +3 -2
- megadetector/postprocessing/compare_batch_results.py +64 -10
- megadetector/postprocessing/convert_output_format.py +12 -8
- megadetector/postprocessing/create_crop_folder.py +137 -10
- megadetector/postprocessing/load_api_results.py +26 -8
- megadetector/postprocessing/md_to_coco.py +4 -4
- megadetector/postprocessing/md_to_labelme.py +18 -7
- megadetector/postprocessing/merge_detections.py +5 -0
- megadetector/postprocessing/postprocess_batch_results.py +6 -3
- megadetector/postprocessing/remap_detection_categories.py +55 -2
- megadetector/postprocessing/render_detection_confusion_matrix.py +9 -6
- megadetector/postprocessing/repeat_detection_elimination/repeat_detections_core.py +2 -2
- megadetector/taxonomy_mapping/map_new_lila_datasets.py +3 -4
- megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +40 -19
- megadetector/taxonomy_mapping/preview_lila_taxonomy.py +1 -1
- megadetector/taxonomy_mapping/species_lookup.py +123 -41
- megadetector/utils/ct_utils.py +133 -113
- megadetector/utils/md_tests.py +93 -13
- megadetector/utils/path_utils.py +137 -107
- megadetector/utils/split_locations_into_train_val.py +2 -2
- megadetector/utils/string_utils.py +7 -7
- megadetector/utils/url_utils.py +81 -58
- megadetector/utils/wi_utils.py +46 -17
- megadetector/visualization/plot_utils.py +13 -9
- megadetector/visualization/render_images_with_thumbnails.py +2 -1
- megadetector/visualization/visualization_utils.py +94 -46
- megadetector/visualization/visualize_db.py +36 -9
- megadetector/visualization/visualize_detector_output.py +4 -4
- {megadetector-5.0.29.dist-info → megadetector-10.0.0.dist-info}/METADATA +135 -135
- megadetector-10.0.0.dist-info/RECORD +139 -0
- {megadetector-5.0.29.dist-info → megadetector-10.0.0.dist-info}/licenses/LICENSE +0 -0
- {megadetector-5.0.29.dist-info → megadetector-10.0.0.dist-info}/top_level.txt +0 -0
- megadetector/api/batch_processing/api_core/__init__.py +0 -0
- megadetector/api/batch_processing/api_core/batch_service/__init__.py +0 -0
- megadetector/api/batch_processing/api_core/batch_service/score.py +0 -438
- megadetector/api/batch_processing/api_core/server.py +0 -294
- megadetector/api/batch_processing/api_core/server_api_config.py +0 -97
- megadetector/api/batch_processing/api_core/server_app_config.py +0 -55
- megadetector/api/batch_processing/api_core/server_batch_job_manager.py +0 -220
- megadetector/api/batch_processing/api_core/server_job_status_table.py +0 -149
- megadetector/api/batch_processing/api_core/server_orchestration.py +0 -360
- megadetector/api/batch_processing/api_core/server_utils.py +0 -88
- megadetector/api/batch_processing/api_core_support/__init__.py +0 -0
- megadetector/api/batch_processing/api_core_support/aggregate_results_manually.py +0 -46
- megadetector/api/batch_processing/api_support/__init__.py +0 -0
- megadetector/api/batch_processing/api_support/summarize_daily_activity.py +0 -152
- megadetector/api/batch_processing/data_preparation/__init__.py +0 -0
- megadetector/api/synchronous/__init__.py +0 -0
- megadetector/api/synchronous/api_core/animal_detection_api/__init__.py +0 -0
- megadetector/api/synchronous/api_core/animal_detection_api/api_backend.py +0 -151
- megadetector/api/synchronous/api_core/animal_detection_api/api_frontend.py +0 -263
- megadetector/api/synchronous/api_core/animal_detection_api/config.py +0 -35
- megadetector/api/synchronous/api_core/tests/__init__.py +0 -0
- megadetector/api/synchronous/api_core/tests/load_test.py +0 -109
- megadetector/utils/azure_utils.py +0 -178
- megadetector/utils/sas_blob_utils.py +0 -513
- megadetector-5.0.29.dist-info/RECORD +0 -163
- /megadetector/{api/batch_processing/__init__.py → __init__.py} +0 -0
- {megadetector-5.0.29.dist-info → megadetector-10.0.0.dist-info}/WHEEL +0 -0
|
@@ -247,8 +247,13 @@ def load_image(input_file, ignore_exif_rotation=False):
|
|
|
247
247
|
return image
|
|
248
248
|
|
|
249
249
|
|
|
250
|
-
def resize_image(image,
|
|
251
|
-
|
|
250
|
+
def resize_image(image,
|
|
251
|
+
target_width=-1,
|
|
252
|
+
target_height=-1,
|
|
253
|
+
output_file=None,
|
|
254
|
+
no_enlarge_width=False,
|
|
255
|
+
verbose=False,
|
|
256
|
+
quality='keep'):
|
|
252
257
|
"""
|
|
253
258
|
Resizes a PIL Image object to the specified width and height; does not resize
|
|
254
259
|
in place. If either width or height are -1, resizes with aspect ratio preservation.
|
|
@@ -378,7 +383,7 @@ def crop_image(detections, image, confidence_threshold=0.15, expansion=0):
|
|
|
378
383
|
|
|
379
384
|
score = float(detection['conf'])
|
|
380
385
|
|
|
381
|
-
if score >= confidence_threshold:
|
|
386
|
+
if (confidence_threshold is None) or (score >= confidence_threshold):
|
|
382
387
|
|
|
383
388
|
x1, y1, w_box, h_box = detection['bbox']
|
|
384
389
|
ymin,xmin,ymax,xmax = y1, x1, y1 + h_box, x1 + w_box
|
|
@@ -422,6 +427,7 @@ def blur_detections(image,detections,blur_radius=40):
|
|
|
422
427
|
image (PIL.Image.Image): image in which we should blur specific regions
|
|
423
428
|
detections (list): list of detections in the MD output format, see render
|
|
424
429
|
detection_bounding_boxes for more detail.
|
|
430
|
+
blur_radius (int, optional): radius of blur kernel in pixels
|
|
425
431
|
"""
|
|
426
432
|
|
|
427
433
|
img_width, img_height = image.size
|
|
@@ -457,7 +463,7 @@ def render_detection_bounding_boxes(detections,
|
|
|
457
463
|
image,
|
|
458
464
|
label_map='show_categories',
|
|
459
465
|
classification_label_map=None,
|
|
460
|
-
confidence_threshold=0,
|
|
466
|
+
confidence_threshold=0.0,
|
|
461
467
|
thickness=DEFAULT_BOX_THICKNESS,
|
|
462
468
|
expansion=0,
|
|
463
469
|
classification_confidence_threshold=0.3,
|
|
@@ -531,7 +537,7 @@ def render_detection_bounding_boxes(detections,
|
|
|
531
537
|
class names. The type of the numeric label (typically strings) needs to be consistent with the keys
|
|
532
538
|
in label_map; no casting is carried out. If [label_map] is None, no labels are shown (not even numbers
|
|
533
539
|
and confidence values).
|
|
534
|
-
confidence_threshold (float or dict, optional)
|
|
540
|
+
confidence_threshold (float or dict, optional): threshold above which boxes are rendered. Can also be a
|
|
535
541
|
dictionary mapping category IDs to thresholds.
|
|
536
542
|
thickness (int, optional): line thickness in pixels
|
|
537
543
|
expansion (int, optional): number of pixels to expand bounding boxes on each side
|
|
@@ -543,8 +549,8 @@ def render_detection_bounding_boxes(detections,
|
|
|
543
549
|
textalign (int, optional): TEXTALIGN_LEFT, TEXTALIGN_CENTER, or TEXTALIGN_RIGHT
|
|
544
550
|
vtextalign (int, optional): VTEXTALIGN_TOP or VTEXTALIGN_BOTTOM
|
|
545
551
|
label_font_size (float, optional): font size for labels
|
|
546
|
-
custom_strings: optional set of strings to append to detection labels, should
|
|
547
|
-
same length as [detections]. Appended before any classification labels.
|
|
552
|
+
custom_strings (list of str, optional): optional set of strings to append to detection labels, should
|
|
553
|
+
have the same length as [detections]. Appended before any classification labels.
|
|
548
554
|
box_sort_order (str, optional): sorting scheme for detection boxes, can be None, "confidence", or
|
|
549
555
|
"reverse_confidence".
|
|
550
556
|
verbose (bool, optional): enable additional debug output
|
|
@@ -587,7 +593,7 @@ def render_detection_bounding_boxes(detections,
|
|
|
587
593
|
|
|
588
594
|
# Always render objects with a confidence of "None", this is typically used
|
|
589
595
|
# for ground truth data.
|
|
590
|
-
if score is None or score >= rendering_threshold:
|
|
596
|
+
if (score is None) or (rendering_threshold is None) or (score >= rendering_threshold):
|
|
591
597
|
|
|
592
598
|
x1, y1, w_box, h_box = detection['bbox']
|
|
593
599
|
display_boxes.append([y1, x1, y1 + h_box, x1 + w_box])
|
|
@@ -668,10 +674,15 @@ def render_detection_bounding_boxes(detections,
|
|
|
668
674
|
if verbose:
|
|
669
675
|
print('Rendering {} of {} detections'.format(len(display_boxes),len(detections)))
|
|
670
676
|
|
|
671
|
-
draw_bounding_boxes_on_image(image,
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
677
|
+
draw_bounding_boxes_on_image(image,
|
|
678
|
+
display_boxes,
|
|
679
|
+
classes,
|
|
680
|
+
display_strs=display_strs,
|
|
681
|
+
thickness=thickness,
|
|
682
|
+
expansion=expansion,
|
|
683
|
+
colormap=colormap,
|
|
684
|
+
textalign=textalign,
|
|
685
|
+
vtextalign=vtextalign,
|
|
675
686
|
label_font_size=label_font_size)
|
|
676
687
|
|
|
677
688
|
# ...render_detection_bounding_boxes(...)
|
|
@@ -869,7 +880,7 @@ def draw_bounding_box_on_image(image,
|
|
|
869
880
|
|
|
870
881
|
try:
|
|
871
882
|
font = ImageFont.truetype('arial.ttf', label_font_size)
|
|
872
|
-
except
|
|
883
|
+
except OSError:
|
|
873
884
|
font = ImageFont.load_default()
|
|
874
885
|
|
|
875
886
|
display_str_heights = [get_text_size(font,ds)[1] for ds in display_str_list]
|
|
@@ -1015,14 +1026,16 @@ def render_db_bounding_boxes(boxes,
|
|
|
1015
1026
|
vtextalign=VTEXTALIGN_TOP,
|
|
1016
1027
|
text_rotation=None,
|
|
1017
1028
|
label_font_size=DEFAULT_LABEL_FONT_SIZE,
|
|
1018
|
-
tags=None
|
|
1029
|
+
tags=None,
|
|
1030
|
+
boxes_are_normalized=False):
|
|
1019
1031
|
"""
|
|
1020
1032
|
Render bounding boxes (with class labels) on an image. This is a wrapper for
|
|
1021
1033
|
draw_bounding_boxes_on_image, allowing the caller to operate on a resized image
|
|
1022
1034
|
by providing the original size of the image; boxes will be scaled accordingly.
|
|
1023
1035
|
|
|
1024
1036
|
This function assumes that bounding boxes are in absolute coordinates, typically
|
|
1025
|
-
because they come from COCO camera traps .json files
|
|
1037
|
+
because they come from COCO camera traps .json files, unless boxes_are_normalized
|
|
1038
|
+
is True.
|
|
1026
1039
|
|
|
1027
1040
|
Args:
|
|
1028
1041
|
boxes (list): list of length-4 tuples, foramtted as (x,y,w,h) (in pixels)
|
|
@@ -1045,6 +1058,7 @@ def render_db_bounding_boxes(boxes,
|
|
|
1045
1058
|
label_font_size (float, optional): font size for labels
|
|
1046
1059
|
tags (list, optional): list of strings of length len(boxes) that should be appended
|
|
1047
1060
|
after each class name (e.g. to show scores)
|
|
1061
|
+
boxes_are_normalized (bool, optional): whether boxes have already been normalized
|
|
1048
1062
|
"""
|
|
1049
1063
|
|
|
1050
1064
|
display_boxes = []
|
|
@@ -1064,11 +1078,21 @@ def render_db_bounding_boxes(boxes,
|
|
|
1064
1078
|
|
|
1065
1079
|
x_min_abs, y_min_abs, width_abs, height_abs = box[0:4]
|
|
1066
1080
|
|
|
1067
|
-
|
|
1068
|
-
|
|
1081
|
+
# Normalize boxes if necessary
|
|
1082
|
+
if boxes_are_normalized:
|
|
1069
1083
|
|
|
1070
|
-
|
|
1071
|
-
|
|
1084
|
+
xmin = x_min_abs
|
|
1085
|
+
xmax = x_min_abs + width_abs
|
|
1086
|
+
ymin = y_min_abs
|
|
1087
|
+
ymax = y_min_abs + height_abs
|
|
1088
|
+
|
|
1089
|
+
else:
|
|
1090
|
+
|
|
1091
|
+
ymin = y_min_abs / img_height
|
|
1092
|
+
ymax = ymin + height_abs / img_height
|
|
1093
|
+
|
|
1094
|
+
xmin = x_min_abs / img_width
|
|
1095
|
+
xmax = xmin + width_abs / img_width
|
|
1072
1096
|
|
|
1073
1097
|
display_boxes.append([ymin, xmin, ymax, xmax])
|
|
1074
1098
|
|
|
@@ -1121,10 +1145,12 @@ def draw_bounding_boxes_on_file(input_file,
|
|
|
1121
1145
|
|
|
1122
1146
|
Args:
|
|
1123
1147
|
input_file (str): filename or URL to load
|
|
1124
|
-
output_file (str
|
|
1148
|
+
output_file (str): filename to which we should write the rendered image
|
|
1125
1149
|
detections (list): a list of dictionaries with keys 'conf', 'bbox', and 'category';
|
|
1126
1150
|
boxes are length-four arrays formatted as [x,y,w,h], normalized,
|
|
1127
1151
|
upper-left origin (this is the standard MD detection format). 'category' is a string-int.
|
|
1152
|
+
confidence_threshold (float, optional): only render detections with confidence above this
|
|
1153
|
+
threshold
|
|
1128
1154
|
detector_label_map (dict, optional): a dict mapping category IDs to strings. If this
|
|
1129
1155
|
is None, no confidence values or identifiers are shown. If this is {}, just category
|
|
1130
1156
|
indices and confidence values are shown.
|
|
@@ -1150,10 +1176,15 @@ def draw_bounding_boxes_on_file(input_file,
|
|
|
1150
1176
|
image = resize_image(image,target_size[0],target_size[1])
|
|
1151
1177
|
|
|
1152
1178
|
render_detection_bounding_boxes(
|
|
1153
|
-
detections,
|
|
1179
|
+
detections,
|
|
1180
|
+
image,
|
|
1181
|
+
label_map=detector_label_map,
|
|
1154
1182
|
confidence_threshold=confidence_threshold,
|
|
1155
|
-
thickness=thickness,
|
|
1156
|
-
|
|
1183
|
+
thickness=thickness,
|
|
1184
|
+
expansion=expansion,
|
|
1185
|
+
colormap=colormap,
|
|
1186
|
+
custom_strings=custom_strings,
|
|
1187
|
+
label_font_size=label_font_size)
|
|
1157
1188
|
|
|
1158
1189
|
if output_file is not None:
|
|
1159
1190
|
image.save(output_file)
|
|
@@ -1196,8 +1227,13 @@ def draw_db_boxes_on_file(input_file,
|
|
|
1196
1227
|
if classes is None:
|
|
1197
1228
|
classes = [0] * len(boxes)
|
|
1198
1229
|
|
|
1199
|
-
render_db_bounding_boxes(boxes,
|
|
1200
|
-
|
|
1230
|
+
render_db_bounding_boxes(boxes,
|
|
1231
|
+
classes,
|
|
1232
|
+
image,
|
|
1233
|
+
original_size=None,
|
|
1234
|
+
label_map=label_map,
|
|
1235
|
+
thickness=thickness,
|
|
1236
|
+
expansion=expansion)
|
|
1201
1237
|
|
|
1202
1238
|
image.save(output_file)
|
|
1203
1239
|
|
|
@@ -1215,8 +1251,8 @@ def gray_scale_fraction(image,crop_size=(0.1,0.1)):
|
|
|
1215
1251
|
|
|
1216
1252
|
Args:
|
|
1217
1253
|
image (str or PIL.Image.Image): Image, filename, or URL to analyze
|
|
1218
|
-
crop_size (optional): a 2-element list/tuple, representing the fraction of
|
|
1219
|
-
image to crop at the top and bottom, respectively, before analyzing (to minimize
|
|
1254
|
+
crop_size (tuple of floats, optional): a 2-element list/tuple, representing the fraction of
|
|
1255
|
+
the image to crop at the top and bottom, respectively, before analyzing (to minimize
|
|
1220
1256
|
the possibility of including color elements in the image overlay)
|
|
1221
1257
|
|
|
1222
1258
|
Returns:
|
|
@@ -1283,14 +1319,14 @@ def gray_scale_fraction(image,crop_size=(0.1,0.1)):
|
|
|
1283
1319
|
|
|
1284
1320
|
|
|
1285
1321
|
def _resize_relative_image(fn_relative,
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1322
|
+
input_folder,
|
|
1323
|
+
output_folder,
|
|
1324
|
+
target_width,
|
|
1325
|
+
target_height,
|
|
1326
|
+
no_enlarge_width,
|
|
1327
|
+
verbose,
|
|
1328
|
+
quality,
|
|
1329
|
+
overwrite=True):
|
|
1294
1330
|
"""
|
|
1295
1331
|
Internal function for resizing an image from one folder to another,
|
|
1296
1332
|
maintaining relative path.
|
|
@@ -1308,8 +1344,11 @@ def _resize_relative_image(fn_relative,
|
|
|
1308
1344
|
try:
|
|
1309
1345
|
_ = resize_image(input_fn_abs,
|
|
1310
1346
|
output_file=output_fn_abs,
|
|
1311
|
-
target_width=target_width,
|
|
1312
|
-
|
|
1347
|
+
target_width=target_width,
|
|
1348
|
+
target_height=target_height,
|
|
1349
|
+
no_enlarge_width=no_enlarge_width,
|
|
1350
|
+
verbose=verbose,
|
|
1351
|
+
quality=quality)
|
|
1313
1352
|
status = 'success'
|
|
1314
1353
|
error = None
|
|
1315
1354
|
except Exception as e:
|
|
@@ -1324,7 +1363,11 @@ def _resize_relative_image(fn_relative,
|
|
|
1324
1363
|
|
|
1325
1364
|
|
|
1326
1365
|
def _resize_absolute_image(input_output_files,
|
|
1327
|
-
|
|
1366
|
+
target_width,
|
|
1367
|
+
target_height,
|
|
1368
|
+
no_enlarge_width,
|
|
1369
|
+
verbose,
|
|
1370
|
+
quality):
|
|
1328
1371
|
"""
|
|
1329
1372
|
Internal wrapper for resize_image used in the context of a batch resize operation.
|
|
1330
1373
|
"""
|
|
@@ -1335,8 +1378,11 @@ def _resize_absolute_image(input_output_files,
|
|
|
1335
1378
|
try:
|
|
1336
1379
|
_ = resize_image(input_fn_abs,
|
|
1337
1380
|
output_file=output_fn_abs,
|
|
1338
|
-
target_width=target_width,
|
|
1339
|
-
|
|
1381
|
+
target_width=target_width,
|
|
1382
|
+
target_height=target_height,
|
|
1383
|
+
no_enlarge_width=no_enlarge_width,
|
|
1384
|
+
verbose=verbose,
|
|
1385
|
+
quality=quality)
|
|
1340
1386
|
status = 'success'
|
|
1341
1387
|
error = None
|
|
1342
1388
|
except Exception as e:
|
|
@@ -1544,7 +1590,8 @@ def resize_image_folder(input_folder,
|
|
|
1544
1590
|
quality=quality,
|
|
1545
1591
|
overwrite=overwrite)
|
|
1546
1592
|
|
|
1547
|
-
results = list(tqdm(pool.imap(p, image_files_relative),
|
|
1593
|
+
results = list(tqdm(pool.imap(p, image_files_relative),
|
|
1594
|
+
total=len(image_files_relative)))
|
|
1548
1595
|
|
|
1549
1596
|
return results
|
|
1550
1597
|
|
|
@@ -1557,6 +1604,7 @@ def get_image_size(im,verbose=False):
|
|
|
1557
1604
|
|
|
1558
1605
|
Args:
|
|
1559
1606
|
im (str or PIL.Image): filename or PIL image
|
|
1607
|
+
verbose (bool, optional): enable additional debug output
|
|
1560
1608
|
|
|
1561
1609
|
Returns:
|
|
1562
1610
|
tuple (w,h), or None if the image fails to load.
|
|
@@ -1600,13 +1648,13 @@ def parallel_get_image_sizes(filenames,
|
|
|
1600
1648
|
parallelization
|
|
1601
1649
|
use_threads (bool, optional): whether to use threads (True) or processes (False) for
|
|
1602
1650
|
parallelization
|
|
1603
|
-
recursive (bool, optional): if [filenames] is a folder, whether to search recursively
|
|
1604
|
-
Ignored if [filenames] is a list.
|
|
1651
|
+
recursive (bool, optional): if [filenames] is a folder, whether to search recursively
|
|
1652
|
+
for images. Ignored if [filenames] is a list.
|
|
1605
1653
|
verbose (bool, optional): enable additional debug output
|
|
1606
1654
|
|
|
1607
1655
|
Returns:
|
|
1608
1656
|
dict: a dict mapping filenames to (w,h) tuples; the value will be None for images that fail
|
|
1609
|
-
to load.
|
|
1657
|
+
to load. Filenames will always be absolute.
|
|
1610
1658
|
"""
|
|
1611
1659
|
|
|
1612
1660
|
if isinstance(filenames,str) and os.path.isdir(filenames):
|
|
@@ -1707,7 +1755,7 @@ def check_image_integrity(filename,modes=None):
|
|
|
1707
1755
|
elif mode == 'skimage':
|
|
1708
1756
|
try:
|
|
1709
1757
|
# This is not a standard dependency
|
|
1710
|
-
from skimage import io as skimage_io # noqa
|
|
1758
|
+
from skimage import io as skimage_io # type: ignore # noqa
|
|
1711
1759
|
except Exception:
|
|
1712
1760
|
result[mode] = 'could not import skimage, run pip install scikit-image'
|
|
1713
1761
|
return result
|
|
@@ -1747,7 +1795,7 @@ def parallel_check_image_integrity(filenames,
|
|
|
1747
1795
|
|
|
1748
1796
|
Args:
|
|
1749
1797
|
filenames (list or str): a list of image filenames or a folder
|
|
1750
|
-
|
|
1798
|
+
modes (list, optional): see check_image_integrity() for documentation on the [modes] parameter
|
|
1751
1799
|
max_workers (int, optional): the number of parallel workers to use; set to <=1 to disable
|
|
1752
1800
|
parallelization
|
|
1753
1801
|
use_threads (bool, optional): whether to use threads (True) or processes (False) for
|
|
@@ -162,6 +162,7 @@ def visualize_db(db_path, output_dir, image_base_dir, options=None):
|
|
|
162
162
|
|
|
163
163
|
Args:
|
|
164
164
|
db_path (str or dict): the .json filename to load, or a previously-loaded database
|
|
165
|
+
output_dir (str): the folder to which we should write annotated images
|
|
165
166
|
image_base_dir (str): the folder where the images live; filenames in [db_path] should
|
|
166
167
|
be relative to this folder.
|
|
167
168
|
options (DbVizOptions, optional): See DbVizOptions for details
|
|
@@ -211,8 +212,11 @@ def visualize_db(db_path, output_dir, image_base_dir, options=None):
|
|
|
211
212
|
|
|
212
213
|
b_has_bbox = [False] * len(annotations)
|
|
213
214
|
for i_ann,ann in enumerate(annotations):
|
|
214
|
-
if 'bbox' in ann:
|
|
215
|
-
|
|
215
|
+
if 'bbox' in ann or 'bbox_relative' in ann:
|
|
216
|
+
if 'bbox' in ann:
|
|
217
|
+
assert isinstance(ann['bbox'],list)
|
|
218
|
+
else:
|
|
219
|
+
assert isinstance(ann['bbox_relative'],list)
|
|
216
220
|
b_has_bbox[i_ann] = True
|
|
217
221
|
annotations_with_boxes = list(compress(annotations, b_has_bbox))
|
|
218
222
|
|
|
@@ -323,6 +327,9 @@ def visualize_db(db_path, output_dir, image_base_dir, options=None):
|
|
|
323
327
|
extra_annotation_field_string = ''
|
|
324
328
|
annotation_level_for_image = ''
|
|
325
329
|
|
|
330
|
+
# Did this image come with already-normalized bounding boxes?
|
|
331
|
+
boxes_are_normalized = None
|
|
332
|
+
|
|
326
333
|
# Iterate over annotations for this image
|
|
327
334
|
# i_ann = 0; anno = annos_i.iloc[i_ann]
|
|
328
335
|
for i_ann,anno in annos_i.iterrows():
|
|
@@ -363,8 +370,22 @@ def visualize_db(db_path, output_dir, image_base_dir, options=None):
|
|
|
363
370
|
category_name,category_name)
|
|
364
371
|
image_categories.add(category_name)
|
|
365
372
|
|
|
366
|
-
|
|
367
|
-
|
|
373
|
+
assert not ('bbox' in anno and 'bbox_relative' in anno), \
|
|
374
|
+
"An annotation can't have both an absolute and a relative bounding box"
|
|
375
|
+
|
|
376
|
+
box_field = 'bbox'
|
|
377
|
+
if 'bbox_relative' in anno:
|
|
378
|
+
box_field = 'bbox_relative'
|
|
379
|
+
assert (boxes_are_normalized is None) or (boxes_are_normalized), \
|
|
380
|
+
"An image can't have both absolute and relative bounding boxes"
|
|
381
|
+
boxes_are_normalized = True
|
|
382
|
+
elif 'bbox' in anno:
|
|
383
|
+
assert (boxes_are_normalized is None) or (not boxes_are_normalized), \
|
|
384
|
+
"An image can't have both absolute and relative bounding boxes"
|
|
385
|
+
boxes_are_normalized = False
|
|
386
|
+
|
|
387
|
+
if box_field in anno:
|
|
388
|
+
bbox = anno[box_field]
|
|
368
389
|
if isinstance(bbox,float):
|
|
369
390
|
assert math.isnan(bbox), "I shouldn't see a bbox that's neither a box nor NaN"
|
|
370
391
|
continue
|
|
@@ -394,7 +415,8 @@ def visualize_db(db_path, output_dir, image_base_dir, options=None):
|
|
|
394
415
|
'box_classes':box_classes,
|
|
395
416
|
'tags':box_score_strings,
|
|
396
417
|
'img_path':img_path,
|
|
397
|
-
'output_file_name':file_name
|
|
418
|
+
'output_file_name':file_name,
|
|
419
|
+
'boxes_are_normalized':boxes_are_normalized}
|
|
398
420
|
rendering_info.append(rendering_info_this_image)
|
|
399
421
|
|
|
400
422
|
label_level_string = ''
|
|
@@ -454,6 +476,7 @@ def visualize_db(db_path, output_dir, image_base_dir, options=None):
|
|
|
454
476
|
img_path = rendering_info['img_path']
|
|
455
477
|
bboxes = rendering_info['bboxes']
|
|
456
478
|
bbox_classes = rendering_info['box_classes']
|
|
479
|
+
boxes_are_normalized = rendering_info['boxes_are_normalized']
|
|
457
480
|
bbox_tags = None
|
|
458
481
|
if 'tags' in rendering_info:
|
|
459
482
|
bbox_tags = rendering_info['tags']
|
|
@@ -473,11 +496,14 @@ def visualize_db(db_path, output_dir, image_base_dir, options=None):
|
|
|
473
496
|
try:
|
|
474
497
|
original_image = vis_utils.open_image(img_path)
|
|
475
498
|
original_size = original_image.size
|
|
476
|
-
if (options.viz_size is None) or
|
|
499
|
+
if (options.viz_size is None) or \
|
|
500
|
+
(options.viz_size[0] == -1 and options.viz_size[1] == -1):
|
|
477
501
|
image = original_image
|
|
478
502
|
else:
|
|
479
|
-
image = vis_utils.resize_image(original_image,
|
|
480
|
-
options.viz_size[
|
|
503
|
+
image = vis_utils.resize_image(original_image,
|
|
504
|
+
options.viz_size[0],
|
|
505
|
+
options.viz_size[1],
|
|
506
|
+
no_enlarge_width=True)
|
|
481
507
|
except Exception as e:
|
|
482
508
|
print('Image {} failed to open, error: {}'.format(img_path, e))
|
|
483
509
|
return False
|
|
@@ -489,7 +515,8 @@ def visualize_db(db_path, output_dir, image_base_dir, options=None):
|
|
|
489
515
|
label_map=label_map,
|
|
490
516
|
thickness=options.box_thickness,
|
|
491
517
|
expansion=options.box_expansion,
|
|
492
|
-
tags=bbox_tags
|
|
518
|
+
tags=bbox_tags,
|
|
519
|
+
boxes_are_normalized=boxes_are_normalized)
|
|
493
520
|
|
|
494
521
|
image.save(output_full_path)
|
|
495
522
|
|
|
@@ -161,7 +161,7 @@ def visualize_detector_output(detector_output_path,
|
|
|
161
161
|
Args:
|
|
162
162
|
detector_output_path (str): path to detector output .json file
|
|
163
163
|
out_dir (str): path to directory for saving annotated images
|
|
164
|
-
images_dir (str): folder where the images live; filenames in
|
|
164
|
+
images_dir (str, optional): folder where the images live; filenames in
|
|
165
165
|
[detector_output_path] should be relative to [image_dir]. Can be None if paths are
|
|
166
166
|
absolute.
|
|
167
167
|
confidence_threshold (float, optional): threshold above which detections will be rendered
|
|
@@ -169,9 +169,9 @@ def visualize_detector_output(detector_output_path,
|
|
|
169
169
|
output_image_width (int, optional): width in pixels to resize images for display,
|
|
170
170
|
preserving aspect ration; set to -1 to use original image width
|
|
171
171
|
random_seed (int, optional): seed to use for choosing images when sample != -1
|
|
172
|
-
render_detections_only (bool): only render images with above-threshold detections.
|
|
173
|
-
images are discarded after sampling, so if you want to see, e.g., 1000 non-empty
|
|
174
|
-
you can set [render_detections_only], but you need to sample more than 1000 images.
|
|
172
|
+
render_detections_only (bool, optional): only render images with above-threshold detections.
|
|
173
|
+
Empty images are discarded after sampling, so if you want to see, e.g., 1000 non-empty
|
|
174
|
+
images, you can set [render_detections_only], but you need to sample more than 1000 images.
|
|
175
175
|
classification_confidence_threshold (float, optional): only show classifications
|
|
176
176
|
above this threshold; does not impact whether images are rendered, only whether
|
|
177
177
|
classification labels (not detection categories) are displayed
|