megadetector 5.0.29__py3-none-any.whl → 10.0.1__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.

Files changed (95) hide show
  1. megadetector/classification/efficientnet/model.py +8 -8
  2. megadetector/classification/efficientnet/utils.py +6 -5
  3. megadetector/classification/prepare_classification_script_mc.py +3 -3
  4. megadetector/data_management/annotations/annotation_constants.py +0 -1
  5. megadetector/data_management/camtrap_dp_to_coco.py +34 -1
  6. megadetector/data_management/cct_json_utils.py +2 -2
  7. megadetector/data_management/coco_to_yolo.py +22 -5
  8. megadetector/data_management/databases/add_width_and_height_to_db.py +85 -12
  9. megadetector/data_management/databases/combine_coco_camera_traps_files.py +2 -2
  10. megadetector/data_management/databases/integrity_check_json_db.py +29 -15
  11. megadetector/data_management/generate_crops_from_cct.py +50 -1
  12. megadetector/data_management/labelme_to_coco.py +4 -2
  13. megadetector/data_management/labelme_to_yolo.py +82 -2
  14. megadetector/data_management/lila/generate_lila_per_image_labels.py +276 -18
  15. megadetector/data_management/lila/get_lila_annotation_counts.py +5 -3
  16. megadetector/data_management/lila/lila_common.py +3 -0
  17. megadetector/data_management/lila/test_lila_metadata_urls.py +15 -5
  18. megadetector/data_management/mewc_to_md.py +5 -0
  19. megadetector/data_management/ocr_tools.py +4 -3
  20. megadetector/data_management/read_exif.py +20 -5
  21. megadetector/data_management/remap_coco_categories.py +66 -4
  22. megadetector/data_management/remove_exif.py +50 -1
  23. megadetector/data_management/rename_images.py +3 -3
  24. megadetector/data_management/resize_coco_dataset.py +563 -95
  25. megadetector/data_management/yolo_output_to_md_output.py +131 -2
  26. megadetector/data_management/yolo_to_coco.py +140 -5
  27. megadetector/detection/change_detection.py +4 -3
  28. megadetector/detection/pytorch_detector.py +60 -22
  29. megadetector/detection/run_detector.py +225 -25
  30. megadetector/detection/run_detector_batch.py +42 -16
  31. megadetector/detection/run_inference_with_yolov5_val.py +12 -2
  32. megadetector/detection/run_tiled_inference.py +1 -0
  33. megadetector/detection/video_utils.py +53 -24
  34. megadetector/postprocessing/add_max_conf.py +4 -0
  35. megadetector/postprocessing/categorize_detections_by_size.py +1 -1
  36. megadetector/postprocessing/classification_postprocessing.py +55 -20
  37. megadetector/postprocessing/combine_batch_outputs.py +3 -2
  38. megadetector/postprocessing/compare_batch_results.py +64 -10
  39. megadetector/postprocessing/convert_output_format.py +12 -8
  40. megadetector/postprocessing/create_crop_folder.py +137 -10
  41. megadetector/postprocessing/load_api_results.py +26 -8
  42. megadetector/postprocessing/md_to_coco.py +4 -4
  43. megadetector/postprocessing/md_to_labelme.py +18 -7
  44. megadetector/postprocessing/merge_detections.py +5 -0
  45. megadetector/postprocessing/postprocess_batch_results.py +6 -3
  46. megadetector/postprocessing/remap_detection_categories.py +55 -2
  47. megadetector/postprocessing/render_detection_confusion_matrix.py +9 -6
  48. megadetector/postprocessing/repeat_detection_elimination/repeat_detections_core.py +2 -2
  49. megadetector/taxonomy_mapping/map_new_lila_datasets.py +3 -4
  50. megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +40 -19
  51. megadetector/taxonomy_mapping/preview_lila_taxonomy.py +1 -1
  52. megadetector/taxonomy_mapping/species_lookup.py +123 -41
  53. megadetector/utils/ct_utils.py +133 -113
  54. megadetector/utils/md_tests.py +93 -13
  55. megadetector/utils/path_utils.py +137 -107
  56. megadetector/utils/split_locations_into_train_val.py +2 -2
  57. megadetector/utils/string_utils.py +7 -7
  58. megadetector/utils/url_utils.py +81 -58
  59. megadetector/utils/wi_utils.py +46 -17
  60. megadetector/visualization/plot_utils.py +13 -9
  61. megadetector/visualization/render_images_with_thumbnails.py +2 -1
  62. megadetector/visualization/visualization_utils.py +94 -46
  63. megadetector/visualization/visualize_db.py +36 -9
  64. megadetector/visualization/visualize_detector_output.py +4 -4
  65. {megadetector-5.0.29.dist-info → megadetector-10.0.1.dist-info}/METADATA +135 -135
  66. megadetector-10.0.1.dist-info/RECORD +139 -0
  67. {megadetector-5.0.29.dist-info → megadetector-10.0.1.dist-info}/licenses/LICENSE +0 -0
  68. {megadetector-5.0.29.dist-info → megadetector-10.0.1.dist-info}/top_level.txt +0 -0
  69. megadetector/api/batch_processing/api_core/__init__.py +0 -0
  70. megadetector/api/batch_processing/api_core/batch_service/__init__.py +0 -0
  71. megadetector/api/batch_processing/api_core/batch_service/score.py +0 -438
  72. megadetector/api/batch_processing/api_core/server.py +0 -294
  73. megadetector/api/batch_processing/api_core/server_api_config.py +0 -97
  74. megadetector/api/batch_processing/api_core/server_app_config.py +0 -55
  75. megadetector/api/batch_processing/api_core/server_batch_job_manager.py +0 -220
  76. megadetector/api/batch_processing/api_core/server_job_status_table.py +0 -149
  77. megadetector/api/batch_processing/api_core/server_orchestration.py +0 -360
  78. megadetector/api/batch_processing/api_core/server_utils.py +0 -88
  79. megadetector/api/batch_processing/api_core_support/__init__.py +0 -0
  80. megadetector/api/batch_processing/api_core_support/aggregate_results_manually.py +0 -46
  81. megadetector/api/batch_processing/api_support/__init__.py +0 -0
  82. megadetector/api/batch_processing/api_support/summarize_daily_activity.py +0 -152
  83. megadetector/api/batch_processing/data_preparation/__init__.py +0 -0
  84. megadetector/api/synchronous/__init__.py +0 -0
  85. megadetector/api/synchronous/api_core/animal_detection_api/__init__.py +0 -0
  86. megadetector/api/synchronous/api_core/animal_detection_api/api_backend.py +0 -151
  87. megadetector/api/synchronous/api_core/animal_detection_api/api_frontend.py +0 -263
  88. megadetector/api/synchronous/api_core/animal_detection_api/config.py +0 -35
  89. megadetector/api/synchronous/api_core/tests/__init__.py +0 -0
  90. megadetector/api/synchronous/api_core/tests/load_test.py +0 -109
  91. megadetector/utils/azure_utils.py +0 -178
  92. megadetector/utils/sas_blob_utils.py +0 -513
  93. megadetector-5.0.29.dist-info/RECORD +0 -163
  94. /megadetector/{api/batch_processing/__init__.py → __init__.py} +0 -0
  95. {megadetector-5.0.29.dist-info → megadetector-10.0.1.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, target_width=-1, target_height=-1, output_file=None,
251
- no_enlarge_width=False, verbose=False, quality='keep'):
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), threshold above which boxes are rendered. Can also be a
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 have the
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, display_boxes, classes,
672
- display_strs=display_strs, thickness=thickness,
673
- expansion=expansion, colormap=colormap,
674
- textalign=textalign, vtextalign=vtextalign,
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 IOError:
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
- ymin = y_min_abs / img_height
1068
- ymax = ymin + height_abs / img_height
1081
+ # Normalize boxes if necessary
1082
+ if boxes_are_normalized:
1069
1083
 
1070
- xmin = x_min_abs / img_width
1071
- xmax = xmin + width_abs / img_width
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, optional): filename to which we should write the rendered image
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, image, label_map=detector_label_map,
1179
+ detections,
1180
+ image,
1181
+ label_map=detector_label_map,
1154
1182
  confidence_threshold=confidence_threshold,
1155
- thickness=thickness,expansion=expansion,colormap=colormap,
1156
- custom_strings=custom_strings,label_font_size=label_font_size)
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, classes, image, original_size=None,
1200
- label_map=label_map, thickness=thickness, expansion=expansion)
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 the
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
- input_folder,
1287
- output_folder,
1288
- target_width,
1289
- target_height,
1290
- no_enlarge_width,
1291
- verbose,
1292
- quality,
1293
- overwrite=True):
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, target_height=target_height,
1312
- no_enlarge_width=no_enlarge_width, verbose=verbose, quality=quality)
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
- target_width,target_height,no_enlarge_width,verbose,quality):
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, target_height=target_height,
1339
- no_enlarge_width=no_enlarge_width, verbose=verbose, quality=quality)
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),total=len(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 for images.
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
- mode (list): see check_image_integrity() for documentation on the [modes] parameter
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
- assert isinstance(ann['bbox'],list)
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
- if 'bbox' in anno:
367
- bbox = anno['bbox']
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 (options.viz_size[0] == -1 and options.viz_size[1] == -1):
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, options.viz_size[0],
480
- options.viz_size[1])
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. Empty
173
- images are discarded after sampling, so if you want to see, e.g., 1000 non-empty images,
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