megadetector 5.0.28__py3-none-any.whl → 5.0.29__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/api/batch_processing/api_core/batch_service/score.py +4 -5
- megadetector/api/batch_processing/api_core_support/aggregate_results_manually.py +1 -1
- megadetector/api/batch_processing/api_support/summarize_daily_activity.py +1 -1
- megadetector/api/batch_processing/integration/digiKam/xmp_integration.py +2 -2
- megadetector/api/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +1 -1
- megadetector/api/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +1 -1
- megadetector/api/synchronous/api_core/tests/load_test.py +2 -3
- megadetector/classification/aggregate_classifier_probs.py +3 -3
- megadetector/classification/analyze_failed_images.py +5 -5
- megadetector/classification/cache_batchapi_outputs.py +5 -5
- megadetector/classification/create_classification_dataset.py +11 -12
- megadetector/classification/crop_detections.py +10 -10
- megadetector/classification/csv_to_json.py +8 -8
- megadetector/classification/detect_and_crop.py +13 -15
- megadetector/classification/evaluate_model.py +7 -7
- megadetector/classification/identify_mislabeled_candidates.py +6 -6
- megadetector/classification/json_to_azcopy_list.py +1 -1
- megadetector/classification/json_validator.py +29 -32
- megadetector/classification/map_classification_categories.py +9 -9
- megadetector/classification/merge_classification_detection_output.py +12 -9
- megadetector/classification/prepare_classification_script.py +19 -19
- megadetector/classification/prepare_classification_script_mc.py +23 -23
- megadetector/classification/run_classifier.py +4 -4
- megadetector/classification/save_mislabeled.py +6 -6
- megadetector/classification/train_classifier.py +1 -1
- megadetector/classification/train_classifier_tf.py +9 -9
- megadetector/classification/train_utils.py +10 -10
- megadetector/data_management/annotations/annotation_constants.py +1 -1
- megadetector/data_management/camtrap_dp_to_coco.py +45 -45
- megadetector/data_management/cct_json_utils.py +101 -101
- megadetector/data_management/cct_to_md.py +49 -49
- megadetector/data_management/cct_to_wi.py +33 -33
- megadetector/data_management/coco_to_labelme.py +75 -75
- megadetector/data_management/coco_to_yolo.py +189 -189
- megadetector/data_management/databases/add_width_and_height_to_db.py +3 -2
- megadetector/data_management/databases/combine_coco_camera_traps_files.py +38 -38
- megadetector/data_management/databases/integrity_check_json_db.py +202 -188
- megadetector/data_management/databases/subset_json_db.py +33 -33
- megadetector/data_management/generate_crops_from_cct.py +38 -38
- megadetector/data_management/get_image_sizes.py +54 -49
- megadetector/data_management/labelme_to_coco.py +130 -124
- megadetector/data_management/labelme_to_yolo.py +78 -72
- megadetector/data_management/lila/create_lila_blank_set.py +81 -83
- megadetector/data_management/lila/create_lila_test_set.py +32 -31
- megadetector/data_management/lila/create_links_to_md_results_files.py +18 -18
- megadetector/data_management/lila/download_lila_subset.py +21 -24
- megadetector/data_management/lila/generate_lila_per_image_labels.py +91 -91
- megadetector/data_management/lila/get_lila_annotation_counts.py +30 -30
- megadetector/data_management/lila/get_lila_image_counts.py +22 -22
- megadetector/data_management/lila/lila_common.py +70 -70
- megadetector/data_management/lila/test_lila_metadata_urls.py +13 -14
- megadetector/data_management/mewc_to_md.py +339 -340
- megadetector/data_management/ocr_tools.py +258 -252
- megadetector/data_management/read_exif.py +231 -224
- megadetector/data_management/remap_coco_categories.py +26 -26
- megadetector/data_management/remove_exif.py +31 -20
- megadetector/data_management/rename_images.py +187 -187
- megadetector/data_management/resize_coco_dataset.py +41 -41
- megadetector/data_management/speciesnet_to_md.py +41 -41
- megadetector/data_management/wi_download_csv_to_coco.py +55 -55
- megadetector/data_management/yolo_output_to_md_output.py +117 -120
- megadetector/data_management/yolo_to_coco.py +195 -188
- megadetector/detection/change_detection.py +831 -0
- megadetector/detection/process_video.py +340 -337
- megadetector/detection/pytorch_detector.py +304 -262
- megadetector/detection/run_detector.py +177 -164
- megadetector/detection/run_detector_batch.py +364 -363
- megadetector/detection/run_inference_with_yolov5_val.py +328 -325
- megadetector/detection/run_tiled_inference.py +256 -249
- megadetector/detection/tf_detector.py +24 -24
- megadetector/detection/video_utils.py +290 -282
- megadetector/postprocessing/add_max_conf.py +15 -11
- megadetector/postprocessing/categorize_detections_by_size.py +44 -44
- megadetector/postprocessing/classification_postprocessing.py +415 -415
- megadetector/postprocessing/combine_batch_outputs.py +20 -21
- megadetector/postprocessing/compare_batch_results.py +528 -517
- megadetector/postprocessing/convert_output_format.py +97 -97
- megadetector/postprocessing/create_crop_folder.py +219 -146
- megadetector/postprocessing/detector_calibration.py +173 -168
- megadetector/postprocessing/generate_csv_report.py +508 -499
- megadetector/postprocessing/load_api_results.py +23 -20
- megadetector/postprocessing/md_to_coco.py +129 -98
- megadetector/postprocessing/md_to_labelme.py +89 -83
- megadetector/postprocessing/md_to_wi.py +40 -40
- megadetector/postprocessing/merge_detections.py +87 -114
- megadetector/postprocessing/postprocess_batch_results.py +313 -298
- megadetector/postprocessing/remap_detection_categories.py +36 -36
- megadetector/postprocessing/render_detection_confusion_matrix.py +205 -199
- megadetector/postprocessing/repeat_detection_elimination/find_repeat_detections.py +57 -57
- megadetector/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +27 -28
- megadetector/postprocessing/repeat_detection_elimination/repeat_detections_core.py +702 -677
- megadetector/postprocessing/separate_detections_into_folders.py +226 -211
- megadetector/postprocessing/subset_json_detector_output.py +265 -262
- megadetector/postprocessing/top_folders_to_bottom.py +45 -45
- megadetector/postprocessing/validate_batch_results.py +70 -70
- megadetector/taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +52 -52
- megadetector/taxonomy_mapping/map_new_lila_datasets.py +15 -15
- megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +14 -14
- megadetector/taxonomy_mapping/preview_lila_taxonomy.py +66 -66
- megadetector/taxonomy_mapping/retrieve_sample_image.py +16 -16
- megadetector/taxonomy_mapping/simple_image_download.py +8 -8
- megadetector/taxonomy_mapping/species_lookup.py +33 -33
- megadetector/taxonomy_mapping/taxonomy_csv_checker.py +14 -14
- megadetector/taxonomy_mapping/taxonomy_graph.py +10 -10
- megadetector/taxonomy_mapping/validate_lila_category_mappings.py +13 -13
- megadetector/utils/azure_utils.py +22 -22
- megadetector/utils/ct_utils.py +1018 -200
- megadetector/utils/directory_listing.py +21 -77
- megadetector/utils/gpu_test.py +22 -22
- megadetector/utils/md_tests.py +541 -518
- megadetector/utils/path_utils.py +1457 -398
- megadetector/utils/process_utils.py +41 -41
- megadetector/utils/sas_blob_utils.py +53 -49
- megadetector/utils/split_locations_into_train_val.py +61 -61
- megadetector/utils/string_utils.py +147 -26
- megadetector/utils/url_utils.py +463 -173
- megadetector/utils/wi_utils.py +2629 -2526
- megadetector/utils/write_html_image_list.py +137 -137
- megadetector/visualization/plot_utils.py +21 -21
- megadetector/visualization/render_images_with_thumbnails.py +37 -73
- megadetector/visualization/visualization_utils.py +401 -397
- megadetector/visualization/visualize_db.py +197 -190
- megadetector/visualization/visualize_detector_output.py +79 -73
- {megadetector-5.0.28.dist-info → megadetector-5.0.29.dist-info}/METADATA +135 -132
- megadetector-5.0.29.dist-info/RECORD +163 -0
- {megadetector-5.0.28.dist-info → megadetector-5.0.29.dist-info}/WHEEL +1 -1
- {megadetector-5.0.28.dist-info → megadetector-5.0.29.dist-info}/licenses/LICENSE +0 -0
- {megadetector-5.0.28.dist-info → megadetector-5.0.29.dist-info}/top_level.txt +0 -0
- megadetector/data_management/importers/add_nacti_sizes.py +0 -52
- megadetector/data_management/importers/add_timestamps_to_icct.py +0 -79
- megadetector/data_management/importers/animl_results_to_md_results.py +0 -158
- megadetector/data_management/importers/auckland_doc_test_to_json.py +0 -373
- megadetector/data_management/importers/auckland_doc_to_json.py +0 -201
- megadetector/data_management/importers/awc_to_json.py +0 -191
- megadetector/data_management/importers/bellevue_to_json.py +0 -272
- megadetector/data_management/importers/cacophony-thermal-importer.py +0 -793
- megadetector/data_management/importers/carrizo_shrubfree_2018.py +0 -269
- megadetector/data_management/importers/carrizo_trail_cam_2017.py +0 -289
- megadetector/data_management/importers/cct_field_adjustments.py +0 -58
- megadetector/data_management/importers/channel_islands_to_cct.py +0 -913
- megadetector/data_management/importers/eMammal/copy_and_unzip_emammal.py +0 -180
- megadetector/data_management/importers/eMammal/eMammal_helpers.py +0 -249
- megadetector/data_management/importers/eMammal/make_eMammal_json.py +0 -223
- megadetector/data_management/importers/ena24_to_json.py +0 -276
- megadetector/data_management/importers/filenames_to_json.py +0 -386
- megadetector/data_management/importers/helena_to_cct.py +0 -283
- megadetector/data_management/importers/idaho-camera-traps.py +0 -1407
- megadetector/data_management/importers/idfg_iwildcam_lila_prep.py +0 -294
- megadetector/data_management/importers/import_desert_lion_conservation_camera_traps.py +0 -387
- megadetector/data_management/importers/jb_csv_to_json.py +0 -150
- megadetector/data_management/importers/mcgill_to_json.py +0 -250
- megadetector/data_management/importers/missouri_to_json.py +0 -490
- megadetector/data_management/importers/nacti_fieldname_adjustments.py +0 -79
- megadetector/data_management/importers/noaa_seals_2019.py +0 -181
- megadetector/data_management/importers/osu-small-animals-to-json.py +0 -364
- megadetector/data_management/importers/pc_to_json.py +0 -365
- megadetector/data_management/importers/plot_wni_giraffes.py +0 -123
- megadetector/data_management/importers/prepare_zsl_imerit.py +0 -131
- megadetector/data_management/importers/raic_csv_to_md_results.py +0 -416
- megadetector/data_management/importers/rspb_to_json.py +0 -356
- megadetector/data_management/importers/save_the_elephants_survey_A.py +0 -320
- megadetector/data_management/importers/save_the_elephants_survey_B.py +0 -329
- megadetector/data_management/importers/snapshot_safari_importer.py +0 -758
- megadetector/data_management/importers/snapshot_serengeti_lila.py +0 -1067
- megadetector/data_management/importers/snapshotserengeti/make_full_SS_json.py +0 -150
- megadetector/data_management/importers/snapshotserengeti/make_per_season_SS_json.py +0 -153
- megadetector/data_management/importers/sulross_get_exif.py +0 -65
- megadetector/data_management/importers/timelapse_csv_set_to_json.py +0 -490
- megadetector/data_management/importers/ubc_to_json.py +0 -399
- megadetector/data_management/importers/umn_to_json.py +0 -507
- megadetector/data_management/importers/wellington_to_json.py +0 -263
- megadetector/data_management/importers/wi_to_json.py +0 -442
- megadetector/data_management/importers/zamba_results_to_md_results.py +0 -180
- megadetector/data_management/lila/add_locations_to_island_camera_traps.py +0 -101
- megadetector/data_management/lila/add_locations_to_nacti.py +0 -151
- megadetector-5.0.28.dist-info/RECORD +0 -209
|
@@ -1,416 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
raic_csv_to_md_results.py
|
|
4
|
-
|
|
5
|
-
Converts classification+detection results in the .csv format provided to the Snapshot
|
|
6
|
-
Serengeti program by the RAIC team to the MD results format.
|
|
7
|
-
|
|
8
|
-
The input format is two .csv files:
|
|
9
|
-
|
|
10
|
-
* One with results, with columns [unnamed], filename, category, x_center, y_center,
|
|
11
|
-
width, height, confidence, datetime
|
|
12
|
-
|
|
13
|
-
* One with class IDs and names, with columns CLASS, SPECIES
|
|
14
|
-
|
|
15
|
-
Filenames are relative paths to .txt files, but with slashes replaced by underscores, e.g. this
|
|
16
|
-
file:
|
|
17
|
-
|
|
18
|
-
B04_R1/I__00122.JPG
|
|
19
|
-
|
|
20
|
-
...appears in the .csv file as:
|
|
21
|
-
|
|
22
|
-
B04_R1_I__00122.txt
|
|
23
|
-
|
|
24
|
-
Image coordinates are in absolute floating-point units, with an upper-left origin.
|
|
25
|
-
|
|
26
|
-
Unknowns at the time I'm writing this:
|
|
27
|
-
|
|
28
|
-
* I don't know what the unnamed column is, but it looks like an ID I can safely ignore.
|
|
29
|
-
|
|
30
|
-
* I believe that MegaDetector was run, then a classifier was run, but there is a
|
|
31
|
-
single "confidence" column in the output. I am writing out the results as if they were a
|
|
32
|
-
single multi-class detector. This is suspicious given the lack of a human class, which suggests
|
|
33
|
-
that this is intended to be run in conjunection with MD.
|
|
34
|
-
|
|
35
|
-
* There is no concept of "empty" in this file format, so by default I assume that images with
|
|
36
|
-
no annotations in the .csv file were processed and determine to have no detections above some
|
|
37
|
-
(unknown) threshold.
|
|
38
|
-
|
|
39
|
-
* I'm not currently handling EXIF rotations, as part of the effort to simplify this file
|
|
40
|
-
for conversion to R (see below).
|
|
41
|
-
|
|
42
|
-
Note to self: this file should not take dependencies on other components of the MD
|
|
43
|
-
repo, at the risk of creating some redundancy. I am going to convert this to R,
|
|
44
|
-
which will be easier if it's not using any non-standard libraries. Anything in the
|
|
45
|
-
"interactive driver" cells gets a pass.
|
|
46
|
-
|
|
47
|
-
"""
|
|
48
|
-
|
|
49
|
-
#%% Imports and constants
|
|
50
|
-
|
|
51
|
-
import os
|
|
52
|
-
import glob
|
|
53
|
-
import json
|
|
54
|
-
import sys
|
|
55
|
-
import argparse
|
|
56
|
-
|
|
57
|
-
import pandas as pd
|
|
58
|
-
from PIL import Image
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
#%% Functions from the MD python package
|
|
62
|
-
|
|
63
|
-
# ...that I'm choosing to copy and paste to facilitate a conversion of this
|
|
64
|
-
# script to R.
|
|
65
|
-
|
|
66
|
-
# Should all be lower-case
|
|
67
|
-
IMG_EXTENSIONS = ('.jpg', '.jpeg', '.gif', '.png', '.tif', '.tiff', '.bmp')
|
|
68
|
-
|
|
69
|
-
def _is_image_file(s, img_extensions=IMG_EXTENSIONS):
|
|
70
|
-
"""
|
|
71
|
-
Checks a file's extension against a hard-coded set of image file
|
|
72
|
-
extensions. Uses case-insensitive comparison.
|
|
73
|
-
|
|
74
|
-
Does not check whether the file exists, only determines whether the filename
|
|
75
|
-
implies it's an image file.
|
|
76
|
-
|
|
77
|
-
Args:
|
|
78
|
-
s (str): filename to evaluate for image-ness
|
|
79
|
-
img_extensions (list, optional): list of known image file extensions
|
|
80
|
-
|
|
81
|
-
Returns:
|
|
82
|
-
bool: True if [s] appears to be an image file, else False
|
|
83
|
-
"""
|
|
84
|
-
|
|
85
|
-
ext = os.path.splitext(s)[1]
|
|
86
|
-
return ext.lower() in img_extensions
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def _find_image_strings(strings):
|
|
90
|
-
"""
|
|
91
|
-
Given a list of strings that are potentially image file names, looks for
|
|
92
|
-
strings that actually look like image file names (based on extension).
|
|
93
|
-
|
|
94
|
-
Args:
|
|
95
|
-
strings (list): list of filenames to check for image-ness
|
|
96
|
-
|
|
97
|
-
Returns:
|
|
98
|
-
list: the subset of [strings] that appear to be image filenames
|
|
99
|
-
"""
|
|
100
|
-
|
|
101
|
-
return [s for s in strings if _is_image_file(s)]
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def _find_images(dirname,
|
|
105
|
-
recursive=False,
|
|
106
|
-
return_relative_paths=False,
|
|
107
|
-
convert_slashes=True):
|
|
108
|
-
"""
|
|
109
|
-
Finds all files in a directory that look like image file names. Returns
|
|
110
|
-
absolute paths unless return_relative_paths is set. Uses the OS-native
|
|
111
|
-
path separator unless convert_slashes is set, in which case will always
|
|
112
|
-
use '/'.
|
|
113
|
-
|
|
114
|
-
Args:
|
|
115
|
-
dirname (str): the folder to search for images
|
|
116
|
-
recursive (bool, optional): whether to search recursively
|
|
117
|
-
return_relative_paths (str, optional): return paths that are relative
|
|
118
|
-
to [dirname], rather than absolute paths
|
|
119
|
-
convert_slashes (bool, optional): force forward slashes in return values
|
|
120
|
-
|
|
121
|
-
Returns:
|
|
122
|
-
list: list of image filenames found in [dirname]
|
|
123
|
-
"""
|
|
124
|
-
|
|
125
|
-
assert os.path.isdir(dirname), '{} is not a folder'.format(dirname)
|
|
126
|
-
|
|
127
|
-
if recursive:
|
|
128
|
-
strings = glob.glob(os.path.join(dirname, '**', '*.*'), recursive=True)
|
|
129
|
-
else:
|
|
130
|
-
strings = glob.glob(os.path.join(dirname, '*.*'))
|
|
131
|
-
|
|
132
|
-
image_files = _find_image_strings(strings)
|
|
133
|
-
|
|
134
|
-
if return_relative_paths:
|
|
135
|
-
image_files = [os.path.relpath(fn,dirname) for fn in image_files]
|
|
136
|
-
|
|
137
|
-
image_files = sorted(image_files)
|
|
138
|
-
|
|
139
|
-
if convert_slashes:
|
|
140
|
-
image_files = [fn.replace('\\', '/') for fn in image_files]
|
|
141
|
-
|
|
142
|
-
return image_files
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
#%% Main conversion function
|
|
146
|
-
|
|
147
|
-
def raic_csv_to_md_results(result_csv_file,
|
|
148
|
-
class_mapping_csv_file,
|
|
149
|
-
image_folder,
|
|
150
|
-
output_file=None,
|
|
151
|
-
unannotated_image_handling='empty'):
|
|
152
|
-
"""
|
|
153
|
-
Converts a pair of .csv files (see file header for details) to MD results format.
|
|
154
|
-
|
|
155
|
-
Currently errors if image filenames are ambiguous, or if any images referred to in
|
|
156
|
-
the results are not available.
|
|
157
|
-
|
|
158
|
-
Args:
|
|
159
|
-
result_csv_file (str): the results file to read (.csv)
|
|
160
|
-
class_mapping_csv_file (str): the class mapping file (.csv)
|
|
161
|
-
image_folder (str): the folder containing all the images referred to in
|
|
162
|
-
[result_csv_file]
|
|
163
|
-
output_file (str, optional): the .json file to which we should write results. Defaults
|
|
164
|
-
to [result_csv_file].json
|
|
165
|
-
unannotated_image_handling (str, optional): can be "empty" (default) to assume
|
|
166
|
-
that images without annotations are empty, "warning", "error", or "skip"
|
|
167
|
-
|
|
168
|
-
Returns:
|
|
169
|
-
str: the output file written, identical to [output_file] if [output_file] was not None
|
|
170
|
-
"""
|
|
171
|
-
|
|
172
|
-
# Validate arguments
|
|
173
|
-
assert os.path.isfile(result_csv_file), \
|
|
174
|
-
'Result file {} not found'.format(result_csv_file)
|
|
175
|
-
assert os.path.isfile(class_mapping_csv_file), \
|
|
176
|
-
'Class mapping file {} not found'.format(class_mapping_csv_file)
|
|
177
|
-
assert os.path.isdir(image_folder), \
|
|
178
|
-
'Image folder {} not found'.format(image_folder)
|
|
179
|
-
|
|
180
|
-
if output_file is None:
|
|
181
|
-
output_file = result_csv_file + '.json'
|
|
182
|
-
|
|
183
|
-
image_files_relative = _find_images(image_folder,
|
|
184
|
-
recursive=True,
|
|
185
|
-
return_relative_paths=True,
|
|
186
|
-
convert_slashes=True)
|
|
187
|
-
image_file_base_flattened_to_image_file_relative = {}
|
|
188
|
-
for fn in image_files_relative:
|
|
189
|
-
# Convert, e.g. B04_R1/I__00108.JPG to B04_R1_I__00108
|
|
190
|
-
fn_flattened = fn.replace('/','_')
|
|
191
|
-
fn_flattened_base = os.path.splitext(fn_flattened)[0]
|
|
192
|
-
image_file_base_flattened_to_image_file_relative[fn_flattened_base] = \
|
|
193
|
-
fn
|
|
194
|
-
|
|
195
|
-
# Read the .csv files
|
|
196
|
-
df_results = pd.read_csv(result_csv_file)
|
|
197
|
-
df_class_mapping = pd.read_csv(class_mapping_csv_file)
|
|
198
|
-
|
|
199
|
-
assert 'CLASS' in df_class_mapping.columns and 'SPECIES' in df_class_mapping.columns, \
|
|
200
|
-
'Unexpected column names in class mapping file {}'.format(class_mapping_csv_file)
|
|
201
|
-
|
|
202
|
-
category_id_to_name = {}
|
|
203
|
-
for i_row,row in df_class_mapping.iterrows():
|
|
204
|
-
class_id = int(row['CLASS'])
|
|
205
|
-
assert class_id not in category_id_to_name, \
|
|
206
|
-
'Class ID {} occurs more than once in class mapping file {}'.format(
|
|
207
|
-
class_id,class_mapping_csv_file)
|
|
208
|
-
category_id_to_name[class_id] = row['SPECIES']
|
|
209
|
-
|
|
210
|
-
if len(category_id_to_name) != len(set(category_id_to_name.values())):
|
|
211
|
-
print('Warning: one or more categories are used more than once in class mapping file {}'.format(
|
|
212
|
-
class_mapping_csv_file))
|
|
213
|
-
|
|
214
|
-
# Convert results
|
|
215
|
-
|
|
216
|
-
fn_relative_to_im = {}
|
|
217
|
-
|
|
218
|
-
# i_row = 0; row = df_results.iloc[i_row]
|
|
219
|
-
for i_row,row in df_results.iterrows():
|
|
220
|
-
|
|
221
|
-
# Map the .txt filename base to a relative path
|
|
222
|
-
bn = row['filename']
|
|
223
|
-
assert bn.lower().endswith('.txt')
|
|
224
|
-
bn_no_ext = os.path.splitext(bn)[0]
|
|
225
|
-
assert bn_no_ext in image_file_base_flattened_to_image_file_relative, \
|
|
226
|
-
'No image found for result row {}'.format(row['filename'])
|
|
227
|
-
|
|
228
|
-
image_fn_relative = image_file_base_flattened_to_image_file_relative[bn_no_ext]
|
|
229
|
-
|
|
230
|
-
# Have we seen another detection for this image?
|
|
231
|
-
if image_fn_relative in fn_relative_to_im:
|
|
232
|
-
|
|
233
|
-
im = fn_relative_to_im[image_fn_relative]
|
|
234
|
-
|
|
235
|
-
# If not, load this image so we can read its size
|
|
236
|
-
else:
|
|
237
|
-
|
|
238
|
-
image_fn_abs = os.path.join(image_folder,image_fn_relative)
|
|
239
|
-
image = Image.open(image_fn_abs)
|
|
240
|
-
w = image.size[0]
|
|
241
|
-
h = image.size[1]
|
|
242
|
-
|
|
243
|
-
im = {}
|
|
244
|
-
im['file'] = image_fn_relative
|
|
245
|
-
im['width'] = w
|
|
246
|
-
im['height'] = h
|
|
247
|
-
im['detections'] = []
|
|
248
|
-
im['datetime'] = str(row['datetime'])
|
|
249
|
-
fn_relative_to_im[image_fn_relative] = im
|
|
250
|
-
|
|
251
|
-
# Convert annotation
|
|
252
|
-
x_center_abs = row['x_center']
|
|
253
|
-
y_center_abs = row['y_center']
|
|
254
|
-
box_width_abs = row['width']
|
|
255
|
-
box_height_abs = row['height']
|
|
256
|
-
|
|
257
|
-
# Convert to relative coordinates
|
|
258
|
-
box_left_abs = x_center_abs - (box_width_abs/2.0)
|
|
259
|
-
box_top_abs = y_center_abs - (box_height_abs/2.0)
|
|
260
|
-
bbox_normalized = [box_left_abs/im['width'],
|
|
261
|
-
box_top_abs/im['height'],
|
|
262
|
-
box_width_abs/im['width'],
|
|
263
|
-
box_height_abs/im['height']]
|
|
264
|
-
|
|
265
|
-
category_id = str(int(row['category']))
|
|
266
|
-
confidence = row['confidence']
|
|
267
|
-
assert isinstance(confidence,float) and confidence <= 1.0 and confidence >= 0.0
|
|
268
|
-
|
|
269
|
-
det = {}
|
|
270
|
-
im['detections'].append(det)
|
|
271
|
-
det['category'] = category_id
|
|
272
|
-
det['conf'] = confidence
|
|
273
|
-
det['bbox'] = bbox_normalized
|
|
274
|
-
|
|
275
|
-
# ...for each row
|
|
276
|
-
|
|
277
|
-
n_empty_images = 0
|
|
278
|
-
|
|
279
|
-
# Handle images without annotations
|
|
280
|
-
for fn_relative in image_files_relative:
|
|
281
|
-
|
|
282
|
-
if fn_relative not in fn_relative_to_im:
|
|
283
|
-
if unannotated_image_handling == 'empty':
|
|
284
|
-
im = {}
|
|
285
|
-
im['file'] = fn_relative
|
|
286
|
-
im['detections'] = []
|
|
287
|
-
fn_relative_to_im[fn_relative] = im
|
|
288
|
-
n_empty_images += 1
|
|
289
|
-
# Don't bother to read width and height here
|
|
290
|
-
elif unannotated_image_handling == 'warning':
|
|
291
|
-
print('Warning: image {} is not represented in the .csv results file'.format(fn_relative))
|
|
292
|
-
elif unannotated_image_handling == 'error':
|
|
293
|
-
raise ValueError('Image {} is not represented in the .csv results file'.format(fn_relative))
|
|
294
|
-
elif unannotated_image_handling == 'skip':
|
|
295
|
-
continue
|
|
296
|
-
|
|
297
|
-
# ...for each image file
|
|
298
|
-
|
|
299
|
-
if n_empty_images > 0:
|
|
300
|
-
print('Warning: assuming {} of {} images without annotations are empty'.format(
|
|
301
|
-
n_empty_images,len(image_files_relative)))
|
|
302
|
-
|
|
303
|
-
images = list(fn_relative_to_im.values())
|
|
304
|
-
|
|
305
|
-
# The MD output format uses string-ints for category IDs, right now we have ints
|
|
306
|
-
detection_categories = {}
|
|
307
|
-
for category_id_int in category_id_to_name:
|
|
308
|
-
detection_categories[str(category_id_int)] = category_id_to_name[category_id_int]
|
|
309
|
-
|
|
310
|
-
info = {}
|
|
311
|
-
info['format_version'] = '1.4'
|
|
312
|
-
info['detector'] = 'RAIC .csv converter'
|
|
313
|
-
|
|
314
|
-
d = {}
|
|
315
|
-
d['images'] = images
|
|
316
|
-
d['detection_categories'] = detection_categories
|
|
317
|
-
d['info'] = info
|
|
318
|
-
|
|
319
|
-
with open(output_file,'w') as f:
|
|
320
|
-
json.dump(d,f,indent=1)
|
|
321
|
-
|
|
322
|
-
return output_file
|
|
323
|
-
|
|
324
|
-
# ...def raic_csv_to_md_results(...)
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
#%% Interactive driver
|
|
328
|
-
|
|
329
|
-
if False:
|
|
330
|
-
|
|
331
|
-
pass
|
|
332
|
-
|
|
333
|
-
#%% Test conversion
|
|
334
|
-
|
|
335
|
-
base_folder = r'G:\temp\S24_B04_R1_output_annotations_for_Dan'
|
|
336
|
-
result_csv_file = os.path.join(base_folder,'S24_B04_R1_output_annotations_for_Dan.csv')
|
|
337
|
-
class_mapping_csv_file = os.path.join(base_folder,'categories_key.csv')
|
|
338
|
-
|
|
339
|
-
# This is wrong, B04_R1 has to be part of the image paths
|
|
340
|
-
# image_folder = os.path.join(base_folder,'B04_R1')
|
|
341
|
-
|
|
342
|
-
image_folder = base_folder
|
|
343
|
-
|
|
344
|
-
output_file = None
|
|
345
|
-
unannotated_image_handling='empty'
|
|
346
|
-
|
|
347
|
-
output_file = raic_csv_to_md_results(result_csv_file=result_csv_file,
|
|
348
|
-
class_mapping_csv_file=class_mapping_csv_file,
|
|
349
|
-
image_folder=image_folder,
|
|
350
|
-
output_file=output_file,
|
|
351
|
-
unannotated_image_handling=unannotated_image_handling)
|
|
352
|
-
|
|
353
|
-
#%% Validate results file
|
|
354
|
-
|
|
355
|
-
from megadetector.postprocessing.validate_batch_results import \
|
|
356
|
-
ValidateBatchResultsOptions, validate_batch_results
|
|
357
|
-
|
|
358
|
-
validation_options = ValidateBatchResultsOptions()
|
|
359
|
-
validation_options.check_image_existence = True
|
|
360
|
-
validation_options.relative_path_base = image_folder
|
|
361
|
-
validation_options.return_data = True
|
|
362
|
-
|
|
363
|
-
results = validate_batch_results(output_file,validation_options)
|
|
364
|
-
assert len(results['validation_results']['errors']) == 0
|
|
365
|
-
assert len(results['validation_results']['warnings']) == 0
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
#%% Preview results
|
|
369
|
-
|
|
370
|
-
from megadetector.postprocessing.postprocess_batch_results import \
|
|
371
|
-
PostProcessingOptions, process_batch_results
|
|
372
|
-
|
|
373
|
-
postprocessing_options = PostProcessingOptions()
|
|
374
|
-
|
|
375
|
-
postprocessing_options.md_results_file = output_file
|
|
376
|
-
postprocessing_options.output_dir = r'g:\temp\serengeti-conversion-preview'
|
|
377
|
-
postprocessing_options.image_base_dir = image_folder
|
|
378
|
-
postprocessing_options.confidence_threshold = 0.2
|
|
379
|
-
postprocessing_options.num_images_to_sample = None
|
|
380
|
-
postprocessing_options.viz_target_width = 1280
|
|
381
|
-
postprocessing_options.line_thickness = 4
|
|
382
|
-
postprocessing_options.parallelize_rendering_n_cores = 10
|
|
383
|
-
postprocessing_options.parallelize_rendering_with_threads = True
|
|
384
|
-
|
|
385
|
-
postprocessing_results = process_batch_results(postprocessing_options)
|
|
386
|
-
|
|
387
|
-
from megadetector.utils.path_utils import open_file
|
|
388
|
-
open_file(postprocessing_results.output_html_file)
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
#%% Command-line driver
|
|
392
|
-
|
|
393
|
-
def main():
|
|
394
|
-
|
|
395
|
-
parser = argparse.ArgumentParser()
|
|
396
|
-
parser.add_argument('result_csv_file', type=str,
|
|
397
|
-
help='csv file containing AI results')
|
|
398
|
-
parser.add_argument('class_mapping_csv_file', type=str,
|
|
399
|
-
help='csv file containing class mappings (with columns CLASS, SPECIES)')
|
|
400
|
-
parser.add_argument('image_folder', type=str,
|
|
401
|
-
help='folder containing the images referred to in [result_csv_file]')
|
|
402
|
-
parser.add_argument('--output_file', type=str, default=None,
|
|
403
|
-
help='.json file to which we should write results (defaults to [result_csv_file].json)')
|
|
404
|
-
|
|
405
|
-
if len(sys.argv[1:])==0:
|
|
406
|
-
parser.print_help()
|
|
407
|
-
parser.exit()
|
|
408
|
-
|
|
409
|
-
args = parser.parse_args()
|
|
410
|
-
raic_csv_to_md_results(result_csv_file=args.result_csv_file,
|
|
411
|
-
class_mapping_csv_file=args.class_mapping_csv_file,
|
|
412
|
-
image_folder=args.image_folder,
|
|
413
|
-
output_file=args.output_file)
|
|
414
|
-
|
|
415
|
-
if __name__ == '__main__':
|
|
416
|
-
main()
|