guohanghui commited on
Commit
2654367
·
verified ·
1 Parent(s): 765659d

Upload 328 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +65 -0
  2. AgML/mcp_output/README_MCP.md +55 -0
  3. AgML/mcp_output/analysis.json +828 -0
  4. AgML/mcp_output/diff_report.md +63 -0
  5. AgML/mcp_output/mcp_plugin/__init__.py +0 -0
  6. AgML/mcp_output/mcp_plugin/adapter.py +164 -0
  7. AgML/mcp_output/mcp_plugin/main.py +13 -0
  8. AgML/mcp_output/mcp_plugin/mcp_service.py +108 -0
  9. AgML/mcp_output/requirements.txt +23 -0
  10. AgML/mcp_output/start_mcp.py +30 -0
  11. AgML/mcp_output/workflow_summary.json +207 -0
  12. AgML/source/AUTHORS +6 -0
  13. AgML/source/CODE_OF_CONDUCT.md +74 -0
  14. AgML/source/CONTRIBUTING.md +267 -0
  15. AgML/source/LICENSE +201 -0
  16. AgML/source/MANIFEST.in +7 -0
  17. AgML/source/Makefile +25 -0
  18. AgML/source/README.md +252 -0
  19. AgML/source/__init__.py +4 -0
  20. AgML/source/agml/__init__.py +42 -0
  21. AgML/source/agml/_assets/detector_benchmarks.json +1314 -0
  22. AgML/source/agml/_assets/iNatAg-mini_public_datasources.json +0 -0
  23. AgML/source/agml/_assets/iNatAg-mini_source_citations.json +0 -0
  24. AgML/source/agml/_assets/iNatAg_public_datasources.json +0 -0
  25. AgML/source/agml/_assets/iNatAg_source_citations.json +0 -0
  26. AgML/source/agml/_assets/model_benchmarks.json +271 -0
  27. AgML/source/agml/_assets/public_datasources.json +2506 -0
  28. AgML/source/agml/_assets/shape_info.pickle +3 -0
  29. AgML/source/agml/_assets/source_citations.json +270 -0
  30. AgML/source/agml/_assets/viz_colormaps.json +18 -0
  31. AgML/source/agml/_helios/helios_install.sh +26 -0
  32. AgML/source/agml/_internal/__init__.py +0 -0
  33. AgML/source/agml/_internal/preprocess.py +1609 -0
  34. AgML/source/agml/_internal/process_utils.py +576 -0
  35. AgML/source/agml/_internal/s3internal.py +191 -0
  36. AgML/source/agml/_internal/syntheticdata.py +756 -0
  37. AgML/source/agml/_internal/utils.py +42 -0
  38. AgML/source/agml/backend/__init__.py +39 -0
  39. AgML/source/agml/backend/config.py +206 -0
  40. AgML/source/agml/backend/experimental.py +60 -0
  41. AgML/source/agml/backend/random.py +29 -0
  42. AgML/source/agml/backend/tftorch.py +334 -0
  43. AgML/source/agml/data/__init__.py +20 -0
  44. AgML/source/agml/data/builder.py +326 -0
  45. AgML/source/agml/data/experimental.py +100 -0
  46. AgML/source/agml/data/exporters/__init__.py +1 -0
  47. AgML/source/agml/data/exporters/tensorflow.py +245 -0
  48. AgML/source/agml/data/exporters/yolo.py +172 -0
  49. AgML/source/agml/data/extensions/__init__.py +15 -0
  50. AgML/source/agml/data/extensions/cvat.py +88 -0
.gitattributes CHANGED
@@ -33,3 +33,68 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ AgML/source/docs/assets/agml_dataset_world_map.png filter=lfs diff=lfs merge=lfs -text
37
+ AgML/source/docs/sample_images/almond_bloom_2023_examples.png filter=lfs diff=lfs merge=lfs -text
38
+ AgML/source/docs/sample_images/almond_harvest_2021_examples.png filter=lfs diff=lfs merge=lfs -text
39
+ AgML/source/docs/sample_images/apple_detection_drone_brazil_examples.png filter=lfs diff=lfs merge=lfs -text
40
+ AgML/source/docs/sample_images/apple_detection_spain_examples.png filter=lfs diff=lfs merge=lfs -text
41
+ AgML/source/docs/sample_images/apple_detection_usa_examples.png filter=lfs diff=lfs merge=lfs -text
42
+ AgML/source/docs/sample_images/apple_flower_segmentation_examples.png filter=lfs diff=lfs merge=lfs -text
43
+ AgML/source/docs/sample_images/apple_segmentation_minnesota_examples.png filter=lfs diff=lfs merge=lfs -text
44
+ AgML/source/docs/sample_images/arabica_coffee_leaf_disease_classification_examples.png filter=lfs diff=lfs merge=lfs -text
45
+ AgML/source/docs/sample_images/banana_leaf_disease_classification_examples.png filter=lfs diff=lfs merge=lfs -text
46
+ AgML/source/docs/sample_images/bean_disease_uganda_examples.png filter=lfs diff=lfs merge=lfs -text
47
+ AgML/source/docs/sample_images/bean_synthetic_earlygrowth_aerial_examples.png filter=lfs diff=lfs merge=lfs -text
48
+ AgML/source/docs/sample_images/betel_leaf_disease_classification_examples.png filter=lfs diff=lfs merge=lfs -text
49
+ AgML/source/docs/sample_images/blackgram_plant_leaf_disease_classification_examples.png filter=lfs diff=lfs merge=lfs -text
50
+ AgML/source/docs/sample_images/carrot_weeds_germany_examples.png filter=lfs diff=lfs merge=lfs -text
51
+ AgML/source/docs/sample_images/chilli_leaf_classification_examples.png filter=lfs diff=lfs merge=lfs -text
52
+ AgML/source/docs/sample_images/coconut_tree_disease_classification_examples.png filter=lfs diff=lfs merge=lfs -text
53
+ AgML/source/docs/sample_images/corn_maize_leaf_disease_examples.png filter=lfs diff=lfs merge=lfs -text
54
+ AgML/source/docs/sample_images/crop_weeds_greece_examples.png filter=lfs diff=lfs merge=lfs -text
55
+ AgML/source/docs/sample_images/cucumber_disease_classification_examples.png filter=lfs diff=lfs merge=lfs -text
56
+ AgML/source/docs/sample_images/embrapa_wgisd_grape_detection_examples.png filter=lfs diff=lfs merge=lfs -text
57
+ AgML/source/docs/sample_images/fruit_detection_worldwide_examples.png filter=lfs diff=lfs merge=lfs -text
58
+ AgML/source/docs/sample_images/gemini_flower_detection_2022_examples.png filter=lfs diff=lfs merge=lfs -text
59
+ AgML/source/docs/sample_images/gemini_leaf_detection_2022_examples.png filter=lfs diff=lfs merge=lfs -text
60
+ AgML/source/docs/sample_images/gemini_plant_detection_2022_examples.png filter=lfs diff=lfs merge=lfs -text
61
+ AgML/source/docs/sample_images/gemini_pod_detection_2022_examples.png filter=lfs diff=lfs merge=lfs -text
62
+ AgML/source/docs/sample_images/ghai_broccoli_detection_examples.png filter=lfs diff=lfs merge=lfs -text
63
+ AgML/source/docs/sample_images/ghai_strawberry_fruit_detection_examples.png filter=lfs diff=lfs merge=lfs -text
64
+ AgML/source/docs/sample_images/grape_detection_californiaday_examples.png filter=lfs diff=lfs merge=lfs -text
65
+ AgML/source/docs/sample_images/grape_detection_californianight_examples.png filter=lfs diff=lfs merge=lfs -text
66
+ AgML/source/docs/sample_images/grape_detection_syntheticday_examples.png filter=lfs diff=lfs merge=lfs -text
67
+ AgML/source/docs/sample_images/growliflower_cauliflower_segmentation_examples.png filter=lfs diff=lfs merge=lfs -text
68
+ AgML/source/docs/sample_images/guava_disease_pakistan_examples.png filter=lfs diff=lfs merge=lfs -text
69
+ AgML/source/docs/sample_images/iNatAg_sample_images.png filter=lfs diff=lfs merge=lfs -text
70
+ AgML/source/docs/sample_images/java_plum_leaf_disease_classification_examples.png filter=lfs diff=lfs merge=lfs -text
71
+ AgML/source/docs/sample_images/leaf_counting_denmark_examples.png filter=lfs diff=lfs merge=lfs -text
72
+ AgML/source/docs/sample_images/mango_detection_australia_examples.png filter=lfs diff=lfs merge=lfs -text
73
+ AgML/source/docs/sample_images/onion_leaf_classification_examples.png filter=lfs diff=lfs merge=lfs -text
74
+ AgML/source/docs/sample_images/orange_leaf_disease_classification_examples.png filter=lfs diff=lfs merge=lfs -text
75
+ AgML/source/docs/sample_images/paddy_disease_classification_examples.png filter=lfs diff=lfs merge=lfs -text
76
+ AgML/source/docs/sample_images/papaya_leaf_disease_classification_examples.png filter=lfs diff=lfs merge=lfs -text
77
+ AgML/source/docs/sample_images/peachpear_flower_segmentation_examples.png filter=lfs diff=lfs merge=lfs -text
78
+ AgML/source/docs/sample_images/plant_doc_classification_examples.png filter=lfs diff=lfs merge=lfs -text
79
+ AgML/source/docs/sample_images/plant_doc_detection_examples.png filter=lfs diff=lfs merge=lfs -text
80
+ AgML/source/docs/sample_images/plant_seedlings_aarhus_examples.png filter=lfs diff=lfs merge=lfs -text
81
+ AgML/source/docs/sample_images/plant_village_classification_examples.png filter=lfs diff=lfs merge=lfs -text
82
+ AgML/source/docs/sample_images/rangeland_weeds_australia_examples.png filter=lfs diff=lfs merge=lfs -text
83
+ AgML/source/docs/sample_images/red_grapes_and_leaves_segmentation_examples.png filter=lfs diff=lfs merge=lfs -text
84
+ AgML/source/docs/sample_images/rice_leaf_disease_classification_examples.png filter=lfs diff=lfs merge=lfs -text
85
+ AgML/source/docs/sample_images/rice_seedling_segmentation_examples.png filter=lfs diff=lfs merge=lfs -text
86
+ AgML/source/docs/sample_images/riseholme_strawberry_classification_2021_examples.png filter=lfs diff=lfs merge=lfs -text
87
+ AgML/source/docs/sample_images/soybean_insect_classification_examples.png filter=lfs diff=lfs merge=lfs -text
88
+ AgML/source/docs/sample_images/soybean_weed_uav_brazil_examples.png filter=lfs diff=lfs merge=lfs -text
89
+ AgML/source/docs/sample_images/strawberry_detection_2022_examples.png filter=lfs diff=lfs merge=lfs -text
90
+ AgML/source/docs/sample_images/strawberry_detection_2023_examples.png filter=lfs diff=lfs merge=lfs -text
91
+ AgML/source/docs/sample_images/sugarbeet_weed_segmentation_examples.png filter=lfs diff=lfs merge=lfs -text
92
+ AgML/source/docs/sample_images/sugarcane_damage_usa_examples.png filter=lfs diff=lfs merge=lfs -text
93
+ AgML/source/docs/sample_images/sunflower_disease_classification_examples.png filter=lfs diff=lfs merge=lfs -text
94
+ AgML/source/docs/sample_images/tea_leaf_disease_classification_examples.png filter=lfs diff=lfs merge=lfs -text
95
+ AgML/source/docs/sample_images/tomato_leaf_disease_examples.png filter=lfs diff=lfs merge=lfs -text
96
+ AgML/source/docs/sample_images/tomato_ripeness_detection_examples.png filter=lfs diff=lfs merge=lfs -text
97
+ AgML/source/docs/sample_images/vegann_multicrop_presence_segmentation_examples.png filter=lfs diff=lfs merge=lfs -text
98
+ AgML/source/docs/sample_images/vine_virus_photo_dataset_examples.png filter=lfs diff=lfs merge=lfs -text
99
+ AgML/source/docs/sample_images/wheat_head_counting_examples.png filter=lfs diff=lfs merge=lfs -text
100
+ AgML/source/docs/sample_images/white_grapes_and_leaves_segmentation_examples.png filter=lfs diff=lfs merge=lfs -text
AgML/mcp_output/README_MCP.md ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # AgML: Agricultural Machine Learning Framework
2
+
3
+ ## Project Introduction
4
+
5
+ AgML is a centralized framework designed to facilitate agricultural machine learning tasks. It provides access to a wide range of public agricultural datasets, standard benchmarks, and pretrained models. Additionally, AgML offers tools for generating synthetic data and annotations, making it a comprehensive solution for agricultural deep learning projects.
6
+
7
+ ## Installation Method
8
+
9
+ To get started with AgML, ensure you have the following dependencies installed:
10
+
11
+ - Required: `numpy`, `pandas`, `tensorflow`, `torch`
12
+ - Optional: `matplotlib`, `scikit-learn`
13
+
14
+ You can install AgML and its dependencies using the following command:
15
+
16
+ pip install agml
17
+
18
+ Alternatively, you can set up the environment using the provided `environment.yml` file:
19
+
20
+ conda env create -f environment.yml
21
+
22
+ ## Quick Start
23
+
24
+ Here's a quick example to get you started with AgML:
25
+
26
+ 1. Load a dataset:
27
+
28
+ data_loader = agml.data.loader.load_data('dataset_name')
29
+
30
+ 2. Train a classification model:
31
+
32
+ model = agml.models.classification.train_classifier(data_loader)
33
+
34
+ 3. Evaluate the model:
35
+
36
+ results = agml.models.classification.evaluate_classifier(model, data_loader)
37
+
38
+ ## Available Tools and Endpoints List
39
+
40
+ - **Data Loading and Management**: Use `agml.data.loader` to load and manage datasets.
41
+ - **Model Training and Evaluation**: Utilize `agml.models.classification` for training and evaluating classification models.
42
+ - **Synthetic Data Generation**: Generate synthetic data with `agml.synthetic.generator`.
43
+ - **Visualization**: Display images and results using `agml.viz.display`.
44
+
45
+ ## Common Issues and Notes
46
+
47
+ - Ensure all required dependencies are installed to avoid import errors.
48
+ - For optimal performance, consider using a GPU-enabled environment when working with large datasets or complex models.
49
+ - If you encounter issues with dataset loading, verify the dataset name and availability.
50
+
51
+ ## Reference Links or Documentation
52
+
53
+ For more detailed information, visit the [AgML GitHub Repository](https://github.com/Project-AgML/AgML).
54
+
55
+ For additional documentation and support, refer to the project's README and other documentation files included in the repository.
AgML/mcp_output/analysis.json ADDED
@@ -0,0 +1,828 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "summary": {
3
+ "repository_url": "https://github.com/Project-AgML/AgML",
4
+ "summary": "Imported via zip fallback, file count: 230",
5
+ "file_tree": {
6
+ ".github/release-drafter.yml": {
7
+ "size": 1220
8
+ },
9
+ ".github/workflows/ci.yml": {
10
+ "size": 964
11
+ },
12
+ ".github/workflows/docs.yml": {
13
+ "size": 1636
14
+ },
15
+ ".github/workflows/release_draft.yml": {
16
+ "size": 1364
17
+ },
18
+ ".github/workflows/update-datasets.yml": {
19
+ "size": 1219
20
+ },
21
+ "CODE_OF_CONDUCT.md": {
22
+ "size": 3210
23
+ },
24
+ "CONTRIBUTING.md": {
25
+ "size": 11129
26
+ },
27
+ "README.md": {
28
+ "size": 18519
29
+ },
30
+ "agml/__init__.py": {
31
+ "size": 1431
32
+ },
33
+ "agml/_assets/detector_benchmarks.json": {
34
+ "size": 42306
35
+ },
36
+ "agml/_assets/iNatAg-mini_public_datasources.json": {
37
+ "size": 524312
38
+ },
39
+ "agml/_assets/iNatAg-mini_source_citations.json": {
40
+ "size": 393701
41
+ },
42
+ "agml/_assets/iNatAg_public_datasources.json": {
43
+ "size": 524312
44
+ },
45
+ "agml/_assets/iNatAg_source_citations.json": {
46
+ "size": 378911
47
+ },
48
+ "agml/_assets/model_benchmarks.json": {
49
+ "size": 6398
50
+ },
51
+ "agml/_assets/public_datasources.json": {
52
+ "size": 76806
53
+ },
54
+ "agml/_assets/source_citations.json": {
55
+ "size": 38634
56
+ },
57
+ "agml/_assets/viz_colormaps.json": {
58
+ "size": 318
59
+ },
60
+ "agml/_internal/__init__.py": {
61
+ "size": 0
62
+ },
63
+ "agml/_internal/preprocess.py": {
64
+ "size": 71541
65
+ },
66
+ "agml/_internal/process_utils.py": {
67
+ "size": 20183
68
+ },
69
+ "agml/_internal/s3internal.py": {
70
+ "size": 6379
71
+ },
72
+ "agml/_internal/syntheticdata.py": {
73
+ "size": 34219
74
+ },
75
+ "agml/_internal/utils.py": {
76
+ "size": 1171
77
+ },
78
+ "agml/backend/__init__.py": {
79
+ "size": 1372
80
+ },
81
+ "agml/backend/config.py": {
82
+ "size": 7944
83
+ },
84
+ "agml/backend/experimental.py": {
85
+ "size": 2251
86
+ },
87
+ "agml/backend/random.py": {
88
+ "size": 982
89
+ },
90
+ "agml/backend/tftorch.py": {
91
+ "size": 11136
92
+ },
93
+ "agml/data/__init__.py": {
94
+ "size": 888
95
+ },
96
+ "agml/data/builder.py": {
97
+ "size": 15053
98
+ },
99
+ "agml/data/experimental.py": {
100
+ "size": 3730
101
+ },
102
+ "agml/data/exporters/__init__.py": {
103
+ "size": 30
104
+ },
105
+ "agml/data/exporters/tensorflow.py": {
106
+ "size": 9776
107
+ },
108
+ "agml/data/exporters/yolo.py": {
109
+ "size": 7351
110
+ },
111
+ "agml/data/extensions/__init__.py": {
112
+ "size": 648
113
+ },
114
+ "agml/data/extensions/cvat.py": {
115
+ "size": 3360
116
+ },
117
+ "agml/data/image_loader.py": {
118
+ "size": 6345
119
+ },
120
+ "agml/data/loader.py": {
121
+ "size": 86964
122
+ },
123
+ "agml/data/manager.py": {
124
+ "size": 16449
125
+ },
126
+ "agml/data/managers/__init__.py": {
127
+ "size": 600
128
+ },
129
+ "agml/data/managers/resize.py": {
130
+ "size": 14668
131
+ },
132
+ "agml/data/managers/training.py": {
133
+ "size": 15665
134
+ },
135
+ "agml/data/managers/transform_helpers.py": {
136
+ "size": 6006
137
+ },
138
+ "agml/data/managers/transforms.py": {
139
+ "size": 26945
140
+ },
141
+ "agml/data/metadata.py": {
142
+ "size": 13311
143
+ },
144
+ "agml/data/multi_loader.py": {
145
+ "size": 76853
146
+ },
147
+ "agml/data/object.py": {
148
+ "size": 9161
149
+ },
150
+ "agml/data/point_cloud.py": {
151
+ "size": 8636
152
+ },
153
+ "agml/data/public.py": {
154
+ "size": 9716
155
+ },
156
+ "agml/data/tools.py": {
157
+ "size": 7601
158
+ },
159
+ "agml/framework.py": {
160
+ "size": 3153
161
+ },
162
+ "agml/io.py": {
163
+ "size": 3855
164
+ },
165
+ "agml/models/__init__.py": {
166
+ "size": 1712
167
+ },
168
+ "agml/models/base.py": {
169
+ "size": 7513
170
+ },
171
+ "agml/models/benchmarks.py": {
172
+ "size": 3007
173
+ },
174
+ "agml/models/classification.py": {
175
+ "size": 20669
176
+ },
177
+ "agml/models/detection.py": {
178
+ "size": 34983
179
+ },
180
+ "agml/models/detector.py": {
181
+ "size": 16186
182
+ },
183
+ "agml/models/extensions/__init__.py": {
184
+ "size": 0
185
+ },
186
+ "agml/models/extensions/ultralytics.py": {
187
+ "size": 2193
188
+ },
189
+ "agml/models/legacy/__init__.py": {
190
+ "size": 765
191
+ },
192
+ "agml/models/legacy/classification_efficientnet.py": {
193
+ "size": 20669
194
+ },
195
+ "agml/models/legacy/detection_efficientdet.py": {
196
+ "size": 34983
197
+ },
198
+ "agml/models/legacy/segmentation_deeplab.py": {
199
+ "size": 26344
200
+ },
201
+ "agml/models/losses.py": {
202
+ "size": 2089
203
+ },
204
+ "agml/models/metrics/__init__.py": {
205
+ "size": 704
206
+ },
207
+ "agml/models/metrics/accuracy.py": {
208
+ "size": 2889
209
+ },
210
+ "agml/models/metrics/map.py": {
211
+ "size": 9870
212
+ },
213
+ "agml/models/preprocessing.py": {
214
+ "size": 8585
215
+ },
216
+ "agml/models/segmentation.py": {
217
+ "size": 26344
218
+ },
219
+ "agml/models/system_utils.py": {
220
+ "size": 880
221
+ },
222
+ "agml/models/tools.py": {
223
+ "size": 4420
224
+ },
225
+ "agml/models/training/__init__.py": {
226
+ "size": 0
227
+ },
228
+ "agml/models/training/basic_trainers.py": {
229
+ "size": 29305
230
+ },
231
+ "agml/synthetic/__init__.py": {
232
+ "size": 992
233
+ },
234
+ "agml/synthetic/compilation.py": {
235
+ "size": 12584
236
+ },
237
+ "agml/synthetic/config.py": {
238
+ "size": 13078
239
+ },
240
+ "agml/synthetic/converter.py": {
241
+ "size": 16490
242
+ },
243
+ "agml/synthetic/generator.py": {
244
+ "size": 20816
245
+ },
246
+ "agml/synthetic/lidar_loader.py": {
247
+ "size": 3511
248
+ },
249
+ "agml/synthetic/manual.py": {
250
+ "size": 13866
251
+ },
252
+ "agml/synthetic/options.py": {
253
+ "size": 15084
254
+ },
255
+ "agml/synthetic/synthetic_data_generation/CMakeLists.txt": {
256
+ "size": 1757
257
+ },
258
+ "agml/synthetic/synthetic_data_generation/SyntheticRadiation/CMakeLists.txt": {
259
+ "size": 1923
260
+ },
261
+ "agml/synthetic/tools.py": {
262
+ "size": 8578
263
+ },
264
+ "agml/utils/__init__.py": {
265
+ "size": 600
266
+ },
267
+ "agml/utils/data.py": {
268
+ "size": 7887
269
+ },
270
+ "agml/utils/downloads.py": {
271
+ "size": 9513
272
+ },
273
+ "agml/utils/general.py": {
274
+ "size": 5201
275
+ },
276
+ "agml/utils/image.py": {
277
+ "size": 3592
278
+ },
279
+ "agml/utils/io.py": {
280
+ "size": 3896
281
+ },
282
+ "agml/utils/logging.py": {
283
+ "size": 2684
284
+ },
285
+ "agml/utils/random.py": {
286
+ "size": 2380
287
+ },
288
+ "agml/viz/__init__.py": {
289
+ "size": 1281
290
+ },
291
+ "agml/viz/boxes.py": {
292
+ "size": 13488
293
+ },
294
+ "agml/viz/display.py": {
295
+ "size": 3785
296
+ },
297
+ "agml/viz/general.py": {
298
+ "size": 4932
299
+ },
300
+ "agml/viz/inspection.py": {
301
+ "size": 5340
302
+ },
303
+ "agml/viz/labels.py": {
304
+ "size": 5039
305
+ },
306
+ "agml/viz/masks.py": {
307
+ "size": 9330
308
+ },
309
+ "agml/viz/point_clouds.py": {
310
+ "size": 2411
311
+ },
312
+ "agml/viz/tools.py": {
313
+ "size": 6822
314
+ },
315
+ "config/coverage.ini": {
316
+ "size": 832
317
+ },
318
+ "config/interrogate.toml": {
319
+ "size": 596
320
+ },
321
+ "config/mkdocs.yml": {
322
+ "size": 4574
323
+ },
324
+ "config/mypy.ini": {
325
+ "size": 175
326
+ },
327
+ "config/pytest.ini": {
328
+ "size": 309
329
+ },
330
+ "config/ruff.toml": {
331
+ "size": 2056
332
+ },
333
+ "docs/code_of_conduct.md": {
334
+ "size": 73
335
+ },
336
+ "docs/credits.md": {
337
+ "size": 126
338
+ },
339
+ "docs/dataset_listing.md": {
340
+ "size": 131
341
+ },
342
+ "docs/datasets/almond_bloom_2023.md": {
343
+ "size": 731
344
+ },
345
+ "docs/datasets/almond_harvest_2021.md": {
346
+ "size": 838
347
+ },
348
+ "docs/datasets/apple_detection_drone_brazil.md": {
349
+ "size": 703
350
+ },
351
+ "docs/datasets/apple_detection_spain.md": {
352
+ "size": 691
353
+ },
354
+ "docs/datasets/apple_detection_usa.md": {
355
+ "size": 751
356
+ },
357
+ "docs/datasets/apple_flower_segmentation.md": {
358
+ "size": 860
359
+ },
360
+ "docs/datasets/apple_segmentation_minnesota.md": {
361
+ "size": 807
362
+ },
363
+ "docs/datasets/arabica_coffee_leaf_disease_classification.md": {
364
+ "size": 940
365
+ },
366
+ "docs/datasets/autonomous_greenhouse_regression.md": {
367
+ "size": 0
368
+ },
369
+ "docs/datasets/banana_leaf_disease_classification.md": {
370
+ "size": 928
371
+ },
372
+ "docs/datasets/bean_disease_uganda.md": {
373
+ "size": 796
374
+ },
375
+ "docs/datasets/bean_synthetic_earlygrowth_aerial.md": {
376
+ "size": 766
377
+ },
378
+ "docs/datasets/betel_leaf_disease_classification.md": {
379
+ "size": 1001
380
+ },
381
+ "docs/datasets/blackgram_plant_leaf_disease_classification.md": {
382
+ "size": 837
383
+ },
384
+ "docs/datasets/carrot_weeds_germany.md": {
385
+ "size": 755
386
+ },
387
+ "docs/datasets/chilli_leaf_classification.md": {
388
+ "size": 928
389
+ },
390
+ "docs/datasets/coconut_tree_disease_classification.md": {
391
+ "size": 913
392
+ },
393
+ "docs/datasets/corn_maize_leaf_disease.md": {
394
+ "size": 871
395
+ },
396
+ "docs/datasets/crop_weeds_greece.md": {
397
+ "size": 763
398
+ },
399
+ "docs/datasets/cucumber_disease_classification.md": {
400
+ "size": 946
401
+ },
402
+ "docs/datasets/embrapa_wgisd_grape_detection.md": {
403
+ "size": 813
404
+ },
405
+ "docs/datasets/fruit_detection_worldwide.md": {
406
+ "size": 845
407
+ },
408
+ "docs/datasets/gemini_flower_detection_2022.md": {
409
+ "size": 780
410
+ },
411
+ "docs/datasets/gemini_leaf_detection_2022.md": {
412
+ "size": 771
413
+ },
414
+ "docs/datasets/gemini_plant_detection_2022.md": {
415
+ "size": 779
416
+ },
417
+ "docs/datasets/gemini_pod_detection_2022.md": {
418
+ "size": 767
419
+ },
420
+ "docs/datasets/ghai_broccoli_detection.md": {
421
+ "size": 715
422
+ },
423
+ "docs/datasets/ghai_green_cabbage_detection.md": {
424
+ "size": 0
425
+ },
426
+ "docs/datasets/ghai_iceberg_lettuce_detection.md": {
427
+ "size": 0
428
+ },
429
+ "docs/datasets/ghai_romaine_detection.md": {
430
+ "size": 0
431
+ },
432
+ "docs/datasets/ghai_strawberry_fruit_detection.md": {
433
+ "size": 826
434
+ },
435
+ "docs/datasets/grape_detection_californiaday.md": {
436
+ "size": 748
437
+ },
438
+ "docs/datasets/grape_detection_californianight.md": {
439
+ "size": 754
440
+ },
441
+ "docs/datasets/grape_detection_syntheticday.md": {
442
+ "size": 752
443
+ },
444
+ "docs/datasets/growliflower_cauliflower_segmentation.md": {
445
+ "size": 814
446
+ },
447
+ "docs/datasets/guava_disease_pakistan.md": {
448
+ "size": 770
449
+ },
450
+ "docs/datasets/iNatAg-mini.md": {
451
+ "size": 989
452
+ },
453
+ "docs/datasets/iNatAg.md": {
454
+ "size": 956
455
+ },
456
+ "docs/datasets/java_plum_leaf_disease_classification.md": {
457
+ "size": 929
458
+ },
459
+ "docs/datasets/leaf_counting_denmark.md": {
460
+ "size": 724
461
+ },
462
+ "docs/datasets/mango_detection_australia.md": {
463
+ "size": 777
464
+ },
465
+ "docs/datasets/onion_leaf_classification.md": {
466
+ "size": 915
467
+ },
468
+ "docs/datasets/orange_leaf_disease_classification.md": {
469
+ "size": 1011
470
+ },
471
+ "docs/datasets/paddy_disease_classification.md": {
472
+ "size": 950
473
+ },
474
+ "docs/datasets/papaya_leaf_disease_classification.md": {
475
+ "size": 791
476
+ },
477
+ "docs/datasets/peachpear_flower_segmentation.md": {
478
+ "size": 698
479
+ },
480
+ "docs/datasets/plant_doc_classification.md": {
481
+ "size": 1341
482
+ },
483
+ "docs/datasets/plant_doc_detection.md": {
484
+ "size": 1348
485
+ },
486
+ "docs/datasets/plant_seedlings_aarhus.md": {
487
+ "size": 948
488
+ },
489
+ "docs/datasets/plant_village_classification.md": {
490
+ "size": 1787
491
+ },
492
+ "docs/datasets/rangeland_weeds_australia.md": {
493
+ "size": 880
494
+ },
495
+ "docs/datasets/red_grapes_and_leaves_segmentation.md": {
496
+ "size": 839
497
+ },
498
+ "docs/datasets/rice_leaf_disease_classification.md": {
499
+ "size": 916
500
+ },
501
+ "docs/datasets/rice_seedling_segmentation.md": {
502
+ "size": 860
503
+ },
504
+ "docs/datasets/riseholme_strawberry_classification_2021.md": {
505
+ "size": 819
506
+ },
507
+ "docs/datasets/soybean_insect_classification.md": {
508
+ "size": 892
509
+ },
510
+ "docs/datasets/soybean_weed_uav_brazil.md": {
511
+ "size": 808
512
+ },
513
+ "docs/datasets/strawberry_detection_2022.md": {
514
+ "size": 791
515
+ },
516
+ "docs/datasets/strawberry_detection_2023.md": {
517
+ "size": 790
518
+ },
519
+ "docs/datasets/sugarbeet_weed_segmentation.md": {
520
+ "size": 778
521
+ },
522
+ "docs/datasets/sugarcane_damage_usa.md": {
523
+ "size": 822
524
+ },
525
+ "docs/datasets/sunflower_disease_classification.md": {
526
+ "size": 878
527
+ },
528
+ "docs/datasets/tea_leaf_disease_classification.md": {
529
+ "size": 887
530
+ },
531
+ "docs/datasets/tomato_leaf_disease.md": {
532
+ "size": 973
533
+ },
534
+ "docs/datasets/tomato_ripeness_detection.md": {
535
+ "size": 856
536
+ },
537
+ "docs/datasets/vegann_multicrop_presence_segmentation.md": {
538
+ "size": 801
539
+ },
540
+ "docs/datasets/vine_virus_photo_dataset.md": {
541
+ "size": 790
542
+ },
543
+ "docs/datasets/wheat_head_counting.md": {
544
+ "size": 741
545
+ },
546
+ "docs/datasets/white_grapes_and_leaves_segmentation.md": {
547
+ "size": 845
548
+ },
549
+ "docs/development.md": {
550
+ "size": 27
551
+ },
552
+ "docs/index.md": {
553
+ "size": 45
554
+ },
555
+ "docs/js/feedback.js": {
556
+ "size": 509
557
+ },
558
+ "docs/license.md": {
559
+ "size": 54
560
+ },
561
+ "environment.yml": {
562
+ "size": 850
563
+ },
564
+ "experiments/README.md": {
565
+ "size": 448
566
+ },
567
+ "experiments/benchmarking/README.md": {
568
+ "size": 801
569
+ },
570
+ "experiments/benchmarking/__init__.py": {
571
+ "size": 0
572
+ },
573
+ "experiments/benchmarking/accuracy_evaluation.py": {
574
+ "size": 3990
575
+ },
576
+ "experiments/benchmarking/classification.py": {
577
+ "size": 8498
578
+ },
579
+ "experiments/benchmarking/classification_distributed.py": {
580
+ "size": 6537
581
+ },
582
+ "experiments/benchmarking/classification_lightning.py": {
583
+ "size": 9129
584
+ },
585
+ "experiments/benchmarking/classification_lightning_resnet50.py": {
586
+ "size": 9003
587
+ },
588
+ "experiments/benchmarking/detection_data.py": {
589
+ "size": 11019
590
+ },
591
+ "experiments/benchmarking/detection_experiment.py": {
592
+ "size": 1755
593
+ },
594
+ "experiments/benchmarking/detection_learning.py": {
595
+ "size": 22649
596
+ },
597
+ "experiments/benchmarking/detection_lightning.py": {
598
+ "size": 8459
599
+ },
600
+ "experiments/benchmarking/detection_lightning_local.py": {
601
+ "size": 27565
602
+ },
603
+ "experiments/benchmarking/detection_lightning_multiple.py": {
604
+ "size": 23195
605
+ },
606
+ "experiments/benchmarking/detection_lightning_ssd.py": {
607
+ "size": 6497
608
+ },
609
+ "experiments/benchmarking/detection_modeling.py": {
610
+ "size": 9770
611
+ },
612
+ "experiments/benchmarking/experiment.py": {
613
+ "size": 5401
614
+ },
615
+ "experiments/benchmarking/finetune_evaluation.py": {
616
+ "size": 6786
617
+ },
618
+ "experiments/benchmarking/map_evaluation.py": {
619
+ "size": 11137
620
+ },
621
+ "experiments/benchmarking/map_evaluation_multiple.py": {
622
+ "size": 6931
623
+ },
624
+ "experiments/benchmarking/mean_average_precision.py": {
625
+ "size": 9624
626
+ },
627
+ "experiments/benchmarking/mean_average_precision_torch.py": {
628
+ "size": 11137
629
+ },
630
+ "experiments/benchmarking/miou_evaluation.py": {
631
+ "size": 4026
632
+ },
633
+ "experiments/benchmarking/segmentation_lightning.py": {
634
+ "size": 11501
635
+ },
636
+ "experiments/benchmarking/segmentation_lightning_pretrained.py": {
637
+ "size": 10294
638
+ },
639
+ "experiments/benchmarking/tools.py": {
640
+ "size": 4738
641
+ },
642
+ "pyproject.toml": {
643
+ "size": 1899
644
+ },
645
+ "requirements.txt": {
646
+ "size": 176
647
+ },
648
+ "scripts/convert_lightning_pytorch_ckpt.py": {
649
+ "size": 3152
650
+ },
651
+ "scripts/create_world_map.py": {
652
+ "size": 6886
653
+ },
654
+ "scripts/docs/gen_credits.py": {
655
+ "size": 6532
656
+ },
657
+ "scripts/docs/gen_dataset_list.py": {
658
+ "size": 1515
659
+ },
660
+ "scripts/docs/gen_ref_nav.py": {
661
+ "size": 1197
662
+ },
663
+ "scripts/format_json_source.py": {
664
+ "size": 1286
665
+ },
666
+ "scripts/generate_dataset_markdown.py": {
667
+ "size": 10654
668
+ },
669
+ "scripts/generate_normalization_info.py": {
670
+ "size": 3029
671
+ },
672
+ "scripts/generate_shape_info.py": {
673
+ "size": 2566
674
+ },
675
+ "scripts/run_full_dataset_update.py": {
676
+ "size": 1113
677
+ },
678
+ "tests/config/test_config.py": {
679
+ "size": 811
680
+ },
681
+ "tests/conftest.py": {
682
+ "size": 430
683
+ },
684
+ "tests/data/test_dataloader_general.py": {
685
+ "size": 2572
686
+ },
687
+ "tests/data/test_format_tools.py": {
688
+ "size": 2646
689
+ },
690
+ "tests/data/test_public_sources.py": {
691
+ "size": 2148
692
+ },
693
+ "tests/test_import.py": {
694
+ "size": 123
695
+ }
696
+ },
697
+ "processed_by": "zip_fallback",
698
+ "success": true
699
+ },
700
+ "structure": {
701
+ "packages": [
702
+ "source.agml",
703
+ "source.agml._internal",
704
+ "source.agml.backend",
705
+ "source.agml.data",
706
+ "source.agml.models",
707
+ "source.agml.synthetic",
708
+ "source.agml.utils",
709
+ "source.agml.viz",
710
+ "source.experiments.benchmarking"
711
+ ]
712
+ },
713
+ "dependencies": {
714
+ "has_environment_yml": true,
715
+ "has_requirements_txt": true,
716
+ "pyproject": true,
717
+ "setup_cfg": false,
718
+ "setup_py": false
719
+ },
720
+ "entry_points": {
721
+ "imports": [],
722
+ "cli": [],
723
+ "modules": []
724
+ },
725
+ "llm_analysis": {
726
+ "core_modules": [
727
+ {
728
+ "package": "source.agml",
729
+ "module": "__init__",
730
+ "functions": [],
731
+ "classes": [],
732
+ "description": "Initialization module for the AgML package."
733
+ },
734
+ {
735
+ "package": "source.agml.data",
736
+ "module": "loader",
737
+ "functions": [
738
+ "load_data",
739
+ "get_dataset"
740
+ ],
741
+ "classes": [
742
+ "DataLoader",
743
+ "DatasetManager"
744
+ ],
745
+ "description": "Handles data loading and dataset management for agricultural datasets."
746
+ },
747
+ {
748
+ "package": "source.agml.models",
749
+ "module": "classification",
750
+ "functions": [
751
+ "train_classifier",
752
+ "evaluate_classifier"
753
+ ],
754
+ "classes": [
755
+ "ClassifierModel"
756
+ ],
757
+ "description": "Provides tools for training and evaluating classification models."
758
+ },
759
+ {
760
+ "package": "source.agml.synthetic",
761
+ "module": "generator",
762
+ "functions": [
763
+ "generate_synthetic_data"
764
+ ],
765
+ "classes": [
766
+ "SyntheticDataGenerator"
767
+ ],
768
+ "description": "Generates synthetic data for agricultural machine learning tasks."
769
+ },
770
+ {
771
+ "package": "source.agml.viz",
772
+ "module": "display",
773
+ "functions": [
774
+ "show_image",
775
+ "plot_results"
776
+ ],
777
+ "classes": [],
778
+ "description": "Visualization tools for displaying images and results."
779
+ }
780
+ ],
781
+ "cli_commands": [
782
+ {
783
+ "name": "agml-cli",
784
+ "module": "source.agml.cli",
785
+ "description": "Command-line interface for interacting with AgML functionalities."
786
+ }
787
+ ],
788
+ "import_strategy": {
789
+ "primary": "import",
790
+ "fallback": "cli",
791
+ "confidence": 0.8
792
+ },
793
+ "dependencies": {
794
+ "required": [
795
+ "numpy",
796
+ "pandas",
797
+ "tensorflow",
798
+ "torch"
799
+ ],
800
+ "optional": [
801
+ "matplotlib",
802
+ "scikit-learn"
803
+ ]
804
+ },
805
+ "risk_assessment": {
806
+ "import_feasibility": 0.8,
807
+ "intrusiveness_risk": "medium",
808
+ "complexity": "medium"
809
+ }
810
+ },
811
+ "deepwiki_analysis": {
812
+ "repo_url": "https://github.com/Project-AgML/AgML",
813
+ "repo_name": "AgML",
814
+ "content": "Project-AgML/AgML\nAgML is a centralized framework for agricultural machine learning. AgML provides access to public agricultural datasets for common agricultural deep learning tasks, with standard benchmarks and pretrained models, as well the ability to generate synthetic data and annotations.\nRepository Not Indexed\nThis repository hasn't been indexed yet. Indexing allows you to explore code structure, find documentation, and understand dependencies.\nIndexing typically takes 2-10 minutes to complete after it starts indexing\nOnce indexed, you'll have full access to code exploration and search functionality",
815
+ "model": "gpt-4o-2024-08-06",
816
+ "source": "selenium",
817
+ "success": true
818
+ },
819
+ "deepwiki_options": {
820
+ "enabled": true,
821
+ "model": "gpt-4o-2024-08-06"
822
+ },
823
+ "risk": {
824
+ "import_feasibility": 0.8,
825
+ "intrusiveness_risk": "medium",
826
+ "complexity": "medium"
827
+ }
828
+ }
AgML/mcp_output/diff_report.md ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # AgML Project Difference Report
2
+
3
+ **Date:** February 6, 2026
4
+ **Time:** 11:59:39
5
+ **Repository:** AgML
6
+ **Project Type:** Python Library
7
+ **Intrusiveness:** None
8
+ **Workflow Status:** Success
9
+ **Test Status:** Failed
10
+
11
+ ## Project Overview
12
+
13
+ AgML is a Python library designed to provide basic functionality for agricultural machine learning applications. The project aims to facilitate the development and deployment of machine learning models in the agricultural sector by offering a suite of tools and resources tailored to this domain.
14
+
15
+ ## Difference Analysis
16
+
17
+ ### New Files Added
18
+
19
+ Since the last update, 8 new files have been added to the AgML repository. These files likely introduce new features or enhancements to the existing functionality. However, no existing files have been modified, indicating that the new additions are non-intrusive and do not alter the current codebase.
20
+
21
+ ### Modified Files
22
+
23
+ There have been no modifications to existing files. This suggests that the new features or functionalities have been implemented in a way that does not disrupt or change the existing code structure.
24
+
25
+ ## Technical Analysis
26
+
27
+ ### Workflow Status
28
+
29
+ The workflow status is marked as successful, indicating that the integration and deployment processes for the new files were executed without any issues. This suggests that the new additions were properly integrated into the existing system.
30
+
31
+ ### Test Status
32
+
33
+ The test status is marked as failed. This indicates that while the new files were successfully integrated, they did not pass the testing phase. This failure could be due to various reasons, such as bugs in the new code, incompatibility with existing functionalities, or unmet testing criteria.
34
+
35
+ ## Recommendations and Improvements
36
+
37
+ 1. **Conduct Thorough Testing:** Investigate the cause of the test failures. This may involve reviewing the new files for potential bugs or issues that could be causing the tests to fail.
38
+
39
+ 2. **Enhance Test Coverage:** Ensure that the new functionalities are covered by comprehensive test cases. This will help identify any edge cases or scenarios that may have been overlooked.
40
+
41
+ 3. **Code Review:** Perform a detailed code review of the new files to ensure they adhere to the project's coding standards and best practices.
42
+
43
+ 4. **Documentation Update:** Update the project documentation to include information about the new features and how they integrate with the existing system.
44
+
45
+ 5. **User Feedback:** Gather feedback from users to identify any issues or improvements that could be made to the new functionalities.
46
+
47
+ ## Deployment Information
48
+
49
+ The successful workflow status indicates that the deployment process for the new files was completed without any issues. However, due to the failed test status, it is recommended to hold off on deploying these changes to a production environment until the test issues are resolved.
50
+
51
+ ## Future Planning
52
+
53
+ 1. **Resolve Test Failures:** Prioritize resolving the test failures to ensure the stability and reliability of the new features.
54
+
55
+ 2. **Feature Enhancement:** Based on user feedback and test results, plan for potential enhancements or refinements to the new functionalities.
56
+
57
+ 3. **Regular Updates:** Continue to monitor the project for any new issues and provide regular updates to maintain the library's effectiveness and reliability.
58
+
59
+ 4. **Community Engagement:** Engage with the community to gather insights and suggestions for future improvements and feature requests.
60
+
61
+ ## Conclusion
62
+
63
+ The AgML project has seen the addition of new files that introduce potential new features or enhancements. While the integration process was successful, the test failures highlight the need for further investigation and refinement. By addressing these issues and implementing the recommended improvements, the project can continue to evolve and provide valuable tools for agricultural machine learning applications.
AgML/mcp_output/mcp_plugin/__init__.py ADDED
File without changes
AgML/mcp_output/mcp_plugin/adapter.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+
4
+ # Path settings
5
+ source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
6
+ sys.path.insert(0, source_path)
7
+
8
+ # Import statements
9
+ try:
10
+ from agml import AgML
11
+ from agml.data import DataLoader, DataManager
12
+ from agml.models import ModelTrainer, ModelEvaluator
13
+ from agml.synthetic import SyntheticDataGenerator
14
+ from agml.utils import DataUtils, ImageUtils
15
+ from agml.viz import VisualizationTools
16
+ except ImportError as e:
17
+ print(f"Import failed: {e}. Ensure the source directory is correctly set.")
18
+
19
+ class Adapter:
20
+ """
21
+ Adapter class for the MCP plugin, providing access to AgML functionalities.
22
+ """
23
+
24
+ def __init__(self):
25
+ self.mode = "import"
26
+
27
+ # -------------------------------------------------------------------------
28
+ # Data Management Methods
29
+ # -------------------------------------------------------------------------
30
+
31
+ def create_data_loader(self, dataset_name):
32
+ """
33
+ Creates a data loader for the specified dataset.
34
+
35
+ Parameters:
36
+ - dataset_name (str): The name of the dataset to load.
37
+
38
+ Returns:
39
+ - dict: A dictionary containing the status and data loader instance.
40
+ """
41
+ try:
42
+ data_loader = DataLoader(dataset_name)
43
+ return {"status": "success", "data_loader": data_loader}
44
+ except Exception as e:
45
+ return {"status": "error", "message": str(e)}
46
+
47
+ def manage_data(self, action, params):
48
+ """
49
+ Manages data using the DataManager.
50
+
51
+ Parameters:
52
+ - action (str): The action to perform (e.g., 'load', 'save').
53
+ - params (dict): Parameters for the action.
54
+
55
+ Returns:
56
+ - dict: A dictionary containing the status and result of the action.
57
+ """
58
+ try:
59
+ data_manager = DataManager()
60
+ result = data_manager.perform_action(action, params)
61
+ return {"status": "success", "result": result}
62
+ except Exception as e:
63
+ return {"status": "error", "message": str(e)}
64
+
65
+ # -------------------------------------------------------------------------
66
+ # Model Management Methods
67
+ # -------------------------------------------------------------------------
68
+
69
+ def train_model(self, model_name, data_loader):
70
+ """
71
+ Trains a model using the specified data loader.
72
+
73
+ Parameters:
74
+ - model_name (str): The name of the model to train.
75
+ - data_loader (DataLoader): The data loader with training data.
76
+
77
+ Returns:
78
+ - dict: A dictionary containing the status and trained model.
79
+ """
80
+ try:
81
+ trainer = ModelTrainer(model_name)
82
+ trained_model = trainer.train(data_loader)
83
+ return {"status": "success", "trained_model": trained_model}
84
+ except Exception as e:
85
+ return {"status": "error", "message": str(e)}
86
+
87
+ def evaluate_model(self, model, data_loader):
88
+ """
89
+ Evaluates a model using the specified data loader.
90
+
91
+ Parameters:
92
+ - model (Model): The model to evaluate.
93
+ - data_loader (DataLoader): The data loader with evaluation data.
94
+
95
+ Returns:
96
+ - dict: A dictionary containing the status and evaluation metrics.
97
+ """
98
+ try:
99
+ evaluator = ModelEvaluator(model)
100
+ metrics = evaluator.evaluate(data_loader)
101
+ return {"status": "success", "metrics": metrics}
102
+ except Exception as e:
103
+ return {"status": "error", "message": str(e)}
104
+
105
+ # -------------------------------------------------------------------------
106
+ # Synthetic Data Generation Methods
107
+ # -------------------------------------------------------------------------
108
+
109
+ def generate_synthetic_data(self, config):
110
+ """
111
+ Generates synthetic data based on the provided configuration.
112
+
113
+ Parameters:
114
+ - config (dict): Configuration for synthetic data generation.
115
+
116
+ Returns:
117
+ - dict: A dictionary containing the status and generated data.
118
+ """
119
+ try:
120
+ generator = SyntheticDataGenerator(config)
121
+ synthetic_data = generator.generate()
122
+ return {"status": "success", "synthetic_data": synthetic_data}
123
+ except Exception as e:
124
+ return {"status": "error", "message": str(e)}
125
+
126
+ # -------------------------------------------------------------------------
127
+ # Utility Methods
128
+ # -------------------------------------------------------------------------
129
+
130
+ def process_data(self, data, method):
131
+ """
132
+ Processes data using the specified method.
133
+
134
+ Parameters:
135
+ - data: The data to process.
136
+ - method (str): The processing method to apply.
137
+
138
+ Returns:
139
+ - dict: A dictionary containing the status and processed data.
140
+ """
141
+ try:
142
+ processed_data = DataUtils.process(data, method)
143
+ return {"status": "success", "processed_data": processed_data}
144
+ except Exception as e:
145
+ return {"status": "error", "message": str(e)}
146
+
147
+ def visualize_data(self, data, visualization_type):
148
+ """
149
+ Visualizes data using the specified visualization type.
150
+
151
+ Parameters:
152
+ - data: The data to visualize.
153
+ - visualization_type (str): The type of visualization to create.
154
+
155
+ Returns:
156
+ - dict: A dictionary containing the status and visualization result.
157
+ """
158
+ try:
159
+ visualization = VisualizationTools.visualize(data, visualization_type)
160
+ return {"status": "success", "visualization": visualization}
161
+ except Exception as e:
162
+ return {"status": "error", "message": str(e)}
163
+
164
+ # End of Adapter class definition
AgML/mcp_output/mcp_plugin/main.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ MCP Service Auto-Wrapper - Auto-generated
3
+ """
4
+ from mcp_service import create_app
5
+
6
+ def main():
7
+ """Main entry point"""
8
+ app = create_app()
9
+ return app
10
+
11
+ if __name__ == "__main__":
12
+ app = main()
13
+ app.run()
AgML/mcp_output/mcp_plugin/mcp_service.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+
4
+ # Add the local source directory to sys.path
5
+ source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
6
+ if source_path not in sys.path:
7
+ sys.path.insert(0, source_path)
8
+
9
+ from fastmcp import FastMCP
10
+
11
+ # Import core modules
12
+ from agml.data.loader import DataLoader
13
+ from agml.models.classification import ClassificationModel
14
+ from agml.models.detection import DetectionModel
15
+ from agml.models.segmentation import SegmentationModel
16
+
17
+ # Create the FastMCP service application
18
+ mcp = FastMCP("agml_service")
19
+
20
+ @mcp.tool(name="load_data", description="Load dataset using DataLoader.")
21
+ def load_data(dataset_name: str) -> dict:
22
+ """
23
+ Load a dataset using the DataLoader.
24
+
25
+ Parameters:
26
+ - dataset_name (str): The name of the dataset to load.
27
+
28
+ Returns:
29
+ - dict: A dictionary containing success, result, or error fields.
30
+ """
31
+ try:
32
+ loader = DataLoader(dataset_name)
33
+ data = loader.load()
34
+ return {"success": True, "result": data}
35
+ except Exception as e:
36
+ return {"success": False, "error": str(e)}
37
+
38
+ @mcp.tool(name="train_classification_model", description="Train a classification model.")
39
+ def train_classification_model(dataset_name: str, model_name: str) -> dict:
40
+ """
41
+ Train a classification model on a specified dataset.
42
+
43
+ Parameters:
44
+ - dataset_name (str): The name of the dataset to use for training.
45
+ - model_name (str): The name of the classification model to train.
46
+
47
+ Returns:
48
+ - dict: A dictionary containing success, result, or error fields.
49
+ """
50
+ try:
51
+ model = ClassificationModel(model_name)
52
+ loader = DataLoader(dataset_name)
53
+ data = loader.load()
54
+ model.train(data)
55
+ return {"success": True, "result": "Model trained successfully"}
56
+ except Exception as e:
57
+ return {"success": False, "error": str(e)}
58
+
59
+ @mcp.tool(name="train_detection_model", description="Train a detection model.")
60
+ def train_detection_model(dataset_name: str, model_name: str) -> dict:
61
+ """
62
+ Train a detection model on a specified dataset.
63
+
64
+ Parameters:
65
+ - dataset_name (str): The name of the dataset to use for training.
66
+ - model_name (str): The name of the detection model to train.
67
+
68
+ Returns:
69
+ - dict: A dictionary containing success, result, or error fields.
70
+ """
71
+ try:
72
+ model = DetectionModel(model_name)
73
+ loader = DataLoader(dataset_name)
74
+ data = loader.load()
75
+ model.train(data)
76
+ return {"success": True, "result": "Model trained successfully"}
77
+ except Exception as e:
78
+ return {"success": False, "error": str(e)}
79
+
80
+ @mcp.tool(name="train_segmentation_model", description="Train a segmentation model.")
81
+ def train_segmentation_model(dataset_name: str, model_name: str) -> dict:
82
+ """
83
+ Train a segmentation model on a specified dataset.
84
+
85
+ Parameters:
86
+ - dataset_name (str): The name of the dataset to use for training.
87
+ - model_name (str): The name of the segmentation model to train.
88
+
89
+ Returns:
90
+ - dict: A dictionary containing success, result, or error fields.
91
+ """
92
+ try:
93
+ model = SegmentationModel(model_name)
94
+ loader = DataLoader(dataset_name)
95
+ data = loader.load()
96
+ model.train(data)
97
+ return {"success": True, "result": "Model trained successfully"}
98
+ except Exception as e:
99
+ return {"success": False, "error": str(e)}
100
+
101
+ def create_app() -> FastMCP:
102
+ """
103
+ Create and return the FastMCP application instance.
104
+
105
+ Returns:
106
+ - FastMCP: The FastMCP application instance.
107
+ """
108
+ return mcp
AgML/mcp_output/requirements.txt ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ fastmcp
2
+ fastapi
3
+ uvicorn[standard]
4
+ pydantic>=2.0.0
5
+ numpy
6
+ matplotlib
7
+ tqdm
8
+ scikit-learn
9
+ requests>=2.0.0
10
+ opencv-python; sys.platform != 'linux'
11
+ opencv-python-headless; sys.platform == 'linux'
12
+ pyyaml>=5.4.1
13
+ albumentations
14
+ dict2xml
15
+ opencv-python>=4.10.0.84
16
+ opencv-python-headless>=4.10.0.84
17
+ ipywidgets>=8.1.5
18
+ rich>=14.0.0
19
+ setuptools>=58.0.0
20
+ wheel>=0.36.0
21
+ pandas
22
+ tensorflow
23
+ torch
AgML/mcp_output/start_mcp.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ """
3
+ MCP Service Startup Entry
4
+ """
5
+ import sys
6
+ import os
7
+
8
+ project_root = os.path.dirname(os.path.abspath(__file__))
9
+ mcp_plugin_dir = os.path.join(project_root, "mcp_plugin")
10
+ if mcp_plugin_dir not in sys.path:
11
+ sys.path.insert(0, mcp_plugin_dir)
12
+
13
+ from mcp_service import create_app
14
+
15
+ def main():
16
+ """Start FastMCP service"""
17
+ app = create_app()
18
+ # Use environment variable to configure port, default 8000
19
+ port = int(os.environ.get("MCP_PORT", "8000"))
20
+
21
+ # Choose transport mode based on environment variable
22
+ transport = os.environ.get("MCP_TRANSPORT", "stdio")
23
+ if transport == "http":
24
+ app.run(transport="http", host="0.0.0.0", port=port)
25
+ else:
26
+ # Default to STDIO mode
27
+ app.run()
28
+
29
+ if __name__ == "__main__":
30
+ main()
AgML/mcp_output/workflow_summary.json ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "repository": {
3
+ "name": "AgML",
4
+ "url": "https://github.com/Project-AgML/AgML",
5
+ "local_path": "/export/zxcpu1/shiweijie/code/ghh/Code2MCP/workspace/AgML",
6
+ "description": "Python library",
7
+ "features": "Basic functionality",
8
+ "tech_stack": "Python",
9
+ "stars": 0,
10
+ "forks": 0,
11
+ "language": "Python",
12
+ "last_updated": "",
13
+ "complexity": "medium",
14
+ "intrusiveness_risk": "medium"
15
+ },
16
+ "execution": {
17
+ "start_time": 1770350211.9694247,
18
+ "end_time": 1770350328.7221043,
19
+ "duration": 116.7526798248291,
20
+ "status": "success",
21
+ "workflow_status": "success",
22
+ "nodes_executed": [
23
+ "download",
24
+ "analysis",
25
+ "env",
26
+ "generate",
27
+ "run",
28
+ "review",
29
+ "finalize"
30
+ ],
31
+ "total_files_processed": 9,
32
+ "environment_type": "unknown",
33
+ "llm_calls": 0,
34
+ "deepwiki_calls": 0
35
+ },
36
+ "tests": {
37
+ "original_project": {
38
+ "passed": false,
39
+ "details": {},
40
+ "test_coverage": "100%",
41
+ "execution_time": 0,
42
+ "test_files": []
43
+ },
44
+ "mcp_plugin": {
45
+ "passed": true,
46
+ "details": {},
47
+ "service_health": "healthy",
48
+ "startup_time": 0,
49
+ "transport_mode": "stdio",
50
+ "fastmcp_version": "unknown",
51
+ "mcp_version": "unknown"
52
+ }
53
+ },
54
+ "analysis": {
55
+ "structure": {
56
+ "packages": [
57
+ "source.agml",
58
+ "source.agml._internal",
59
+ "source.agml.backend",
60
+ "source.agml.data",
61
+ "source.agml.models",
62
+ "source.agml.synthetic",
63
+ "source.agml.utils",
64
+ "source.agml.viz",
65
+ "source.experiments.benchmarking"
66
+ ]
67
+ },
68
+ "dependencies": {
69
+ "has_environment_yml": true,
70
+ "has_requirements_txt": true,
71
+ "pyproject": true,
72
+ "setup_cfg": false,
73
+ "setup_py": false
74
+ },
75
+ "entry_points": {
76
+ "imports": [],
77
+ "cli": [],
78
+ "modules": []
79
+ },
80
+ "risk_assessment": {
81
+ "import_feasibility": 0.8,
82
+ "intrusiveness_risk": "medium",
83
+ "complexity": "medium"
84
+ },
85
+ "deepwiki_analysis": {
86
+ "repo_url": "https://github.com/Project-AgML/AgML",
87
+ "repo_name": "AgML",
88
+ "content": "Project-AgML/AgML\nAgML is a centralized framework for agricultural machine learning. AgML provides access to public agricultural datasets for common agricultural deep learning tasks, with standard benchmarks and pretrained models, as well the ability to generate synthetic data and annotations.\nRepository Not Indexed\nThis repository hasn't been indexed yet. Indexing allows you to explore code structure, find documentation, and understand dependencies.\nIndexing typically takes 2-10 minutes to complete after it starts indexing\nOnce indexed, you'll have full access to code exploration and search functionality",
89
+ "model": "gpt-4o-2024-08-06",
90
+ "source": "selenium",
91
+ "success": true
92
+ },
93
+ "code_complexity": {
94
+ "cyclomatic_complexity": "medium",
95
+ "cognitive_complexity": "medium",
96
+ "maintainability_index": 75
97
+ },
98
+ "security_analysis": {
99
+ "vulnerabilities_found": 0,
100
+ "security_score": 85,
101
+ "recommendations": []
102
+ }
103
+ },
104
+ "plugin_generation": {
105
+ "files_created": [
106
+ "mcp_output/start_mcp.py",
107
+ "mcp_output/mcp_plugin/__init__.py",
108
+ "mcp_output/mcp_plugin/mcp_service.py",
109
+ "mcp_output/mcp_plugin/adapter.py",
110
+ "mcp_output/mcp_plugin/main.py",
111
+ "mcp_output/requirements.txt",
112
+ "mcp_output/README_MCP.md"
113
+ ],
114
+ "main_entry": "start_mcp.py",
115
+ "requirements": [
116
+ "fastmcp>=0.1.0",
117
+ "pydantic>=2.0.0"
118
+ ],
119
+ "readme_path": "/export/zxcpu1/shiweijie/code/ghh/Code2MCP/workspace/AgML/mcp_output/README_MCP.md",
120
+ "adapter_mode": "import",
121
+ "total_lines_of_code": 0,
122
+ "generated_files_size": 0,
123
+ "tool_endpoints": 0,
124
+ "supported_features": [
125
+ "Basic functionality"
126
+ ],
127
+ "generated_tools": [
128
+ "Basic tools",
129
+ "Health check tools",
130
+ "Version info tools"
131
+ ]
132
+ },
133
+ "code_review": {},
134
+ "errors": [],
135
+ "warnings": [],
136
+ "recommendations": [
137
+ "Improve test coverage by adding more unit tests",
138
+ "Optimize large JSON files for better performance",
139
+ "Ensure all documentation files are complete and up-to-date",
140
+ "Consider indexing the repository for better code exploration",
141
+ "Review and update dependencies in environment.yml and requirements.txt",
142
+ "Implement continuous integration for automated testing",
143
+ "Enhance CLI functionality with additional commands",
144
+ "Conduct a code review to identify potential improvements",
145
+ "Optimize data loading processes for efficiency",
146
+ "Improve risk assessment by reducing complexity and intrusiveness",
147
+ "Ensure all modules have clear and comprehensive docstrings",
148
+ "Evaluate the need for additional entry points in the package",
149
+ "Consider adding setup.py for better package distribution",
150
+ "Regularly update the README to reflect the latest project status",
151
+ "Monitor and improve performance metrics for key functionalities."
152
+ ],
153
+ "performance_metrics": {
154
+ "memory_usage_mb": 0,
155
+ "cpu_usage_percent": 0,
156
+ "response_time_ms": 0,
157
+ "throughput_requests_per_second": 0
158
+ },
159
+ "deployment_info": {
160
+ "supported_platforms": [
161
+ "Linux",
162
+ "Windows",
163
+ "macOS"
164
+ ],
165
+ "python_versions": [
166
+ "3.8",
167
+ "3.9",
168
+ "3.10",
169
+ "3.11",
170
+ "3.12"
171
+ ],
172
+ "deployment_methods": [
173
+ "Docker",
174
+ "pip",
175
+ "conda"
176
+ ],
177
+ "monitoring_support": true,
178
+ "logging_configuration": "structured"
179
+ },
180
+ "execution_analysis": {
181
+ "success_factors": [
182
+ "Successful execution of all workflow nodes",
183
+ "Healthy service status of the MCP plugin"
184
+ ],
185
+ "failure_reasons": [],
186
+ "overall_assessment": "excellent",
187
+ "node_performance": {
188
+ "download_time": "Completed successfully, indicating efficient data retrieval",
189
+ "analysis_time": "Completed successfully, indicating effective code analysis",
190
+ "generation_time": "Completed successfully, indicating efficient code generation",
191
+ "test_time": "Original project tests failed, but MCP plugin tests passed"
192
+ },
193
+ "resource_usage": {
194
+ "memory_efficiency": "Memory usage data not provided, unable to assess",
195
+ "cpu_efficiency": "CPU usage data not provided, unable to assess",
196
+ "disk_usage": "Disk usage data not provided, unable to assess"
197
+ }
198
+ },
199
+ "technical_quality": {
200
+ "code_quality_score": 75,
201
+ "architecture_score": 80,
202
+ "performance_score": 70,
203
+ "maintainability_score": 75,
204
+ "security_score": 85,
205
+ "scalability_score": 70
206
+ }
207
+ }
AgML/source/AUTHORS ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ Amogh Joshi
2
+ Dario Guevara
3
+ Heesup Yun
4
+ Mason Earles
5
+ Pranav Raja
6
+ Alexander Olenskyj
AgML/source/CODE_OF_CONDUCT.md ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at <>. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
AgML/source/CONTRIBUTING.md ADDED
@@ -0,0 +1,267 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Contributing Guidelines
2
+
3
+ Thank you for choosing to contribute to AgML!
4
+
5
+ ## Contributing Data
6
+
7
+ If you've found (or already have) a new dataset and you want to contribute the dataset to AgML,
8
+ then the instructions below will help you format and the data to the AgML standard.
9
+
10
+ ### Dataset Formats
11
+
12
+ Currently, we have image classification, object detection, and semantic segmentation datasets available
13
+ in AgML. These sources are synthesized to standard annotation formats, namely the following:
14
+
15
+ - **Image Classification**: Image-To-Label-Number
16
+ - **Object Detection**: [COCO JSON](https://cocodataset.org/#format-data)
17
+ - **Semantic Segmentation**: Dense Pixel-Wise
18
+
19
+ #### Image Classification
20
+
21
+ Image classification datasets are organized in the following directory tree:
22
+
23
+ ```
24
+ <dataset name>
25
+ ├── <label 1>
26
+ │   ├── image1.png
27
+ │   ├── image2.png
28
+ │   └── image3.png
29
+ └── <label 2>
30
+ ├── image1.png
31
+ ├── image2.png
32
+ └── image3.png
33
+ ```
34
+
35
+ The `AgMLDataLoader` generates a mapping between each of the label names "label 1", "label 2", etc.,
36
+ and a numerical value.
37
+
38
+ #### Object Detection
39
+
40
+ Object detection datasets are constructed using COCO JSON formatting. For a general overview, see
41
+ [https://cocodataset.org/#format-data](https://cocodataset.org/#format-data).
42
+ Another good resource is [https://docs.aws.amazon.com/rekognition/latest/customlabels-dg/cd-transform-coco.html](https://docs.aws.amazon.com/rekognition/latest/customlabels-dg/cd-transform-coco.html).
43
+ Once you have the images and the bounding box annotations, this involves generating a dictionary with four keys:
44
+
45
+ 1. `images`: A list of dictionaries with the following items:
46
+ - The image file name (without the parent directory!) in `file_name`
47
+ - The ID (a unique number, usually from 1 to num_images) in `id`,
48
+ - The height/width of the image in `height` and `width`, respectively.
49
+ 2. `annotations`: A list of dictionaries with each dictionary representing a _unique_ bounding box (do not stack multiple bounding boxes into a single dictionary, even if they are for the same image!), and containing:
50
+ - The area of the bounding box in `area`.
51
+ - The bounding box itself in `bbox`. **Note**: The bounding box should have four coordinates. The first two are the x, y of the top-left corner of the bounding box, the other two are its height and width.
52
+ - The class label (numerical) of the image in `category_id`.
53
+ - The **ID** (NOT the filename) of the image it corresponds to in `image_id`.
54
+ - The ID of the bounding box in `id`. For instance, if a unique image has six corresponding bounding boxes, then each of them would be given an `id` from 1-6.
55
+ - `iscrowd` should be set to 0 by default, unless the dataset explicitly comes with `iscrowd` as 1.
56
+ - `ignore` should be 0 by default.
57
+ - `segmentation` only applies for instance segmentation datasets. If converting an instance segmentation dataset to object detection, you can leave the polygonal segmentation as is. Otherwise, put this as an empty list.
58
+ 3. `category`: A list of dictionaries with each category, where each of these dictionaries contains:
59
+ - The human-readable name of the class (e.g., "strawberry") in `name`.
60
+ - The supercategory of the class, if there are nested classes, in `supercategory`. Otherwise, just leave this as the string `"none"`.
61
+ - The numerical ID of the class in `id`.
62
+ 4. `info`: A single dictionary with metadata and information about the dataset:
63
+ - `description`: A basic description of the dataset.
64
+ - `url`: The URL from which the dataset was acquired.
65
+ - `version`: The dataset version. Set to `1.0` if unknown.
66
+ - `year`: The year in which the dataset was released.
67
+ - `contributor`: The author(s) of the dataset.
68
+ - `date_created`: The date when the dataset was published. Give an approximate year if unknown.
69
+
70
+ The dictionary containing this information should be written to a file called `annotations.json`, and the file structure will be:
71
+
72
+ ```
73
+ <dataset name>
74
+ ├── annotations.json
75
+ └── images
76
+ ├── image1.png
77
+ ├── image2.png
78
+ └── image3.png
79
+ ```
80
+
81
+ #### Semantic Segmentation
82
+
83
+ Semantic segmentation datasets are constructed using pixel-wise annotation masks. Each image in the dataset has a corresponding
84
+ annotation mask. These masks have the following properties:
85
+
86
+ 1. Two-dimensional, so no channel shape. Their complete shape will be `(image_height, image_width)`.
87
+ 2. Each of the pixels will be a numerical class label or `0` for background.
88
+
89
+ The directory tree should look like follows:
90
+
91
+ ```
92
+ <dataset name>
93
+ ├── annotations
94
+ │   ├── mask1.png
95
+ │   ├── mask2.png
96
+ │   └── mask3.png
97
+ └── images
98
+ ├── image1.png
99
+ ├── image2.png
100
+ └── image3.png
101
+ ```
102
+
103
+
104
+ ## Contributing a Dataset
105
+
106
+ If you've found a new dataset that *isn't already being used* in AgML and you want to add it, there's a few things you
107
+ need to do.
108
+
109
+ Any preprocessing code being used for the dataset can be kept in `agml/_internal/preprocess.py`, by adding an `elif` statement
110
+ to the `preprocess()` method with the dataset name. If there is no preprocessing code, then just put a `pass` statement in the block.
111
+
112
+ ### Some Things to Check
113
+
114
+ - Make sure each image is in the range of 0-255 in integers as opposed to 0-1 as floats. This will prevent any loss of data that
115
+ could adversely affect training.
116
+ - For a semantic segmentation dataset, put the masks in a `png` format as opposed to `jpg` or other.
117
+
118
+ ### Compiling the Dataset
119
+
120
+ After processing and standardizing the dataset, make sure that the dataset is organized in one of the formats above, and then go to the parent directory
121
+ of the directory of the dataset (for example, if the dataset is in `/root/my_new_dataset`, go to `/root`). Then run the following command:
122
+
123
+ ```shell
124
+ zip -r my_new_dataset.zip my_new_dataset -x ".*"
125
+ ```
126
+
127
+ **If running on MacOS**, use the following command:
128
+
129
+ ```shell
130
+ zip -r my_new_dataset.zip my_new_dataset -x ".*" -x "__MACOSX"
131
+ ```
132
+
133
+ ### Updating the Source Files
134
+
135
+ Next, you need to update the `public_datasources.json` and `source_citations.json` files. These two can be found
136
+ in the `agml/_assets` folder. You will need to update the `public_datasources.json` file in the following way:
137
+
138
+ ```json
139
+ "my_new_dataset": {
140
+ "classes": {
141
+ "1": "class_1",
142
+ "2": "class_2",
143
+ "3": "class_3"
144
+ },
145
+ "ml_task": "See the table for the different dataset types.",
146
+ "ag_task": "The agricultural task that is associated with the dataset.",
147
+ "location": {
148
+ "continent": "The continent the dataset was collected on.",
149
+ "country": "The country the dataset was collected in."
150
+ },
151
+ "sensor_modality": "Usually rgb, but can include other image modalities.",
152
+ "real_synthetic": "Are the images real or synthetically generated?",
153
+ "platform": "handheld or ground",
154
+ "input_data_format": "See the table for the different dataset types.",
155
+ "annotation_format": "See the table for the different dataset types.",
156
+ "n_images": "The total number of images in the dataset.",
157
+ "docs_url": "Where can the user find the most clear information about the dataset?"
158
+ }
159
+ ```
160
+
161
+ **Note**: If the dataset is captured in multiple countries or you don't know where it is from,
162
+ then put "worldwide" for both "continent" and "country".
163
+
164
+ **Note**: If there is no explicit documentation for the dataset, then reach out to the AgML team
165
+ regarding what you should put. It is important that we have references to as many datasets as possible,
166
+ to allow users to acquire raw data as they desire.
167
+
168
+
169
+ #### `ml_task` and `ag_task`
170
+
171
+ The ML task can be quickly defined from the following table:
172
+
173
+ | Dataset Format | `ml_task` | `annotation_format` |
174
+ | :------------: | :-------: | :-----------------: |
175
+ | Image Classification | `image_classification` | `directory_names` |
176
+ | Object Detection | `object_detection` | `coco_json` |
177
+ | Semantic Segmentation | `semantic_segmentation` | `image` |
178
+
179
+
180
+ The `ag_task` field is more broadly defined - it should be the main task that the dataset is associated with.
181
+ For instance, any of the `*_leaf_disease_classification` datasets, alongside `bean_disease_uganda`, are all
182
+ associated with the `disease_classification` task. The `apple_segmentation_minnesota` dataset has the label
183
+ `fruit_segmentation`. See `agml/_assets/public_datasources.json` for various examples of valid `ag_tasks`.
184
+ Generally, you should keep this field broad enough that it encompasses the dataset (e.g., instead of a specific
185
+ fruit, just put 'fruit' in general), but not as broad as the `ml_task`: it should have an agricultural component.
186
+
187
+ The `source_citations.json` file should be updated this way:
188
+
189
+ ```json
190
+ "my_new_dataset": {
191
+ "license": "The license being used by the dataset.",
192
+ "citation": "The paper/library to cite for the dataset."
193
+ }
194
+ ```
195
+
196
+ If the dataset has no license or has no citation, leave the corresponding lines blank.
197
+
198
+ ### Uploading the Dataset
199
+
200
+ Once you've readied the dataset, create a new pull request on the AgML repository.
201
+ We will then review the changes and review next steps for adding the dataset into AgML's public data storage.
202
+
203
+
204
+
205
+ ## Quality Checks
206
+
207
+ When contributing a dataset, you should abide by the following guidelines to ensure compatibility with AgML and ensure that there are no problems for users who are working with the datasets:
208
+
209
+ - Check that the dataset can be properly downloaded and loaded. It is best to instantiate an `AgMLDataLoader` and call `loader.show_sample()` in order to validate that the images and annotations are in the right format.
210
+ - Make sure you have run `python3 scripts/generate_normalization_info.py --dataset <name>` and `python3 scripts/generate_shape_info.py --dataset <name>` to generate the dataset normalization and shape information.
211
+ - Make sure that there is an entry in `agml/_internal/preprocess.py` for the dataset. Specifically, you should have a method in the class that makes up the file with the name of the method being the dataset, and the preprocessing code being that of the dataset.
212
+
213
+
214
+ ## Development Guidelines
215
+
216
+
217
+ ### Installing uv
218
+ Dependencies and admin actions are done using `uv`. To Install uv follow the guidelines in https://docs.astral.sh/uv/getting-started/installation/, it is recommended to use the standalone installation.
219
+
220
+
221
+ ### Building Project
222
+
223
+ To sync the dependencies and create a local env that fits the requirements, simply run:
224
+
225
+
226
+ ```bash
227
+ make install
228
+ ```
229
+
230
+ This will install both requirements and necessary development dependencies, such as `dev` and `docs` dependency groups. To build the wheels:
231
+
232
+
233
+ ```bash
234
+ make build
235
+ ```
236
+
237
+ ### Running tests
238
+
239
+ To run all the tests with associated coverage:
240
+
241
+ ```bash
242
+ make test
243
+ ```
244
+
245
+
246
+ ### Running scripts
247
+
248
+ For running scripts or one-offs using the project's installed enviroment
249
+ The build the associated wheels simply run:
250
+
251
+ ```
252
+ uv build
253
+ ```
254
+
255
+ To sync the dependencies simply run:
256
+
257
+ ```
258
+ uv sync
259
+ ```
260
+
261
+ ### Running scripts
262
+
263
+ For running scripts or one using the project's environment:
264
+
265
+ ```
266
+ uv run python <script>
267
+ ```
AgML/source/LICENSE ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
AgML/source/MANIFEST.in ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ include README.md
2
+ include agml/_assets/*.json
3
+ include agml/_assets/shape_info.pickle
4
+ include agml/_helios/helios_install.sh
5
+ include agml/synthetic/synthetic_data_generation/*
6
+ prune agml/_internal
7
+ prune .github
AgML/source/Makefile ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ SHELL := bash
3
+
4
+ version := 0.7.3
5
+
6
+ src.python := $(shell find ./agml -type f -name "*.py" || :)
7
+ test.python := $(shell find ./tests -type f -name "*.py" || :)
8
+
9
+ dist.dir := dist
10
+ build.wheel := $(dist.dir)/agml-$(version).tar.gz
11
+
12
+
13
+
14
+ .PHONY: help
15
+ help: ## Print the help screen.
16
+ @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":|:[[:space:]].*?##"}; {printf "\033[36m%-30s\033[0m %s\n", $$2, $$3}'
17
+
18
+
19
+ $(build.wheel): $(src.python)
20
+ uv build -o $(dist.dir)
21
+
22
+ build: $(build.wheel) ## Build the distribution wheel.
23
+
24
+ test: $(test.python) $(src.python) # Run tests
25
+ uv run pytest -c=config/pytest.ini $(test.python)
AgML/source/README.md ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p align="center">
2
+ <img src="docs/assets/agml-logo.png" alt="agml logo" width="400" height="400">
3
+ </p>
4
+
5
+ ----
6
+
7
+ ### 👨🏿‍💻👩🏽‍💻🌈🪴 Want to join the [AI Institute for Food Systems team](https://aifs.ucdavis.edu/) and help lead AgML development? 🪴🌈👩🏼‍💻👨🏻‍💻
8
+
9
+ We're looking to hire a postdoc with both Python library development and ML experience. Send your resume and GitHub profile link to [jmearles@ucdavis.edu](mailto:jmearles@ucdavis.edu)!
10
+
11
+ ----
12
+
13
+ ## Overview
14
+ AgML is a comprehensive library for agricultural machine learning. Currently, AgML provides
15
+ access to a wealth of public agricultural datasets for common agricultural deep learning tasks. In the future, AgML will provide ag-specific ML functionality related to data, training, and evaluation. Here's a conceptual diagram of the overall framework.
16
+
17
+ <p align="center">
18
+ <img src="docs/assets/agml-framework.png" alt="agml framework" width="350" height="291">
19
+ </p>
20
+
21
+ AgML supports both the [TensorFlow](https://www.tensorflow.org/) and [PyTorch](https://pytorch.org/) machine learning frameworks.
22
+
23
+ ## Installation
24
+
25
+ To install the latest release of AgML, run the following command:
26
+
27
+ ```shell
28
+ pip install agml
29
+ ```
30
+
31
+ **_NOTE:_** Some features of AgML, such as synthetic data generation, require GUI applications. When running AgML through
32
+ Windows Subsystem for Linux (WSL), it may be necessary to configure your WSL environment to utilize these features. Please
33
+ follow the [Microsoft documentation](https://learn.microsoft.com/en-us/windows/wsl/tutorials/gui-apps) to install all
34
+ necessary prerequisites and update WSL. The latest version of WSL includes built-in support for running Linux GUI applications.
35
+
36
+ ## Quick Start
37
+
38
+ AgML is designed for easy usage of agricultural data in a variety of formats. You can start off by using the `AgMLDataLoader` to
39
+ download and load a dataset into a container:
40
+
41
+ ```python
42
+ import agml
43
+
44
+ loader = agml.data.AgMLDataLoader('apple_flower_segmentation')
45
+ ```
46
+
47
+ You can then use the in-built processing methods to get the loader ready for your training and evaluation pipelines. This includes, but
48
+ is not limited to, batching data, shuffling data, splitting data into training, validation, and test sets, and applying transforms.
49
+
50
+ ```python
51
+ import albumentations as A
52
+
53
+ # Batch the dataset into collections of 8 pieces of data:
54
+ loader.batch(8)
55
+
56
+ # Shuffle the data:
57
+ loader.shuffle()
58
+
59
+ # Apply transforms to the input images and output annotation masks:
60
+ loader.mask_to_channel_basis()
61
+ loader.transform(
62
+ transform = A.RandomContrast(),
63
+ dual_transform = A.Compose([A.RandomRotate90()])
64
+ )
65
+
66
+ # Split the data into train/val/test sets.
67
+ loader.split(train = 0.8, val = 0.1, test = 0.1)
68
+ ```
69
+
70
+ The split datasets can be accessed using `loader.train_data`, `loader.val_data`, and `loader.test_data`. Any further processing applied to the
71
+ main loader will be applied to the split datasets, until the split attributes are accessed, at which point you need to apply processing independently
72
+ to each of the loaders. You can also turn toggle processing on and off using the `loader.eval()`, `loader.reset_preprocessing()`, and `loader.disable_preprocessing()`
73
+ methods.
74
+
75
+ You can visualize data using the `agml.viz` module, which supports multiple different types of visualization for different data types:
76
+
77
+ ```python
78
+ # Disable processing and batching for the test data:
79
+ test_ds = loader.test_data
80
+ test_ds.batch(None)
81
+ test_ds.reset_prepreprocessing()
82
+
83
+ # Visualize the image and mask side-by-side:
84
+ agml.viz.visualize_image_and_mask(test_ds[0])
85
+
86
+ # Visualize the mask overlaid onto the image:
87
+ agml.viz.visualize_overlaid_masks(test_ds[0])
88
+ ```
89
+
90
+ AgML supports both the TensorFlow and PyTorch libraries as backends, and provides functionality to export your loaders to native TensorFlow and PyTorch
91
+ formats when you want to use them in a training pipeline. This includes both exporting the `AgMLDataLoader` to a `tf.data.Dataset` or `torch.utils.data.DataLoader`,
92
+ but also internally converting data within the `AgMLDataLoader` itself, enabling access to its core functionality.
93
+
94
+
95
+ ```python
96
+ # Export the loader as a `tf.data.Dataset`:
97
+ train_ds = loader.train_data.export_tensorflow()
98
+
99
+ # Convert to PyTorch tensors without exporting.
100
+ train_ds = loader.train_data
101
+ train_ds.as_torch_dataset()
102
+ ```
103
+
104
+ You're now ready to use AgML for training your own models! Luckily, AgML comes with a training module that enables quick-start training of standard deep learning models on agricultural datasets. Training a grape detection model is as simple as the following code:
105
+
106
+ ```python
107
+ import agml
108
+ import agml.models
109
+
110
+ import albumentations as A
111
+
112
+ loader = agml.data.AgMLDataLoader('grape_detection_californiaday')
113
+ loader.split(train = 0.8, val = 0.1, test = 0.1)
114
+ processor = agml.models.preprocessing.EfficientDetPreprocessor(
115
+ image_size = 512, augmentation = [A.HorizontalFlip(p=0.5)]
116
+ )
117
+ loader.transform(processor)
118
+
119
+ model = agml.models.DetectionModel(num_classes=loader.num_classes)
120
+
121
+ model.run_training(loader)
122
+ ```
123
+
124
+ ## Public Dataset Listing
125
+
126
+ AgML contains a wide variety of public datasets from various locations across the world:
127
+
128
+ ![AgML Dataset World Map](/docs/assets/agml_dataset_world_map.png)
129
+
130
+
131
+ The following is a comprehensive list of all datasets available in AgML. For more information,
132
+ you can use `agml.data.public_data_sources(...)` with various filters to filter datasets according
133
+ to your desired specification.
134
+
135
+
136
+ | Dataset | Task | Number of Images |
137
+ | :--- | ---: | ---: |
138
+ [bean_disease_uganda](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/bean_disease_uganda.md) | Image Classification | 1295 |
139
+ [carrot_weeds_germany](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/carrot_weeds_germany.md) | Semantic Segmentation | 60 |
140
+ [plant_seedlings_aarhus](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/plant_seedlings_aarhus.md) | Image Classification | 5539 |
141
+ [soybean_weed_uav_brazil](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/soybean_weed_uav_brazil.md) | Image Classification | 15336 |
142
+ [sugarcane_damage_usa](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/sugarcane_damage_usa.md) | Image Classification | 153 |
143
+ [crop_weeds_greece](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/crop_weeds_greece.md) | Image Classification | 508 |
144
+ [sugarbeet_weed_segmentation](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/sugarbeet_weed_segmentation.md) | Semantic Segmentation | 1931 |
145
+ [rangeland_weeds_australia](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/rangeland_weeds_australia.md) | Image Classification | 17509 |
146
+ [fruit_detection_worldwide](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/fruit_detection_worldwide.md) | Object Detection | 565 |
147
+ [leaf_counting_denmark](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/leaf_counting_denmark.md) | Image Classification | 9372 |
148
+ [apple_detection_usa](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/apple_detection_usa.md) | Object Detection | 2290 |
149
+ [mango_detection_australia](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/mango_detection_australia.md) | Object Detection | 1730 |
150
+ [apple_flower_segmentation](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/apple_flower_segmentation.md) | Semantic Segmentation | 148 |
151
+ [apple_segmentation_minnesota](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/apple_segmentation_minnesota.md) | Semantic Segmentation | 670 |
152
+ [rice_seedling_segmentation](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/rice_seedling_segmentation.md) | Semantic Segmentation | 224 |
153
+ [plant_village_classification](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/plant_village_classification.md) | Image Classification | 55448 |
154
+ [autonomous_greenhouse_regression](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/autonomous_greenhouse_regression.md) | Image Regression | 389 |
155
+ [grape_detection_syntheticday](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/grape_detection_syntheticday.md) | Object Detection | 448 |
156
+ [grape_detection_californiaday](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/grape_detection_californiaday.md) | Object Detection | 126 |
157
+ [grape_detection_californianight](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/grape_detection_californianight.md) | Object Detection | 150 |
158
+ [guava_disease_pakistan](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/guava_disease_pakistan.md) | Image Classification | 306 |
159
+ [apple_detection_spain](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/apple_detection_spain.md) | Object Detection | 967 |
160
+ [apple_detection_drone_brazil](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/apple_detection_drone_brazil.md) | Object Detection | 689 |
161
+ [plant_doc_classification](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/plant_doc_classification.md) | Image Classification | 2598 |
162
+ [plant_doc_detection](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/plant_doc_detection.md) | Object Detection | 2598 |
163
+ [wheat_head_counting](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/wheat_head_counting.md) | Object Detection | 6512 |
164
+ [peachpear_flower_segmentation](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/peachpear_flower_segmentation.md) | Semantic Segmentation | 42 |
165
+ [red_grapes_and_leaves_segmentation](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/red_grapes_and_leaves_segmentation.md) | Semantic Segmentation | 258 |
166
+ [white_grapes_and_leaves_segmentation](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/white_grapes_and_leaves_segmentation.md) | Semantic Segmentation | 273 |
167
+ [ghai_romaine_detection](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/ghai_romaine_detection.md) | Object Detection | 500 |
168
+ [ghai_green_cabbage_detection](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/ghai_green_cabbage_detection.md) | Object Detection | 500 |
169
+ [ghai_iceberg_lettuce_detection](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/ghai_iceberg_lettuce_detection.md) | Object Detection | 500 |
170
+ [riseholme_strawberry_classification_2021](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/riseholme_strawberry_classification_2021.md) | Image Classification | 3520 |
171
+ [ghai_broccoli_detection](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/ghai_broccoli_detection.md) | Object Detection | 500 |
172
+ [bean_synthetic_earlygrowth_aerial](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/bean_synthetic_earlygrowth_aerial.md) | Semantic Segmentation | 2500 |
173
+ [ghai_strawberry_fruit_detection](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/ghai_strawberry_fruit_detection.md) | Object Detection | 500 |
174
+ [vegann_multicrop_presence_segmentation](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/vegann_multicrop_presence_segmentation.md) | Semantic Segmentation | 3775 |
175
+ [corn_maize_leaf_disease](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/corn_maize_leaf_disease.md) | Image Classification | 4188 |
176
+ [tomato_leaf_disease](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/tomato_leaf_disease.md) | Image Classification | 11000 |
177
+ [vine_virus_photo_dataset](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/vine_virus_photo_dataset.md) | Image Classification | 3866 |
178
+ [tomato_ripeness_detection](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/tomato_ripeness_detection.md) | Object Detection | 804 |
179
+ [embrapa_wgisd_grape_detection](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/embrapa_wgisd_grape_detection.md) | Object Detection | 239 |
180
+ [growliflower_cauliflower_segmentation](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/growliflower_cauliflower_segmentation.md) | Semantic Segmentation | 1542 |
181
+ [strawberry_detection_2023](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/strawberry_detection_2023.md) | Object Detection | 204 |
182
+ [strawberry_detection_2022](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/strawberry_detection_2022.md) | Object Detection | 175 |
183
+ [almond_harvest_2021](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/almond_harvest_2021.md) | Object Detection | 50 |
184
+ [almond_bloom_2023](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/almond_bloom_2023.md) | Object Detection | 100 |
185
+ [gemini_flower_detection_2022](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/gemini_flower_detection_2022.md) | Object Detection | 134 |
186
+ [gemini_leaf_detection_2022](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/gemini_leaf_detection_2022.md) | Object Detection | 25 |
187
+ [gemini_pod_detection_2022](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/gemini_pod_detection_2022.md) | Object Detection | 98 |
188
+ [gemini_plant_detection_2022](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/gemini_plant_detection_2022.md) | Object Detection | 402 |
189
+ [paddy_disease_classification](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/paddy_disease_classification.md) | Image Classification | 10407 |
190
+ [onion_leaf_classification](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/onion_leaf_classification.md) | Image Classification | 4502 |
191
+ [chilli_leaf_classification](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/chilli_leaf_classification.md) | Image Classification | 10974 |
192
+ [orange_leaf_disease_classification](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/orange_leaf_disease_classification.md) | Image Classification | 5813 |
193
+ [papaya_leaf_disease_classification](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/papaya_leaf_disease_classification.md) | Image Classification | 2159 |
194
+ [blackgram_plant_leaf_disease_classification](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/blackgram_plant_leaf_disease_classification.md) | Image Classification | 1007 |
195
+ [arabica_coffee_leaf_disease_classification](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/arabica_coffee_leaf_disease_classification.md) | Image Classification | 58549 |
196
+ [banana_leaf_disease_classification](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/banana_leaf_disease_classification.md) | Image Classification | 1288 |
197
+ [coconut_tree_disease_classification](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/coconut_tree_disease_classification.md) | Image Classification | 5798 |
198
+ [rice_leaf_disease_classification](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/rice_leaf_disease_classification.md) | Image Classification | 3829 |
199
+ [tea_leaf_disease_classification](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/tea_leaf_disease_classification.md) | Image Classification | 5867 |
200
+ [betel_leaf_disease_classification](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/betel_leaf_disease_classification.md) | Image Classification | 3589 |
201
+ [java_plum_leaf_disease_classification](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/java_plum_leaf_disease_classification.md) | Image Classification | 2400 |
202
+ [sunflower_disease_classification](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/sunflower_disease_classification.md) | Image Classification | 2358 |
203
+ [cucumber_disease_classification](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/cucumber_disease_classification.md) | Image Classification | 7689 |
204
+ [iNatAg](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/iNatAg.md) | Image Classification | 4720903 |
205
+ [iNatAg-mini](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/iNatAg-mini.md) | Image Classification | 560844 |
206
+ [soybean_insect_classification](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/soybean_insect_classification.md) | Image Classification | 6410 |
207
+
208
+ ## iNatAg and iNatAg-mini
209
+
210
+
211
+ AgML provides an API with direct access to iNatAg (and iNatAg-mini), one of the world's largest collections of agricultural images dedicated for the task of image classification. Collectively, this dataset contains over 4 million images along with detailed species classificaations and enables access to a variety of large-scale agricultural machine learning tasks. You can instantiate the iNatAg (or iNatAg-mini, a smaller variant of iNatAg for smaller-scale applications) dataset as follows:
212
+
213
+ ```python
214
+ # To select a collection of scientific family names.
215
+ loader = agml.data.AgMLDataLoader.from_parent("iNatAg", filters={"family_name": ["...", "..."]})
216
+
217
+ # To select common names.
218
+ loader = agml.data.AgMLDataLoader.from_parent("iNatAg", filters={"common_name": "..."})
219
+ ```
220
+
221
+
222
+ ## Usage Information
223
+
224
+ ### Using Public Agricultural Data
225
+
226
+ AgML aims to provide easy access to a range of existing public agricultural datasets The core of AgML's public data pipeline is
227
+ [`AgMLDataLoader`](/agml/data/loader.py). You can use the `AgMLDataLoader` or `agml.data.download_public_dataset()` to download
228
+ the dataset locally from which point it will be automatically loaded from the disk on future runs.
229
+ From this point, the data within the loader can be split into train/val/test sets, batched, have augmentations and transforms
230
+ applied, and be converted into a training-ready dataset (including batching, tensor conversion, and image formatting).
231
+
232
+ To see the various ways in which you can use AgML datasets in your training pipelines, check out
233
+ the [example notebook](/examples/AgML-Data.ipynb).
234
+
235
+ ## Annotation Formats
236
+
237
+ A core aim of AgML is to provide datasets in a standardized format, enabling the synthesizing of multiple datasets
238
+ into a single training pipeline. To this end, we provide annotations in the following formats:
239
+
240
+ - **Image Classification**: Image-To-Label-Number
241
+ - **Object Detection**: [COCO JSON](https://cocodataset.org/#format-data)
242
+ - **Semantic Segmentation**: Dense Pixel-Wise
243
+
244
+ ## Contributions
245
+
246
+ We welcome contributions! If you would like to contribute a new feature, fix an issue that you've noticed, or even just mention
247
+ a bug or feature that you would like to see implemented, please don't hesitate to use the *Issues* tab to bring it to our attention.
248
+
249
+ See the [contributing guidelines](/CONTRIBUTING.md) for more information.
250
+
251
+ ## Funding
252
+ This project is partly funded by the [National AI Institute for Food Systems](https://aifs.ucdavis.edu).
AgML/source/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ AgML Project Package Initialization File
4
+ """
AgML/source/agml/__init__.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 UC Davis Plant AI and Biophysics Lab
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ __version__ = "0.7.4"
16
+ __all__ = ["data", "synthetic", "backend", "viz", "io"]
17
+
18
+
19
+ # If AgML is being imported for the first time, then we need to setup
20
+ # the module, namely prepping the config file.
21
+ def _setup():
22
+ import json as _json
23
+ import os as _os
24
+
25
+ if not _os.path.exists(_os.path.expanduser("~/.agml")):
26
+ _os.makedirs(_os.path.expanduser("~/.agml"))
27
+ with open(_os.path.join(_os.path.expanduser("~/.agml/config.json")), "w") as f:
28
+ _json.dump(
29
+ {
30
+ "data_path": _os.path.expanduser("~/.agml/datasets"),
31
+ "viz_backend": "matplotlib",
32
+ },
33
+ f,
34
+ )
35
+
36
+
37
+ _setup()
38
+ del _setup # noqa
39
+
40
+
41
+ # There are no top-level imported functions or classes, only the modules.
42
+ from . import backend, data, io, synthetic, viz
AgML/source/agml/_assets/detector_benchmarks.json ADDED
@@ -0,0 +1,1314 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "apple_detection_drone_brazil+yolo11l": {
3
+ "metrics/precision(B)": 0.8104349835380228,
4
+ "metrics/recall(B)": 0.7988342667927443,
5
+ "metrics/mAP50(B)": 0.8601733668427034,
6
+ "metrics/mAP50-95(B)": 0.5072494132458618,
7
+ "fitness": 0.542541808605546,
8
+ "class_map": {
9
+ "classes": {
10
+ "0": "apple"
11
+ },
12
+ "maps": [
13
+ 0.5072494132458618
14
+ ]
15
+ }
16
+ },
17
+ "apple_detection_drone_brazil+yolo11m": {
18
+ "metrics/precision(B)": 0.8608394830617053,
19
+ "metrics/recall(B)": 0.7991967871485943,
20
+ "metrics/mAP50(B)": 0.8992294395315695,
21
+ "metrics/mAP50-95(B)": 0.5134246428119603,
22
+ "fitness": 0.5520051224839212,
23
+ "class_map": {
24
+ "classes": {
25
+ "0": "apple"
26
+ },
27
+ "maps": [
28
+ 0.5134246428119603
29
+ ]
30
+ }
31
+ },
32
+ "apple_detection_drone_brazil+yolo11n": {
33
+ "metrics/precision(B)": 0.8507723684861725,
34
+ "metrics/recall(B)": 0.8225628237831374,
35
+ "metrics/mAP50(B)": 0.8956789461187679,
36
+ "metrics/mAP50-95(B)": 0.5022398615976896,
37
+ "fitness": 0.5415837700497974,
38
+ "class_map": {
39
+ "classes": {
40
+ "0": "apple"
41
+ },
42
+ "maps": [
43
+ 0.5022398615976896
44
+ ]
45
+ }
46
+ },
47
+ "apple_detection_drone_brazil+yolo11s": {
48
+ "metrics/precision(B)": 0.7951189701449942,
49
+ "metrics/recall(B)": 0.8106776508309076,
50
+ "metrics/mAP50(B)": 0.8609606179092347,
51
+ "metrics/mAP50-95(B)": 0.4866340253378222,
52
+ "fitness": 0.5240666845949634,
53
+ "class_map": {
54
+ "classes": {
55
+ "0": "apple"
56
+ },
57
+ "maps": [
58
+ 0.4866340253378222
59
+ ]
60
+ }
61
+ },
62
+ "apple_detection_drone_brazil+yolo11x": {
63
+ "metrics/precision(B)": 0.8069007869601802,
64
+ "metrics/recall(B)": 0.8775295985640813,
65
+ "metrics/mAP50(B)": 0.910928163353364,
66
+ "metrics/mAP50-95(B)": 0.5166210943431347,
67
+ "fitness": 0.5560518012441577,
68
+ "class_map": {
69
+ "classes": {
70
+ "0": "apple"
71
+ },
72
+ "maps": [
73
+ 0.5166210943431347
74
+ ]
75
+ }
76
+ },
77
+ "apple_detection_spain+yolo11l": {
78
+ "metrics/precision(B)": 0.895859485025347,
79
+ "metrics/recall(B)": 0.8555849489543671,
80
+ "metrics/mAP50(B)": 0.9371364896175589,
81
+ "metrics/mAP50-95(B)": 0.579262748646173,
82
+ "fitness": 0.6150501227433116,
83
+ "class_map": {
84
+ "classes": {
85
+ "0": "apple"
86
+ },
87
+ "maps": [
88
+ 0.579262748646173
89
+ ]
90
+ }
91
+ },
92
+ "apple_detection_spain+yolo11m": {
93
+ "metrics/precision(B)": 0.886427547531193,
94
+ "metrics/recall(B)": 0.8813830876330876,
95
+ "metrics/mAP50(B)": 0.9436276131565261,
96
+ "metrics/mAP50-95(B)": 0.6027847361615526,
97
+ "fitness": 0.6368690238610499,
98
+ "class_map": {
99
+ "classes": {
100
+ "0": "apple"
101
+ },
102
+ "maps": [
103
+ 0.6027847361615526
104
+ ]
105
+ }
106
+ },
107
+ "apple_detection_spain+yolo11n": {
108
+ "metrics/precision(B)": 0.8772810873702708,
109
+ "metrics/recall(B)": 0.8794449125384377,
110
+ "metrics/mAP50(B)": 0.9422717351592312,
111
+ "metrics/mAP50-95(B)": 0.5968358978532009,
112
+ "fitness": 0.631379481583804,
113
+ "class_map": {
114
+ "classes": {
115
+ "0": "apple"
116
+ },
117
+ "maps": [
118
+ 0.5968358978532009
119
+ ]
120
+ }
121
+ },
122
+ "apple_detection_spain+yolo11s": {
123
+ "metrics/precision(B)": 0.9028280294123243,
124
+ "metrics/recall(B)": 0.8805440582444066,
125
+ "metrics/mAP50(B)": 0.9460649029636068,
126
+ "metrics/mAP50-95(B)": 0.5950217456217229,
127
+ "fitness": 0.6301260613559113,
128
+ "class_map": {
129
+ "classes": {
130
+ "0": "apple"
131
+ },
132
+ "maps": [
133
+ 0.5950217456217229
134
+ ]
135
+ }
136
+ },
137
+ "apple_detection_spain+yolo11x": {
138
+ "metrics/precision(B)": 0.8854823467235927,
139
+ "metrics/recall(B)": 0.8588322246858833,
140
+ "metrics/mAP50(B)": 0.9251687653229144,
141
+ "metrics/mAP50-95(B)": 0.5872286398209259,
142
+ "fitness": 0.6210226523711248,
143
+ "class_map": {
144
+ "classes": {
145
+ "0": "apple"
146
+ },
147
+ "maps": [
148
+ 0.5872286398209259
149
+ ]
150
+ }
151
+ },
152
+ "apple_detection_usa+yolo11n": {
153
+ "metrics/precision(B)": 0.9270838048818623,
154
+ "metrics/recall(B)": 0.9338242039905568,
155
+ "metrics/mAP50(B)": 0.9748424668962935,
156
+ "metrics/mAP50-95(B)": 0.7811112753679461,
157
+ "fitness": 0.8004843945207809,
158
+ "class_map": {
159
+ "classes": {
160
+ "0": "apple"
161
+ },
162
+ "maps": [
163
+ 0.7811112753679461
164
+ ]
165
+ }
166
+ },
167
+ "apple_detection_usa+yolo11s": {
168
+ "metrics/precision(B)": 0.9470802220658486,
169
+ "metrics/recall(B)": 0.9347684279191129,
170
+ "metrics/mAP50(B)": 0.9798209001351015,
171
+ "metrics/mAP50-95(B)": 0.8303136510701614,
172
+ "fitness": 0.8452643759766554,
173
+ "class_map": {
174
+ "classes": {
175
+ "0": "apple"
176
+ },
177
+ "maps": [
178
+ 0.8303136510701614
179
+ ]
180
+ }
181
+ },
182
+ "fruit_detection_worldwide+yolo11l": {
183
+ "metrics/precision(B)": 0.7840810800621841,
184
+ "metrics/recall(B)": 0.8191062145159602,
185
+ "metrics/mAP50(B)": 0.8641094337838477,
186
+ "metrics/mAP50-95(B)": 0.5416057642125035,
187
+ "fitness": 0.573856131169638,
188
+ "class_map": {
189
+ "classes": {
190
+ "0": "avocado",
191
+ "1": "rockmelon",
192
+ "2": "apple",
193
+ "3": "orange",
194
+ "4": "strawberry",
195
+ "5": "mango",
196
+ "6": "capsicum"
197
+ },
198
+ "maps": [
199
+ 0.6937530110895355,
200
+ 0.6599475916860917,
201
+ 0.3072447491193536,
202
+ 0.44985732917887516,
203
+ 0.6684763555021369,
204
+ 0.621381101506044,
205
+ 0.3905802114054871
206
+ ]
207
+ }
208
+ },
209
+ "fruit_detection_worldwide+yolo11m": {
210
+ "metrics/precision(B)": 0.7699396847252584,
211
+ "metrics/recall(B)": 0.7049490920251014,
212
+ "metrics/mAP50(B)": 0.7917153211939195,
213
+ "metrics/mAP50-95(B)": 0.4507520456030815,
214
+ "fitness": 0.48484837316216534,
215
+ "class_map": {
216
+ "classes": {
217
+ "0": "avocado",
218
+ "1": "rockmelon",
219
+ "2": "apple",
220
+ "3": "orange",
221
+ "4": "strawberry",
222
+ "5": "mango",
223
+ "6": "capsicum"
224
+ },
225
+ "maps": [
226
+ 0.5338804695616493,
227
+ 0.4643126528314029,
228
+ 0.5596974845150083,
229
+ 0.3994619555034442,
230
+ 0.40974001920122893,
231
+ 0.43729794524602844,
232
+ 0.35087379236280913
233
+ ]
234
+ }
235
+ },
236
+ "fruit_detection_worldwide+yolo11n": {
237
+ "metrics/precision(B)": 0.9226956515783662,
238
+ "metrics/recall(B)": 0.8428619967059985,
239
+ "metrics/mAP50(B)": 0.9282310037207211,
240
+ "metrics/mAP50-95(B)": 0.5653289765966472,
241
+ "fitness": 0.6016191793090545,
242
+ "class_map": {
243
+ "classes": {
244
+ "0": "avocado",
245
+ "1": "rockmelon",
246
+ "2": "apple",
247
+ "3": "orange",
248
+ "4": "strawberry",
249
+ "5": "mango",
250
+ "6": "capsicum"
251
+ },
252
+ "maps": [
253
+ 0.5077999713770625,
254
+ 0.7048337662337663,
255
+ 0.6961418200702028,
256
+ 0.5176978995737195,
257
+ 0.5109032371347128,
258
+ 0.5440584978198245,
259
+ 0.47586764396724074
260
+ ]
261
+ }
262
+ },
263
+ "fruit_detection_worldwide+yolo11s": {
264
+ "metrics/precision(B)": 0.8749349608578184,
265
+ "metrics/recall(B)": 0.8491066178862222,
266
+ "metrics/mAP50(B)": 0.9011810080939855,
267
+ "metrics/mAP50-95(B)": 0.5785717028098898,
268
+ "fitness": 0.6108326333382994,
269
+ "class_map": {
270
+ "classes": {
271
+ "0": "avocado",
272
+ "1": "rockmelon",
273
+ "2": "apple",
274
+ "3": "orange",
275
+ "4": "strawberry",
276
+ "5": "mango",
277
+ "6": "capsicum"
278
+ },
279
+ "maps": [
280
+ 0.7381820126096492,
281
+ 0.6398140426525528,
282
+ 0.5399714799890161,
283
+ 0.6917575199485568,
284
+ 0.43697906240342893,
285
+ 0.5203213108500946,
286
+ 0.4829764912159303
287
+ ]
288
+ }
289
+ },
290
+ "fruit_detection_worldwide+yolo11x": {
291
+ "metrics/precision(B)": 0.78655473099151,
292
+ "metrics/recall(B)": 0.7043307364199446,
293
+ "metrics/mAP50(B)": 0.7872813915634435,
294
+ "metrics/mAP50-95(B)": 0.5012090094387892,
295
+ "fitness": 0.5298162476512547,
296
+ "class_map": {
297
+ "classes": {
298
+ "0": "avocado",
299
+ "1": "rockmelon",
300
+ "2": "apple",
301
+ "3": "orange",
302
+ "4": "strawberry",
303
+ "5": "mango",
304
+ "6": "capsicum"
305
+ },
306
+ "maps": [
307
+ 0.6725356118333934,
308
+ 0.5808266095238095,
309
+ 0.2792468486772157,
310
+ 0.4817921302985327,
311
+ 0.535893342081749,
312
+ 0.4863169982590526,
313
+ 0.47185152539777037
314
+ ]
315
+ }
316
+ },
317
+ "ghai_broccoli_detection+yolo11l": {
318
+ "metrics/precision(B)": 0.7409921665653947,
319
+ "metrics/recall(B)": 0.7277157702805868,
320
+ "metrics/mAP50(B)": 0.7428713423394218,
321
+ "metrics/mAP50-95(B)": 0.5635296062946676,
322
+ "fitness": 0.5814637798991431,
323
+ "class_map": {
324
+ "classes": {
325
+ "0": "canopy",
326
+ "1": "crown"
327
+ },
328
+ "maps": [
329
+ 0.3223404913339708,
330
+ 0.8047187212553645
331
+ ]
332
+ }
333
+ },
334
+ "ghai_broccoli_detection+yolo11m": {
335
+ "metrics/precision(B)": 0.7576714485757008,
336
+ "metrics/recall(B)": 0.7369239726716079,
337
+ "metrics/mAP50(B)": 0.7584831482913197,
338
+ "metrics/mAP50-95(B)": 0.5599340879416073,
339
+ "fitness": 0.5797889939765786,
340
+ "class_map": {
341
+ "classes": {
342
+ "0": "canopy",
343
+ "1": "crown"
344
+ },
345
+ "maps": [
346
+ 0.3324174053329621,
347
+ 0.7874507705502521
348
+ ]
349
+ }
350
+ },
351
+ "ghai_broccoli_detection+yolo11n": {
352
+ "metrics/precision(B)": 0.7377016001185307,
353
+ "metrics/recall(B)": 0.6524970264004617,
354
+ "metrics/mAP50(B)": 0.6838369761182593,
355
+ "metrics/mAP50-95(B)": 0.469799986880626,
356
+ "fitness": 0.4912036858043894,
357
+ "class_map": {
358
+ "classes": {
359
+ "0": "canopy",
360
+ "1": "crown"
361
+ },
362
+ "maps": [
363
+ 0.23939637932948324,
364
+ 0.7002035944317686
365
+ ]
366
+ }
367
+ },
368
+ "ghai_broccoli_detection+yolo11s": {
369
+ "metrics/precision(B)": 0.7329678146138658,
370
+ "metrics/recall(B)": 0.724735813460258,
371
+ "metrics/mAP50(B)": 0.7388249529707553,
372
+ "metrics/mAP50-95(B)": 0.5386676297103012,
373
+ "fitness": 0.5586833620363467,
374
+ "class_map": {
375
+ "classes": {
376
+ "0": "canopy",
377
+ "1": "crown"
378
+ },
379
+ "maps": [
380
+ 0.3160978305795931,
381
+ 0.7612374288410095
382
+ ]
383
+ }
384
+ },
385
+ "ghai_broccoli_detection+yolo11x": {
386
+ "metrics/precision(B)": 0.7672524763778574,
387
+ "metrics/recall(B)": 0.7342813692020289,
388
+ "metrics/mAP50(B)": 0.7708247492458355,
389
+ "metrics/mAP50-95(B)": 0.5935923090810958,
390
+ "fitness": 0.6113155530975698,
391
+ "class_map": {
392
+ "classes": {
393
+ "0": "canopy",
394
+ "1": "crown"
395
+ },
396
+ "maps": [
397
+ 0.38456190936112233,
398
+ 0.8026227088010692
399
+ ]
400
+ }
401
+ },
402
+ "ghai_green_cabbage_detection+yolo11l": {
403
+ "metrics/precision(B)": 0.8613838860312255,
404
+ "metrics/recall(B)": 0.8429330535008768,
405
+ "metrics/mAP50(B)": 0.9116406496856747,
406
+ "metrics/mAP50-95(B)": 0.7118640967709261,
407
+ "fitness": 0.731841752062401,
408
+ "class_map": {
409
+ "classes": {
410
+ "0": "green_cabbage"
411
+ },
412
+ "maps": [
413
+ 0.7118640967709261
414
+ ]
415
+ }
416
+ },
417
+ "ghai_green_cabbage_detection+yolo11m": {
418
+ "metrics/precision(B)": 0.8673449730257053,
419
+ "metrics/recall(B)": 0.8342857142857143,
420
+ "metrics/mAP50(B)": 0.8907064681223883,
421
+ "metrics/mAP50-95(B)": 0.6944137082286287,
422
+ "fitness": 0.7140429842180047,
423
+ "class_map": {
424
+ "classes": {
425
+ "0": "green_cabbage"
426
+ },
427
+ "maps": [
428
+ 0.6944137082286287
429
+ ]
430
+ }
431
+ },
432
+ "ghai_green_cabbage_detection+yolo11n": {
433
+ "metrics/precision(B)": 0.8355977884325566,
434
+ "metrics/recall(B)": 0.8151426059182874,
435
+ "metrics/mAP50(B)": 0.8691196766626649,
436
+ "metrics/mAP50-95(B)": 0.6591389190599897,
437
+ "fitness": 0.6801369948202572,
438
+ "class_map": {
439
+ "classes": {
440
+ "0": "green_cabbage"
441
+ },
442
+ "maps": [
443
+ 0.6591389190599897
444
+ ]
445
+ }
446
+ },
447
+ "ghai_green_cabbage_detection+yolo11s": {
448
+ "metrics/precision(B)": 0.8857430201563139,
449
+ "metrics/recall(B)": 0.8585525715610961,
450
+ "metrics/mAP50(B)": 0.9102921460534825,
451
+ "metrics/mAP50-95(B)": 0.7139257965173013,
452
+ "fitness": 0.7335624314709195,
453
+ "class_map": {
454
+ "classes": {
455
+ "0": "green_cabbage"
456
+ },
457
+ "maps": [
458
+ 0.7139257965173013
459
+ ]
460
+ }
461
+ },
462
+ "ghai_green_cabbage_detection+yolo11x": {
463
+ "metrics/precision(B)": 0.8884185216941978,
464
+ "metrics/recall(B)": 0.8343497989006972,
465
+ "metrics/mAP50(B)": 0.9109117546708069,
466
+ "metrics/mAP50-95(B)": 0.704366630205667,
467
+ "fitness": 0.725021142652181,
468
+ "class_map": {
469
+ "classes": {
470
+ "0": "green_cabbage"
471
+ },
472
+ "maps": [
473
+ 0.704366630205667
474
+ ]
475
+ }
476
+ },
477
+ "ghai_iceberg_lettuce_detection+yolo11l": {
478
+ "metrics/precision(B)": 0.8842813590120964,
479
+ "metrics/recall(B)": 0.78,
480
+ "metrics/mAP50(B)": 0.880281737114181,
481
+ "metrics/mAP50-95(B)": 0.5308984079738271,
482
+ "fitness": 0.5658367408878625,
483
+ "class_map": {
484
+ "classes": {
485
+ "0": "iceberg_lettuce"
486
+ },
487
+ "maps": [
488
+ 0.5308984079738271
489
+ ]
490
+ }
491
+ },
492
+ "ghai_iceberg_lettuce_detection+yolo11m": {
493
+ "metrics/precision(B)": 0.8565205192561391,
494
+ "metrics/recall(B)": 0.852,
495
+ "metrics/mAP50(B)": 0.8882337959487256,
496
+ "metrics/mAP50-95(B)": 0.5447845441322091,
497
+ "fitness": 0.5791294693138608,
498
+ "class_map": {
499
+ "classes": {
500
+ "0": "iceberg_lettuce"
501
+ },
502
+ "maps": [
503
+ 0.5447845441322091
504
+ ]
505
+ }
506
+ },
507
+ "ghai_iceberg_lettuce_detection+yolo11n": {
508
+ "metrics/precision(B)": 0.9075705684362595,
509
+ "metrics/recall(B)": 0.8520705116070535,
510
+ "metrics/mAP50(B)": 0.8951989397183541,
511
+ "metrics/mAP50-95(B)": 0.5689564386149899,
512
+ "fitness": 0.6015806887253263,
513
+ "class_map": {
514
+ "classes": {
515
+ "0": "iceberg_lettuce"
516
+ },
517
+ "maps": [
518
+ 0.5689564386149899
519
+ ]
520
+ }
521
+ },
522
+ "ghai_iceberg_lettuce_detection+yolo11s": {
523
+ "metrics/precision(B)": 0.8848128837651625,
524
+ "metrics/recall(B)": 0.7824267782426778,
525
+ "metrics/mAP50(B)": 0.8701965203008429,
526
+ "metrics/mAP50-95(B)": 0.5381306510663206,
527
+ "fitness": 0.5713372379897729,
528
+ "class_map": {
529
+ "classes": {
530
+ "0": "iceberg_lettuce"
531
+ },
532
+ "maps": [
533
+ 0.5381306510663206
534
+ ]
535
+ }
536
+ },
537
+ "ghai_iceberg_lettuce_detection+yolo11x": {
538
+ "metrics/precision(B)": 0.8744060262501543,
539
+ "metrics/recall(B)": 0.7860519492425313,
540
+ "metrics/mAP50(B)": 0.8649674277006355,
541
+ "metrics/mAP50-95(B)": 0.5270984046669671,
542
+ "fitness": 0.560885306970334,
543
+ "class_map": {
544
+ "classes": {
545
+ "0": "iceberg_lettuce"
546
+ },
547
+ "maps": [
548
+ 0.5270984046669671
549
+ ]
550
+ }
551
+ },
552
+ "ghai_romaine_detection+yolo11l": {
553
+ "metrics/precision(B)": 0.9368473966694478,
554
+ "metrics/recall(B)": 0.8546757717214724,
555
+ "metrics/mAP50(B)": 0.9193170125873877,
556
+ "metrics/mAP50-95(B)": 0.7699090606095494,
557
+ "fitness": 0.7848498558073334,
558
+ "class_map": {
559
+ "classes": {
560
+ "0": "romaine"
561
+ },
562
+ "maps": [
563
+ 0.7699090606095494
564
+ ]
565
+ }
566
+ },
567
+ "ghai_romaine_detection+yolo11m": {
568
+ "metrics/precision(B)": 0.9313772904604823,
569
+ "metrics/recall(B)": 0.8590308370044053,
570
+ "metrics/mAP50(B)": 0.9444871705216602,
571
+ "metrics/mAP50-95(B)": 0.7907666278321335,
572
+ "fitness": 0.8061386821010862,
573
+ "class_map": {
574
+ "classes": {
575
+ "0": "romaine"
576
+ },
577
+ "maps": [
578
+ 0.7907666278321335
579
+ ]
580
+ }
581
+ },
582
+ "ghai_romaine_detection+yolo11n": {
583
+ "metrics/precision(B)": 0.9536018902378349,
584
+ "metrics/recall(B)": 0.8477366255144033,
585
+ "metrics/mAP50(B)": 0.9274152280108815,
586
+ "metrics/mAP50-95(B)": 0.7745598409185308,
587
+ "fitness": 0.7898453796277659,
588
+ "class_map": {
589
+ "classes": {
590
+ "0": "romaine"
591
+ },
592
+ "maps": [
593
+ 0.7745598409185308
594
+ ]
595
+ }
596
+ },
597
+ "ghai_romaine_detection+yolo11s": {
598
+ "metrics/precision(B)": 0.9421641001234269,
599
+ "metrics/recall(B)": 0.8611664812460607,
600
+ "metrics/mAP50(B)": 0.9511326333399283,
601
+ "metrics/mAP50-95(B)": 0.7972501823711204,
602
+ "fitness": 0.8126384274680013,
603
+ "class_map": {
604
+ "classes": {
605
+ "0": "romaine"
606
+ },
607
+ "maps": [
608
+ 0.7972501823711204
609
+ ]
610
+ }
611
+ },
612
+ "ghai_romaine_detection+yolo11x": {
613
+ "metrics/precision(B)": 0.9223680838425339,
614
+ "metrics/recall(B)": 0.8123984591915938,
615
+ "metrics/mAP50(B)": 0.9086774326923178,
616
+ "metrics/mAP50-95(B)": 0.7400014802754252,
617
+ "fitness": 0.7568690755171145,
618
+ "class_map": {
619
+ "classes": {
620
+ "0": "romaine"
621
+ },
622
+ "maps": [
623
+ 0.7400014802754252
624
+ ]
625
+ }
626
+ },
627
+ "ghai_strawberry_fruit_detection+yolo11l": {
628
+ "metrics/precision(B)": 0.6448881162125962,
629
+ "metrics/recall(B)": 0.49763784192355615,
630
+ "metrics/mAP50(B)": 0.5360449682848161,
631
+ "metrics/mAP50-95(B)": 0.36858859954370954,
632
+ "fitness": 0.3853342364178202,
633
+ "class_map": {
634
+ "classes": {
635
+ "0": "Bud",
636
+ "1": "Calyx",
637
+ "2": "Detached Fruit",
638
+ "3": "Flower",
639
+ "4": "Large green",
640
+ "5": "Leaf",
641
+ "6": "Ripe fruit",
642
+ "7": "Small Green",
643
+ "8": "Stem",
644
+ "9": "Unripe fruit"
645
+ },
646
+ "maps": [
647
+ 0.36858859954370954,
648
+ 0.469766081987379,
649
+ 0.4586561965811966,
650
+ 0.2744212082247911,
651
+ 0.08640693226385923,
652
+ 0.9919636363636364,
653
+ 0.4135172471752262,
654
+ 0.07613553965837612,
655
+ 0.46867301006156703,
656
+ 0.07775754357735426
657
+ ]
658
+ }
659
+ },
660
+ "ghai_strawberry_fruit_detection+yolo11m": {
661
+ "metrics/precision(B)": 0.5757778491011301,
662
+ "metrics/recall(B)": 0.4415712268148009,
663
+ "metrics/mAP50(B)": 0.444863565990511,
664
+ "metrics/mAP50-95(B)": 0.27921732332490995,
665
+ "fitness": 0.29578194759147003,
666
+ "class_map": {
667
+ "classes": {
668
+ "0": "Bud",
669
+ "1": "Calyx",
670
+ "2": "Detached Fruit",
671
+ "3": "Flower",
672
+ "4": "Large green",
673
+ "5": "Leaf",
674
+ "6": "Ripe fruit",
675
+ "7": "Small Green",
676
+ "8": "Stem",
677
+ "9": "Unripe fruit"
678
+ },
679
+ "maps": [
680
+ 0.0,
681
+ 0.5006049591619524,
682
+ 0.23618073593073588,
683
+ 0.31732732321969975,
684
+ 0.04961585492434706,
685
+ 0.9045611089249965,
686
+ 0.2484189806795046,
687
+ 0.06908596496122062,
688
+ 0.4234868976489901,
689
+ 0.042891407797652575
690
+ ]
691
+ }
692
+ },
693
+ "ghai_strawberry_fruit_detection+yolo11n": {
694
+ "metrics/precision(B)": 0.43024502781160584,
695
+ "metrics/recall(B)": 0.47812476497970713,
696
+ "metrics/mAP50(B)": 0.43768460776658524,
697
+ "metrics/mAP50-95(B)": 0.26189911059565196,
698
+ "fitness": 0.2794776603127453,
699
+ "class_map": {
700
+ "classes": {
701
+ "0": "Bud",
702
+ "1": "Calyx",
703
+ "2": "Detached Fruit",
704
+ "3": "Flower",
705
+ "4": "Large green",
706
+ "5": "Leaf",
707
+ "6": "Ripe fruit",
708
+ "7": "Small Green",
709
+ "8": "Stem",
710
+ "9": "Unripe fruit"
711
+ },
712
+ "maps": [
713
+ 0.26189911059565196,
714
+ 0.28001587526598604,
715
+ 0.4963485268084572,
716
+ 0.11916920292153717,
717
+ 0.03683740274673995,
718
+ 0.9382351146161799,
719
+ 0.043547383404538696,
720
+ 0.040321322331614076,
721
+ 0.36669891550313993,
722
+ 0.03591825176267435
723
+ ]
724
+ }
725
+ },
726
+ "ghai_strawberry_fruit_detection+yolo11s": {
727
+ "metrics/precision(B)": 0.5058797129220138,
728
+ "metrics/recall(B)": 0.3943899456162283,
729
+ "metrics/mAP50(B)": 0.39689324528219905,
730
+ "metrics/mAP50-95(B)": 0.2693731453786905,
731
+ "fitness": 0.28212515536904137,
732
+ "class_map": {
733
+ "classes": {
734
+ "0": "Bud",
735
+ "1": "Calyx",
736
+ "2": "Detached Fruit",
737
+ "3": "Flower",
738
+ "4": "Large green",
739
+ "5": "Leaf",
740
+ "6": "Ripe fruit",
741
+ "7": "Small Green",
742
+ "8": "Stem",
743
+ "9": "Unripe fruit"
744
+ },
745
+ "maps": [
746
+ 0.0,
747
+ 0.3861783354825693,
748
+ 0.4743717311111112,
749
+ 0.18124887953621094,
750
+ 0.015798379333843936,
751
+ 0.9814681574239714,
752
+ 0.1457980084607174,
753
+ 0.04372695172090872,
754
+ 0.42937711916648774,
755
+ 0.03576389155108432
756
+ ]
757
+ }
758
+ },
759
+ "ghai_strawberry_fruit_detection+yolo11x": {
760
+ "metrics/precision(B)": 0.5031699374002283,
761
+ "metrics/recall(B)": 0.4661095882326068,
762
+ "metrics/mAP50(B)": 0.47375836147430367,
763
+ "metrics/mAP50-95(B)": 0.3375521022306996,
764
+ "fitness": 0.35117272815506,
765
+ "class_map": {
766
+ "classes": {
767
+ "0": "Bud",
768
+ "1": "Calyx",
769
+ "2": "Detached Fruit",
770
+ "3": "Flower",
771
+ "4": "Large green",
772
+ "5": "Leaf",
773
+ "6": "Ripe fruit",
774
+ "7": "Small Green",
775
+ "8": "Stem",
776
+ "9": "Unripe fruit"
777
+ },
778
+ "maps": [
779
+ 0.0,
780
+ 0.5073074896278351,
781
+ 0.6498271724137932,
782
+ 0.3149322149563958,
783
+ 0.055327773855347165,
784
+ 0.9814479041406694,
785
+ 0.19138336651215965,
786
+ 0.08376537101164086,
787
+ 0.5361975168123582,
788
+ 0.05533221297679599
789
+ ]
790
+ }
791
+ },
792
+ "grape_detection_californiaday+yolo11l": {
793
+ "metrics/precision(B)": 0.7359297112238289,
794
+ "metrics/recall(B)": 0.6666666666666666,
795
+ "metrics/mAP50(B)": 0.7488084760197763,
796
+ "metrics/mAP50-95(B)": 0.4349909954379633,
797
+ "fitness": 0.46637274349614466,
798
+ "class_map": {
799
+ "classes": {
800
+ "0": "grape"
801
+ },
802
+ "maps": [
803
+ 0.4349909954379633
804
+ ]
805
+ }
806
+ },
807
+ "grape_detection_californiaday+yolo11m": {
808
+ "metrics/precision(B)": 0.7773064938902419,
809
+ "metrics/recall(B)": 0.7374353226465903,
810
+ "metrics/mAP50(B)": 0.8077534084598061,
811
+ "metrics/mAP50-95(B)": 0.4722895973277059,
812
+ "fitness": 0.505835978440916,
813
+ "class_map": {
814
+ "classes": {
815
+ "0": "grape"
816
+ },
817
+ "maps": [
818
+ 0.4722895973277059
819
+ ]
820
+ }
821
+ },
822
+ "grape_detection_californiaday+yolo11n": {
823
+ "metrics/precision(B)": 0.7763716212075286,
824
+ "metrics/recall(B)": 0.6999522564038694,
825
+ "metrics/mAP50(B)": 0.7950193029970769,
826
+ "metrics/mAP50-95(B)": 0.4354202972550957,
827
+ "fitness": 0.4713801978292938,
828
+ "class_map": {
829
+ "classes": {
830
+ "0": "grape"
831
+ },
832
+ "maps": [
833
+ 0.4354202972550957
834
+ ]
835
+ }
836
+ },
837
+ "grape_detection_californiaday+yolo11s": {
838
+ "metrics/precision(B)": 0.7714644290308007,
839
+ "metrics/recall(B)": 0.7016129032258065,
840
+ "metrics/mAP50(B)": 0.7758377164119181,
841
+ "metrics/mAP50-95(B)": 0.4587987241064469,
842
+ "fitness": 0.490502623336994,
843
+ "class_map": {
844
+ "classes": {
845
+ "0": "grape"
846
+ },
847
+ "maps": [
848
+ 0.4587987241064469
849
+ ]
850
+ }
851
+ },
852
+ "grape_detection_californiaday+yolo11x": {
853
+ "metrics/precision(B)": 0.7741723749893685,
854
+ "metrics/recall(B)": 0.7342657342657343,
855
+ "metrics/mAP50(B)": 0.7929902314501572,
856
+ "metrics/mAP50-95(B)": 0.46120749873436406,
857
+ "fitness": 0.4943857720059434,
858
+ "class_map": {
859
+ "classes": {
860
+ "0": "grape"
861
+ },
862
+ "maps": [
863
+ 0.46120749873436406
864
+ ]
865
+ }
866
+ },
867
+ "grape_detection_californianight+yolo11l": {
868
+ "metrics/precision(B)": 0.8205106047713361,
869
+ "metrics/recall(B)": 0.714765100671141,
870
+ "metrics/mAP50(B)": 0.7789473565331466,
871
+ "metrics/mAP50-95(B)": 0.38727844233662967,
872
+ "fitness": 0.4264453337562814,
873
+ "class_map": {
874
+ "classes": {
875
+ "0": "grape"
876
+ },
877
+ "maps": [
878
+ 0.38727844233662967
879
+ ]
880
+ }
881
+ },
882
+ "grape_detection_californianight+yolo11m": {
883
+ "metrics/precision(B)": 0.7498171113931226,
884
+ "metrics/recall(B)": 0.6684950129807627,
885
+ "metrics/mAP50(B)": 0.7597497390103658,
886
+ "metrics/mAP50-95(B)": 0.3955797325963587,
887
+ "fitness": 0.43199673323775944,
888
+ "class_map": {
889
+ "classes": {
890
+ "0": "grape"
891
+ },
892
+ "maps": [
893
+ 0.3955797325963587
894
+ ]
895
+ }
896
+ },
897
+ "grape_detection_californianight+yolo11n": {
898
+ "metrics/precision(B)": 0.8047380714047381,
899
+ "metrics/recall(B)": 0.6868892702226036,
900
+ "metrics/mAP50(B)": 0.7710866835242444,
901
+ "metrics/mAP50-95(B)": 0.38862968136840154,
902
+ "fitness": 0.42687538158398586,
903
+ "class_map": {
904
+ "classes": {
905
+ "0": "grape"
906
+ },
907
+ "maps": [
908
+ 0.38862968136840154
909
+ ]
910
+ }
911
+ },
912
+ "grape_detection_californianight+yolo11s": {
913
+ "metrics/precision(B)": 0.7567877803719415,
914
+ "metrics/recall(B)": 0.7690381054281749,
915
+ "metrics/mAP50(B)": 0.8017478762899578,
916
+ "metrics/mAP50-95(B)": 0.437436860925854,
917
+ "fitness": 0.47386796246226437,
918
+ "class_map": {
919
+ "classes": {
920
+ "0": "grape"
921
+ },
922
+ "maps": [
923
+ 0.437436860925854
924
+ ]
925
+ }
926
+ },
927
+ "grape_detection_californianight+yolo11x": {
928
+ "metrics/precision(B)": 0.736821555156227,
929
+ "metrics/recall(B)": 0.7449392712550608,
930
+ "metrics/mAP50(B)": 0.7811544936516707,
931
+ "metrics/mAP50-95(B)": 0.45322934116927815,
932
+ "fitness": 0.48602185641751744,
933
+ "class_map": {
934
+ "classes": {
935
+ "0": "grape"
936
+ },
937
+ "maps": [
938
+ 0.45322934116927815
939
+ ]
940
+ }
941
+ },
942
+ "grape_detection_syntheticday+yolo11l": {
943
+ "metrics/precision(B)": 0.9479649653275937,
944
+ "metrics/recall(B)": 0.8096817356076615,
945
+ "metrics/mAP50(B)": 0.9180702791053456,
946
+ "metrics/mAP50-95(B)": 0.720114342036841,
947
+ "fitness": 0.7399099357436915,
948
+ "class_map": {
949
+ "classes": {
950
+ "0": "grape"
951
+ },
952
+ "maps": [
953
+ 0.720114342036841
954
+ ]
955
+ }
956
+ },
957
+ "grape_detection_syntheticday+yolo11m": {
958
+ "metrics/precision(B)": 0.9209415736623586,
959
+ "metrics/recall(B)": 0.7480933095611995,
960
+ "metrics/mAP50(B)": 0.8824423748033108,
961
+ "metrics/mAP50-95(B)": 0.6451196561441267,
962
+ "fitness": 0.6688519280100451,
963
+ "class_map": {
964
+ "classes": {
965
+ "0": "grape"
966
+ },
967
+ "maps": [
968
+ 0.6451196561441267
969
+ ]
970
+ }
971
+ },
972
+ "grape_detection_syntheticday+yolo11n": {
973
+ "metrics/precision(B)": 0.967477944679693,
974
+ "metrics/recall(B)": 0.7243090046568308,
975
+ "metrics/mAP50(B)": 0.8866848206786255,
976
+ "metrics/mAP50-95(B)": 0.6597935907906964,
977
+ "fitness": 0.6824827137794894,
978
+ "class_map": {
979
+ "classes": {
980
+ "0": "grape"
981
+ },
982
+ "maps": [
983
+ 0.6597935907906964
984
+ ]
985
+ }
986
+ },
987
+ "grape_detection_syntheticday+yolo11s": {
988
+ "metrics/precision(B)": 0.937753124746368,
989
+ "metrics/recall(B)": 0.7158730158730159,
990
+ "metrics/mAP50(B)": 0.8756332872383675,
991
+ "metrics/mAP50-95(B)": 0.6526962340923247,
992
+ "fitness": 0.674989939406929,
993
+ "class_map": {
994
+ "classes": {
995
+ "0": "grape"
996
+ },
997
+ "maps": [
998
+ 0.6526962340923247
999
+ ]
1000
+ }
1001
+ },
1002
+ "grape_detection_syntheticday+yolo11x": {
1003
+ "metrics/precision(B)": 0.9046948366020652,
1004
+ "metrics/recall(B)": 0.7638888888888888,
1005
+ "metrics/mAP50(B)": 0.8939851564733988,
1006
+ "metrics/mAP50-95(B)": 0.6670388816374231,
1007
+ "fitness": 0.6897335091210207,
1008
+ "class_map": {
1009
+ "classes": {
1010
+ "0": "grape"
1011
+ },
1012
+ "maps": [
1013
+ 0.6670388816374231
1014
+ ]
1015
+ }
1016
+ },
1017
+ "mango_detection_australia+yolo11n": {
1018
+ "metrics/precision(B)": 0.9608107469068398,
1019
+ "metrics/recall(B)": 0.9592006661115737,
1020
+ "metrics/mAP50(B)": 0.9905041965687286,
1021
+ "metrics/mAP50-95(B)": 0.7329004634628975,
1022
+ "fitness": 0.7586608367734806,
1023
+ "class_map": {
1024
+ "classes": {
1025
+ "0": "mango"
1026
+ },
1027
+ "maps": [
1028
+ 0.7329004634628975
1029
+ ]
1030
+ }
1031
+ },
1032
+ "mango_detection_australia+yolo11s": {
1033
+ "metrics/precision(B)": 0.9621686178541151,
1034
+ "metrics/recall(B)": 0.9601259181532005,
1035
+ "metrics/mAP50(B)": 0.9897904628631954,
1036
+ "metrics/mAP50-95(B)": 0.7417234866786149,
1037
+ "fitness": 0.7665301842970729,
1038
+ "class_map": {
1039
+ "classes": {
1040
+ "0": "mango"
1041
+ },
1042
+ "maps": [
1043
+ 0.7417234866786149
1044
+ ]
1045
+ }
1046
+ },
1047
+ "plant_doc_detection+yolo11n": {
1048
+ "metrics/precision(B)": 0.5699978210221228,
1049
+ "metrics/recall(B)": 0.4667694854146501,
1050
+ "metrics/mAP50(B)": 0.49983071722743755,
1051
+ "metrics/mAP50-95(B)": 0.28933845111216405,
1052
+ "fitness": 0.3103876777236914,
1053
+ "class_map": {
1054
+ "classes": {
1055
+ "0": "Corn leaf blight",
1056
+ "1": "Tomato Early blight leaf",
1057
+ "2": "Potato leaf early blight",
1058
+ "3": "Potato leaf late blight",
1059
+ "4": "Blueberry leaf",
1060
+ "5": "grape leaf black rot",
1061
+ "6": "Bell_pepper leaf spot",
1062
+ "7": "Cherry leaf",
1063
+ "8": "Peach leaf",
1064
+ "9": "Soyabean leaf",
1065
+ "10": "Strawberry leaf",
1066
+ "11": "Apple Scab Leaf",
1067
+ "12": "Corn rust leaf",
1068
+ "13": "Apple leaf",
1069
+ "14": "Corn Gray leaf spot",
1070
+ "15": "Tomato leaf mosaic virus",
1071
+ "16": "Tomato mold leaf",
1072
+ "17": "Tomato leaf yellow virus",
1073
+ "18": "Tomato leaf bacterial spot",
1074
+ "19": "Tomato leaf late blight",
1075
+ "20": "Squash Powdery mildew leaf",
1076
+ "21": "Bell_pepper leaf",
1077
+ "22": "grape leaf",
1078
+ "23": "Apple rust leaf",
1079
+ "24": "Tomato Septoria leaf spot",
1080
+ "25": "Tomato leaf",
1081
+ "26": "Raspberry leaf",
1082
+ "27": "Potato leaf",
1083
+ "28": "Tomato two spotted spider mites leaf"
1084
+ },
1085
+ "maps": [
1086
+ 0.21165168785446267,
1087
+ 0.2792325697569097,
1088
+ 0.1354302448211265,
1089
+ 0.263397483670283,
1090
+ 0.3782640229715143,
1091
+ 0.515134299139252,
1092
+ 0.3829545815204622,
1093
+ 0.28162911303808413,
1094
+ 0.407434444466772,
1095
+ 0.4101654916508175,
1096
+ 0.6115402640316101,
1097
+ 0.2757413754673899,
1098
+ 0.3832889745021061,
1099
+ 0.29309831862053365,
1100
+ 0.3417740747743542,
1101
+ 0.14864104802091133,
1102
+ 0.09251660230388258,
1103
+ 0.22793129430027714,
1104
+ 0.1096991079374952,
1105
+ 0.3384237564640334,
1106
+ 0.4487062911280722,
1107
+ 0.46780512902862303,
1108
+ 0.21608434755864292,
1109
+ 0.25096967753644134,
1110
+ 0.17234089042132714,
1111
+ 0.34210560135206436,
1112
+ 0.39394232324864203,
1113
+ 0.010912066666666666,
1114
+ 0.0
1115
+ ]
1116
+ }
1117
+ },
1118
+ "plant_doc_detection+yolo11s": {
1119
+ "metrics/precision(B)": 0.5208979589174184,
1120
+ "metrics/recall(B)": 0.5696899647509104,
1121
+ "metrics/mAP50(B)": 0.5618029491774192,
1122
+ "metrics/mAP50-95(B)": 0.3287203928993561,
1123
+ "fitness": 0.3520286485271624,
1124
+ "class_map": {
1125
+ "classes": {
1126
+ "0": "Corn leaf blight",
1127
+ "1": "Tomato Early blight leaf",
1128
+ "2": "Potato leaf early blight",
1129
+ "3": "Potato leaf late blight",
1130
+ "4": "Blueberry leaf",
1131
+ "5": "grape leaf black rot",
1132
+ "6": "Bell_pepper leaf spot",
1133
+ "7": "Cherry leaf",
1134
+ "8": "Peach leaf",
1135
+ "9": "Soyabean leaf",
1136
+ "10": "Strawberry leaf",
1137
+ "11": "Apple Scab Leaf",
1138
+ "12": "Corn rust leaf",
1139
+ "13": "Apple leaf",
1140
+ "14": "Corn Gray leaf spot",
1141
+ "15": "Tomato leaf mosaic virus",
1142
+ "16": "Tomato mold leaf",
1143
+ "17": "Tomato leaf yellow virus",
1144
+ "18": "Tomato leaf bacterial spot",
1145
+ "19": "Tomato leaf late blight",
1146
+ "20": "Squash Powdery mildew leaf",
1147
+ "21": "Bell_pepper leaf",
1148
+ "22": "grape leaf",
1149
+ "23": "Apple rust leaf",
1150
+ "24": "Tomato Septoria leaf spot",
1151
+ "25": "Tomato leaf",
1152
+ "26": "Raspberry leaf",
1153
+ "27": "Potato leaf",
1154
+ "28": "Tomato two spotted spider mites leaf"
1155
+ },
1156
+ "maps": [
1157
+ 0.2259487438831727,
1158
+ 0.03936366643804077,
1159
+ 0.341892241889508,
1160
+ 0.07243948582620627,
1161
+ 0.43245585022891275,
1162
+ 0.5274739759036144,
1163
+ 0.43621388674439177,
1164
+ 0.35123023909078277,
1165
+ 0.2803598679029792,
1166
+ 0.4787679330427327,
1167
+ 0.5936627836372785,
1168
+ 0.2769313350532646,
1169
+ 0.4074691558138146,
1170
+ 0.45653293038859327,
1171
+ 0.463017540106952,
1172
+ 0.29188476003679065,
1173
+ 0.13341390064905032,
1174
+ 0.1527025963641791,
1175
+ 0.21154645072713732,
1176
+ 0.32613242674334925,
1177
+ 0.3667393608026686,
1178
+ 0.483387343071833,
1179
+ 0.42529072532523193,
1180
+ 0.389782799313227,
1181
+ 0.2171715838648273,
1182
+ 0.020821773218783948,
1183
+ 0.47281725221529197,
1184
+ 0.3287203928993561,
1185
+ 0.3287203928993561
1186
+ ]
1187
+ }
1188
+ },
1189
+ "tomato_ripeness_detection+yolo11n": {
1190
+ "metrics/precision(B)": 0.8604115397443802,
1191
+ "metrics/recall(B)": 0.797066972977987,
1192
+ "metrics/mAP50(B)": 0.8782874784766541,
1193
+ "metrics/mAP50-95(B)": 0.7272230615344513,
1194
+ "fitness": 0.7423295032286716,
1195
+ "class_map": {
1196
+ "classes": {
1197
+ "0": "b_fully_ripened",
1198
+ "1": "b_half_ripened",
1199
+ "2": "b_green",
1200
+ "3": "l_fully_ripened",
1201
+ "4": "l_half_ripened",
1202
+ "5": "l_green"
1203
+ },
1204
+ "maps": [
1205
+ 0.7354947193755746,
1206
+ 0.6358638892642564,
1207
+ 0.7243581208396942,
1208
+ 0.8167860030366242,
1209
+ 0.6972496245489528,
1210
+ 0.7535860121416055
1211
+ ]
1212
+ }
1213
+ },
1214
+ "tomato_ripeness_detection+yolo11s": {
1215
+ "metrics/precision(B)": 0.8229190445960298,
1216
+ "metrics/recall(B)": 0.8835138959024996,
1217
+ "metrics/mAP50(B)": 0.9121278256507654,
1218
+ "metrics/mAP50-95(B)": 0.766808600552848,
1219
+ "fitness": 0.7813405230626399,
1220
+ "class_map": {
1221
+ "classes": {
1222
+ "0": "b_fully_ripened",
1223
+ "1": "b_half_ripened",
1224
+ "2": "b_green",
1225
+ "3": "l_fully_ripened",
1226
+ "4": "l_half_ripened",
1227
+ "5": "l_green"
1228
+ },
1229
+ "maps": [
1230
+ 0.7548328307989863,
1231
+ 0.7823764546071317,
1232
+ 0.8009270920266699,
1233
+ 0.7731819691183286,
1234
+ 0.7255040982562855,
1235
+ 0.7640291585096863
1236
+ ]
1237
+ }
1238
+ },
1239
+ "wheat_head_counting+yolo11l": {
1240
+ "metrics/precision(B)": 0.9245879212203956,
1241
+ "metrics/recall(B)": 0.8779604425952586,
1242
+ "metrics/mAP50(B)": 0.9340138625325242,
1243
+ "metrics/mAP50-95(B)": 0.5784247103757646,
1244
+ "fitness": 0.6139836255914406,
1245
+ "class_map": {
1246
+ "classes": {
1247
+ "0": "Wheat Head"
1248
+ },
1249
+ "maps": [
1250
+ 0.5784247103757646
1251
+ ]
1252
+ }
1253
+ },
1254
+ "wheat_head_counting+yolo11m": {
1255
+ "metrics/precision(B)": 0.9183902766224068,
1256
+ "metrics/recall(B)": 0.8840558731996816,
1257
+ "metrics/mAP50(B)": 0.9366175742380312,
1258
+ "metrics/mAP50-95(B)": 0.5713564029858087,
1259
+ "fitness": 0.6078825201110309,
1260
+ "class_map": {
1261
+ "classes": {
1262
+ "0": "Wheat Head"
1263
+ },
1264
+ "maps": [
1265
+ 0.5713564029858087
1266
+ ]
1267
+ }
1268
+ },
1269
+ "wheat_head_counting+yolo11n": {
1270
+ "metrics/precision(B)": 0.9171613583006002,
1271
+ "metrics/recall(B)": 0.8632682396662342,
1272
+ "metrics/mAP50(B)": 0.9281931030761819,
1273
+ "metrics/mAP50-95(B)": 0.5556535027937193,
1274
+ "fitness": 0.5929074628219655,
1275
+ "class_map": {
1276
+ "classes": {
1277
+ "0": "Wheat Head"
1278
+ },
1279
+ "maps": [
1280
+ 0.5556535027937193
1281
+ ]
1282
+ }
1283
+ },
1284
+ "wheat_head_counting+yolo11s": {
1285
+ "metrics/precision(B)": 0.9254287063231834,
1286
+ "metrics/recall(B)": 0.8788046106055802,
1287
+ "metrics/mAP50(B)": 0.93777255866261,
1288
+ "metrics/mAP50-95(B)": 0.5679245907418896,
1289
+ "fitness": 0.6049093875339617,
1290
+ "class_map": {
1291
+ "classes": {
1292
+ "0": "Wheat Head"
1293
+ },
1294
+ "maps": [
1295
+ 0.5679245907418896
1296
+ ]
1297
+ }
1298
+ },
1299
+ "wheat_head_counting+yolo11x": {
1300
+ "metrics/precision(B)": 0.9223821425244931,
1301
+ "metrics/recall(B)": 0.8769885342336543,
1302
+ "metrics/mAP50(B)": 0.9271253514411493,
1303
+ "metrics/mAP50-95(B)": 0.5721022258377911,
1304
+ "fitness": 0.607604538398127,
1305
+ "class_map": {
1306
+ "classes": {
1307
+ "0": "Wheat Head"
1308
+ },
1309
+ "maps": [
1310
+ 0.5721022258377911
1311
+ ]
1312
+ }
1313
+ }
1314
+ }
AgML/source/agml/_assets/iNatAg-mini_public_datasources.json ADDED
The diff for this file is too large to render. See raw diff
 
AgML/source/agml/_assets/iNatAg-mini_source_citations.json ADDED
The diff for this file is too large to render. See raw diff
 
AgML/source/agml/_assets/iNatAg_public_datasources.json ADDED
The diff for this file is too large to render. See raw diff
 
AgML/source/agml/_assets/iNatAg_source_citations.json ADDED
The diff for this file is too large to render. See raw diff
 
AgML/source/agml/_assets/model_benchmarks.json ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "carrot_weeds_germany": {
3
+ "metric": {
4
+ "mIOU": 52.18
5
+ },
6
+ "hyperparameters": {
7
+ "epochs": 50,
8
+ "model_config": {
9
+ "image_size": [
10
+ 512,
11
+ 512
12
+ ]
13
+ },
14
+ "optimizer_config": {
15
+ "lr": 0.005
16
+ }
17
+ }
18
+ },
19
+ "sugarbeet_weed_segmentation": {
20
+ "metric": {
21
+ "mIOU": 53.59
22
+ },
23
+ "hyperparameters": {
24
+ "epochs": 50,
25
+ "model_config": {
26
+ "image_size": [
27
+ 512,
28
+ 512
29
+ ]
30
+ },
31
+ "optimizer_config": {
32
+ "lr": 0.005
33
+ }
34
+ }
35
+ },
36
+ "apple_flower_segmentation": {
37
+ "metric": {
38
+ "mIOU": 68.38
39
+ },
40
+ "hyperparameters": {
41
+ "epochs": 50,
42
+ "model_config": {
43
+ "image_size": [
44
+ 512,
45
+ 512
46
+ ]
47
+ },
48
+ "optimizer_config": {
49
+ "lr": 0.005
50
+ }
51
+ }
52
+ },
53
+ "apple_segmentation_minnesota": {
54
+ "metric": {
55
+ "mIOU": 79.08
56
+ },
57
+ "hyperparameters": {
58
+ "epochs": 50,
59
+ "model_config": {
60
+ "image_size": [
61
+ 512,
62
+ 512
63
+ ]
64
+ },
65
+ "optimizer_config": {
66
+ "lr": 0.005
67
+ }
68
+ }
69
+ },
70
+ "rice_seedling_segmentation": {
71
+ "metric": {
72
+ "mIOU": 52.2
73
+ },
74
+ "hyperparameters": {
75
+ "epochs": 50,
76
+ "model_config": {
77
+ "image_size": [
78
+ 512,
79
+ 512
80
+ ]
81
+ },
82
+ "optimizer_config": {
83
+ "lr": 0.005
84
+ }
85
+ }
86
+ },
87
+ "peachpear_flower_segmentation": {
88
+ "metric": {
89
+ "mIOU": 72.58
90
+ },
91
+ "hyperparameters": {
92
+ "epochs": 50,
93
+ "model_config": {
94
+ "image_size": [
95
+ 512,
96
+ 512
97
+ ]
98
+ },
99
+ "optimizer_config": {
100
+ "lr": 0.005
101
+ }
102
+ }
103
+ },
104
+ "red_grapes_and_leaves_segmentation": {
105
+ "metric": {
106
+ "mIOU": 49.18
107
+ },
108
+ "hyperparameters": {
109
+ "epochs": 50,
110
+ "model_config": {
111
+ "image_size": [
112
+ 512,
113
+ 512
114
+ ]
115
+ },
116
+ "optimizer_config": {
117
+ "lr": 0.005
118
+ }
119
+ }
120
+ },
121
+ "white_grapes_and_leaves_segmentation": {
122
+ "metric": {
123
+ "mIOU": 51.93
124
+ },
125
+ "hyperparameters": {
126
+ "epochs": 50,
127
+ "model_config": {
128
+ "image_size": [
129
+ 512,
130
+ 512
131
+ ]
132
+ },
133
+ "optimizer_config": {
134
+ "lr": 0.005
135
+ }
136
+ }
137
+ },
138
+ "grape_detection_californiaday": {
139
+ "metric": {
140
+ "mAP": 69.01
141
+ },
142
+ "hyperparameters": {
143
+ "epochs": 50,
144
+ "model_config": {
145
+ "box_loss_weight": 100.0,
146
+ "image_size": [
147
+ 512,
148
+ 512
149
+ ],
150
+ "confidence_threshold": 0.3
151
+ },
152
+ "optimizer_config": {
153
+ "lr": 0.0002
154
+ }
155
+ }
156
+ },
157
+ "grape_detection_californianight": {
158
+ "metric": {
159
+ "mAP": 63.99
160
+ },
161
+ "hyperparameters": {
162
+ "epochs": 50,
163
+ "model_config": {
164
+ "box_loss_weight": 100.0,
165
+ "image_size": [
166
+ 512,
167
+ 512
168
+ ],
169
+ "confidence_threshold": 0.3
170
+ },
171
+ "optimizer_config": {
172
+ "lr": 0.0002
173
+ }
174
+ }
175
+ },
176
+ "fruit_detection_worldwide": {
177
+ "metric": {
178
+ "mIOU": 70.35
179
+ },
180
+ "hyperparameters": {
181
+ "epochs": 50,
182
+ "model_config": {
183
+ "box_loss_weight": 50.0,
184
+ "image_size": [
185
+ 512,
186
+ 512
187
+ ],
188
+ "confidence_threshold": 0.3
189
+ },
190
+ "optimizer_config": {
191
+ "lr": 0.0008
192
+ }
193
+ }
194
+ },
195
+ "mango_detection_australia": {
196
+ "metric": {
197
+ "mAP": 95.32
198
+ },
199
+ "hyperparameters": {
200
+ "epochs": 50,
201
+ "model_config": {
202
+ "box_loss_weight": 100.0,
203
+ "image_size": [
204
+ 512,
205
+ 512
206
+ ],
207
+ "confidence_threshold": 0.3
208
+ },
209
+ "optimizer_config": {
210
+ "lr": 0.0002
211
+ }
212
+ }
213
+ },
214
+ "apple_detection_usa": {
215
+ "metric": {
216
+ "mAP": 94.16
217
+ },
218
+ "hyperparameters": {
219
+ "epochs": 50,
220
+ "model_config": {
221
+ "box_loss_weight": 100.0,
222
+ "image_size": [
223
+ 512,
224
+ 512
225
+ ],
226
+ "confidence_threshold": 0.3
227
+ },
228
+ "optimizer_config": {
229
+ "lr": 0.0002
230
+ }
231
+ }
232
+ },
233
+ "apple_detection_spain": {
234
+ "metric": {
235
+ "mAP": 86.65
236
+ },
237
+ "hyperparameters": {
238
+ "epochs": 50,
239
+ "model_config": {
240
+ "box_loss_weight": 100.0,
241
+ "image_size": [
242
+ 512,
243
+ 512
244
+ ],
245
+ "confidence_threshold": 0.3
246
+ },
247
+ "optimizer_config": {
248
+ "lr": 0.0002
249
+ }
250
+ }
251
+ },
252
+ "apple_detection_drone_brazil": {
253
+ "metric": {
254
+ "mAP": 79.62
255
+ },
256
+ "hyperparameters": {
257
+ "epochs": 50,
258
+ "model_config": {
259
+ "box_loss_weight": 100.0,
260
+ "image_size": [
261
+ 512,
262
+ 512
263
+ ],
264
+ "confidence_threshold": 0.3
265
+ },
266
+ "optimizer_config": {
267
+ "lr": 0.0002
268
+ }
269
+ }
270
+ }
271
+ }
AgML/source/agml/_assets/public_datasources.json ADDED
@@ -0,0 +1,2506 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bean_disease_uganda": {
3
+ "ml_task": "image_classification",
4
+ "ag_task": "disease_classification",
5
+ "location": {
6
+ "continent": "africa",
7
+ "country": "uganda"
8
+ },
9
+ "sensor_modality": "rgb",
10
+ "real_synthetic": "real",
11
+ "platform": "handheld",
12
+ "input_data_format": "jpg",
13
+ "annotation_format": "directory_names",
14
+ "n_images": "1295",
15
+ "docs_url": "https://github.com/AI-Lab-Makerere/ibean/",
16
+ "classes": {
17
+ "0": "angular_leaf_spot",
18
+ "1": "bean_rust",
19
+ "2": "healthy"
20
+ },
21
+ "external_image_sources": [],
22
+ "stats": {
23
+ "mean": [
24
+ 0.484832227230072,
25
+ 0.5186858773231506,
26
+ 0.31070876121520996
27
+ ],
28
+ "std": [
29
+ 0.18201105296611786,
30
+ 0.1994817703962326,
31
+ 0.16856099665164948
32
+ ]
33
+ },
34
+ "parent_dataset": "",
35
+ "extra_metadata": {}
36
+ },
37
+ "carrot_weeds_germany": {
38
+ "ml_task": "semantic_segmentation",
39
+ "ag_task": "weed_segmentation",
40
+ "location": {
41
+ "continent": "europe",
42
+ "country": "germany"
43
+ },
44
+ "sensor_modality": "rgb",
45
+ "real_synthetic": "real",
46
+ "platform": "ground_mobile",
47
+ "input_data_format": "png",
48
+ "annotation_format": "image",
49
+ "n_images": "60",
50
+ "docs_url": "https://github.com/cwfid/dataset",
51
+ "classes": {
52
+ "0": "carrot",
53
+ "1": "weeds"
54
+ },
55
+ "external_image_sources": [],
56
+ "stats": {
57
+ "mean": [
58
+ 0.2736402750015259,
59
+ 0.31067565083503723,
60
+ 0.2736402750015259
61
+ ],
62
+ "std": [
63
+ 0.08643443882465363,
64
+ 0.0995175763964653,
65
+ 0.08643443882465363
66
+ ]
67
+ },
68
+ "parent_dataset": "",
69
+ "extra_metadata": {}
70
+ },
71
+ "plant_seedlings_aarhus": {
72
+ "ml_task": "image_classification",
73
+ "ag_task": "weed_classification",
74
+ "location": {
75
+ "continent": "europe",
76
+ "country": "denmark"
77
+ },
78
+ "sensor_modality": "rgb",
79
+ "real_synthetic": "real",
80
+ "platform": "ground_fixed",
81
+ "input_data_format": "png",
82
+ "annotation_format": "directory_names",
83
+ "n_images": "5539",
84
+ "docs_url": "https://vision.eng.au.dk/plant-seedlings-dataset/",
85
+ "classes": {
86
+ "0": "maize",
87
+ "1": "common_wheat",
88
+ "2": "sugar_beet",
89
+ "3": "scentless_mayweed",
90
+ "4": "common_chickweed",
91
+ "5": "shepherds_purse",
92
+ "6": "cleavers",
93
+ "7": "charlock",
94
+ "8": "fat_hen",
95
+ "9": "smallflowered_cranesbill",
96
+ "10": "black_grass",
97
+ "11": "loose_silkybent"
98
+ },
99
+ "external_image_sources": [],
100
+ "stats": {
101
+ "mean": [
102
+ 0.3287692368030548,
103
+ 0.28884512186050415,
104
+ 0.20677343010902405
105
+ ],
106
+ "std": [
107
+ 0.09281572699546814,
108
+ 0.09644099324941635,
109
+ 0.10552676767110825
110
+ ]
111
+ },
112
+ "parent_dataset": "",
113
+ "extra_metadata": {}
114
+ },
115
+ "soybean_weed_uav_brazil": {
116
+ "ml_task": "image_classification",
117
+ "ag_task": "weed_classification",
118
+ "location": {
119
+ "continent": "south_america",
120
+ "country": "brazil"
121
+ },
122
+ "sensor_modality": "rgb",
123
+ "real_synthetic": "real",
124
+ "platform": "uav",
125
+ "input_data_format": "tif",
126
+ "annotation_format": "directory_names",
127
+ "n_images": "15336",
128
+ "docs_url": "https://data.mendeley.com/datasets/3fmjm7ncc6/2",
129
+ "classes": {
130
+ "0": "broadleaf",
131
+ "1": "grass",
132
+ "2": "soil",
133
+ "3": "soybean"
134
+ },
135
+ "external_image_sources": [],
136
+ "stats": {
137
+ "mean": [
138
+ 0.32902154326438904,
139
+ 0.33378341794013977,
140
+ 0.1865464597940445
141
+ ],
142
+ "std": [
143
+ 0.24514122307300568,
144
+ 0.2491522580385208,
145
+ 0.1570599377155304
146
+ ]
147
+ },
148
+ "parent_dataset": "",
149
+ "extra_metadata": {}
150
+ },
151
+ "sugarcane_damage_usa": {
152
+ "ml_task": "image_classification",
153
+ "ag_task": "damage_classification",
154
+ "location": {
155
+ "continent": "north_america",
156
+ "country": "usa"
157
+ },
158
+ "sensor_modality": "rgb",
159
+ "platform": "ground_fixed",
160
+ "input_data_format": "bmp",
161
+ "annotation_format": "directory_names",
162
+ "n_images": "153",
163
+ "docs_url": "https://github.com/The77Lab/SugarcaneBilletsDataset",
164
+ "classes": {
165
+ "0": "cracked",
166
+ "1": "crushed",
167
+ "2": "no_buds",
168
+ "3": "two_buds",
169
+ "4": "single_damaged_buds",
170
+ "5": "no_damage"
171
+ },
172
+ "external_image_sources": [],
173
+ "stats": {
174
+ "mean": [
175
+ 0.24388393759727478,
176
+ 0.24691258370876312,
177
+ 0.26203399896621704
178
+ ],
179
+ "std": [
180
+ 0.22363615036010742,
181
+ 0.21735334396362305,
182
+ 0.1923859715461731
183
+ ]
184
+ },
185
+ "parent_dataset": "",
186
+ "extra_metadata": {}
187
+ },
188
+ "crop_weeds_greece": {
189
+ "ml_task": "image_classification",
190
+ "ag_task": "weed_classification",
191
+ "location": {
192
+ "continent": "europe",
193
+ "country": "greece"
194
+ },
195
+ "sensor_modality": "rgb",
196
+ "platform": "handheld",
197
+ "input_data_format": "jpg",
198
+ "annotation_format": "directory_names",
199
+ "n_images": "508",
200
+ "docs_url": "https://github.com/AUAgroup/early-crop-weed",
201
+ "classes": {
202
+ "0": "black_nightshade",
203
+ "1": "velvet_leaf",
204
+ "2": "cotton",
205
+ "3": "tomato"
206
+ },
207
+ "external_image_sources": [],
208
+ "stats": {
209
+ "mean": [
210
+ 0.7247000336647034,
211
+ 0.6694437265396118,
212
+ 0.5436030030250549
213
+ ],
214
+ "std": [
215
+ 0.16351649165153503,
216
+ 0.15307274460792542,
217
+ 0.13750970363616943
218
+ ]
219
+ },
220
+ "parent_dataset": "",
221
+ "extra_metadata": {}
222
+ },
223
+ "sugarbeet_weed_segmentation": {
224
+ "ml_task": "semantic_segmentation",
225
+ "ag_task": "weed_segmentation",
226
+ "location": {
227
+ "continent": "none",
228
+ "country": "none"
229
+ },
230
+ "sensor_modality": "rgb",
231
+ "real_synthetic": "real",
232
+ "platform": "ground",
233
+ "input_data_format": "png",
234
+ "annotation_format": "image",
235
+ "n_images": "125",
236
+ "docs_url": "https://github.com/inkyusa/weedNet",
237
+ "stats": {
238
+ "mean": [
239
+ 0.374398797750473,
240
+ 0.374398797750473,
241
+ 0.374398797750473
242
+ ],
243
+ "std": [
244
+ 0.16996806859970093,
245
+ 0.16996806859970093,
246
+ 0.16996806859970093
247
+ ]
248
+ },
249
+ "classes": {
250
+ "0": "sugar_beet",
251
+ "1": "random_weeds"
252
+ },
253
+ "external_image_sources": [],
254
+ "parent_dataset": "",
255
+ "extra_metadata": {}
256
+ },
257
+ "rangeland_weeds_australia": {
258
+ "ml_task": "image_classification",
259
+ "ag_task": "weed_classification",
260
+ "location": {
261
+ "continent": "oceania",
262
+ "country": "australia"
263
+ },
264
+ "sensor_modality": "rgb",
265
+ "real_synthetic": "real",
266
+ "platform": "ground",
267
+ "input_data_format": "jpg",
268
+ "annotation_format": "directory_names",
269
+ "n_images": "17509",
270
+ "docs_url": "https://github.com/AlexOlsen/DeepWeeds",
271
+ "classes": {
272
+ "0": "chinee_apple",
273
+ "1": "lantana",
274
+ "2": "negative",
275
+ "3": "parkinsonia",
276
+ "4": "parthenium",
277
+ "5": "prickly_acacia",
278
+ "6": "rubber_vine",
279
+ "7": "siam_weed",
280
+ "8": "snake_weed"
281
+ },
282
+ "external_image_sources": [],
283
+ "stats": {
284
+ "mean": [
285
+ 0.3785800635814667,
286
+ 0.38957375288009644,
287
+ 0.3797687590122223
288
+ ],
289
+ "std": [
290
+ 0.22421929240226746,
291
+ 0.22450383007526398,
292
+ 0.22274655103683472
293
+ ]
294
+ },
295
+ "parent_dataset": "",
296
+ "extra_metadata": {}
297
+ },
298
+ "fruit_detection_worldwide": {
299
+ "ml_task": "object_detection",
300
+ "ag_task": "fruit_detection",
301
+ "location": {
302
+ "continent": "worldwide",
303
+ "country": "worldwide"
304
+ },
305
+ "sensor_modality": "rgb",
306
+ "real_synthetic": "real",
307
+ "platform": "mixed",
308
+ "input_data_format": "jpg",
309
+ "annotation_format": "coco_json",
310
+ "n_images": "565",
311
+ "docs_url": "https://drive.google.com/drive/folders/1CmsZb1caggLRN7ANfika8WuPiywo4mBb",
312
+ "stats": {
313
+ "mean": [
314
+ 0.3779049813747406,
315
+ 0.4085286557674408,
316
+ 0.28682708740234375
317
+ ],
318
+ "std": [
319
+ 0.2306382656097412,
320
+ 0.22500208020210266,
321
+ 0.2183498740196228
322
+ ]
323
+ },
324
+ "classes": {
325
+ "1": "avocado",
326
+ "2": "rockmelon",
327
+ "3": "apple",
328
+ "4": "orange",
329
+ "5": "strawberry",
330
+ "6": "mango",
331
+ "7": "capsicum"
332
+ },
333
+ "external_image_sources": [],
334
+ "parent_dataset": "",
335
+ "extra_metadata": {}
336
+ },
337
+ "leaf_counting_denmark": {
338
+ "ml_task": "image_classification",
339
+ "ag_task": "leaf_counting",
340
+ "location": {
341
+ "continent": "europe",
342
+ "country": "denmark"
343
+ },
344
+ "platform": "mixed",
345
+ "input_data_format": "png",
346
+ "annotation_format": "directory_names",
347
+ "n_images": "9372",
348
+ "docs_url": "https://vision.eng.au.dk/leaf-counting-dataset/",
349
+ "classes": {
350
+ "0": "1",
351
+ "1": "2",
352
+ "2": "3",
353
+ "3": "4",
354
+ "4": "5",
355
+ "5": "6",
356
+ "6": "7",
357
+ "7": "8",
358
+ "8": "9+"
359
+ },
360
+ "external_image_sources": [],
361
+ "stats": {
362
+ "mean": [
363
+ 0.42661556601524353,
364
+ 0.427418053150177,
365
+ 0.3417948782444
366
+ ],
367
+ "std": [
368
+ 0.12296482920646667,
369
+ 0.14348869025707245,
370
+ 0.1148977279663086
371
+ ]
372
+ },
373
+ "parent_dataset": "",
374
+ "extra_metadata": {}
375
+ },
376
+ "apple_detection_usa": {
377
+ "ml_task": "object_detection",
378
+ "ag_task": "fruit_detection",
379
+ "location": {
380
+ "continent": "north_america",
381
+ "country": "usa"
382
+ },
383
+ "sensor_modality": "rgb",
384
+ "real_synthetic": "real",
385
+ "platform": "ground",
386
+ "input_data_format": "png",
387
+ "annotation_format": "coco_json",
388
+ "n_images": "2290",
389
+ "docs_url": "https://hdl.handle.net/2376/17721",
390
+ "stats": {
391
+ "mean": [
392
+ 0.2810896933078766,
393
+ 0.29005560278892517,
394
+ 0.2775411605834961
395
+ ],
396
+ "std": [
397
+ 0.18863406777381897,
398
+ 0.18647761642932892,
399
+ 0.1885077804327011
400
+ ]
401
+ },
402
+ "classes": {
403
+ "1": "apple"
404
+ },
405
+ "external_image_sources": [],
406
+ "parent_dataset": "",
407
+ "extra_metadata": {}
408
+ },
409
+ "mango_detection_australia": {
410
+ "ml_task": "object_detection",
411
+ "ag_task": "fruit_detection",
412
+ "location": {
413
+ "continent": "oceania",
414
+ "country": "australia"
415
+ },
416
+ "sensor_modality": "rgb",
417
+ "real_synthetic": "real",
418
+ "platform": "handheld",
419
+ "input_data_format": "jpg",
420
+ "annotation_format": "coco_json",
421
+ "n_images": "1242",
422
+ "docs_url": "https://researchdata.edu.au/mangoyolo-set/1697505",
423
+ "stats": {
424
+ "mean": [
425
+ 0.07288186252117157,
426
+ 0.1193767637014389,
427
+ 0.050045181065797806
428
+ ],
429
+ "std": [
430
+ 0.058673080056905746,
431
+ 0.07924807816743851,
432
+ 0.043408144265413284
433
+ ]
434
+ },
435
+ "classes": {
436
+ "1": "mango"
437
+ },
438
+ "external_image_sources": [],
439
+ "parent_dataset": "",
440
+ "extra_metadata": {}
441
+ },
442
+ "tomato_ripeness_detection": {
443
+ "ml_task": "object_detection",
444
+ "ag_task": "fruit_ripeness_detection",
445
+ "location": {
446
+ "continent": "worldwide",
447
+ "country": "worldwide"
448
+ },
449
+ "sensor_modality": "rgb",
450
+ "real_synthetic": "real",
451
+ "platform": "ground",
452
+ "input_data_format": "jpg",
453
+ "annotation_format": "coco_json",
454
+ "n_images": "804",
455
+ "docs_url": "https://www.kaggle.com/datasets/nexuswho/laboro-tomato",
456
+ "classes": {
457
+ "1": "b_fully_ripened",
458
+ "2": "b_half_ripened",
459
+ "3": "b_green",
460
+ "4": "l_fully_ripened",
461
+ "5": "l_half_ripened",
462
+ "6": "l_green"
463
+ },
464
+ "external_image_sources": [],
465
+ "stats": {
466
+ "mean": [
467
+ 0.46756285429000854,
468
+ 0.5048850178718567,
469
+ 0.317764014005661
470
+ ],
471
+ "std": [
472
+ 0.21034087240695953,
473
+ 0.19280721247196198,
474
+ 0.22368815541267395
475
+ ]
476
+ },
477
+ "parent_dataset": "",
478
+ "extra_metadata": {}
479
+ },
480
+ "apple_flower_segmentation": {
481
+ "ml_task": "semantic_segmentation",
482
+ "ag_task": "flower_segmentation",
483
+ "location": {
484
+ "continent": "north_america",
485
+ "country": "usa"
486
+ },
487
+ "sensor_modality": "rgb",
488
+ "real_synthetic": "real",
489
+ "platform": "ground",
490
+ "input_data_format": "jpg",
491
+ "annotation_format": "image",
492
+ "n_images": "148",
493
+ "docs_url": "https://data.nal.usda.gov/dataset/data-multi-species-fruit-flower-detection-using-refined-semantic-segmentation-network",
494
+ "stats": {
495
+ "mean": [
496
+ 0.4580204486846924,
497
+ 0.47888585925102234,
498
+ 0.4053090512752533
499
+ ],
500
+ "std": [
501
+ 0.2040298879146576,
502
+ 0.19258278608322144,
503
+ 0.2156316637992859
504
+ ]
505
+ },
506
+ "classes": {
507
+ "0": "apple"
508
+ },
509
+ "external_image_sources": [],
510
+ "parent_dataset": "",
511
+ "extra_metadata": {}
512
+ },
513
+ "apple_segmentation_minnesota": {
514
+ "ml_task": "semantic_segmentation",
515
+ "ag_task": "fruit_segmentation",
516
+ "location": {
517
+ "continent": "north_america",
518
+ "country": "usa"
519
+ },
520
+ "sensor_modality": "rgb",
521
+ "real_synthetic": "real",
522
+ "platform": "ground",
523
+ "input_data_format": "png",
524
+ "annotation_format": "image",
525
+ "n_images": "670",
526
+ "docs_url": "https://rsn.umn.edu/projects/orchard-monitoring/minneapple",
527
+ "stats": {
528
+ "mean": [
529
+ 0.3736153841018677,
530
+ 0.43716397881507874,
531
+ 0.33839237689971924
532
+ ],
533
+ "std": [
534
+ 0.2627662420272827,
535
+ 0.28767505288124084,
536
+ 0.33136168122291565
537
+ ]
538
+ },
539
+ "classes": {
540
+ "0": "apple"
541
+ },
542
+ "external_image_sources": [],
543
+ "parent_dataset": "",
544
+ "extra_metadata": {}
545
+ },
546
+ "rice_seedling_segmentation": {
547
+ "ml_task": "semantic_segmentation",
548
+ "ag_task": "weed_segmentation",
549
+ "location": {
550
+ "continent": "asia",
551
+ "country": "china"
552
+ },
553
+ "sensor_modality": "rgb",
554
+ "real_synthetic": "real",
555
+ "platform": "handheld",
556
+ "input_data_format": "jpg",
557
+ "annotation_format": "image",
558
+ "n_images": "224",
559
+ "docs_url": "https://github.com/kabbas570/CED-Net-Crops-and-Weeds-Segmentation-for-Smart-Farming-Using-a-Small-Cascaded-Encoder-Decoder-Archi",
560
+ "stats": {
561
+ "mean": [
562
+ 0.6552301049232483,
563
+ 0.7014582753181458,
564
+ 0.6353084444999695
565
+ ],
566
+ "std": [
567
+ 0.08477462828159332,
568
+ 0.1075846329331398,
569
+ 0.11058590561151505
570
+ ]
571
+ },
572
+ "classes": {
573
+ "0": "Rice",
574
+ "1": "Weed"
575
+ },
576
+ "external_image_sources": [],
577
+ "parent_dataset": "",
578
+ "extra_metadata": {}
579
+ },
580
+ "plant_village_classification": {
581
+ "ml_task": "image_classification",
582
+ "ag_task": "disease_classification",
583
+ "location": {
584
+ "continent": "north_america",
585
+ "country": "usa"
586
+ },
587
+ "sensor_modality": "rgb",
588
+ "real_synthetic": "real",
589
+ "platform": "ground",
590
+ "input_data_format": "jpg",
591
+ "annotation_format": "directory_names",
592
+ "n_images": "55448",
593
+ "docs_url": "https://github.com/spMohanty/PlantVillage-Dataset",
594
+ "classes": {
595
+ "0": "Apple___Apple_scab",
596
+ "1": "Apple___Black_rot",
597
+ "2": "Apple___Cedar_apple_rust",
598
+ "3": "Apple___healthy",
599
+ "4": "Background_without_leaves",
600
+ "5": "Blueberry___healthy",
601
+ "6": "Cherry___Powdery_mildew",
602
+ "7": "Cherry___healthy",
603
+ "8": "Corn___Cercospora_leaf_spot Gray_leaf_spot",
604
+ "9": "Corn___Common_rust",
605
+ "10": "Corn___Northern_Leaf_Blight",
606
+ "11": "Corn___healthy",
607
+ "12": "Grape___Black_rot",
608
+ "13": "Grape___Esca_(Black_Measles)",
609
+ "14": "Grape___Leaf_blight_(Isariopsis_Leaf_Spot)",
610
+ "15": "Grape___healthy",
611
+ "16": "Orange___Haunglongbing_(Citrus_greening)",
612
+ "17": "Peach___Bacterial_spot",
613
+ "18": "Peach___healthy",
614
+ "19": "Pepper,_bell___Bacterial_spot",
615
+ "20": "Pepper,_bell___healthy",
616
+ "21": "Potato___Early_blight",
617
+ "22": "Potato___Late_blight",
618
+ "23": "Potato___healthy",
619
+ "24": "Raspberry___healthy",
620
+ "25": "Soybean___healthy",
621
+ "26": "Squash___Powdery_mildew",
622
+ "27": "Strawberry___Leaf_scorch",
623
+ "28": "Strawberry___healthy",
624
+ "29": "Tomato___Bacterial_spot",
625
+ "30": "Tomato___Early_blight",
626
+ "31": "Tomato___Late_blight",
627
+ "32": "Tomato___Leaf_Mold",
628
+ "33": "Tomato___Septoria_leaf_spot",
629
+ "34": "Tomato___Spider_mites Two-spotted_spider_mite",
630
+ "35": "Tomato___Target_Spot",
631
+ "36": "Tomato___Tomato_Yellow_Leaf_Curl_Virus",
632
+ "37": "Tomato___Tomato_mosaic_virus",
633
+ "38": "Tomato___healthy"
634
+ },
635
+ "external_image_sources": [],
636
+ "stats": {
637
+ "mean": [
638
+ 0.46678444743156433,
639
+ 0.4892062544822693,
640
+ 0.4119776785373688
641
+ ],
642
+ "std": [
643
+ 0.17720915377140045,
644
+ 0.15169931948184967,
645
+ 0.19375933706760406
646
+ ]
647
+ },
648
+ "parent_dataset": "",
649
+ "extra_metadata": {}
650
+ },
651
+ "autonomous_greenhouse_regression": {
652
+ "ml_task": "image_regression",
653
+ "ag_task": "ag_regression",
654
+ "sensor_modality": "rgb",
655
+ "real_synthetic": "real",
656
+ "platform": "ground",
657
+ "input_data_format": "png",
658
+ "annotation_format": "json",
659
+ "n_images": "389",
660
+ "docs_url": "https://data.4tu.nl/articles/dataset/3rd_Autonomous_Greenhouse_Challenge_Online_Challenge_Lettuce_Images/15023088/1",
661
+ "location": {
662
+ "continent": "europe",
663
+ "country": "netherlands"
664
+ },
665
+ "classes": {
666
+ "regression": {
667
+ "0": "FreshWeightShoot",
668
+ "1": "DryWeightShoot",
669
+ "2": "Height",
670
+ "3": "Diameter",
671
+ "4": "LeafArea"
672
+ },
673
+ "classification": {
674
+ "0": "Aphylion",
675
+ "1": "Lugano",
676
+ "2": "Salanova",
677
+ "3": "Satine"
678
+ }
679
+ },
680
+ "external_image_sources": [
681
+ "depth"
682
+ ],
683
+ "stats": {
684
+ "mean": [
685
+ 0.4848844110965729,
686
+ 0.45935600996017456,
687
+ 0.4063475430011749
688
+ ],
689
+ "std": [
690
+ 0.13606780767440796,
691
+ 0.13940763473510742,
692
+ 0.19247490167617798
693
+ ]
694
+ },
695
+ "parent_dataset": "",
696
+ "extra_metadata": {}
697
+ },
698
+ "grape_detection_syntheticday": {
699
+ "classes": {
700
+ "1": "grape"
701
+ },
702
+ "ml_task": "object_detection",
703
+ "ag_task": "fruit_detection",
704
+ "location": {
705
+ "continent": "digital",
706
+ "country": "digital"
707
+ },
708
+ "sensor_modality": "rgb",
709
+ "real_synthetic": "synthetic",
710
+ "platform": "ground",
711
+ "input_data_format": "jpg",
712
+ "annotation_format": "coco_json",
713
+ "n_images": "448",
714
+ "docs_url": "N/A",
715
+ "external_image_sources": [],
716
+ "stats": {
717
+ "mean": [
718
+ 0.24965472519397736,
719
+ 0.2763761281967163,
720
+ 0.20683138072490692
721
+ ],
722
+ "std": [
723
+ 0.15098054707050323,
724
+ 0.18630442023277283,
725
+ 0.2241816222667694
726
+ ]
727
+ },
728
+ "parent_dataset": "",
729
+ "extra_metadata": {}
730
+ },
731
+ "grape_detection_californiaday": {
732
+ "classes": {
733
+ "1": "grape"
734
+ },
735
+ "ml_task": "object_detection",
736
+ "ag_task": "fruit_detection",
737
+ "location": {
738
+ "continent": "north_america",
739
+ "country": "usa"
740
+ },
741
+ "sensor_modality": "rgb",
742
+ "real_synthetic": "real",
743
+ "platform": "ground",
744
+ "input_data_format": "jpg",
745
+ "annotation_format": "image",
746
+ "n_images": "126",
747
+ "docs_url": "",
748
+ "external_image_sources": [],
749
+ "stats": {
750
+ "mean": [
751
+ 0.4455319046974182,
752
+ 0.4638977646827698,
753
+ 0.27496835589408875
754
+ ],
755
+ "std": [
756
+ 0.2677213251590729,
757
+ 0.25918272137641907,
758
+ 0.2625304162502289
759
+ ]
760
+ },
761
+ "parent_dataset": "",
762
+ "extra_metadata": {}
763
+ },
764
+ "grape_detection_californianight": {
765
+ "classes": {
766
+ "1": "grape"
767
+ },
768
+ "ml_task": "object_detection",
769
+ "ag_task": "fruit_detection",
770
+ "location": {
771
+ "continent": "north_america",
772
+ "country": "usa"
773
+ },
774
+ "sensor_modality": "rgb",
775
+ "real_synthetic": "real",
776
+ "platform": "ground",
777
+ "input_data_format": "jpg",
778
+ "annotation_format": "image",
779
+ "n_images": "150",
780
+ "docs_url": "",
781
+ "external_image_sources": [],
782
+ "stats": {
783
+ "mean": [
784
+ 0.22235676646232605,
785
+ 0.21305303275585175,
786
+ 0.22475562989711761
787
+ ],
788
+ "std": [
789
+ 0.20471057295799255,
790
+ 0.20105913281440735,
791
+ 0.20582404732704163
792
+ ]
793
+ },
794
+ "parent_dataset": "",
795
+ "extra_metadata": {}
796
+ },
797
+ "guava_disease_pakistan": {
798
+ "classes": {
799
+ "0": "Canker",
800
+ "1": "Dot",
801
+ "2": "Mummification",
802
+ "3": "Rust"
803
+ },
804
+ "ml_task": "image_classification",
805
+ "ag_task": "disease_classification",
806
+ "location": {
807
+ "continent": "asia",
808
+ "country": "pakistan"
809
+ },
810
+ "sensor_modality": "rgb",
811
+ "platform": "ground",
812
+ "input_data_format": "jpg",
813
+ "annotation_format": "directory_names",
814
+ "n_images": "306",
815
+ "docs_url": "https://data.mendeley.com/datasets/s8x6jn5cvr/1",
816
+ "external_image_sources": [],
817
+ "stats": {
818
+ "mean": [
819
+ 0.44554802775382996,
820
+ 0.45087501406669617,
821
+ 0.32288917899131775
822
+ ],
823
+ "std": [
824
+ 0.18661096692085266,
825
+ 0.18359650671482086,
826
+ 0.18744416534900665
827
+ ]
828
+ },
829
+ "parent_dataset": "",
830
+ "extra_metadata": {}
831
+ },
832
+ "apple_detection_spain": {
833
+ "classes": {
834
+ "1": "apple"
835
+ },
836
+ "ml_task": "object_detection",
837
+ "ag_task": "fruit_detection",
838
+ "location": {
839
+ "continent": "europe",
840
+ "country": "spain"
841
+ },
842
+ "sensor_modality": "rgb",
843
+ "platform": "ground",
844
+ "input_data_format": "jpg",
845
+ "annotation_format": "coco_json",
846
+ "n_images": "967",
847
+ "docs_url": "https://www.grap.udl.cat/en/publications/KFuji_RGBDS_database.html",
848
+ "external_image_sources": [],
849
+ "stats": {
850
+ "mean": [
851
+ 0.3745550215244293,
852
+ 0.4699203670024872,
853
+ 0.3931747078895569
854
+ ],
855
+ "std": [
856
+ 0.2724320888519287,
857
+ 0.27973315119743347,
858
+ 0.2829023003578186
859
+ ]
860
+ },
861
+ "parent_dataset": "",
862
+ "extra_metadata": {}
863
+ },
864
+ "apple_detection_drone_brazil": {
865
+ "classes": {
866
+ "1": "apple"
867
+ },
868
+ "ml_task": "object_detection",
869
+ "ag_task": "fruit_detection",
870
+ "location": {
871
+ "continent": "south_america",
872
+ "country": "brazil"
873
+ },
874
+ "sensor_modality": "rgb",
875
+ "platform": "ground",
876
+ "input_data_format": "jpg",
877
+ "annotation_format": "coco_json",
878
+ "n_images": "689",
879
+ "docs_url": "https://github.com/thsant/add256/tree/zenodo-1.0",
880
+ "external_image_sources": [],
881
+ "stats": {
882
+ "mean": [
883
+ 0.3362397849559784,
884
+ 0.48177945613861084,
885
+ 0.32026395201683044
886
+ ],
887
+ "std": [
888
+ 0.18435567617416382,
889
+ 0.1873108297586441,
890
+ 0.1734534204006195
891
+ ]
892
+ },
893
+ "parent_dataset": "",
894
+ "extra_metadata": {}
895
+ },
896
+ "plant_doc_classification": {
897
+ "classes": {
898
+ "0": "Corn leaf blight",
899
+ "1": "Tomato Early blight leaf",
900
+ "2": "Potato leaf early blight",
901
+ "3": "Potato leaf late blight",
902
+ "4": "Blueberry leaf",
903
+ "5": "grape leaf black rot",
904
+ "6": "Bell_pepper leaf spot",
905
+ "7": "Cherry leaf",
906
+ "8": "Peach leaf",
907
+ "9": "Soyabean leaf",
908
+ "10": "Strawberry leaf",
909
+ "11": "Apple Scab Leaf",
910
+ "12": "Corn rust leaf",
911
+ "13": "Apple leaf",
912
+ "14": "Corn Gray leaf spot",
913
+ "15": "Tomato leaf mosaic virus",
914
+ "16": "Tomato mold leaf",
915
+ "17": "Tomato leaf yellow virus",
916
+ "18": "Tomato leaf bacterial spot",
917
+ "19": "Tomato leaf late blight",
918
+ "20": "Squash Powdery mildew leaf",
919
+ "21": "Bell_pepper leaf",
920
+ "22": "grape leaf",
921
+ "23": "Apple rust leaf",
922
+ "24": "Tomato Septoria leaf spot",
923
+ "25": "Tomato leaf",
924
+ "26": "Raspberry leaf",
925
+ "27": "Tomato two spotted spider mites leaf"
926
+ },
927
+ "ml_task": "image_classification",
928
+ "ag_task": "disease_classification",
929
+ "location": {
930
+ "continent": "worldwide",
931
+ "country": "worldwide"
932
+ },
933
+ "sensor_modality": "rgb",
934
+ "real_synthetic": "real",
935
+ "platform": "ground",
936
+ "input_data_format": "jpg",
937
+ "annotation_format": "directory_names",
938
+ "n_images": "2598",
939
+ "docs_url": "https://github.com/pratikkayal/PlantDoc-Dataset",
940
+ "external_image_sources": [],
941
+ "stats": {
942
+ "mean": [
943
+ 0.48221418261528015,
944
+ 0.549874484539032,
945
+ 0.3833079934120178
946
+ ],
947
+ "std": [
948
+ 0.21240688860416412,
949
+ 0.2036021202802658,
950
+ 0.22107268869876862
951
+ ]
952
+ },
953
+ "parent_dataset": "",
954
+ "extra_metadata": {}
955
+ },
956
+ "plant_doc_detection": {
957
+ "classes": {
958
+ "1": "Corn leaf blight",
959
+ "2": "Tomato Early blight leaf",
960
+ "3": "Potato leaf early blight",
961
+ "4": "Potato leaf late blight",
962
+ "5": "Blueberry leaf",
963
+ "6": "grape leaf black rot",
964
+ "7": "Bell_pepper leaf spot",
965
+ "8": "Cherry leaf",
966
+ "9": "Peach leaf",
967
+ "10": "Soyabean leaf",
968
+ "11": "Strawberry leaf",
969
+ "12": "Apple Scab Leaf",
970
+ "13": "Corn rust leaf",
971
+ "14": "Apple leaf",
972
+ "15": "Corn Gray leaf spot",
973
+ "16": "Tomato leaf mosaic virus",
974
+ "17": "Tomato mold leaf",
975
+ "18": "Tomato leaf yellow virus",
976
+ "19": "Tomato leaf bacterial spot",
977
+ "20": "Tomato leaf late blight",
978
+ "21": "Squash Powdery mildew leaf",
979
+ "22": "Bell_pepper leaf",
980
+ "23": "grape leaf",
981
+ "24": "Apple rust leaf",
982
+ "25": "Tomato Septoria leaf spot",
983
+ "26": "Tomato leaf",
984
+ "27": "Raspberry leaf",
985
+ "28": "Potato leaf",
986
+ "29": "Tomato two spotted spider mites leaf"
987
+ },
988
+ "ml_task": "object_detection",
989
+ "ag_task": "disease_detection",
990
+ "location": {
991
+ "continent": "worldwide",
992
+ "country": "worldwide"
993
+ },
994
+ "sensor_modality": "rgb",
995
+ "real_synthetic": "real",
996
+ "platform": "ground",
997
+ "input_data_format": "jpg",
998
+ "annotation_format": "coco_json",
999
+ "n_images": "2346",
1000
+ "docs_url": "https://github.com/pratikkayal/PlantDoc-Object-Detection-Dataset",
1001
+ "external_image_sources": [],
1002
+ "stats": {
1003
+ "mean": [
1004
+ 0.4765770733356476,
1005
+ 0.5437355637550354,
1006
+ 0.3779796361923218
1007
+ ],
1008
+ "std": [
1009
+ 0.21106934547424316,
1010
+ 0.2036709189414978,
1011
+ 0.21807792782783508
1012
+ ]
1013
+ },
1014
+ "parent_dataset": "",
1015
+ "extra_metadata": {}
1016
+ },
1017
+ "wheat_head_counting": {
1018
+ "classes": {
1019
+ "1": "Wheat Head"
1020
+ },
1021
+ "ml_task": "object_detection",
1022
+ "ag_task": "wheat_head_counting",
1023
+ "location": {
1024
+ "continent": "worldwide",
1025
+ "country": "worldwide"
1026
+ },
1027
+ "sensor_modality": "rgb",
1028
+ "real_synthetic": "real",
1029
+ "platform": "ground",
1030
+ "input_data_format": "jpg",
1031
+ "annotation_format": "coco_json",
1032
+ "n_images": "6512",
1033
+ "docs_url": "https://zenodo.org/record/5092309",
1034
+ "external_image_sources": [],
1035
+ "stats": {
1036
+ "mean": [
1037
+ 0.371184766292572,
1038
+ 0.36074304580688477,
1039
+ 0.23014496266841888
1040
+ ],
1041
+ "std": [
1042
+ 0.2190379649400711,
1043
+ 0.21708978712558746,
1044
+ 0.17700935900211334
1045
+ ]
1046
+ },
1047
+ "parent_dataset": "",
1048
+ "extra_metadata": {}
1049
+ },
1050
+ "peachpear_flower_segmentation": {
1051
+ "classes": {
1052
+ "1": "Flower"
1053
+ },
1054
+ "ml_task": "semantic_segmentation",
1055
+ "ag_task": "flower_segmentation",
1056
+ "location": {
1057
+ "continent": "worldwide",
1058
+ "country": "worldwide"
1059
+ },
1060
+ "sensor_modality": "rgb",
1061
+ "real_synthetic": "real",
1062
+ "platform": "handheld/ground",
1063
+ "input_data_format": "jpg",
1064
+ "annotation_format": "image",
1065
+ "n_images": "42",
1066
+ "docs_url": "https://data.nal.usda.gov/dataset/data-multi-species-fruit-flower-detection-using-refined-semantic-segmentation-network",
1067
+ "external_image_sources": [],
1068
+ "stats": {
1069
+ "mean": [
1070
+ 0.4377596378326416,
1071
+ 0.4347001910209656,
1072
+ 0.4147627353668213
1073
+ ],
1074
+ "std": [
1075
+ 0.20811232924461365,
1076
+ 0.2141699343919754,
1077
+ 0.2441396266222
1078
+ ]
1079
+ },
1080
+ "parent_dataset": "",
1081
+ "extra_metadata": {}
1082
+ },
1083
+ "red_grapes_and_leaves_segmentation": {
1084
+ "classes": {
1085
+ "1": "Leaf",
1086
+ "2": "Grape"
1087
+ },
1088
+ "ml_task": "semantic_segmentation",
1089
+ "ag_task": "vineyard_scene_segmentation",
1090
+ "location": {
1091
+ "continent": "europe",
1092
+ "country": "greece"
1093
+ },
1094
+ "sensor_modality": "rgb",
1095
+ "real_synthetic": "real",
1096
+ "platform": "handheld/ground",
1097
+ "input_data_format": "jpg",
1098
+ "annotation_format": "image",
1099
+ "n_images": "258",
1100
+ "docs_url": "https://link.springer.com/chapter/10.1007/978-3-030-48791-1_22",
1101
+ "external_image_sources": [],
1102
+ "stats": {
1103
+ "mean": [
1104
+ 0.392019122838974,
1105
+ 0.49319183826446533,
1106
+ 0.5254659056663513
1107
+ ],
1108
+ "std": [
1109
+ 0.2721235156059265,
1110
+ 0.2871488034725189,
1111
+ 0.2831265926361084
1112
+ ]
1113
+ },
1114
+ "parent_dataset": "",
1115
+ "extra_metadata": {}
1116
+ },
1117
+ "white_grapes_and_leaves_segmentation": {
1118
+ "classes": {
1119
+ "1": "Leaf",
1120
+ "2": "Grape"
1121
+ },
1122
+ "ml_task": "semantic_segmentation",
1123
+ "ag_task": "vineyard_scene_segmentation",
1124
+ "location": {
1125
+ "continent": "europe",
1126
+ "country": "greece"
1127
+ },
1128
+ "sensor_modality": "rgb",
1129
+ "real_synthetic": "real",
1130
+ "platform": "handheld/ground",
1131
+ "input_data_format": "jpg",
1132
+ "annotation_format": "image",
1133
+ "n_images": "273",
1134
+ "docs_url": "https://link.springer.com/chapter/10.1007/978-3-030-48791-1_22",
1135
+ "external_image_sources": [],
1136
+ "stats": {
1137
+ "mean": [
1138
+ 0.30906906723976135,
1139
+ 0.47296029329299927,
1140
+ 0.5238653421401978
1141
+ ],
1142
+ "std": [
1143
+ 0.23329640924930573,
1144
+ 0.24588093161582947,
1145
+ 0.25335168838500977
1146
+ ]
1147
+ },
1148
+ "parent_dataset": "",
1149
+ "extra_metadata": {}
1150
+ },
1151
+ "ghai_romaine_detection": {
1152
+ "classes": {
1153
+ "1": "romaine"
1154
+ },
1155
+ "ml_task": "object_detection",
1156
+ "ag_task": "crop_detection",
1157
+ "location": {
1158
+ "continent": "north_america",
1159
+ "country": "usa"
1160
+ },
1161
+ "sensor_modality": "rgb",
1162
+ "real_synthetic": "real",
1163
+ "platform": "handheld/ground",
1164
+ "input_data_format": "jpg",
1165
+ "annotation_format": "coco_json",
1166
+ "n_images": "500",
1167
+ "docs_url": "https://github.com/AxisAg/GHAIDatasets/blob/main/datasets/romaine.md",
1168
+ "external_image_sources": [],
1169
+ "stats": {
1170
+ "mean": [
1171
+ 0.3551516532897949,
1172
+ 0.4719887971878052,
1173
+ 0.33378446102142334
1174
+ ],
1175
+ "std": [
1176
+ 0.253168523311615,
1177
+ 0.26515713334083557,
1178
+ 0.2393222600221634
1179
+ ]
1180
+ },
1181
+ "parent_dataset": "",
1182
+ "extra_metadata": {}
1183
+ },
1184
+ "ghai_green_cabbage_detection": {
1185
+ "classes": {
1186
+ "1": "green_cabbage"
1187
+ },
1188
+ "ml_task": "object_detection",
1189
+ "ag_task": "crop_detection",
1190
+ "location": {
1191
+ "continent": "north_america",
1192
+ "country": "usa"
1193
+ },
1194
+ "sensor_modality": "rgb",
1195
+ "real_synthetic": "real",
1196
+ "platform": "handheld/ground",
1197
+ "input_data_format": "jpg",
1198
+ "annotation_format": "coco_json",
1199
+ "n_images": "500",
1200
+ "docs_url": "https://github.com/AxisAg/GHAIDatasets/blob/main/datasets/green_cabbage.md",
1201
+ "external_image_sources": [],
1202
+ "stats": {
1203
+ "mean": [
1204
+ 0.4649180769920349,
1205
+ 0.49569201469421387,
1206
+ 0.46829652786254883
1207
+ ],
1208
+ "std": [
1209
+ 0.28846681118011475,
1210
+ 0.27976617217063904,
1211
+ 0.276553213596344
1212
+ ]
1213
+ },
1214
+ "parent_dataset": "",
1215
+ "extra_metadata": {}
1216
+ },
1217
+ "ghai_iceberg_lettuce_detection": {
1218
+ "classes": {
1219
+ "1": "iceberg_lettuce"
1220
+ },
1221
+ "ml_task": "object_detection",
1222
+ "ag_task": "crop_detection",
1223
+ "location": {
1224
+ "continent": "north_america",
1225
+ "country": "usa"
1226
+ },
1227
+ "sensor_modality": "rgb",
1228
+ "real_synthetic": "real",
1229
+ "platform": "handheld/ground",
1230
+ "input_data_format": "jpg",
1231
+ "annotation_format": "coco_json",
1232
+ "n_images": "500",
1233
+ "docs_url": "https://github.com/AxisAg/GHAIDatasets/blob/main/datasets/iceberg.md",
1234
+ "external_image_sources": [],
1235
+ "stats": {
1236
+ "mean": [
1237
+ 0.3193310499191284,
1238
+ 0.4143226742744446,
1239
+ 0.3003973960876465
1240
+ ],
1241
+ "std": [
1242
+ 0.2828165590763092,
1243
+ 0.2841264605522156,
1244
+ 0.25660744309425354
1245
+ ]
1246
+ },
1247
+ "parent_dataset": "",
1248
+ "extra_metadata": {}
1249
+ },
1250
+ "riseholme_strawberry_classification_2021": {
1251
+ "classes": {
1252
+ "0": "anomalous",
1253
+ "1": "occluded",
1254
+ "2": "ripe",
1255
+ "3": "unripe"
1256
+ },
1257
+ "ml_task": "image_classification",
1258
+ "ag_task": "disease_classification",
1259
+ "sensor_modality": "rgb",
1260
+ "real_synthetic": "real",
1261
+ "platform": "ground",
1262
+ "input_data_format": "png",
1263
+ "annotation_format": "directory_names",
1264
+ "n_images": "3520",
1265
+ "docs_url": "https://github.com/ctyeong/Riseholme-2021",
1266
+ "location": {
1267
+ "continent": "europe",
1268
+ "country": "uk"
1269
+ },
1270
+ "external_image_sources": [],
1271
+ "stats": {
1272
+ "mean": [
1273
+ 0.5753851532936096,
1274
+ 0.46378713846206665,
1275
+ 0.36370429396629333
1276
+ ],
1277
+ "std": [
1278
+ 0.19837889075279236,
1279
+ 0.19222107529640198,
1280
+ 0.1773509532213211
1281
+ ]
1282
+ },
1283
+ "parent_dataset": "",
1284
+ "extra_metadata": {}
1285
+ },
1286
+ "ghai_broccoli_detection": {
1287
+ "classes": {
1288
+ "1": "canopy",
1289
+ "2": "crown"
1290
+ },
1291
+ "ml_task": "object_detection",
1292
+ "ag_task": "crop_detection",
1293
+ "location": {
1294
+ "continent": "north_america",
1295
+ "country": "usa"
1296
+ },
1297
+ "sensor_modality": "rgb",
1298
+ "real_synthetic": "real",
1299
+ "platform": "handheld/ground",
1300
+ "input_data_format": "jpg",
1301
+ "annotation_format": "coco_json",
1302
+ "n_images": "500",
1303
+ "docs_url": "https://github.com/AxisAg/GHAIDatasets/blob/main/datasets/broccoli.md",
1304
+ "external_image_sources": [],
1305
+ "stats": {
1306
+ "mean": [
1307
+ 0.42832377552986145,
1308
+ 0.45015057921409607,
1309
+ 0.43816232681274414
1310
+ ],
1311
+ "std": [
1312
+ 0.24546495079994202,
1313
+ 0.23975253105163574,
1314
+ 0.23829665780067444
1315
+ ]
1316
+ },
1317
+ "parent_dataset": "",
1318
+ "extra_metadata": {}
1319
+ },
1320
+ "bean_synthetic_earlygrowth_aerial": {
1321
+ "classes": {
1322
+ "1": "leaves",
1323
+ "2": "branches"
1324
+ },
1325
+ "ml_task": "semantic_segmentation",
1326
+ "ag_task": "plant_segmentation",
1327
+ "location": {
1328
+ "continent": "digital",
1329
+ "country": "digital"
1330
+ },
1331
+ "sensor_modality": "rgb",
1332
+ "real_synthetic": "synthetic",
1333
+ "platform": "aerial",
1334
+ "input_data_format": "jpg",
1335
+ "annotation_format": "image",
1336
+ "n_images": "2500",
1337
+ "docs_url": "https://github.com/Project-AgML/AgML/blob/main/docs/datasets/bean_synthetic_earlygrowth_aerial.md",
1338
+ "external_image_sources": [],
1339
+ "stats": {
1340
+ "mean": [
1341
+ 0.24804304540157318,
1342
+ 0.2218194305896759,
1343
+ 0.16611839830875397
1344
+ ],
1345
+ "std": [
1346
+ 0.10789012163877487,
1347
+ 0.12054657936096191,
1348
+ 0.08992248773574829
1349
+ ]
1350
+ },
1351
+ "parent_dataset": "",
1352
+ "extra_metadata": {}
1353
+ },
1354
+ "ghai_strawberry_fruit_detection": {
1355
+ "classes": {
1356
+ "1": "Bud",
1357
+ "2": "Calyx",
1358
+ "3": "Detached Fruit",
1359
+ "4": "Flower",
1360
+ "5": "Large green",
1361
+ "6": "Leaf",
1362
+ "7": "Ripe fruit",
1363
+ "8": "Small Green",
1364
+ "9": "Stem",
1365
+ "10": "Unripe fruit"
1366
+ },
1367
+ "ml_task": "object_detection",
1368
+ "ag_task": "crop_detection",
1369
+ "location": {
1370
+ "continent": "north_america",
1371
+ "country": "usa"
1372
+ },
1373
+ "sensor_modality": "rgb",
1374
+ "real_synthetic": "real",
1375
+ "platform": "handheld/ground",
1376
+ "input_data_format": "jpg",
1377
+ "annotation_format": "coco_json",
1378
+ "n_images": "500",
1379
+ "docs_url": "https://github.com/AxisAg/GHAIDatasets/blob/main/datasets/strawberry.md",
1380
+ "external_image_sources": [],
1381
+ "stats": {
1382
+ "mean": [
1383
+ 0.49159616231918335,
1384
+ 0.5238277316093445,
1385
+ 0.4485996663570404
1386
+ ],
1387
+ "std": [
1388
+ 0.18163496255874634,
1389
+ 0.16137710213661194,
1390
+ 0.18042609095573425
1391
+ ]
1392
+ },
1393
+ "parent_dataset": "",
1394
+ "extra_metadata": {}
1395
+ },
1396
+ "vegann_multicrop_presence_segmentation": {
1397
+ "classes": {
1398
+ "1": "plant"
1399
+ },
1400
+ "ml_task": "semantic_segmentation",
1401
+ "ag_task": "vegetation_segmentation",
1402
+ "location": {
1403
+ "continent": "worldwide",
1404
+ "country": "worldwide"
1405
+ },
1406
+ "sensor_modality": "rgb",
1407
+ "real_synthetic": "real",
1408
+ "platform": "aerial",
1409
+ "input_data_format": "png",
1410
+ "annotation_format": "image",
1411
+ "n_images": "3775",
1412
+ "docs_url": "https://zenodo.org/records/7636408",
1413
+ "stats": {
1414
+ "mean": [
1415
+ 16.391704559326172,
1416
+ 18.685382843017578,
1417
+ 14.235113143920898
1418
+ ],
1419
+ "std": [
1420
+ 4.5903120040893555,
1421
+ 5.818264007568359,
1422
+ 4.189508438110352
1423
+ ]
1424
+ },
1425
+ "parent_dataset": "",
1426
+ "extra_metadata": {}
1427
+ },
1428
+ "corn_maize_leaf_disease": {
1429
+ "classes": {
1430
+ "0": "Blight",
1431
+ "1": "Common_Rust",
1432
+ "2": "Gray_Leaf_Spot",
1433
+ "3": "Healthy"
1434
+ },
1435
+ "ml_task": "image_classification",
1436
+ "ag_task": "disease_classification",
1437
+ "location": {
1438
+ "continent": "worldwide",
1439
+ "country": "worldwide"
1440
+ },
1441
+ "sensor_modality": "rgb",
1442
+ "real_synthetic": "real",
1443
+ "platform": "handheld",
1444
+ "input_data_format": [
1445
+ "jpg",
1446
+ "png",
1447
+ "jpeg",
1448
+ "JPG"
1449
+ ],
1450
+ "annotation_format": "directory_names",
1451
+ "n_images": "4188",
1452
+ "docs_url": "https://www.kaggle.com/datasets/smaranjitghose/corn-or-maize-leaf-disease-dataset/data",
1453
+ "stats": {
1454
+ "mean": [
1455
+ 0.4375976622104645,
1456
+ 0.49800175428390503,
1457
+ 0.3756123483181
1458
+ ],
1459
+ "std": [
1460
+ 0.17942173779010773,
1461
+ 0.1671891063451767,
1462
+ 0.17422494292259216
1463
+ ]
1464
+ },
1465
+ "parent_dataset": "",
1466
+ "extra_metadata": {}
1467
+ },
1468
+ "tomato_leaf_disease": {
1469
+ "classes": {
1470
+ "0": "Bacterial Spot",
1471
+ "1": "Early Blight",
1472
+ "2": "Healthy",
1473
+ "3": "Late Blight",
1474
+ "4": "Leaf Mold",
1475
+ "5": "Septoria Leaf Spot",
1476
+ "6": "Spider Mites Two-spotted Spider Mite",
1477
+ "7": "Target Spot",
1478
+ "8": "Tomato Mosaic Virus",
1479
+ "9": "Tomato Yellow Leaf Curl Virus"
1480
+ },
1481
+ "ml_task": "image_classification",
1482
+ "ag_task": "disease_classification",
1483
+ "location": {
1484
+ "continent": "worldwide",
1485
+ "country": "worldwide"
1486
+ },
1487
+ "sensor_modality": "rgb",
1488
+ "real_synthetic": "real",
1489
+ "platform": "handheld",
1490
+ "input_data_format": [
1491
+ "jpeg"
1492
+ ],
1493
+ "annotation_format": "directory_names",
1494
+ "n_images": "11000",
1495
+ "docs_url": "https://www.kaggle.com/datasets/kaustubhb999/tomatoleaf?resource=download",
1496
+ "stats": {
1497
+ "mean": [
1498
+ 0.4527473449707031,
1499
+ 0.4633065462112427,
1500
+ 0.41868826746940613
1501
+ ],
1502
+ "std": [
1503
+ 0.16920849680900574,
1504
+ 0.1477183699607849,
1505
+ 0.18538573384284973
1506
+ ]
1507
+ },
1508
+ "parent_dataset": "",
1509
+ "extra_metadata": {}
1510
+ },
1511
+ "vine_virus_photo_dataset": {
1512
+ "classes": {
1513
+ "0": "Leafroll 3",
1514
+ "1": "No Virus",
1515
+ "2": "Other Red",
1516
+ "3": "Red Blotch"
1517
+ },
1518
+ "ml_task": "image_classification",
1519
+ "ag_task": "disease_classification",
1520
+ "location": {
1521
+ "continent": "worldwide",
1522
+ "country": "worldwide"
1523
+ },
1524
+ "sensor_modality": "rgb",
1525
+ "real_synthetic": "real",
1526
+ "platform": "handheld/ground",
1527
+ "input_data_format": [
1528
+ "jpg",
1529
+ "png",
1530
+ "jpeg",
1531
+ "JPG"
1532
+ ],
1533
+ "annotation_format": "directory_names",
1534
+ "n_images": "3866",
1535
+ "docs_url": "",
1536
+ "stats": {
1537
+ "mean": [
1538
+ 0.44989314675331116,
1539
+ 0.4489104151725769,
1540
+ 0.29500243067741394
1541
+ ],
1542
+ "std": [
1543
+ 0.2310592532157898,
1544
+ 0.2280450314283371,
1545
+ 0.23016904294490814
1546
+ ]
1547
+ },
1548
+ "parent_dataset": "",
1549
+ "extra_metadata": {}
1550
+ },
1551
+ "embrapa_wgisd_grape_detection": {
1552
+ "classes": {
1553
+ "0": "Chardonnay",
1554
+ "1": "PinotGris",
1555
+ "2": "PinotNoir"
1556
+ },
1557
+ "ml_task": "object_detection",
1558
+ "ag_task": "fruit_detection",
1559
+ "location": {
1560
+ "continent": "worldwide",
1561
+ "country": "worldwide"
1562
+ },
1563
+ "sensor_modality": "rgb",
1564
+ "real_synthetic": "real",
1565
+ "platform": "handheld/ground",
1566
+ "input_data_format": "jpg",
1567
+ "annotation_format": "coco_json",
1568
+ "n_images": "239",
1569
+ "docs_url": "https://github.com/thsant/wgisd/tree/master",
1570
+ "external_image_sources": [],
1571
+ "stats": {
1572
+ "mean": [
1573
+ 0.5015993118286133,
1574
+ 0.5361655354499817,
1575
+ 0.4303760826587677
1576
+ ],
1577
+ "std": [
1578
+ 0.2352389246225357,
1579
+ 0.2119981199502945,
1580
+ 0.2218495011329651
1581
+ ]
1582
+ },
1583
+ "parent_dataset": "",
1584
+ "extra_metadata": {}
1585
+ },
1586
+ "growliflower_cauliflower_segmentation": {
1587
+ "classes": {
1588
+ "1": "maskLeaves",
1589
+ "2": "maskPlants",
1590
+ "3": "maskStems",
1591
+ "4": "maskVoid"
1592
+ },
1593
+ "ml_task": "semantic_segmentation",
1594
+ "ag_task": "crop_segmentation",
1595
+ "location": {
1596
+ "continent": "worldwide",
1597
+ "country": "worldwide"
1598
+ },
1599
+ "sensor_modality": "rgb",
1600
+ "real_synthetic": "real",
1601
+ "platform": "handheld/ground",
1602
+ "input_data_format": "jpg",
1603
+ "annotation_format": "image",
1604
+ "n_images": "1542",
1605
+ "docs_url": "https://phenoroam.phenorob.de/geonetwork/srv/eng/catalog.search#/metadata/cb328232-31f5-4b84-a929-8e1ee551d66a",
1606
+ "stats": {
1607
+ "mean": [
1608
+ 0.48514866828918457,
1609
+ 0.4264565408229828,
1610
+ 0.33096641302108765
1611
+ ],
1612
+ "std": [
1613
+ 0.12302214652299881,
1614
+ 0.1260126680135727,
1615
+ 0.11423511058092117
1616
+ ]
1617
+ },
1618
+ "parent_dataset": "",
1619
+ "extra_metadata": {}
1620
+ },
1621
+ "strawberry_detection_2023": {
1622
+ "classes": {
1623
+ "0": "strawberry",
1624
+ "1": "flower",
1625
+ "2": "green",
1626
+ "3": "large_white",
1627
+ "4": "pink",
1628
+ "5": "red",
1629
+ "6": "small_white"
1630
+ },
1631
+ "ml_task": "object_detection",
1632
+ "ag_task": "fruit_detection",
1633
+ "location": {
1634
+ "continent": "north_america",
1635
+ "country": "usa"
1636
+ },
1637
+ "sensor_modality": "rgb",
1638
+ "real_synthetic": "real",
1639
+ "platform": "handheld/ground",
1640
+ "annotation_format": "coco_json",
1641
+ "input_data_format": "jpg",
1642
+ "n_images": "204",
1643
+ "docs_url": "https://universe.roboflow.com/strawberrydet/strawberry_2023/3",
1644
+ "external_image_sources": [],
1645
+ "stats": {
1646
+ "mean": [
1647
+ 0.2719569802284241,
1648
+ 0.2755374312400818,
1649
+ 0.2003764510154724
1650
+ ],
1651
+ "std": [
1652
+ 0.14823083579540253,
1653
+ 0.1477075070142746,
1654
+ 0.14832836389541626
1655
+ ]
1656
+ },
1657
+ "parent_dataset": "",
1658
+ "extra_metadata": {}
1659
+ },
1660
+ "strawberry_detection_2022": {
1661
+ "classes": {
1662
+ "0": "strawberry",
1663
+ "1": "flower",
1664
+ "2": "green",
1665
+ "3": "large_white",
1666
+ "4": "pink",
1667
+ "5": "red",
1668
+ "6": "small_white"
1669
+ },
1670
+ "ml_task": "object_detection",
1671
+ "ag_task": "fruit_detection",
1672
+ "location": {
1673
+ "continent": "north_america",
1674
+ "country": "usa"
1675
+ },
1676
+ "sensor_modality": "rgb",
1677
+ "real_synthetic": "real",
1678
+ "platform": "handheld/ground",
1679
+ "annotation_format": "coco_json",
1680
+ "input_data_format": "jpg",
1681
+ "n_images": "175",
1682
+ "docs_url": "https://universe.roboflow.com/strawberrydet/strawberry_2022/dataset/4",
1683
+ "external_image_sources": [],
1684
+ "stats": {
1685
+ "mean": [
1686
+ 0.287511944770813,
1687
+ 0.3060769736766815,
1688
+ 0.29882678389549255
1689
+ ],
1690
+ "std": [
1691
+ 0.21001286804676056,
1692
+ 0.19917000830173492,
1693
+ 0.20504336059093475
1694
+ ]
1695
+ },
1696
+ "parent_dataset": "",
1697
+ "extra_metadata": {}
1698
+ },
1699
+ "almond_harvest_2021": {
1700
+ "classes": {
1701
+ "0": "Visible Fruit/Flower",
1702
+ "1": "Partially Visible Fruit/Flower",
1703
+ "2": "Visible Occluded Fruit/Flower",
1704
+ "3": "Partially Visible Occluded Fruit/Flower"
1705
+ },
1706
+ "ml_task": "object_detection",
1707
+ "ag_task": "fruit_detection",
1708
+ "location": {
1709
+ "continent": "north_america",
1710
+ "country": "usa"
1711
+ },
1712
+ "sensor_modality": "rgb",
1713
+ "real_synthetic": "real",
1714
+ "platform": "ground",
1715
+ "annotation_format": "coco_json",
1716
+ "input_data_format": "jpg",
1717
+ "n_images": "50",
1718
+ "docs_url": "",
1719
+ "external_image_sources": [],
1720
+ "stats": {
1721
+ "mean": [
1722
+ 0.1509094387292862,
1723
+ 0.15222010016441345,
1724
+ 0.14836227893829346
1725
+ ],
1726
+ "std": [
1727
+ 0.16955046355724335,
1728
+ 0.1702313870191574,
1729
+ 0.1667901575565338
1730
+ ]
1731
+ },
1732
+ "parent_dataset": "",
1733
+ "extra_metadata": {}
1734
+ },
1735
+ "almond_bloom_2023": {
1736
+ "classes": {
1737
+ "0": "Visible Fruit/Flower"
1738
+ },
1739
+ "ml_task": "object_detection",
1740
+ "ag_task": "flower_detection",
1741
+ "location": {
1742
+ "continent": "north_america",
1743
+ "country": "usa"
1744
+ },
1745
+ "sensor_modality": "rgb",
1746
+ "real_synthetic": "real",
1747
+ "platform": "ground",
1748
+ "annotation_format": "coco_json",
1749
+ "input_data_format": "jpg",
1750
+ "n_images": "100",
1751
+ "docs_url": "",
1752
+ "external_image_sources": [],
1753
+ "stats": {
1754
+ "mean": [
1755
+ 0.32803982496261597,
1756
+ 0.34916892647743225,
1757
+ 0.3486247956752777
1758
+ ],
1759
+ "std": [
1760
+ 0.23713527619838715,
1761
+ 0.24729833006858826,
1762
+ 0.23989765346050262
1763
+ ]
1764
+ },
1765
+ "parent_dataset": "",
1766
+ "extra_metadata": {}
1767
+ },
1768
+ "gemini_flower_detection_2022": {
1769
+ "classes": {
1770
+ "1": "object"
1771
+ },
1772
+ "ml_task": "object_detection",
1773
+ "ag_task": "flower_detection",
1774
+ "location": {
1775
+ "continent": "north_america",
1776
+ "country": "usa"
1777
+ },
1778
+ "sensor_modality": "rgb",
1779
+ "real_synthetic": "real",
1780
+ "platform": "ground",
1781
+ "annotation_format": "coco_json",
1782
+ "input_data_format": "jpg",
1783
+ "n_images": "134",
1784
+ "docs_url": "http://gemini-breeding.github.io/",
1785
+ "external_image_sources": [],
1786
+ "stats": {
1787
+ "mean": [
1788
+ 0.43233251571655273,
1789
+ 0.46158429980278015,
1790
+ 0.3074590265750885
1791
+ ],
1792
+ "std": [
1793
+ 0.15803606808185577,
1794
+ 0.1518802046775818,
1795
+ 0.1447635143995285
1796
+ ]
1797
+ },
1798
+ "parent_dataset": "",
1799
+ "extra_metadata": {}
1800
+ },
1801
+ "gemini_leaf_detection_2022": {
1802
+ "classes": {
1803
+ "1": "object"
1804
+ },
1805
+ "ml_task": "object_detection",
1806
+ "ag_task": "leaf_detection",
1807
+ "location": {
1808
+ "continent": "north_america",
1809
+ "country": "usa"
1810
+ },
1811
+ "sensor_modality": "rgb",
1812
+ "real_synthetic": "real",
1813
+ "platform": "ground",
1814
+ "annotation_format": "coco_json",
1815
+ "input_data_format": "jpg",
1816
+ "n_images": "25",
1817
+ "docs_url": "http://gemini-breeding.github.io/",
1818
+ "external_image_sources": [],
1819
+ "stats": {
1820
+ "mean": [
1821
+ 0.5152409672737122,
1822
+ 0.48080527782440186,
1823
+ 0.4170548915863037
1824
+ ],
1825
+ "std": [
1826
+ 0.17544656991958618,
1827
+ 0.1531846821308136,
1828
+ 0.13160519301891327
1829
+ ]
1830
+ },
1831
+ "parent_dataset": "",
1832
+ "extra_metadata": {}
1833
+ },
1834
+ "gemini_pod_detection_2022": {
1835
+ "classes": {
1836
+ "1": "object"
1837
+ },
1838
+ "ml_task": "object_detection",
1839
+ "ag_task": "pod_detection",
1840
+ "location": {
1841
+ "continent": "north_america",
1842
+ "country": "usa"
1843
+ },
1844
+ "sensor_modality": "rgb",
1845
+ "real_synthetic": "real",
1846
+ "platform": "ground",
1847
+ "annotation_format": "coco_json",
1848
+ "input_data_format": "jpg",
1849
+ "n_images": "98",
1850
+ "docs_url": "http://gemini-breeding.github.io/",
1851
+ "external_image_sources": [],
1852
+ "stats": {
1853
+ "mean": [
1854
+ 0.4158003032207489,
1855
+ 0.47277751564979553,
1856
+ 0.28544333577156067
1857
+ ],
1858
+ "std": [
1859
+ 0.16562676429748535,
1860
+ 0.1610204428434372,
1861
+ 0.1648048311471939
1862
+ ]
1863
+ },
1864
+ "parent_dataset": "",
1865
+ "extra_metadata": {}
1866
+ },
1867
+ "gemini_plant_detection_2022": {
1868
+ "classes": {
1869
+ "1": "plant",
1870
+ "2": "weed"
1871
+ },
1872
+ "ml_task": "object_detection",
1873
+ "ag_task": "plant_detection",
1874
+ "location": {
1875
+ "continent": "north_america",
1876
+ "country": "usa"
1877
+ },
1878
+ "sensor_modality": "rgb",
1879
+ "real_synthetic": "real",
1880
+ "platform": "ground",
1881
+ "annotation_format": "coco_json",
1882
+ "input_data_format": "jpg",
1883
+ "n_images": "402",
1884
+ "docs_url": "http://gemini-breeding.github.io/",
1885
+ "external_image_sources": [],
1886
+ "stats": {
1887
+ "mean": [
1888
+ 0.5137307047843933,
1889
+ 0.47968026995658875,
1890
+ 0.4163574278354645
1891
+ ],
1892
+ "std": [
1893
+ 0.17421172559261322,
1894
+ 0.15248580276966095,
1895
+ 0.13018731772899628
1896
+ ]
1897
+ },
1898
+ "parent_dataset": "",
1899
+ "extra_metadata": {}
1900
+ },
1901
+ "paddy_disease_classification": {
1902
+ "ml_task": "image_classification",
1903
+ "ag_task": "disease_classification",
1904
+ "location": {
1905
+ "continent": "asia",
1906
+ "country": "india"
1907
+ },
1908
+ "sensor_modality": "rgb",
1909
+ "real_synthetic": "real",
1910
+ "platform": "uav",
1911
+ "input_data_format": "jpg",
1912
+ "annotation_format": "directory_names",
1913
+ "n_images": "10407",
1914
+ "docs_url": "https://www.kaggle.com/competitions/paddy-disease-classification/data",
1915
+ "classes": {
1916
+ "0": "bacterial_leaf_blight",
1917
+ "1": "bacterial_leaf_streak",
1918
+ "2": "bacterial_panicle_blight",
1919
+ "3": "blast",
1920
+ "4": "brown_spot",
1921
+ "5": "dead_heart",
1922
+ "6": "downy_mildew",
1923
+ "7": "hispa",
1924
+ "8": "normal",
1925
+ "9": "tungro"
1926
+ },
1927
+ "external_image_sources": [],
1928
+ "stats": {
1929
+ "mean": [
1930
+ 0.49716252088546753,
1931
+ 0.5882205963134766,
1932
+ 0.2304224669933319
1933
+ ],
1934
+ "std": [
1935
+ 0.23286384344100952,
1936
+ 0.23301087319850922,
1937
+ 0.1900700032711029
1938
+ ]
1939
+ },
1940
+ "parent_dataset": "",
1941
+ "extra_metadata": {}
1942
+ },
1943
+ "onion_leaf_classification": {
1944
+ "ml_task": "image_classification",
1945
+ "ag_task": "leaf_classification",
1946
+ "location": {
1947
+ "continent": "asia",
1948
+ "country": "india"
1949
+ },
1950
+ "sensor_modality": "rgb",
1951
+ "real_synthetic": "real",
1952
+ "platform": "uav",
1953
+ "input_data_format": "jpg",
1954
+ "annotation_format": "directory_names",
1955
+ "n_images": "4502",
1956
+ "docs_url": "https://www.researchgate.net/publication/380611658_Dataset_of_Chilli_and_Onion_Plant_Leaf_Images_for_Classification_and_Detection",
1957
+ "classes": {
1958
+ "0": "healthy",
1959
+ "1": "iris_yellow_virus",
1960
+ "2": "leaf_blight",
1961
+ "3": "purple_blotch"
1962
+ },
1963
+ "external_image_sources": [],
1964
+ "stats": {
1965
+ "mean": [
1966
+ 0.49259838461875916,
1967
+ 0.5055249929428101,
1968
+ 0.33485615253448486
1969
+ ],
1970
+ "std": [
1971
+ 0.18601194024085999,
1972
+ 0.14629681408405304,
1973
+ 0.15651942789554596
1974
+ ]
1975
+ },
1976
+ "parent_dataset": "",
1977
+ "extra_metadata": {}
1978
+ },
1979
+ "chilli_leaf_classification": {
1980
+ "ml_task": "image_classification",
1981
+ "ag_task": "leaf_classification",
1982
+ "location": {
1983
+ "continent": "asia",
1984
+ "country": "india"
1985
+ },
1986
+ "sensor_modality": "rgb",
1987
+ "real_synthetic": "real",
1988
+ "platform": "uav",
1989
+ "input_data_format": "jpg",
1990
+ "annotation_format": "directory_names",
1991
+ "n_images": "10974",
1992
+ "docs_url": "https://www.researchgate.net/publication/380611658_Dataset_of_Chilli_and_Onion_Plant_Leaf_Images_for_Classification_and_Detection",
1993
+ "classes": {
1994
+ "0": "cercospora",
1995
+ "1": "healthy",
1996
+ "2": "mites_and_trips",
1997
+ "3": "nutritional",
1998
+ "4": "powdery_mildew"
1999
+ },
2000
+ "external_image_sources": [],
2001
+ "stats": {
2002
+ "mean": [
2003
+ 0.5395056009292603,
2004
+ 0.5616798996925354,
2005
+ 0.4004570543766022
2006
+ ],
2007
+ "std": [
2008
+ 0.17784084379673004,
2009
+ 0.1770888715982437,
2010
+ 0.18177233636379242
2011
+ ]
2012
+ },
2013
+ "parent_dataset": "",
2014
+ "extra_metadata": {}
2015
+ },
2016
+ "orange_leaf_disease_classification": {
2017
+ "ml_task": "image_classification",
2018
+ "ag_task": "disease_classification",
2019
+ "location": {
2020
+ "continent": "asia",
2021
+ "country": "bangladesh"
2022
+ },
2023
+ "sensor_modality": "rgb",
2024
+ "real_synthetic": "real",
2025
+ "platform": "uav",
2026
+ "input_data_format": "jpeg",
2027
+ "annotation_format": "directory_names",
2028
+ "n_images": "5813",
2029
+ "docs_url": "https://www.sciencedirect.com/science/article/pii/S2352340924006802#sec0004",
2030
+ "classes": {
2031
+ "0": "citrus_canker",
2032
+ "1": "citrus_greening",
2033
+ "2": "citrus_mealybugs",
2034
+ "3": "die_back",
2035
+ "4": "foliage_damaged",
2036
+ "5": "healthy_leaf",
2037
+ "6": "powdery_mildew",
2038
+ "7": "shot_hole",
2039
+ "8": "spiny_whitefly",
2040
+ "9": "yellow_dragon",
2041
+ "10": "yellow_leaves"
2042
+ },
2043
+ "external_image_sources": [],
2044
+ "stats": {
2045
+ "mean": [
2046
+ 0.641664981842041,
2047
+ 0.645190954208374,
2048
+ 0.5412733554840088
2049
+ ],
2050
+ "std": [
2051
+ 0.21496868133544922,
2052
+ 0.19639450311660767,
2053
+ 0.2651123106479645
2054
+ ]
2055
+ },
2056
+ "parent_dataset": "",
2057
+ "extra_metadata": {}
2058
+ },
2059
+ "papaya_leaf_disease_classification": {
2060
+ "ml_task": "image_classification",
2061
+ "ag_task": "disease_classification",
2062
+ "location": {
2063
+ "continent": "asia",
2064
+ "country": "bangladesh"
2065
+ },
2066
+ "sensor_modality": "rgb",
2067
+ "real_synthetic": "real",
2068
+ "platform": "uav",
2069
+ "input_data_format": "jpg",
2070
+ "annotation_format": "directory_names",
2071
+ "n_images": "2159",
2072
+ "docs_url": "https://www.sciencedirect.com/science/article/pii/S2352340924008734",
2073
+ "classes": {
2074
+ "0": "Anthracnose",
2075
+ "1": "BacterialSpot",
2076
+ "2": "Curl",
2077
+ "3": "Healthy",
2078
+ "4": "RingSpot"
2079
+ },
2080
+ "external_image_sources": [],
2081
+ "stats": {
2082
+ "mean": [
2083
+ 0.4037705361843109,
2084
+ 0.5427719950675964,
2085
+ 0.29178404808044434
2086
+ ],
2087
+ "std": [
2088
+ 0.1690509021282196,
2089
+ 0.15967564284801483,
2090
+ 0.14206627011299133
2091
+ ]
2092
+ },
2093
+ "parent_dataset": "",
2094
+ "extra_metadata": {}
2095
+ },
2096
+ "blackgram_plant_leaf_disease_classification": {
2097
+ "ml_task": "image_classification",
2098
+ "ag_task": "disease_classification",
2099
+ "location": {
2100
+ "continent": "asia",
2101
+ "country": "india"
2102
+ },
2103
+ "sensor_modality": "rgb",
2104
+ "real_synthetic": "real",
2105
+ "platform": "uav",
2106
+ "input_data_format": "jpg",
2107
+ "annotation_format": "directory_names",
2108
+ "n_images": "1007",
2109
+ "docs_url": "https://www.sciencedirect.com/science/article/pii/S2352340922009295",
2110
+ "classes": {
2111
+ "0": "anthracnose",
2112
+ "1": "healthy",
2113
+ "2": "leaf_crinckle",
2114
+ "3": "powdery_mildew",
2115
+ "4": "yellow_mosaic"
2116
+ },
2117
+ "external_image_sources": [],
2118
+ "stats": {
2119
+ "mean": [
2120
+ 0.4454658031463623,
2121
+ 0.5082071423530579,
2122
+ 0.3293055593967438
2123
+ ],
2124
+ "std": [
2125
+ 0.19813409447669983,
2126
+ 0.1792915314435959,
2127
+ 0.16840000450611115
2128
+ ]
2129
+ },
2130
+ "parent_dataset": "",
2131
+ "extra_metadata": {}
2132
+ },
2133
+ "arabica_coffee_leaf_disease_classification": {
2134
+ "ml_task": "image_classification",
2135
+ "ag_task": "disease_classification",
2136
+ "location": {
2137
+ "continent": "africa",
2138
+ "country": "kenya"
2139
+ },
2140
+ "sensor_modality": "rgb",
2141
+ "real_synthetic": "real",
2142
+ "platform": "uav",
2143
+ "input_data_format": [
2144
+ "jpg",
2145
+ "jpeg"
2146
+ ],
2147
+ "annotation_format": "directory_names",
2148
+ "n_images": "58549",
2149
+ "docs_url": "https://www.sciencedirect.com/science/article/pii/S2352340921004261?via%3Dihub#sec0001",
2150
+ "classes": {
2151
+ "0": "Cerscospora",
2152
+ "1": "Healthy",
2153
+ "2": "Leaf_rust",
2154
+ "3": "Miner",
2155
+ "4": "Phoma"
2156
+ },
2157
+ "external_image_sources": [],
2158
+ "stats": {
2159
+ "mean": [
2160
+ 0.4663342535495758,
2161
+ 0.6128407716751099,
2162
+ 0.40549468994140625
2163
+ ],
2164
+ "std": [
2165
+ 0.1319405734539032,
2166
+ 0.10137982666492462,
2167
+ 0.13884715735912323
2168
+ ]
2169
+ },
2170
+ "parent_dataset": "",
2171
+ "extra_metadata": {}
2172
+ },
2173
+ "banana_leaf_disease_classification": {
2174
+ "ml_task": "image_classification",
2175
+ "ag_task": "disease_classification",
2176
+ "location": {
2177
+ "continent": "africa",
2178
+ "country": "ethiopia"
2179
+ },
2180
+ "sensor_modality": "rgb",
2181
+ "real_synthetic": "real",
2182
+ "platform": "uav",
2183
+ "input_data_format": "jpg",
2184
+ "annotation_format": "directory_names",
2185
+ "n_images": "1288",
2186
+ "docs_url": "https://www.researchgate.net/publication/380900090_Sigatoka_and_Xanthomonas_Banana_Leaf_Disease_Detection_Via_Transfer_Learning",
2187
+ "classes": {
2188
+ "0": "healthy",
2189
+ "1": "segatoka",
2190
+ "2": "xamthomonas"
2191
+ },
2192
+ "external_image_sources": [],
2193
+ "stats": {
2194
+ "mean": [
2195
+ 0.4403895437717438,
2196
+ 0.4788191020488739,
2197
+ 0.22997669875621796
2198
+ ],
2199
+ "std": [
2200
+ 0.2052917778491974,
2201
+ 0.20397576689720154,
2202
+ 0.18596293032169342
2203
+ ]
2204
+ },
2205
+ "parent_dataset": "",
2206
+ "extra_metadata": {}
2207
+ },
2208
+ "coconut_tree_disease_classification": {
2209
+ "ml_task": "image_classification",
2210
+ "ag_task": "disease_classification",
2211
+ "location": {
2212
+ "continent": "asia",
2213
+ "country": "india"
2214
+ },
2215
+ "sensor_modality": "rgb",
2216
+ "real_synthetic": "real",
2217
+ "platform": "uav",
2218
+ "input_data_format": "jpg",
2219
+ "annotation_format": "directory_names",
2220
+ "n_images": "5798",
2221
+ "docs_url": "https://www.sciencedirect.com/science/article/pii/S2352340923007692#sec0003",
2222
+ "classes": {
2223
+ "0": "Bud_Root_Dropping",
2224
+ "1": "Bud_Rot",
2225
+ "2": "Gray_Leaf_Spot",
2226
+ "3": "Leaf_Rot",
2227
+ "4": "Stem_Bleeding"
2228
+ },
2229
+ "external_image_sources": [],
2230
+ "stats": {
2231
+ "mean": [
2232
+ 0.49172350764274597,
2233
+ 0.49519237875938416,
2234
+ 0.4346628189086914
2235
+ ],
2236
+ "std": [
2237
+ 0.2121831327676773,
2238
+ 0.20774872601032257,
2239
+ 0.22041980922222137
2240
+ ]
2241
+ },
2242
+ "parent_dataset": "",
2243
+ "extra_metadata": {}
2244
+ },
2245
+ "rice_leaf_disease_classification": {
2246
+ "ml_task": "image_classification",
2247
+ "ag_task": "disease_classification",
2248
+ "location": {
2249
+ "continent": "worldwide",
2250
+ "country": "worldwide"
2251
+ },
2252
+ "sensor_modality": "rgb",
2253
+ "real_synthetic": "real",
2254
+ "platform": "uav",
2255
+ "input_data_format": "jpg",
2256
+ "annotation_format": "directory_names",
2257
+ "n_images": "3829",
2258
+ "docs_url": "https://www.kaggle.com/datasets/anshulm257/rice-disease-dataset/data",
2259
+ "classes": {
2260
+ "0": "Bacterial_Leaf_Blight",
2261
+ "1": "Brown_Spot",
2262
+ "2": "Healthy_Rice_Leaf",
2263
+ "3": "Leaf_Blast",
2264
+ "4": "Leaf_Scald",
2265
+ "5": "Sheath_Blight"
2266
+ },
2267
+ "external_image_sources": [],
2268
+ "stats": {
2269
+ "mean": [
2270
+ 0.3801848888397217,
2271
+ 0.5112283229827881,
2272
+ 0.24517497420310974
2273
+ ],
2274
+ "std": [
2275
+ 0.1857737898826599,
2276
+ 0.1747269332408905,
2277
+ 0.13470299541950226
2278
+ ]
2279
+ },
2280
+ "parent_dataset": "",
2281
+ "extra_metadata": {}
2282
+ },
2283
+ "tea_leaf_disease_classification": {
2284
+ "ml_task": "image_classification",
2285
+ "ag_task": "disease_classification",
2286
+ "location": {
2287
+ "continent": "asia",
2288
+ "country": "india"
2289
+ },
2290
+ "sensor_modality": "rgb",
2291
+ "real_synthetic": "real",
2292
+ "platform": "uav",
2293
+ "input_data_format": "jpg",
2294
+ "annotation_format": "directory_names",
2295
+ "n_images": "5867",
2296
+ "docs_url": "https://www.kaggle.com/datasets/saikatdatta1994/tea-leaf-disease",
2297
+ "classes": {
2298
+ "0": "algal_spot",
2299
+ "1": "brown_blight",
2300
+ "2": "gray_blight",
2301
+ "3": "healthy",
2302
+ "4": "helopeltis",
2303
+ "5": "red_spot"
2304
+ },
2305
+ "external_image_sources": [],
2306
+ "stats": {
2307
+ "mean": [
2308
+ 0.720391035079956,
2309
+ 0.7148104310035706,
2310
+ 0.7214117646217346
2311
+ ],
2312
+ "std": [
2313
+ 0.2642996311187744,
2314
+ 0.24272359907627106,
2315
+ 0.32473450899124146
2316
+ ]
2317
+ },
2318
+ "parent_dataset": "",
2319
+ "extra_metadata": {}
2320
+ },
2321
+ "betel_leaf_disease_classification": {
2322
+ "ml_task": "image_classification",
2323
+ "ag_task": "disease_classification",
2324
+ "location": {
2325
+ "continent": "asia",
2326
+ "country": "bangladesh"
2327
+ },
2328
+ "sensor_modality": "rgb",
2329
+ "real_synthetic": "real",
2330
+ "platform": "uav",
2331
+ "input_data_format": "jpg",
2332
+ "annotation_format": "directory_names",
2333
+ "n_images": "3589",
2334
+ "docs_url": "https://www.semanticscholar.org/paper/Betel-Leaf-Diseases-Classification-using-Machine-A-David-Mukunthan/38208c9d2306444e3b9f8593715a46e2dcf26f44#paper-topics",
2335
+ "classes": {
2336
+ "0": "Bacterial_Leaf_Disease",
2337
+ "1": "Dried_Leaf",
2338
+ "2": "Fungal_Brown_Spot_Disease",
2339
+ "3": "Healthy_Leaf"
2340
+ },
2341
+ "external_image_sources": [],
2342
+ "stats": {
2343
+ "mean": [
2344
+ 0.44302278757095337,
2345
+ 0.5203492045402527,
2346
+ 0.32380297780036926
2347
+ ],
2348
+ "std": [
2349
+ 0.18618987500667572,
2350
+ 0.19697120785713196,
2351
+ 0.21828916668891907
2352
+ ]
2353
+ },
2354
+ "parent_dataset": "",
2355
+ "extra_metadata": {}
2356
+ },
2357
+ "java_plum_leaf_disease_classification": {
2358
+ "ml_task": "image_classification",
2359
+ "ag_task": "disease_classification",
2360
+ "location": {
2361
+ "continent": "asia",
2362
+ "country": "bangladesh"
2363
+ },
2364
+ "sensor_modality": "rgb",
2365
+ "real_synthetic": "real",
2366
+ "platform": "uav",
2367
+ "input_data_format": "jpg",
2368
+ "annotation_format": "directory_names",
2369
+ "n_images": "2400",
2370
+ "docs_url": "https://www.sciencedirect.com/science/article/pii/S2772375524001059#sec0003",
2371
+ "classes": {
2372
+ "0": "Bacterial_Spot",
2373
+ "1": "Brown_Blight",
2374
+ "2": "Dry",
2375
+ "3": "Healthy",
2376
+ "4": "Powdery_Mildew",
2377
+ "5": "Sooty_Mold"
2378
+ },
2379
+ "external_image_sources": [],
2380
+ "stats": {
2381
+ "mean": [
2382
+ 0.6888663172721863,
2383
+ 0.7057309746742249,
2384
+ 0.6495749950408936
2385
+ ],
2386
+ "std": [
2387
+ 0.21865394711494446,
2388
+ 0.20790858566761017,
2389
+ 0.3019416928291321
2390
+ ]
2391
+ },
2392
+ "parent_dataset": "",
2393
+ "extra_metadata": {}
2394
+ },
2395
+ "sunflower_disease_classification": {
2396
+ "ml_task": "image_classification",
2397
+ "ag_task": "disease_classification",
2398
+ "location": {
2399
+ "continent": "asia",
2400
+ "country": "bangladesh"
2401
+ },
2402
+ "sensor_modality": "rgb",
2403
+ "real_synthetic": "real",
2404
+ "platform": "uav",
2405
+ "input_data_format": "jpg",
2406
+ "annotation_format": "directory_names",
2407
+ "n_images": "2358",
2408
+ "docs_url": "https://www.sciencedirect.com/science/article/pii/S2352340922002542",
2409
+ "classes": {
2410
+ "0": "Downy_mildew",
2411
+ "1": "Fresh_leaf",
2412
+ "2": "Gray_mold",
2413
+ "3": "Leaf_scars"
2414
+ },
2415
+ "external_image_sources": [],
2416
+ "stats": {
2417
+ "mean": [
2418
+ 0.45603200793266296,
2419
+ 0.45415663719177246,
2420
+ 0.2436216175556183
2421
+ ],
2422
+ "std": [
2423
+ 0.22495068609714508,
2424
+ 0.21411556005477905,
2425
+ 0.17952653765678406
2426
+ ]
2427
+ },
2428
+ "parent_dataset": "",
2429
+ "extra_metadata": {}
2430
+ },
2431
+ "cucumber_disease_classification": {
2432
+ "ml_task": "image_classification",
2433
+ "ag_task": "disease_classification",
2434
+ "location": {
2435
+ "continent": "asia",
2436
+ "country": "bangladesh"
2437
+ },
2438
+ "sensor_modality": "rgb",
2439
+ "real_synthetic": "real",
2440
+ "platform": "uav",
2441
+ "input_data_format": "jpg",
2442
+ "annotation_format": "directory_names",
2443
+ "n_images": "7689",
2444
+ "docs_url": "https://www.sciencedirect.com/science/article/pii/S2352340923004389",
2445
+ "classes": {
2446
+ "0": "Anthracnose",
2447
+ "1": "Bacterial_Wilt",
2448
+ "2": "Belly_Rot",
2449
+ "3": "Downy_Mildew",
2450
+ "4": "Fresh_Cucumber",
2451
+ "5": "Fresh_Leaf",
2452
+ "6": "Gummy_Stem_Blight",
2453
+ "7": "Pythium_Fruit_Rot"
2454
+ },
2455
+ "external_image_sources": [],
2456
+ "stats": {
2457
+ "mean": [
2458
+ 0.46584174036979675,
2459
+ 0.5267260074615479,
2460
+ 0.3983174264431
2461
+ ],
2462
+ "std": [
2463
+ 0.19197316467761993,
2464
+ 0.18358547985553741,
2465
+ 0.17932511866092682
2466
+ ]
2467
+ },
2468
+ "parent_dataset": "",
2469
+ "extra_metadata": {}
2470
+ },
2471
+ "soybean_insect_classification": {
2472
+ "ml_task": "image_classification",
2473
+ "ag_task": "pest_classification",
2474
+ "location": {
2475
+ "continent": "south_america",
2476
+ "country": "brazil"
2477
+ },
2478
+ "sensor_modality": "rgb",
2479
+ "real_synthetic": "real",
2480
+ "platform": "uav",
2481
+ "input_data_format": "jpg",
2482
+ "annotation_format": "directory_names",
2483
+ "n_images": "6410",
2484
+ "docs_url": "https://data.mendeley.com/datasets/bycbh73438/1",
2485
+ "classes": {
2486
+ "0": "Caterpillar",
2487
+ "1": "Diabrotica_speciosa",
2488
+ "2": "Healthy"
2489
+ },
2490
+ "external_image_sources": [],
2491
+ "parent_dataset": "",
2492
+ "extra_metadata": {},
2493
+ "stats": {
2494
+ "mean": [
2495
+ 0.4129256308078766,
2496
+ 0.515846848487854,
2497
+ 0.3347107470035553
2498
+ ],
2499
+ "std": [
2500
+ 0.22007715702056885,
2501
+ 0.22993944585323334,
2502
+ 0.20302338898181915
2503
+ ]
2504
+ }
2505
+ }
2506
+ }
AgML/source/agml/_assets/shape_info.pickle ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:483dfc00dac1e09bc963e6b2c9ae30556c60425164b1a3bb9929e4452d8b3229
3
+ size 499283
AgML/source/agml/_assets/source_citations.json ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bean_disease_uganda": {
3
+ "license": "MIT",
4
+ "citation": ""
5
+ },
6
+ "carrot_weeds_germany": {
7
+ "license": "",
8
+ "citation": "@inproceedings{haug15,\n author={Haug, Sebastian and Ostermann, J{\\\"o}rn},\n title={A Crop/Weed Field Image Dataset for the Evaluation of Computer Vision Based Precision Agriculture Tasks},\n year={2015},\n booktitle={Computer Vision - ECCV 2014 Workshops},\n doi={10.1007/978-3-319-16220-1_8},\n url={http://dx.doi.org/10.1007/978-3-319-16220-1_8},\n pages={105--116}\n}"
9
+ },
10
+ "plant_seedlings_aarhus": {
11
+ "license": "CC BY-SA 4.0",
12
+ "citation": "@article{Giselsson2017,\n author = {Giselsson, Thomas Mosgaard and Dyrmann, Mads and J{\\o}rgensen, Rasmus Nyholm and Jensen, Peter Kryger and Midtiby, Henrik Skov},\n journal = {arXiv preprint},\n keywords = {benchmark,database,plant seedlings,segmentation,site-specific weed control},\n title = {{A Public Image Database for Benchmark of Plant Seedling Classification Algorithms}},\n year = {2017}\n}"
13
+ },
14
+ "soybean_weed_uav_brazil": {
15
+ "license": "CC BY-NC 3.0",
16
+ "citation": "dos Santos Ferreira, Alessandro; Pistori, Hemerson; Matte Freitas, Daniel; Gon\u00e7alves da Silva, Gercina (2017), \u201cData for: Weed Detection in Soybean Crops Using ConvNets\u201d, Mendeley Data, V2, doi: 10.17632/3fmjm7ncc6.2"
17
+ },
18
+ "sugarcane_damage_usa": {
19
+ "license": "",
20
+ "citation": "@ARTICLE{8412587,\n author={Alencastre-Miranda, Moises and Davidson, Joseph R. and Johnson, Richard M. and Waguespack, Herman and Krebs, Hermano Igo},\n journal={IEEE Robotics and Automation Letters}, \n title={Robotics for Sugarcane Cultivation: Analysis of Billet Quality using Computer Vision}, \n year={2018},\n volume={3},\n number={4},\n pages={3828-3835},\n doi={10.1109/LRA.2018.2856999}}"
21
+ },
22
+ "crop_weeds_greece": {
23
+ "license": "MIT",
24
+ "citation": "@article{ESPEJOGARCIA2020105306,\n title = {Towards weeds identification assistance through transfer learning},\n journal = {Computers and Electronics in Agriculture},\n volume = {171},\n pages = {105306},\n year = {2020},\n issn = {0168-1699},\n doi = {https://doi.org/10.1016/j.compag.2020.105306},\n url = {https://www.sciencedirect.com/science/article/pii/S0168169919319854},\n author = {Borja Espejo-Garcia and Nikos Mylonas and Loukas Athanasakos and Spyros Fountas and Ioannis Vasilakoglou},\n keywords = {Weed identification, Deep learning, Transfer learning, Open data, Precision agriculture},\n abstract = {Reducing the use of pesticides through selective spraying is an important component towards a more sustainable computer-assisted agriculture. Weed identification at early growth stage contributes to reduced herbicide rates. However, while computer vision alongside deep learning have overcome the performance of approaches that use hand-crafted features, there are still some open challenges in the development of a reliable automatic plant identification system. These type of systems have to take into account different sources of variability, such as growth stages and soil conditions, with the added constraint of the limited size of usual datasets. This study proposes a novel crop/weed identification system that relies on a combination of fine-tuning pre-trained convolutional networks (Xception, Inception-Resnet, VGNets, Mobilenet and Densenet) with the \u201ctraditional\u201d machine learning classifiers (Support Vector Machines, XGBoost and Logistic Regression) trained with the previously deep extracted features. The aim of this approach was to avoid overfitting and to obtain a robust and consistent performance. To evaluate this approach, an open access dataset of two crop [tomato (Solanum lycopersicum L.) and cotton (Gossypium hirsutum L.)] and two weed species [black nightshade (Solanum nigrum L.) and velvetleaf (Abutilon theophrasti Medik.)] was generated. The pictures were taken by different production sites across Greece under natural variable light conditions from RGB cameras. The results revealed that a combination of fine-tuned Densenet and Support Vector Machine achieved a micro F1 score of 99.29% with a very low performance difference between train and test sets. Other evaluated approaches also obtained repeatedly more than 95% F1 score. Additionally, our results analysis provides some heuristics for designing transfer-learning based systems to avoid overfitting without decreasing performance.}\n}"
25
+ },
26
+ "sugarbeet_weed_segmentation": {
27
+ "license": "GPL-3.0",
28
+ "citation": "@ARTICLE{8115245,\n author={I. Sa and Z. Chen and M. Popovi\u0107 and R. Khanna and F. Liebisch and J. Nieto and R. Siegwart},\n journal={IEEE Robotics and Automation Letters},\n title={weedNet: Dense Semantic Weed Classification Using Multispectral Images and MAV for Smart Farming},\n year={2018},\n volume={3},\n number={1},\n pages={588-595},\n keywords={agriculture;agrochemicals;autonomous aerial vehicles;control engineering computing;convolution;crops;feature extraction;image classification;learning (artificial intelligence);neural nets;vegetation;MAV;SegNet;convolutional neural network;crop health;crop management;curve classification metrics;dense semantic classes;dense semantic weed classification;encoder-decoder;input image channels;multispectral images;selective weed treatment;vegetation index;weed detection;Agriculture;Cameras;Image segmentation;Robots;Semantics;Training;Vegetation mapping;Aerial systems;agricultural automation;applications;robotics in agriculture and forestry},\n doi={10.1109/LRA.2017.2774979},\n ISSN={},\n month={Jan}\n}"
29
+ },
30
+ "rangeland_weeds_australia": {
31
+ "license": "CC BY-SA 4.0",
32
+ "citation": "@Article{Olsen2019,\n author={Olsen, Alex and Konovalov, Dmitry A. and Philippa, Bronson and Ridd, Peter and Wood, Jake C. and Johns, Jamie and Banks, Wesley and Girgenti, Benjamin and Kenny, Owen and Whinney, James and Calvert, Brendan and Azghadi, Mostafa Rahimi and White, Ronald D.},\n title={DeepWeeds: A Multiclass Weed Species Image Dataset for Deep Learning},\n journal={Scientific Reports},\n year={2019},\n month={Feb},\n day={14},\n volume={9},\n number={1},\n pages={2058},\n abstract={Robotic weed control has seen increased research of late with its potential for boosting productivity in agriculture. Majority of works focus on developing robotics for croplands, ignoring the weed management problems facing rangeland stock farmers. Perhaps the greatest obstacle to widespread uptake of robotic weed control is the robust classification of weed species in their natural environment. The unparalleled successes of deep learning make it an ideal candidate for recognising various weed species in the complex rangeland environment. This work contributes the first large, public, multiclass image dataset of weed species from the Australian rangelands; allowing for the development of robust classification methods to make robotic weed control viable. The DeepWeeds dataset consists of 17,509 labelled images of eight nationally significant weed species native to eight locations across northern Australia. This paper presents a baseline for classification performance on the dataset using the benchmark deep learning models, Inception-v3 and ResNet-50. These models achieved an average classification accuracy of 95.1{\\%} and 95.7{\\%}, respectively. We also demonstrate real time performance of the ResNet-50 architecture, with an average inference time of 53.4 ms per image. These strong results bode well for future field implementation of robotic weed control methods in the Australian rangelands.},\n issn={2045-2322},\n doi={10.1038/s41598-018-38343-3},\n url={https://doi.org/10.1038/s41598-018-38343-3}\n}"
33
+ },
34
+ "fruit_detection_worldwide": {
35
+ "license": "",
36
+ "citation": "@Article{s16081222,\n AUTHOR = {Sa, Inkyu and Ge, Zongyuan and Dayoub, Feras and Upcroft, Ben and Perez, Tristan and McCool, Chris},\n TITLE = {DeepFruits: A Fruit Detection System Using Deep Neural Networks},\n JOURNAL = {Sensors},\n VOLUME = {16},\n YEAR = {2016},\n NUMBER = {8},\n ARTICLE-NUMBER = {1222},\n URL = {https://www.mdpi.com/1424-8220/16/8/1222},\n ISSN = {1424-8220},\n ABSTRACT = {This paper presents a novel approach to fruit detection using deep convolutional neural networks. The aim is to build an accurate, fast and reliable fruit detection system, which is a vital element of an autonomous agricultural robotic platform; it is a key element for fruit yield estimation and automated harvesting. Recent work in deep neural networks has led to the development of a state-of-the-art object detector termed Faster Region-based CNN (Faster R-CNN). We adapt this model, through transfer learning, for the task of fruit detection using imagery obtained from two modalities: colour (RGB) and Near-Infrared (NIR). Early and late fusion methods are explored for combining the multi-modal (RGB and NIR) information. This leads to a novel multi-modal Faster R-CNN model, which achieves state-of-the-art results compared to prior work with the F1 score, which takes into account both precision and recall performances improving from 0 . 807 to 0 . 838 for the detection of sweet pepper. In addition to improved accuracy, this approach is also much quicker to deploy for new fruits, as it requires bounding box annotation rather than pixel-level annotation (annotating bounding boxes is approximately an order of magnitude quicker to perform). The model is retrained to perform the detection of seven fruits, with the entire process taking four hours to annotate and train the new model per fruit.},\n DOI = {10.3390/s16081222}\n}"
37
+ },
38
+ "leaf_counting_denmark": {
39
+ "license": "CC BY-SA 4.0",
40
+ "citation": "@Article{s18051580,\n author = {Teimouri, Nima and Dyrmann, Mads and Nielsen, Per Rydahl and Mathiassen, Solvejg Kopp and Somerville, Gayle J. and J\u00f8rgensen, Rasmus Nyholm},\n title = {Weed Growth Stage Estimator Using Deep Convolutional Neural Networks},\n journal = {Sensors},\n volume = {18},\n year = {2018},\n number = {5},\n url = {http://www.mdpi.com/1424-8220/18/5/1580},\n issn = {1424-8220}\n}"
41
+ },
42
+ "apple_detection_usa": {
43
+ "license": "",
44
+ "citation": "@article{karkee2019apple,\n title={Apple Dataset Benchmark from Orchard Environment in Modern Fruiting Wall},\n author={Karkee, Manoj and Bhusal, Santosh and Zhang, Qin},\n year={2019}\n}"
45
+ },
46
+ "mango_detection_australia": {
47
+ "license": "",
48
+ "citation": "@Misc{Koirala2019,\n author={Koirala, Anand and Walsh, Kerry and Wang, Z. and McCarthy, C.},\n title={MangoYOLO data set},\n year={2019},\n month={2021},\n day={10-19},\n publisher={Central Queensland University},\n keywords={Mango images; Fruit detection; Yield estimation; Mango; Agricultural Land Management; Horticultural Crop Growth and Development},\n abstract={Datasets and directories are structured similar to the PASCAL VOC dataset, avoiding the need to change scripts already available, with the detection frameworks ready to parse PASCAL VOC annotations into their format. The sub-directory JPEGImages consist of 1730 images (612x512 pixels) used for train, test and validation. Each image has at least one annotated fruit. The sub-directory Annotations consists of all the annotation files (record of bounding box coordinates for each image) in xml format and have the same name as the image name. The sub-directory Main consists of the text file that contains image names (without extension) used for train, test and validation. Training set (train.txt) lists 1300 train images Validation set (val.txt) lists 130 validation images Test set (test.txt) lists 300 test images Each image has an XML annotation file (filename = image name) and each image set (training validation and test set) has associated text files (train.txt, val.txt and test.txt) containing the list of image names to be used for training and testing. The XML annotation file contains the image attributes (name, width, height), the object attributes (class name, object bounding box co-ordinates (xmin, ymin, xmax, ymax)). (xmin, ymin) and (xmax, ymax) are the pixel co-ordinates of the bounding box's top-left corner and bottom-right corner respectively.},\n note={CC-BY-4.0},\n url={https://figshare.com/articles/dataset/MangoYOLO_data_set/13450661, https://researchdata.edu.au/mangoyolo-set},\n language={English}\n}"
49
+ },
50
+ "tomato_ripeness_detection": {
51
+ "license": "CC BY-NC-SA 4.0",
52
+ "citation": ""
53
+ },
54
+ "apple_flower_segmentation": {
55
+ "license": "US Public Domain",
56
+ "citation": "@ARTICLE{8392727,\n author={Dias, Philipe A. and Tabb, Amy and Medeiros, Henry},\n journal={IEEE Robotics and Automation Letters}, \n title={Multispecies Fruit Flower Detection Using a Refined Semantic Segmentation Network}, \n year={2018},\n volume={3},\n number={4},\n pages={3003-3010},\n doi={10.1109/LRA.2018.2849498}}"
57
+ },
58
+ "apple_segmentation_minnesota": {
59
+ "license": "MIT",
60
+ "citation": "@misc{hani2019minneapple,\n title={MinneApple: A Benchmark Dataset for Apple Detection and Segmentation},\n author={Nicolai H\u00e4ni and Pravakar Roy and Volkan Isler}\n year={2019},\n eprint={1909.06441},\n archivePrefix={arXiv},\n primaryClass={cs.CV}\n}"
61
+ },
62
+ "rice_seedling_segmentation": {
63
+ "license": "",
64
+ "citation": "@Article{electronics9101602,\n AUTHOR = {Khan, Abbas and Ilyas, Talha and Umraiz, Muhammad and Mannan, Zubaer Ibna and Kim, Hyongsuk},\n TITLE = {CED-Net: Crops and Weeds Segmentation for Smart Farming Using a Small Cascaded Encoder-Decoder Architecture},\n JOURNAL = {Electronics},\n VOLUME = {9},\n YEAR = {2020},\n NUMBER = {10},\n ARTICLE-NUMBER = {1602},\n URL = {https://www.mdpi.com/2079-9292/9/10/1602},\n ISSN = {2079-9292},\n ABSTRACT = {Convolutional neural networks (CNNs) have achieved state-of-the-art performance in numerous aspects of human life and the agricultural sector is no exception. One of the main objectives of deep learning for smart farming is to identify the precise location of weeds and crops on farmland. In this paper, we propose a semantic segmentation method based on a cascaded encoder-decoder network, namely CED-Net, to differentiate weeds from crops. The existing architectures for weeds and crops segmentation are quite deep, with millions of parameters that require longer training time. To overcome such limitations, we propose an idea of training small networks in cascade to obtain coarse-to-fine predictions, which are then combined to produce the final results. Evaluation of the proposed network and comparison with other state-of-the-art networks are conducted using four publicly available datasets: rice seeding and weed dataset, BoniRob dataset, carrot crop vs. weed dataset, and a paddy&ndash;millet dataset. The experimental results and their comparisons proclaim that the proposed network outperforms state-of-the-art architectures, such as U-Net, SegNet, FCN-8s, and DeepLabv3, over intersection over union (IoU), F1-score, sensitivity, true detection rate, and average precision comparison metrics by utilizing only (1/5.74 &times; U-Net), (1/5.77 &times; SegNet), (1/3.04 &times; FCN-8s), and (1/3.24 &times; DeepLabv3) fractions of total parameters.},\n DOI = {10.3390/electronics9101602}\n}"
65
+ },
66
+ "plant_village_classification": {
67
+ "license": "",
68
+ "citation": "@article{DBLP:journals/corr/HughesS15,\n author = {David P. Hughes and\n Marcel Salath{'{e} } },\n title = {An open access repository of images on plant health to enable the\n development of mobile disease diagnostics through machine\n learning and crowdsourcing},\n journal = {CoRR},\n volume = {abs/1511.08060},\n year = {2015},\n url = {http://arxiv.org/abs/1511.08060},\n archivePrefix = {arXiv},\n eprint = {1511.08060},\n timestamp = {Mon, 13 Aug 2018 16:48:21 +0200},\n biburl = {https://dblp.org/rec/bib/journals/corr/HughesS15},\n bibsource = {dblp computer science bibliography, https://dblp.org}\n}"
69
+ },
70
+ "plant_doc_classification": {
71
+ "license": "CC BY-SA 4.0",
72
+ "citation": "@inproceedings{10.1145/3371158.3371196,\n author = {Singh, Davinder and Jain, Naman and Jain, Pranjali and Kayal, Pratik and Kumawat, Sudhakar and Batra, Nipun},\n title = {PlantDoc: A Dataset for Visual Plant Disease Detection},\n year = {2020},\n isbn = {9781450377386},\n publisher = {Association for Computing Machinery},\n address = {New York, NY, USA},\n url = {https://doi.org/10.1145/3371158.3371196},\n doi = {10.1145/3371158.3371196},\n booktitle = {Proceedings of the 7th ACM IKDD CoDS and 25th COMAD},\n pages = {249–253},\n numpages = {5},\n keywords = {Deep Learning, Object Detection, Image Classification},\n location = {Hyderabad, India},\n series = {CoDS COMAD 2020}\n }"
73
+ },
74
+ "autonomous_greenhouse_regression": {
75
+ "license": "CC BY-SA 4.0",
76
+ "citation": "@misc{https://doi.org/10.4121/15023088.v1,\n doi = {10.4121/15023088.V1},\n url = {https://data.4tu.nl/articles/_/15023088/1},\n author = {Hemming, S. (Silke) and de Zwart, H.F. (Feije) and Elings, A. (Anne) and bijlaard, monique and Marrewijk, van, Bart and Petropoulou, Anna},\n keywords = {Horticultural Crops, Mechanical Engineering, FOS: Mechanical engineering, Artificial Intelligence and Image Processing, FOS: Computer and information sciences, Horticultural Production, FOS: Agriculture, forestry and fisheries, Autonomous Greenhouse Challenge, autonomous greenhouse, Artificial Intelligence, image processing, computer vision, Horticulture, Lettuce, sensors, non-destructive sensing},\n title = {3rd Autonomous Greenhouse Challenge: Online Challenge Lettuce Images},\n publisher = {4TU.ResearchData},\n year = {2021},\n copyright = {Creative Commons Attribution 4.0 International}\n}"
77
+ },
78
+ "grape_detection_syntheticday": {
79
+ "license": "",
80
+ "citation": "@ARTICLE{10.3389/fpls.2019.01185,\n \nAUTHOR={Bailey, Brian N.}, \n\t \nTITLE={Helios: A Scalable 3D Plant and Environmental Biophysical Modeling Framework}, \n\t\nJOURNAL={Frontiers in Plant Science}, \n\t\nVOLUME={10}, \n\t\nYEAR={2019}, \n\t \nURL={https://www.frontiersin.org/article/10.3389/fpls.2019.01185}, \n\t\nDOI={10.3389/fpls.2019.01185}, \n\t\nISSN={1664-462X}, \n \nABSTRACT={This article presents an overview of Helios, a new three-dimensional (3D) plant and environmental modeling framework. Helios is a model coupling framework designed to provide maximum flexibility in integrating and running arbitrary 3D environmental system models. Users interact with Helios through a well-documented open-source C++ API. Version 1.0 comes with model plug-ins for radiation transport, the surface energy balance, stomatal conductance, photosynthesis, solar position, and procedural tree generation. Additional plug-ins are also available for visualizing model geometry and data and for processing and integrating LiDAR scanning data. Many of the plug-ins perform calculations on the graphics processing unit, which allows for efficient simulation of very large domains with high detail. An example modeling study is presented in which leaf-level heterogeneity in water usage and photosynthesis of an orchard is examined to understand how this leaf-scale variability contributes to whole-tree and -canopy fluxes.}\n}"
81
+ },
82
+ "grape_detection_californiaday": {
83
+ "license": "",
84
+ "citation": "@misc{GrapeDay,\n author = {Plant AI and Biophysics Lab},\n title = {Grape Detection 2019 Day},\n year = {2019},\n url = {https://github.com/plant-ai-biophysics-lab/AgML} \n "
85
+ },
86
+ "grape_detection_californianight": {
87
+ "license": "",
88
+ "citation": "@misc{GrapeNight,\n author = {Plant AI and Biophysics Lab},\n title = {Grape Detection 2020 Night},\n year = {2020},\n url = {https://github.com/plant-ai-biophysics-lab/AgML} \n "
89
+ },
90
+ "guava_disease_pakistan": {
91
+ "license": "",
92
+ "citation": "@article{Rauf_Lali_2021, \n title={A Guava Fruits and Leaves Dataset for Detection and Classification of Guava Diseases through Machine Learning}, \n volume={1}, \n url={https://data.mendeley.com/datasets/s8x6jn5cvr/1}, \n DOI={10.17632/s8x6jn5cvr.1}, \n abstractNote={(1) Plant diseases are the primary cause of reduced productivity in agriculture, which results in economic losses. Guava is a big source of nutrients for humans all over the world. Guava diseases, on the other hand, harm the yield and quality of the crop. (2) For the identification and classification of plant diseases, computer vision and image processing methods have been commonly used. (3) The dataset includes an image gallery of healthy and unhealthy Guava fruits and leaves that could be used by researchers to adopt advanced computer vision techniques to protect plants from disease. Dot, Canker, Mummification, and Rust are the diseases targeted in the data sets. (4) The dataset contains 306 images of healthy and unhealthy images for both Guava fruits and leaves collectively. Each image contains 6000 * 4000 dimensions with 300 dpi resolution. (5) All images were acquired from the tropical areas of Pakistan under the supervision of Prof. Dr. Ikramullah Lali. (6) All images were annotated manually by the domain expert such as For Guava fruits and leaves; Dot (76), Canker (77), Mummification (83), and Rust (70) Note: The data labeling was manual and can be updated by automatic labeling through machine learning. In the meantime, the authors can also use the data set for the clustering problem.}, \n author={Rauf, Hafiz Tayyab and Lali, Muhammad Ikram Ullah}, \n year={2021}, month={Apr} \n}\n"
93
+ },
94
+ "apple_detection_spain": {
95
+ "license": "",
96
+ "citation": "@article{GENEMOLA2019104289,\ntitle = {KFuji RGB-DS database: Fuji apple multi-modal images for fruit detection with color, depth and range-corrected IR data},\njournal = {Data in Brief},\nvolume = {25},\npages = {104289},\nyear = {2019},\nissn = {2352-3409},\ndoi = {https://doi.org/10.1016/j.dib.2019.104289},\nurl = {https://www.sciencedirect.com/science/article/pii/S2352340919306432},\nauthor = {Jordi Gené-Mola and Verónica Vilaplana and Joan R. Rosell-Polo and Josep-Ramon Morros and Javier Ruiz-Hidalgo and Eduard Gregorio},\nkeywords = {Multi-modal dataset, Fruit detection, Depth cameras, RGB-D, Fruit reflectance, Fuji apple},\nabstract = {This article contains data related to the research article entitle “Multi-modal Deep Learning for Fruit Detection Using RGB-D Cameras and their Radiometric Capabilities” [1]. The development of reliable fruit detection and localization systems is essential for future sustainable agronomic management of high-value crops. RGB-D sensors have shown potential for fruit detection and localization since they provide 3D information with color data. However, the lack of substantial datasets is a barrier for exploiting the use of these sensors. This article presents the KFuji RGB-DS database which is composed by 967 multi-modal images of Fuji apples on trees captured using Microsoft Kinect v2 (Microsoft, Redmond, WA, USA). Each image contains information from 3 different modalities: color (RGB), depth (D) and range corrected IR intensity (S). Ground truth fruit locations were manually annotated, labeling a total of 12,839 apples in all the dataset. The current dataset is publicly available at http://www.grap.udl.cat/publicacions/datasets.html.}\n}"
97
+ },
98
+ "apple_detection_drone_brazil": {
99
+ "license": "CC BY-SA 4.0",
100
+ "citation": "@article{DBLP:journals/corr/abs-2110-12331,\n author = {Thiago T. Santos and\n Luciano Gebler},\n title = {A methodology for detection and localization of fruits in apples orchards\n from aerial images},\n journal = {CoRR},\n volume = {abs/2110.12331},\n year = {2021},\n url = {https://arxiv.org/abs/2110.12331},\n eprinttype = {arXiv},\n eprint = {2110.12331},\n timestamp = {Thu, 28 Oct 2021 15:25:31 +0200},\n biburl = {https://dblp.org/rec/journals/corr/abs-2110-12331.bib},\n bibsource = {dblp computer science bibliography, https://dblp.org}\n}"
101
+ },
102
+ "plant_doc_detection": {
103
+ "license": "CC BY-SA 4.0",
104
+ "citation": "@inproceedings{10.1145/3371158.3371196,\n author = {Singh, Davinder and Jain, Naman and Jain, Pranjali and Kayal, Pratik and Kumawat, Sudhakar and Batra, Nipun},\n title = {PlantDoc: A Dataset for Visual Plant Disease Detection},\n year = {2020},\n isbn = {9781450377386},\n publisher = {Association for Computing Machinery},\n address = {New York, NY, USA},\n url = {https://doi.org/10.1145/3371158.3371196},\n doi = {10.1145/3371158.3371196},\n booktitle = {Proceedings of the 7th ACM IKDD CoDS and 25th COMAD},\n pages = {249–253},\n numpages = {5},\n keywords = {Deep Learning, Object Detection, Image Classification},\n location = {Hyderabad, India},\n series = {CoDS COMAD 2020}\n }"
105
+ },
106
+ "wheat_head_counting": {
107
+ "license": "CC BY-SA 4.0",
108
+ "citation": "@article{david2020global,\n title={Global Wheat Head Detection (GWHD) dataset: a large and diverse dataset of high-resolution RGB-labelled images to develop and benchmark wheat head detection methods},\n author={David, Etienne and Madec, Simon and Sadeghi-Tehran, Pouria and Aasen, Helge and Zheng, Bangyou and Liu, Shouyang and Kirchgessner, Norbert and Ishikawa, Goro and Nagasawa, Koichi and Badhon, Minhajul A and others},\n journal={Plant Phenomics},\n volume={2020},\n year={2020},\n publisher={Science Partner Journal}\n }"
109
+ },
110
+ "peachpear_flower_segmentation": {
111
+ "license": "US Public Domain",
112
+ "citation": "@ARTICLE{8392727,\n author={Dias, Philipe A. and Tabb, Amy and Medeiros, Henry},\n journal={IEEE Robotics and Automation Letters}, \n title={Multispecies Fruit Flower Detection Using a Refined Semantic Segmentation Network}, \n year={2018},\n volume={3},\n number={4},\n pages={3003-3010},\n doi={10.1109/LRA.2018.2849498}}"
113
+ },
114
+ "red_grapes_and_leaves_segmentation": {
115
+ "license": "",
116
+ "citation": "@inproceedings{kalampokas2020semantic,\n title={Semantic segmentation of vineyard images using convolutional neural networks},\n author={Kalampokas, Theofanis and Tziridis, Konstantinos and Nikolaou, Alexandros and Vrochidou, Eleni and Papakostas, George A and Pachidis, Theodore and Kaburlasos, Vassilis G},\n booktitle={International Conference on Engineering Applications of Neural Networks},\n pages={292--303},\n year={2020},\n organization={Springer}}"
117
+ },
118
+ "white_grapes_and_leaves_segmentation": {
119
+ "license": "",
120
+ "citation": "@inproceedings{kalampokas2020semantic,\n title={Semantic segmentation of vineyard images using convolutional neural networks},\n author={Kalampokas, Theofanis and Tziridis, Konstantinos and Nikolaou, Alexandros and Vrochidou, Eleni and Papakostas, George A and Pachidis, Theodore and Kaburlasos, Vassilis G},\n booktitle={International Conference on Engineering Applications of Neural Networks},\n pages={292--303},\n year={2020},\n organization={Springer}}"
121
+ },
122
+ "ghai_romaine_detection": {
123
+ "license": "CC BY-SA 4.0",
124
+ "citation": ""
125
+ },
126
+ "ghai_green_cabbage_detection": {
127
+ "license": "CC BY-SA 4.0",
128
+ "citation": ""
129
+ },
130
+ "ghai_iceberg_lettuce_detection": {
131
+ "license": "CC BY-SA 4.0",
132
+ "citation": ""
133
+ },
134
+ "riseholme_strawberry_classification_2021": {
135
+ "license": "",
136
+ "citation": "@inproceedings{CWSC21,\n title={Self-supervised Representation Learning for Reliable Robotic Monitoring of Fruit Anomalies},\n author={Choi, Taeyeong and Would, Owen and Salazar-Gomez, Adrian and Cielniak, Grzegorz},\n booktitle={2022 International Conference on Robotics and Automation (ICRA)},\n pages={2266--2272},\n year={2022},\n organization={IEEE}\n}"
137
+ },
138
+ "ghai_broccoli_detection": {
139
+ "license": "CC BY-SA 4.0",
140
+ "citation": ""
141
+ },
142
+ "bean_synthetic_earlygrowth_aerial": {
143
+ "license": "MIT",
144
+ "citation": "@ARTICLE{10.3389/fpls.2019.01185,\n \nAUTHOR={Bailey, Brian N.}, \n\t \nTITLE={Helios: A Scalable 3D Plant and Environmental Biophysical Modeling Framework}, \n\t\nJOURNAL={Frontiers in Plant Science}, \n\t\nVOLUME={10}, \n\t\nYEAR={2019}, \n\t \nURL={https://www.frontiersin.org/article/10.3389/fpls.2019.01185}, \n\t\nDOI={10.3389/fpls.2019.01185}, \n\t\nISSN={1664-462X}, \n \nABSTRACT={This article presents an overview of Helios, a new three-dimensional (3D) plant and environmental modeling framework. Helios is a model coupling framework designed to provide maximum flexibility in integrating and running arbitrary 3D environmental system models. Users interact with Helios through a well-documented open-source C++ API. Version 1.0 comes with model plug-ins for radiation transport, the surface energy balance, stomatal conductance, photosynthesis, solar position, and procedural tree generation. Additional plug-ins are also available for visualizing model geometry and data and for processing and integrating LiDAR scanning data. Many of the plug-ins perform calculations on the graphics processing unit, which allows for efficient simulation of very large domains with high detail. An example modeling study is presented in which leaf-level heterogeneity in water usage and photosynthesis of an orchard is examined to understand how this leaf-scale variability contributes to whole-tree and -canopy fluxes.}\n}"
145
+ },
146
+ "ghai_strawberry_fruit_detection": {
147
+ "license": "CC BY-SA 4.0",
148
+ "citation": ""
149
+ },
150
+ "vegann_multicrop_presence_segmentation": {
151
+ "license": "CC BY-SA 4.0",
152
+ "citation": " @article{Madec_Irfan_Velumani_Baret_David_Daubige_Samatan_Serouart_Smith_James_et al._2023, title={VegAnn, Vegetation Annotation of multi-crop RGB images acquired under diverse conditions for segmentation}, volume={10}, ISSN={2052-4463}, url={https://www.nature.com/articles/s41597-023-02098-y}, DOI={10.1038/s41597-023-02098-y}, abstractNote={Abstract\n \n Applying deep learning to images of cropping systems provides new knowledge and insights in research and commercial applications. Semantic segmentation or pixel-wise classification, of RGB images acquired at the ground level, into vegetation and background is a critical step in the estimation of several canopy traits. Current state of the art methodologies based on convolutional neural networks (CNNs) are trained on datasets acquired under controlled or indoor environments. These models are unable to generalize to real-world images and hence need to be fine-tuned using new labelled datasets. This motivated the creation of the VegAnn -\n Veg\n etation\n Ann\n otation - dataset, a collection of 3775 multi-crop RGB images acquired for different phenological stages using different systems and platforms in diverse illumination conditions. We anticipate that VegAnn will help improving segmentation algorithm performances, facilitate benchmarking and promote large-scale crop vegetation segmentation research.}, number={1}, journal={Scientific Data}, author={Madec, Simon and Irfan, Kamran and Velumani, Kaaviya and Baret, Frederic and David, Etienne and Daubige, Gaetan and Samatan, Lucas Bernigaud and Serouart, Mario and Smith, Daniel and James, Chrisbin and Camacho, Fernando and Guo, Wei and De Solan, Benoit and Chapman, Scott C. and Weiss, Marie}, year={2023}, month=may, pages={302}, language={en} }\n"
153
+ },
154
+ "corn_maize_leaf_disease": {
155
+ "license": "",
156
+ "citation": "Singh D, Jain N, Jain P, Kayal P, Kumawat S, Batra N. PlantDoc: a dataset for visual plant disease detection. InProceedings of the 7th ACM IKDD CoDS and 25th COMAD 2020 Jan 5 (pp. 249-253)."
157
+ },
158
+ "tomato_leaf_disease": {
159
+ "license": "CC0: Public Domain",
160
+ "citation": ""
161
+ },
162
+ "vine_virus_photo_dataset": {
163
+ "license": "Apache 2.0",
164
+ "citation": ""
165
+ },
166
+ "embrapa_wgisd_grape_detection": {
167
+ "license": "CC BY-NC 4.0",
168
+ "citation": ""
169
+ },
170
+ "growliflower_cauliflower_segmentation": {
171
+ "license": "",
172
+ "citation": "Kierdorf, Jana & Junker-Frohn, Laura & Delaney, Mike & Olave, Mariele & Burkart, Andreas & Jaenicke, Hannah & Muller, Onno & Roscher, Ribana. (2022). GrowliFlower: An image time‐series dataset for GROWth analysis of cauLIFLOWER. Journal of Field Robotics. 40. 10.1002/rob.22122. "
173
+ },
174
+ "strawberry_detection_2023": {
175
+ "license": "CC BY 4.0",
176
+ "citation": ""
177
+ },
178
+ "strawberry_detection_2022": {
179
+ "license": "CC BY 4.0",
180
+ "citation": ""
181
+ },
182
+ "almond_harvest_2021": {
183
+ "license": "Apache 2.0",
184
+ "citation": ""
185
+ },
186
+ "almond_bloom_2023": {
187
+ "license": "Apache 2.0",
188
+ "citation": ""
189
+ },
190
+ "gemini_flower_detection_2022": {
191
+ "license": "Apache 2.0",
192
+ "citation": ""
193
+ },
194
+ "gemini_leaf_detection_2022": {
195
+ "license": "Apache 2.0",
196
+ "citation": ""
197
+ },
198
+ "gemini_pod_detection_2022": {
199
+ "license": "Apache 2.0",
200
+ "citation": ""
201
+ },
202
+ "gemini_plant_detection_2022": {
203
+ "license": "Apache 2.0",
204
+ "citation": ""
205
+ },
206
+ "paddy_disease_classification": {
207
+ "license": "CC BY 4.0",
208
+ "citation": "Petchiammal A, Briskline Kiruba S, Murugan D, Pandarasamy Arjunan. (2022). Paddy Doctor: A Visual Image Dataset for Automated Paddy Disease Classification and Benchmarking. IEEE Dataport. https://dx.doi.org/10.21227/hz4v-af08"
209
+ },
210
+ "onion_leaf_classification": {
211
+ "license": "CC BY 4.0",
212
+ "citation": "Aishwarya, M.P & Reddy, A.. (2024). Dataset of Chilli and Onion Plant Leaf Images for Classification and Detection. Data in Brief. 54. 110524. 10.1016/j.dib.2024.110524. "
213
+ },
214
+ "chilli_leaf_classification": {
215
+ "license": "CC BY 4.0",
216
+ "citation": "Aishwarya, M.P & Reddy, A.. (2024). Dataset of Chilli and Onion Plant Leaf Images for Classification and Detection. Data in Brief. 54. 110524. 10.1016/j.dib.2024.110524. "
217
+ },
218
+ "orange_leaf_disease_classification": {
219
+ "license": "CC BY 4.0",
220
+ "citation": "Emon, Yousuf Rayhan; Ahad, Md Taimur (2023), “Multi-format open-source sweet orange leaf dataset for disease detection, classification, and analysis.”, Mendeley Data, V1, doi: 10.17632/f7cr74mwpj.1"
221
+ },
222
+ "papaya_leaf_disease_classification": {
223
+ "license": "CC BY 4.0",
224
+ "citation": "Sarker, Arpita ; Mustofa, Sumaya; Ahad, Md Taimur (2023), “BDPapayaLeaf: A annotation based image dataset of papaya leaf disease.”, Mendeley Data, V1, doi: 10.17632/p997fvf526.1"
225
+ },
226
+ "blackgram_plant_leaf_disease_classification": {
227
+ "license": "CC BY 4.0",
228
+ "citation": "Talasila, Srinivas; Rawal, Kirti; Sethi, Gaurav; MSS, Sanjay; M, Surya Prakash Reddy (2022), “Blackgram Plant Leaf Disease Dataset”, Mendeley Data, V3, doi: 10.17632/zfcv9fmrgv.3"
229
+ },
230
+ "arabica_coffee_leaf_disease_classification": {
231
+ "license": "CC BY 4.0",
232
+ "citation": "@article{JEPKOECH2021107142, title = {Arabica coffee leaf images dataset for coffee leaf disease detection and classification}, journal = {Data in Brief}, volume = {36}, pages = {107142}, year = {2021}, issn = {2352-3409}, doi = {https://doi.org/10.1016/j.dib.2021.107142}, url = {https://www.sciencedirect.com/science/article/pii/S2352340921004261}, author = {Jennifer Jepkoech and David Muchangi Mugo and Benson K. Kenduiywo and Edna Chebet Too}}"
233
+ },
234
+ "banana_leaf_disease_classification": {
235
+ "license": "CC BY 4.0",
236
+ "citation": "hailu, yordanos (2021), “Banana Leaf Disease Images”, Mendeley Data, V1, doi: 10.17632/rjykr62kdh.1"
237
+ },
238
+ "coconut_tree_disease_classification": {
239
+ "license": "CC BY 4.0",
240
+ "citation": "PATIL, Kailas; Thite, Sandip; Suryawanshi, Yogesh; chumchu, prawit (2023), “Coconut Tree Disease Dataset”, Mendeley Data, V1, doi: 10.17632/gh56wbsnj5.1"
241
+ },
242
+ "rice_leaf_disease_classification": {
243
+ "license": "",
244
+ "citation": ""
245
+ },
246
+ "tea_leaf_disease_classification": {
247
+ "license": "CC BY-NC 4.0",
248
+ "citation": "@article{BALASUNDARAM2025103784, title = {Tea leaf disease detection using segment anything model and deep convolutional neural networks}, journal = {Results in Engineering}, volume = {25}, pages = {103784}, year = {2025}, issn = {2590-1230}, doi = {https://doi.org/10.1016/j.rineng.2024.103784}, url = {https://www.sciencedirect.com/science/article/pii/S2590123024020279}, author = {Ananthakrishnan Balasundaram and Prem Sundaresan and Aryan Bhavsar and Mishti Mattu and Muthu Subash Kavitha and Ayesha Shaik}}"
249
+ },
250
+ "betel_leaf_disease_classification": {
251
+ "license": "CC BY 4.0",
252
+ "citation": "Rashid, Mohammad Rifat Ahmmad; Hossain, Md. Miskat ; Biswas, Joy ; Majumder, Hredoy (2024), “Betel Leaf Image Dataset from Bangladesh”, Mendeley Data, V2, doi: 10.17632/g7fpgj57wc.2"
253
+ },
254
+ "java_plum_leaf_disease_classification": {
255
+ "license": "CC BY 4.0",
256
+ "citation": "Bhowmik, Auvick Chandra; Ahad, Taimur (2024), “Java Plum Leaf Disease Dataset”, Mendeley Data, V3, doi: 10.17632/43d75vptz4.3"
257
+ },
258
+ "sunflower_disease_classification": {
259
+ "license": "CC BY 4.0",
260
+ "citation": "Rajbongshi, Aditya; Sara, Umme ; Akter, Bonna ; Shakil, Rashiduzzaman ; Sazzad, Sadia (2022), “Sun Flower Fruits and Leaves dataset for Sunflower Disease Classification through Machine Learning and Deep Learning”, Mendeley Data, V1, doi: 10.17632/b83hmrzth8.1"
261
+ },
262
+ "cucumber_disease_classification": {
263
+ "license": "CC BY 4.0",
264
+ "citation": "Sultana, Nusrat; Shorif, Sumaita Binte ; Akter, Morium ; Uddin, Mohammad Shorif (2022), “Cucumber Disease Recognition Dataset”, Mendeley Data, V1, doi: 10.17632/y6d3z6f8z9.1"
265
+ },
266
+ "soybean_insect_classification": {
267
+ "license": "CC BY 4.0",
268
+ "citation": "Mignoni, Maria Eloisa (2021), “Images of Soybean Leaves”, Mendeley Data, V1, doi: 10.17632/bycbh73438.1"
269
+ }
270
+ }
AgML/source/agml/_assets/viz_colormaps.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "default": [
3
+ [0, 255, 0],
4
+ [255, 0, 0],
5
+ [0, 0, 255],
6
+ [255, 255, 0],
7
+ [255, 0, 255],
8
+ [0, 255, 255]
9
+ ],
10
+ "agriculture": [
11
+ [0, 255, 0],
12
+ [50, 168, 82],
13
+ [42, 84, 24],
14
+ [17, 71, 4],
15
+ [57, 66, 53],
16
+ [181, 204, 171]
17
+ ]
18
+ }
AgML/source/agml/_helios/helios_install.sh ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+
3
+
4
+ # This script will almost certainly not be run manually, and instead
5
+ # be called by the AgML Helios module. So, we can't rely on using the
6
+ # existing parent directory to install Helios. Instead, we need to get
7
+ # the path of this actual file itself, and then
8
+ PATH_TO_ME="$(readlink -nf "$0")"
9
+ INSTALL_PATH="$(dirname "$(dirname "$PATH_TO_ME")")/_helios/Helios"
10
+
11
+ # Install or Update Helios, depending on whether the directory for Helios
12
+ # already exists. While the actual Python installation script which calls
13
+ # this shell script has slightly more complex logic (for figuring out
14
+ # version Helios is on, and in turn, whether it needs an update or not),
15
+ # this simply installs/updates based on the existence of the directory.
16
+ if [ ! -d "$INSTALL_PATH" ]; then
17
+ git clone -b master https://github.com/PlantSimulationLab/Helios.git "$INSTALL_PATH"
18
+ else
19
+ ORIGINAL_DIR="$PWD"
20
+ cd "$INSTALL_PATH"
21
+ git pull https://github.com/PlantSimulationLab/Helios.git master
22
+ cd "$ORIGINAL_DIR" || echo "Issue when trying to update Helios. Please report this to the AgML team."; exit
23
+ fi
24
+
25
+
26
+
AgML/source/agml/_internal/__init__.py ADDED
File without changes
AgML/source/agml/_internal/preprocess.py ADDED
@@ -0,0 +1,1609 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 UC Davis Plant AI and Biophysics Lab
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """
16
+ Preprocessing code for AgML public data sources.
17
+ This file stores the preprocessing code used to preprocess a public
18
+ dataset when added to AgML's public data sources.
19
+ If you want to use this preprocessing code, run `pip install agml[dev]`
20
+ to install the necessary preprocessing packages.
21
+ """
22
+
23
+ import argparse
24
+ import csv
25
+ import glob
26
+ import json
27
+ import os
28
+ import shutil
29
+ import sys
30
+
31
+ import cv2
32
+ import numpy as np
33
+ import pandas as pd
34
+ from PIL import Image
35
+
36
+ from agml._internal.process_utils import (
37
+ convert_bbox_to_coco,
38
+ convert_xmls_to_cocojson,
39
+ get_coco_annotation_from_obj,
40
+ get_image_info,
41
+ get_label2id,
42
+ move_segmentation_dataset,
43
+ read_txt_file,
44
+ )
45
+ from agml.utils.data import load_public_sources
46
+ from agml.utils.io import create_dir, get_dir_list, get_file_list, nested_dir_list
47
+ from agml.utils.logging import tqdm
48
+
49
+ def yolo_to_coco_bbox(x_center, y_center, width, height, img_width, img_height):
50
+ """Convert YOLO format (x_center, y_center, width, height) to COCO format (xmin, ymin, width, height)."""
51
+ xmin = (x_center - (width / 2)) * img_width
52
+ ymin = (y_center - (height / 2)) * img_height
53
+ width = width * img_width
54
+ height = height * img_height
55
+ return [xmin, ymin, width, height]
56
+
57
+
58
+ class PublicDataPreprocessor(object):
59
+ """Internal data preprocessing class.
60
+
61
+ Parameters
62
+ ----------
63
+ data_dir : str
64
+ The directory with a folder `original` and `processed` to hold
65
+ the original and processed datasets, respectively.
66
+ """
67
+
68
+ def __init__(self, data_dir):
69
+ self.data_dir = os.path.abspath(data_dir)
70
+ self.data_original_dir = os.path.join(self.data_dir, "original")
71
+ self.data_processed_dir = os.path.join(self.data_dir, "processed")
72
+ self.data_sources = load_public_sources()
73
+
74
+ def preprocess(self, dataset_name):
75
+ """Preprocesses the provided dataset.
76
+ Parameters
77
+ ----------
78
+ dataset_name : str
79
+ name of dataset to preprocess
80
+ """
81
+ getattr(self, dataset_name)(dataset_name)
82
+
83
+ def vine_virus_photo_dataset(self, dataset_name):
84
+ """Preprocesses the Vine Virus Photo Dataset."""
85
+ # Get the dataset directory directly (no need for 'original' directory)
86
+ base_path = self.data_dir
87
+ classes = sorted([d for d in os.listdir(base_path) if os.path.isdir(os.path.join(base_path, d))])
88
+
89
+ # Create output directory
90
+ output_path = os.path.join(self.data_processed_dir, dataset_name)
91
+ if not os.path.exists(output_path):
92
+ os.makedirs(output_path)
93
+
94
+ # Create subdirectories for each class inside the output directory
95
+ for cls in classes:
96
+ class_output_path = os.path.join(output_path, cls)
97
+ if not os.path.exists(class_output_path):
98
+ os.makedirs(class_output_path)
99
+
100
+ # Process and copy the dataset images to the processed directory
101
+ for cls in classes:
102
+ class_path = os.path.join(base_path, cls)
103
+ for img in os.listdir(class_path):
104
+ if img.endswith(("jpg", "png", "jpeg", "JPG")):
105
+ img_path = os.path.join(class_path, img)
106
+ shutil.copyfile(img_path, os.path.join(output_path, cls, img))
107
+
108
+ print(f"Dataset {dataset_name} has been preprocessed and saved to {output_path}")
109
+
110
+ def corn_maize_leaf_disease(self, dataset_name):
111
+ """Preprocesses the Corn or Maize Leaf Disease Dataset."""
112
+ # Get the dataset directory directly (no need for 'original' directory)
113
+ base_path = self.data_dir
114
+ classes = sorted([d for d in os.listdir(base_path) if os.path.isdir(os.path.join(base_path, d))])
115
+
116
+ # Create output directory
117
+ output_path = os.path.join(self.data_processed_dir, dataset_name)
118
+ if not os.path.exists(output_path):
119
+ os.makedirs(output_path)
120
+
121
+ # Create subdirectories for each class inside the output directory
122
+ for cls in classes:
123
+ class_output_path = os.path.join(output_path, cls)
124
+ if not os.path.exists(class_output_path):
125
+ os.makedirs(class_output_path)
126
+
127
+ # Process and copy the dataset images to the processed directory
128
+ for cls in classes:
129
+ class_path = os.path.join(base_path, cls)
130
+ for img in os.listdir(class_path):
131
+ if img.endswith(("jpg", "png", "jpeg", "JPG")):
132
+ img_path = os.path.join(class_path, img)
133
+
134
+ # Open the image using Pillow
135
+ with Image.open(img_path) as image:
136
+ # Convert image to a NumPy array
137
+ img_array = np.array(image)
138
+
139
+ # Ensure the image is in range [0, 255] and dtype is uint8
140
+ if img_array.dtype != np.uint8:
141
+ # If the image is in float [0-1], scale it to [0-255]
142
+ img_array = (img_array * 255).astype(np.uint8)
143
+
144
+ # Convert back to PIL Image to save
145
+ processed_image = Image.fromarray(img_array)
146
+
147
+ if processed_image.mode == "RGBA":
148
+ processed_image = processed_image.convert("RGB")
149
+
150
+ # Save the processed image to the output directory
151
+ processed_image.save(os.path.join(output_path, cls, img))
152
+
153
+ print(f"Dataset {dataset_name} has been preprocessed and saved to {output_path}")
154
+
155
+ def tomato_leaf_disease(self, dataset_name):
156
+ """Preprocesses the Tomato Leaf Disease Dataset."""
157
+ # Get the dataset directory directly (no need for 'original' directory)
158
+ base_path = self.data_dir
159
+ classes = sorted([d for d in os.listdir(base_path) if os.path.isdir(os.path.join(base_path, d))])
160
+
161
+ # Create output directory
162
+ output_path = os.path.join(self.data_processed_dir, dataset_name)
163
+ if not os.path.exists(output_path):
164
+ os.makedirs(output_path)
165
+
166
+ # Create subdirectories for each class inside the output directory
167
+ for cls in classes:
168
+ class_output_path = os.path.join(output_path, cls)
169
+ if not os.path.exists(class_output_path):
170
+ os.makedirs(class_output_path)
171
+
172
+ # Process and copy the dataset images to the processed directory
173
+ for cls in classes:
174
+ class_path = os.path.join(base_path, cls)
175
+ for img in os.listdir(class_path):
176
+ if img.endswith(("jpg", "png", "jpeg", "JPG")):
177
+ img_path = os.path.join(class_path, img)
178
+
179
+ # Open the image using Pillow
180
+ with Image.open(img_path) as image:
181
+ # Convert image to a NumPy array
182
+ img_array = np.array(image)
183
+
184
+ # Ensure the image is in range [0, 255] and dtype is uint8
185
+ if img_array.dtype != np.uint8:
186
+ # If the image is in float [0-1], scale it to [0-255]
187
+ img_array = (img_array * 255).astype(np.uint8)
188
+
189
+ # Convert back to PIL Image to save
190
+ processed_image = Image.fromarray(img_array)
191
+
192
+ if processed_image.mode == "RGBA":
193
+ processed_image = processed_image.convert("RGB")
194
+
195
+ # Save the processed image to the output directory
196
+ processed_image.save(os.path.join(output_path, cls, img))
197
+
198
+ print(f"Dataset {dataset_name} has been preprocessed and saved to {output_path}")
199
+
200
+ def bean_disease_uganda(self, dataset_name):
201
+ # Get the dataset classes and paths
202
+ base_path = os.path.join(self.data_original_dir, dataset_name)
203
+ dirs = ["train", "validation", "test"]
204
+ classes = sorted(os.listdir(os.path.join(base_path, dirs[0])))[1:]
205
+
206
+ # Construct output directories
207
+ output = os.path.join(self.data_processed_dir, dataset_name)
208
+ os.makedirs(output, exist_ok=True)
209
+ for cls in classes:
210
+ os.makedirs(os.path.join(output, cls), exist_ok=True)
211
+
212
+ # Move the dataset
213
+ for dir_ in dirs:
214
+ for cls in classes:
215
+ path = os.path.join(base_path, dir_, cls)
216
+ for p in os.listdir(path):
217
+ if p.endswith("jpg") or p.endswith("png"):
218
+ img = os.path.join(base_path, dir_, cls, p)
219
+ shutil.copyfile(img, os.path.join(output, cls, p))
220
+
221
+ def leaf_counting_denmark(self, dataset_name):
222
+ pass
223
+
224
+ def plant_seedlings_aarhus(self, dataset_name):
225
+ pass
226
+
227
+ def crop_weeds_greece(self, dataset_name):
228
+ pass
229
+
230
+ def rangeland_weeds_australia(self, dataset_name):
231
+ # Get the file information.
232
+ dataset_dir = os.path.join(self.data_original_dir, dataset_name)
233
+ images = get_file_list(os.path.join(dataset_dir, "images"))
234
+ df = pd.read_csv(os.path.join(dataset_dir, "labels.csv"))
235
+
236
+ # Construct the new structure.
237
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
238
+ unique_labels = np.unique(df["Species"])
239
+ for unique_label in unique_labels:
240
+ os.makedirs(os.path.join(processed_dir, unique_label.title()), exist_ok=True)
241
+ for file in tqdm(images, desc="Moving Images", file=sys.stdout):
242
+ save_dir = df.loc[df["Filename"] == os.path.basename(file)]["Species"].values[0].title()
243
+ shutil.copyfile(
244
+ os.path.join(dataset_dir, "images", file),
245
+ os.path.join(processed_dir, save_dir, os.path.basename(file)),
246
+ )
247
+
248
+ def fruit_detection_worldwide(self, dataset_name):
249
+ # Get the dataset directory
250
+ dataset_dir = os.path.join(self.data_original_dir, dataset_name, "datasets")
251
+
252
+ # Get folder list
253
+ dataset_folders = get_dir_list(dataset_dir)
254
+ label2id = get_label2id(dataset_folders)
255
+ anno_data_all = []
256
+ for folder in dataset_folders:
257
+ annotations = ["test_RGB.txt", "train_RGB.txt"]
258
+ dataset_path = os.path.join(dataset_dir, folder)
259
+ # @TODO: Make separate json files for train and test?
260
+ for anno_file_name in annotations:
261
+ # Read annotations
262
+ try:
263
+ anno_data = read_txt_file(os.path.join(dataset_path, anno_file_name))
264
+ except:
265
+ try:
266
+ anno_data = read_txt_file(os.path.join(dataset_path, anno_file_name + ".txt"))
267
+ except Exception as e:
268
+ raise e
269
+
270
+ # Concat fruit name at head of line
271
+ for i, anno in enumerate(anno_data):
272
+ # Change to test path if the text file is test
273
+ if "test" in anno_file_name and "TRAIN" in anno[0]:
274
+ anno_data[i][0] = anno[0].replace("TRAIN", "TEST")
275
+ anno_data[i][0] = os.path.join(dataset_path, anno_data[i][0])
276
+
277
+ anno_data_all += anno_data
278
+
279
+ # Process annotation files
280
+ save_dir_anno = os.path.join(self.data_processed_dir, dataset_name, "annotations")
281
+ create_dir(save_dir_anno)
282
+ output_json_file = os.path.join(save_dir_anno, "instances.json")
283
+
284
+ general_info = {
285
+ "description": "fruits dataset",
286
+ "url": "https://drive.google.com/drive/folders/1CmsZb1caggLRN7ANfika8WuPiywo4mBb",
287
+ "version": "1.0",
288
+ "year": 2018,
289
+ "contributor": "Inkyu Sa",
290
+ "date_created": "2018/11/12",
291
+ }
292
+
293
+ # Process image files
294
+ output_img_path = os.path.join(self.data_processed_dir, dataset_name, "images")
295
+ create_dir(output_img_path)
296
+
297
+ convert_bbox_to_coco(anno_data_all, label2id, output_json_file, output_img_path, general_info)
298
+
299
+ def apple_detection_usa(self, dataset_name, fix=False):
300
+ # Just a quick fix to clip over-sized bounding boxes.
301
+ if fix:
302
+ # Load in the annotations.
303
+ dataset_dir = os.path.join(self.data_original_dir, dataset_name)
304
+ with open(os.path.join(dataset_dir, "annotations.json"), "r") as f:
305
+ annotations = json.load(f)
306
+
307
+ # Get the images and all of their heights/widths.
308
+ images = annotations["images"]
309
+ image_id_content_map = {}
310
+ for image in images:
311
+ image_id_content_map[image["id"]] = (image["height"], image["width"])
312
+
313
+ # Load all of the annotations.
314
+ new_annotations = []
315
+ for a in annotations["annotations"]:
316
+ new_a = a.copy()
317
+ height, width = image_id_content_map[a["image_id"]]
318
+ (x, y, w, h) = a["bbox"]
319
+ x1, y1, x2, y2 = x, y, x + w, y + h
320
+ x1 = np.clip(x1, 0, width)
321
+ x2 = np.clip(x2, 0, width)
322
+ y1 = np.clip(y1, 0, height)
323
+ y2 = np.clip(y2, 0, height)
324
+ new_a["bbox"] = [int(i) for i in [x1, y1, x2 - x1, y2 - y1]]
325
+ new_annotations.append(new_a)
326
+
327
+ # Save the annotations.
328
+ annotations["annotations"] = new_annotations
329
+ with open(os.path.join(dataset_dir, "annotations.json"), "w") as f:
330
+ json.dump(annotations, f)
331
+ return
332
+
333
+ # resize the dataset
334
+ resize = 1.0
335
+
336
+ # Read public_datasources.json to get class information
337
+ category_info = self.data_sources[dataset_name]["classes"]
338
+ labels_str = []
339
+ labels_ids = []
340
+ for info in category_info:
341
+ labels_str.append(category_info[info])
342
+ labels_ids.append(int(info))
343
+
344
+ label2id = dict(zip(labels_str, labels_ids))
345
+
346
+ # Task 1: Image classification
347
+ dataset_dir = os.path.join(self.data_original_dir, dataset_name)
348
+ obj_Detection_data = os.path.join(dataset_dir, "Dataset")
349
+
350
+ # get folders
351
+ plant_folders = nested_dir_list(obj_Detection_data)
352
+
353
+ # do tasks along folders
354
+ anno_data_all = []
355
+ for folder in plant_folders:
356
+ # Get image file and xml file
357
+ full_path = os.path.join(obj_Detection_data, folder)
358
+ all_files = get_file_list(full_path)
359
+ anno_files = [x for x in all_files if "txt" in x]
360
+ for anno_file in anno_files:
361
+ anno_line = []
362
+ anno_path = os.path.join(full_path, anno_file)
363
+ # Opening annotation file
364
+ anno_data = read_txt_file(anno_path, delimiter=",")[0]
365
+
366
+ for i, anno in enumerate(anno_data):
367
+ new_anno = [os.path.join(dataset_dir, anno_data[i][0])]
368
+ # Add bbox count
369
+ # Update image file path to abs path
370
+ bbox_cnt = int((len(anno_data[i]) - 1) / 4)
371
+ new_anno.append(str(bbox_cnt))
372
+ for idx in range(bbox_cnt):
373
+ xmin = int(anno[1 + 4 * idx])
374
+ ymin = int(anno[1 + 4 * idx + 1])
375
+ w = int(anno[1 + 4 * idx + 2])
376
+ h = int(anno[1 + 4 * idx + 3])
377
+
378
+ new_anno.append(str(xmin)) # xmin
379
+ new_anno.append(str(ymin)) # ymin
380
+ new_anno.append(str(xmin + w)) # xmax
381
+ new_anno.append(str(ymin + h)) # ymax
382
+ new_anno.append(str(1)) # label
383
+ anno_data[i] = new_anno
384
+ anno_data_all += anno_data
385
+
386
+ # Process annotation files
387
+ save_dir_anno = os.path.join(self.data_processed_dir, dataset_name, "annotations")
388
+ create_dir(save_dir_anno)
389
+ output_json_file = os.path.join(save_dir_anno, "instances.json")
390
+
391
+ general_info = {
392
+ "description": "apple dataset",
393
+ "url": "https://research.libraries.wsu.edu:8443/xmlui/handle/2376/17721",
394
+ "version": "1.0",
395
+ "year": 2019,
396
+ "contributor": "Bhusal, Santosh, Karkee, Manoj, Zhang, Qin",
397
+ "date_created": "2019/04/20",
398
+ }
399
+
400
+ # Process image files
401
+ output_img_path = os.path.join(self.data_processed_dir, dataset_name, "images")
402
+ create_dir(output_img_path)
403
+ convert_bbox_to_coco(
404
+ anno_data_all,
405
+ label2id,
406
+ output_json_file,
407
+ output_img_path,
408
+ general_info,
409
+ None,
410
+ None,
411
+ get_label_from_folder=False,
412
+ resize=resize,
413
+ add_foldername=True,
414
+ )
415
+
416
+ def mango_detection_australia(self, dataset_name):
417
+ # resize the dataset
418
+ resize = 1.0
419
+
420
+ # Read public_datasources.json to get class information
421
+ datasource_file = os.path.join(os.path.dirname(__file__), "../_assets/public_datasources.json")
422
+ with open(datasource_file) as f:
423
+ data = json.load(f)
424
+ category_info = data[dataset_name]["crop_types"]
425
+ labels_str = []
426
+ labels_ids = []
427
+ for info in category_info:
428
+ labels_str.append(category_info[info])
429
+ labels_ids.append(int(info))
430
+
431
+ name_converter = dict(zip(["M"], ["mango"])) # src -> dst
432
+ label2id = dict(zip(labels_str, labels_ids))
433
+
434
+ dataset_dir = os.path.join(self.data_original_dir, dataset_name)
435
+ ann_dir = os.path.join(dataset_dir, "VOCDevkit/VOC2007/Annotations")
436
+
437
+ # Get image file and xml file
438
+ all_files = get_file_list(ann_dir)
439
+ anno_files = [os.path.join(ann_dir, x) for x in all_files if "xml" in x]
440
+ img_files = [x.replace(".xml", ".jpg").replace("Annotations", "JPEGImages") for x in anno_files]
441
+
442
+ # Process annotation files
443
+ save_dir_anno = os.path.join(self.data_processed_dir, dataset_name, "annotations")
444
+ create_dir(save_dir_anno)
445
+ output_json_file = os.path.join(save_dir_anno, "instances.json")
446
+
447
+ # Process image files
448
+ output_img_path = os.path.join(self.data_processed_dir, dataset_name, "images")
449
+ create_dir(output_img_path)
450
+
451
+ general_info = {
452
+ "description": "MangoYOLO data set",
453
+ "url": "https://researchdata.edu.au/mangoyolo-set/1697505",
454
+ "version": "1.0",
455
+ "year": 2019,
456
+ "contributor": "Anand Koirala, Kerry Walsh, Z Wang, C McCarthy",
457
+ "date_created": "2019/02/25",
458
+ }
459
+
460
+ convert_xmls_to_cocojson(
461
+ general_info,
462
+ annotation_paths=anno_files,
463
+ img_paths=img_files,
464
+ label2id=label2id,
465
+ name_converter=name_converter,
466
+ output_jsonpath=output_json_file,
467
+ output_imgpath=output_img_path,
468
+ extract_num_from_imgid=True,
469
+ )
470
+
471
+ def tomato_ripeness_detection(self, dataset_name):
472
+ pass
473
+
474
+ def cotton_seedling_counting(self, dataset_name):
475
+ # Get all of the relevant data
476
+ dataset_dir = os.path.join(self.data_original_dir, dataset_name)
477
+ image_dir = os.path.join(dataset_dir, "Images")
478
+ images = sorted([os.path.join(image_dir, i) for i in os.listdir(image_dir)])
479
+ with open(os.path.join(dataset_dir, "Images.json"), "r") as f:
480
+ annotations = json.load(f)
481
+
482
+ # Get all of the unique labels
483
+ labels = []
484
+ for label_set in annotations["frames"].values():
485
+ for individual_set in label_set:
486
+ labels.extend(individual_set["tags"])
487
+ labels = np.unique(labels).tolist()
488
+ label2id = get_label2id(labels) # noqa
489
+
490
+ # Extract all of the bounding boxes and images
491
+ image_data = []
492
+ annotation_data = []
493
+ valid_paths = [] # some paths are not in the annotations, track the ones which are
494
+ for indx, (img_path, annotation) in enumerate(
495
+ zip(
496
+ tqdm(images, file=sys.stdout, desc="Generating Data"),
497
+ annotations["frames"].values(),
498
+ )
499
+ ):
500
+ image_data.append(get_image_info(img_path, indx))
501
+ valid_paths.append(img_path)
502
+ for a_set in annotation:
503
+ formatted_set = [
504
+ a_set["x1"],
505
+ a_set["y1"],
506
+ a_set["x2"],
507
+ a_set["y2"],
508
+ label2id[a_set["tags"][0]],
509
+ ]
510
+ base_annotation_data = get_coco_annotation_from_obj(formatted_set, a_set["name"])
511
+ base_annotation_data["image_id"] = indx + 1
512
+ annotation_data.append(base_annotation_data)
513
+
514
+ # Set up the annotation dictionary
515
+ all_annotation_data = {
516
+ "images": [],
517
+ "type": "instances",
518
+ "annotations": [],
519
+ "categories": [],
520
+ "info": {
521
+ "description": "cotton seedling counting dataset",
522
+ "url": "https://figshare.com/s/616956f8633c17ceae9b",
523
+ "version": "1.0",
524
+ "year": 2019,
525
+ "contributor": "Yu Jiang",
526
+ "date_created": "2019/11/23",
527
+ },
528
+ }
529
+
530
+ # Populate the annotation dictionary
531
+ for label, label_id in label2id.items():
532
+ category_info = {"supercategory": "none", "id": label_id, "name": label}
533
+ all_annotation_data["categories"].append(category_info)
534
+ all_annotation_data["images"] = image_data
535
+ all_annotation_data["annotations"] = annotation_data
536
+
537
+ # Recreate the dataset and zip it
538
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
539
+ processed_img_dir = os.path.join(processed_dir, "images")
540
+ if os.path.exists(processed_dir):
541
+ shutil.rmtree(processed_dir)
542
+ os.makedirs(processed_dir, exist_ok=True)
543
+ os.makedirs(processed_img_dir, exist_ok=True)
544
+ for path in images:
545
+ if path not in valid_paths:
546
+ continue
547
+ shutil.copyfile(path, os.path.join(processed_img_dir, os.path.basename(path)))
548
+ with open(os.path.join(processed_dir, "annotations.json"), "w") as f:
549
+ json.dump(all_annotation_data, f, indent=4)
550
+
551
+ # Zip the dataset
552
+ shutil.make_archive(processed_dir, "zip", os.path.dirname(processed_dir))
553
+
554
+ def apple_flower_segmentation(self, dataset_name):
555
+ # Get all of the relevant data.
556
+ dataset_dir = os.path.join(self.data_original_dir, dataset_name)
557
+ apple_a_dir = os.path.join(dataset_dir, "FlowerImages")
558
+ apple_a_images = os.listdir(apple_a_dir)
559
+ apple_a_label_dir = os.path.join(dataset_dir, "AppleA_Labels")
560
+ apple_a_labels = os.listdir(apple_a_label_dir)
561
+ apple_b_dir = os.path.join(dataset_dir, "AppleB")
562
+ apple_b_images = os.listdir(apple_b_dir)
563
+ apple_b_label_dir = os.path.join(dataset_dir, "AppleB_Labels")
564
+ apple_b_labels = os.listdir(apple_b_label_dir)
565
+
566
+ # Map image filenames with their corresponding labels.
567
+ fname_map_a, fname_map_b = {}, {}
568
+ for fname in apple_a_images:
569
+ fname_id = str(int(float(os.path.splitext(fname)[0].split("_")[-1]))) + ".png"
570
+ if fname_id in apple_a_labels:
571
+ fname_map_a[os.path.join(apple_a_dir, fname)] = os.path.join(apple_a_label_dir, fname_id)
572
+ for fname in apple_b_images:
573
+ fname_id = str(int(float(os.path.splitext(fname)[0].split("_")[-1]))) + ".png"
574
+ if fname_id in apple_b_labels:
575
+ fname_map_b[os.path.join(apple_b_dir, fname)] = os.path.join(apple_b_label_dir, fname_id)
576
+
577
+ # Process and move the images.
578
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
579
+ os.makedirs(processed_dir, exist_ok=True)
580
+ processed_image_dir = os.path.join(processed_dir, "images")
581
+ os.makedirs(processed_image_dir, exist_ok=True)
582
+ processed_annotation_dir = os.path.join(processed_dir, "annotations")
583
+ os.makedirs(processed_annotation_dir, exist_ok=True)
584
+ for image_path, label_path in tqdm(fname_map_a.items(), desc="Processing Part A", file=sys.stdout):
585
+ image = cv2.resize(cv2.imread(image_path), (2074, 1382))
586
+ label = cv2.resize(cv2.imread(label_path), (2074, 1382)) // 255
587
+ label_path = os.path.basename(label_path)
588
+ out_image_path = os.path.join(processed_image_dir, label_path)
589
+ out_label_path = os.path.join(processed_annotation_dir, label_path)
590
+ cv2.imwrite(out_image_path.replace(".png", ".jpg"), image)
591
+ cv2.imwrite(out_label_path, label)
592
+ for image_path, label_path in tqdm(fname_map_b.items(), desc="Processing Part B", file=sys.stdout):
593
+ image = cv2.resize(cv2.imread(image_path), (2074, 1382))
594
+ label = cv2.resize(cv2.imread(label_path), (2074, 1382)) // 255
595
+ label_path = os.path.basename(label_path)
596
+ out_image_path = os.path.join(processed_image_dir, label_path)
597
+ out_label_path = os.path.join(processed_annotation_dir, label_path)
598
+ cv2.imwrite(out_image_path.replace(".png", ".jpg"), image)
599
+ cv2.imwrite(out_label_path, label)
600
+
601
+ def sugarbeet_weed_segmentation(self, dataset_name):
602
+ # Get all of the relevant data
603
+ dataset_dir = os.path.join(self.data_original_dir, dataset_name)
604
+ train_dir = os.path.join(dataset_dir, "train")
605
+ train_images = sorted(get_file_list(train_dir))
606
+ annotation_dir = os.path.join(dataset_dir, "trainannot") # noqa
607
+ annotation_images = sorted(get_file_list(annotation_dir))
608
+
609
+ # Move the images to the new directory
610
+ move_segmentation_dataset(
611
+ self.data_processed_dir,
612
+ dataset_name,
613
+ train_images,
614
+ annotation_images,
615
+ train_dir,
616
+ annotation_dir,
617
+ )
618
+
619
+ def carrot_weeds_germany(self, dataset_name):
620
+ # Get all of the relevant data.
621
+ dataset_dir = os.path.join(self.data_original_dir, dataset_name)
622
+ train_dir = os.path.join(dataset_dir, "images")
623
+ train_images = sorted(get_file_list(train_dir))
624
+ annotation_dir = os.path.join(dataset_dir, "annotations")
625
+ annotation_images = sorted(get_file_list(annotation_dir, ext="png"))
626
+
627
+ # Move the images to the new directory.
628
+ def _annotation_preprocess_fn(annotation_path, out_path):
629
+ an_img = cv2.cvtColor(cv2.imread(annotation_path), cv2.COLOR_BGR2RGB)
630
+ crop, weed = (0, 255, 0), (255, 0, 0)
631
+ out_annotation = np.zeros(shape=an_img.shape[:-1])
632
+ crop_indices = np.stack(np.where(np.all(an_img == crop, axis=-1))).T
633
+ weed_indices = np.stack(np.where(np.all(an_img == weed, axis=-1))).T
634
+ for indxs in crop_indices:
635
+ out_annotation[indxs[0]][indxs[1]] = 1
636
+ for indxs in weed_indices:
637
+ out_annotation[indxs[0]][indxs[1]] = 2
638
+ return cv2.imwrite(out_path, out_annotation.astype(np.int8))
639
+
640
+ move_segmentation_dataset(
641
+ self.data_processed_dir,
642
+ dataset_name,
643
+ train_images,
644
+ annotation_images,
645
+ train_dir,
646
+ annotation_dir,
647
+ annotation_preprocess_fn=_annotation_preprocess_fn,
648
+ )
649
+
650
+ def apple_segmentation_minnesota(self, dataset_name):
651
+ # Get all of the relevant data.
652
+ dataset_dir = os.path.join(self.data_original_dir, dataset_name)
653
+ train_dir = os.path.join(dataset_dir, "train", "images")
654
+ train_images = sorted(get_file_list(train_dir))
655
+ masks_dir = os.path.join(dataset_dir, "train", "masks")
656
+ mask_images = sorted(get_file_list(masks_dir))
657
+
658
+ # Move the images to the new directory.
659
+ def _annotation_preprocess_fn(annotation_path, out_path):
660
+ mask = cv2.imread(annotation_path, cv2.IMREAD_GRAYSCALE)
661
+ ids = np.unique(mask)[1:]
662
+ masks = mask == ids[:, np.newaxis, np.newaxis]
663
+ masks = masks.astype(np.int32)
664
+ if len(masks) == 1:
665
+ mask = mask
666
+ elif len(masks) >= 2:
667
+ mask = np.logical_or(masks[0], masks[1])
668
+ if len(masks) > 2:
669
+ for mask_ in masks[2:]:
670
+ mask = np.logical_or(mask, mask_)
671
+ mask = mask.astype(np.int32)
672
+ return cv2.imwrite(out_path, mask)
673
+
674
+ move_segmentation_dataset(
675
+ self.data_processed_dir,
676
+ dataset_name,
677
+ train_images,
678
+ mask_images,
679
+ train_dir,
680
+ masks_dir,
681
+ annotation_preprocess_fn=_annotation_preprocess_fn,
682
+ )
683
+
684
+ def rice_seedling_segmentation(self, dataset_name, fix=False):
685
+ # Re-mapping labels to remove the `Background` class.
686
+ if fix:
687
+ data_dir = os.path.join(self.data_original_dir, dataset_name)
688
+ annotations = sorted(
689
+ [os.path.join(data_dir, "annotations", i) for i in os.listdir(os.path.join(data_dir, "annotations"))]
690
+ )
691
+ os.makedirs(os.path.join(data_dir, "new_annotations"))
692
+
693
+ # Create the remap.
694
+ for annotation in tqdm(annotations):
695
+ a = cv2.imread(annotation)
696
+ a[a == 2] = 0
697
+ a[a == 3] = 2
698
+ cv2.imwrite(
699
+ os.path.join(data_dir, "new_annotations", os.path.basename(annotation)),
700
+ a,
701
+ )
702
+ return
703
+
704
+ # Get all of the relevant data.
705
+ data_dir = os.path.join(self.data_original_dir, dataset_name)
706
+ images = sorted(glob.glob(os.path.join(data_dir, "image_*.jpg")))
707
+ labels = sorted(glob.glob(os.path.join(data_dir, "Label_*.png")))
708
+ images = [os.path.basename(p) for p in images]
709
+ labels = [os.path.basename(p) for p in labels]
710
+
711
+ # Move the images to the new directory.
712
+ move_segmentation_dataset(self.data_processed_dir, dataset_name, images, labels, data_dir, data_dir)
713
+
714
+ def sugarcane_damage_usa(self, dataset_name):
715
+ pass
716
+
717
+ def soybean_weed_uav_brazil(self, dataset_name):
718
+ pass
719
+
720
+ def plant_village_classification(self, dataset_name):
721
+ pass
722
+
723
+ def autonomous_greenhouse_regression(self, dataset_name):
724
+ # Get all of the data paths.
725
+ dataset_dir = os.path.join(self.data_original_dir, dataset_name)
726
+ image_dir = os.path.join(dataset_dir, "RGBImages")
727
+ depth_dir = os.path.join(dataset_dir, "DepthImages")
728
+ with open(os.path.join(dataset_dir, "dataset.json"), "r") as f:
729
+ contents = json.load(f)
730
+
731
+ # Construct the output annotation JSON file.
732
+ out = []
733
+ for sample in contents["labels"].values():
734
+ out.append(
735
+ {
736
+ "image": sample["rgb_image_path"],
737
+ "depth_image": sample["depth_image_path"],
738
+ "outputs": {
739
+ "regression": sample["regression_outputs"],
740
+ "classification": sample["classification_outputs"]["Variety"],
741
+ },
742
+ }
743
+ )
744
+
745
+ # Copy the images over.
746
+ out_dir = os.path.join(self.data_processed_dir, dataset_name)
747
+ out_image_dir = os.path.join(out_dir, "images")
748
+ os.makedirs(out_image_dir, exist_ok=True)
749
+ out_depth_dir = os.path.join(out_dir, "depth_images")
750
+ os.makedirs(out_depth_dir, exist_ok=True)
751
+ for image in tqdm(os.listdir(image_dir), desc="Moving Images", file=sys.stdout):
752
+ shutil.copyfile(os.path.join(image_dir, image), os.path.join(out_image_dir, image))
753
+ for depth in tqdm(os.listdir(depth_dir), desc="Moving Depth Images", file=sys.stdout):
754
+ shutil.copyfile(os.path.join(depth_dir, depth), os.path.join(out_depth_dir, depth))
755
+
756
+ # Save the annotation file.
757
+ with open(os.path.join(out_dir, "annotations.json"), "w") as f:
758
+ json.dump(out, f)
759
+
760
+ def guava_disease_pakistan(self, dataset_name):
761
+ # Get all of the images.
762
+ dataset_dir = os.path.join(self.data_original_dir, dataset_name)
763
+ classes = os.listdir(dataset_dir)
764
+ all_images = []
765
+ for cls in classes:
766
+ all_images.extend([os.path.join(dataset_dir, cls, i) for i in os.listdir(os.path.join(dataset_dir, cls))])
767
+
768
+ # Resize all of the images.
769
+ out_dir = os.path.join(self.data_processed_dir, dataset_name)
770
+ os.makedirs(out_dir, exist_ok=True)
771
+ for cls in classes:
772
+ os.makedirs(os.path.join(out_dir, cls), exist_ok=True)
773
+ for image in tqdm(all_images, "Resizing Images"):
774
+ out_image = image.replace("/original/", "/processed/")
775
+ im = cv2.imread(image, cv2.IMREAD_UNCHANGED)
776
+ im = cv2.resize(im, (im.shape[1] // 5, im.shape[0] // 5), cv2.INTER_LINEAR)
777
+ cv2.imwrite(out_image, im)
778
+
779
+ def apple_detection_spain(self, dataset_name):
780
+ # resize the dataset
781
+ resize = 1.0
782
+
783
+ # Read public_datasources.json to get class information
784
+ datasource_file = os.path.join(os.path.dirname(__file__), "../_assets/public_datasources.json")
785
+ with open(datasource_file) as f:
786
+ data = json.load(f)
787
+ category_info = data[dataset_name]["crop_types"]
788
+ labels_str = []
789
+ labels_ids = []
790
+ for info in category_info:
791
+ labels_str.append(category_info[info])
792
+ labels_ids.append(int(info))
793
+
794
+ name_converter = dict(zip(["Poma"], ["apple"])) # src -> dst
795
+ label2id = dict(zip(labels_str, labels_ids))
796
+
797
+ dataset_dir = os.path.join(self.data_original_dir, dataset_name)
798
+ ann_dir = os.path.join(dataset_dir, "preprocessed data/square_annotations1")
799
+
800
+ # Get image file and xml file
801
+ all_files = get_file_list(ann_dir)
802
+ anno_files = [os.path.join(ann_dir, x) for x in all_files if "xml" in x]
803
+ img_files = [x.replace(".xml", "hr.jpg").replace("square_annotations1", "images") for x in anno_files]
804
+
805
+ # Process annotation files
806
+ save_dir_anno = os.path.join(self.data_processed_dir, dataset_name, "annotations")
807
+ create_dir(save_dir_anno)
808
+ output_json_file = os.path.join(save_dir_anno, "instances.json")
809
+
810
+ # Process image files
811
+ output_img_path = os.path.join(self.data_processed_dir, dataset_name, "images")
812
+ create_dir(output_img_path)
813
+
814
+ general_info = {
815
+ "description": "KFuji RGB-DS database",
816
+ "url": "http://www.grap.udl.cat/en/publications/KFuji_RGBDS_database.html",
817
+ "version": "1.0",
818
+ "year": 2018,
819
+ "contributor": "Gené-Mola J, Vilaplana V, Rosell-Polo JR, Morros JR, Ruiz-Hidalgo J, Gregorio E",
820
+ "date_created": "2018/10/19",
821
+ }
822
+
823
+ convert_xmls_to_cocojson(
824
+ general_info,
825
+ annotation_paths=anno_files,
826
+ img_paths=img_files,
827
+ label2id=label2id,
828
+ name_converter=name_converter,
829
+ output_jsonpath=output_json_file,
830
+ output_imgpath=output_img_path,
831
+ extract_num_from_imgid=True,
832
+ )
833
+
834
+ def apple_detection_drone_brazil(self, dataset_name):
835
+ # Get the data directory and rename it if necessary.
836
+ dataset_dir = os.path.join(self.data_original_dir, dataset_name)
837
+ if not os.path.exists(dataset_dir):
838
+ fallback = os.path.join(self.data_original_dir, "thsant-add256-68d2f88") # noqa
839
+ if os.path.exists(fallback):
840
+ os.rename(fallback, dataset_dir)
841
+
842
+ # Get all of the images which have valid annotations.
843
+ with open(os.path.join(dataset_dir, "all.json"), "r") as f:
844
+ original_annotations = json.load(f)
845
+ valid_annotations = {k: v for k, v in original_annotations.items() if v != []}
846
+
847
+ # Construct the `images` part of the COCO JSON.
848
+ image_coco = []
849
+ image_id_map = {}
850
+ image_dir = os.path.join(dataset_dir, "images")
851
+ for idx, image_name in tqdm(
852
+ enumerate(valid_annotations.keys()),
853
+ desc="Parsing Images",
854
+ total=len(valid_annotations),
855
+ ):
856
+ height, width = cv2.imread(os.path.join(image_dir, image_name)).shape[:2]
857
+ image_coco.append({"file_name": image_name, "height": height, "width": width, "id": idx})
858
+ image_id_map[image_name] = idx
859
+
860
+ # Construct the `annotations` part of the COCO JSON.
861
+ annotation_idx = 0
862
+ annotation_coco = []
863
+ for image_name, annotation_list in valid_annotations.items():
864
+ for annotation in annotation_list:
865
+ # Coordinates are in form (center_x, center_y, radius). We convert
866
+ # these to (top left x, top left y, width, height)
867
+ x_c, y_c, r = annotation["cx"], annotation["cy"], annotation["r"]
868
+ x, y = x_c - r, y_c - r
869
+ w = h = r * 2
870
+ annotation_coco.append(
871
+ {
872
+ "area": w * h,
873
+ "iscrowd": 0,
874
+ "bbox": [x, y, w, h],
875
+ "category_id": 1,
876
+ "ignore": 0,
877
+ "segmentation": 0,
878
+ "image_id": image_id_map[image_name],
879
+ "id": annotation_idx,
880
+ }
881
+ )
882
+ annotation_idx += 1
883
+
884
+ # Set up the annotation dictionary.
885
+ category_info = [{"supercategory": "none", "id": 1, "name": "apple"}]
886
+ all_annotation_data = {
887
+ "images": image_coco,
888
+ "type": "instances",
889
+ "annotations": annotation_coco,
890
+ "categories": category_info,
891
+ "info": {
892
+ "description": "apple detection dataset with drone imagery",
893
+ "url": "https://github.com/thsant/add256/tree/zenodo-1.0",
894
+ "version": "1.0",
895
+ "year": 2021,
896
+ "contributor": "Thiago T. Santos and Luciano Gebler",
897
+ "date_created": "2021/10/2021",
898
+ },
899
+ }
900
+
901
+ # Recreate the dataset and zip it
902
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
903
+ processed_img_dir = os.path.join(processed_dir, "images")
904
+ if os.path.exists(processed_dir):
905
+ shutil.rmtree(processed_dir)
906
+ os.makedirs(processed_dir, exist_ok=True)
907
+ os.makedirs(processed_img_dir, exist_ok=True)
908
+ for path in tqdm(valid_annotations.keys(), desc="Moving Images"):
909
+ full_path = os.path.join(image_dir, path)
910
+ shutil.copyfile(full_path, os.path.join(processed_img_dir, os.path.basename(path)))
911
+ with open(os.path.join(processed_dir, "annotations.json"), "w") as f:
912
+ json.dump(all_annotation_data, f)
913
+
914
+ def plant_doc_classification(self, dataset_name):
915
+ category_info = self.data_sources[dataset_name]["classes"]
916
+
917
+ # paths to original files
918
+ dataset_dir = os.path.join(self.data_original_dir, dataset_name)
919
+ train_dir = os.path.join(dataset_dir, "train")
920
+ test_dir = os.path.join(dataset_dir, "test")
921
+
922
+ # make output dir
923
+ output = os.path.join(self.data_processed_dir, dataset_name)
924
+ os.makedirs(output)
925
+
926
+ for key in category_info:
927
+ category = category_info[key]
928
+
929
+ # make output dir for each crop type
930
+ output_catg_dir = os.path.join(output, category)
931
+ os.makedirs(output_catg_dir)
932
+
933
+ # put train and test images of same category into same folder
934
+ train_catg_dir = os.path.join(train_dir, category)
935
+ test_catg_dir = os.path.join(test_dir, category)
936
+
937
+ for img_name in get_file_list(train_catg_dir):
938
+ img = os.path.join(train_catg_dir, img_name)
939
+ shutil.copyfile(img, os.path.join(output_catg_dir, img_name))
940
+
941
+ if os.path.exists(test_catg_dir):
942
+ for img_name in get_file_list(test_catg_dir):
943
+ img = os.path.join(test_catg_dir, img_name)
944
+ shutil.copyfile(img, os.path.join(output_catg_dir, img_name))
945
+
946
+ def wheat_head_counting(self, dataset_name):
947
+ label2id = {"Wheat Head": 1}
948
+ dataset_dir = os.path.join(self.data_original_dir, dataset_name)
949
+ anno_files = [
950
+ os.path.join(dataset_dir, "competition_train.csv"),
951
+ os.path.join(dataset_dir, "competition_test.csv"),
952
+ os.path.join(dataset_dir, "competition_val.csv"),
953
+ ]
954
+
955
+ annotations = []
956
+ for anno_file in anno_files:
957
+ with open(anno_file, "r") as file:
958
+ reader = csv.reader(file)
959
+ for row in reader:
960
+ img_path = os.path.join(dataset_dir, "images", row[0])
961
+ anno = [img_path]
962
+ bboxs = row[1].split(";")
963
+ anno.append(len(bboxs))
964
+ for bbox in bboxs:
965
+ if bbox != "no_box":
966
+ bbox = bbox.split(" ")
967
+ bbox.append("1")
968
+ anno.append(bbox)
969
+ annotations.append(anno)
970
+
971
+ # Define path to processed annotation files
972
+ output_json_file = os.path.join(self.data_processed_dir, dataset_name, "annotations.json")
973
+
974
+ # Create directory for processed image files
975
+ output_img_path = os.path.join(self.data_processed_dir, dataset_name, "images")
976
+ create_dir(output_img_path)
977
+
978
+ general_info = {
979
+ "description": "Global Wheat Head Detection (GWHD) dataset",
980
+ "url": "http://www.global-wheat.com/",
981
+ "version": "1.0",
982
+ "year": 2021,
983
+ "contributor": "David, Etienne and Madec, Simon and Sadeghi-Tehran, Pouria and Aasen, Helge and Zheng, Bangyou and Liu, Shouyang and Kirchgessner, Norbert and Ishikawa, Goro and Nagasawa, Koichi and Badhon, Minhajul A and others",
984
+ "date_created": "2021/7/12",
985
+ }
986
+
987
+ convert_bbox_to_coco(
988
+ annotations,
989
+ label2id,
990
+ output_json_file,
991
+ output_img_path,
992
+ general_info,
993
+ resize=512 / 1024,
994
+ )
995
+
996
+ def peachpear_flower_segmentation(self, dataset_name):
997
+ # Create processed directories
998
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
999
+ os.makedirs(processed_dir, exist_ok=True)
1000
+ processed_image_dir = os.path.join(processed_dir, "images")
1001
+ os.makedirs(processed_image_dir, exist_ok=True)
1002
+ processed_annotation_dir = os.path.join(processed_dir, "annotations")
1003
+ os.makedirs(processed_annotation_dir, exist_ok=True)
1004
+
1005
+ dataset_dir = os.path.join(self.data_original_dir, dataset_name)
1006
+
1007
+ # Get image files
1008
+ img_dirs = ["Peach", "Pear"]
1009
+ img_paths = []
1010
+ for img_dir in img_dirs:
1011
+ img_paths += [
1012
+ os.path.join(dataset_dir, img_dir, file_name)
1013
+ for file_name in get_file_list(os.path.join(dataset_dir, img_dir))
1014
+ ]
1015
+
1016
+ # Save all images as jpg in processed directory
1017
+ for img_path in img_paths:
1018
+ processed_path = os.path.join(processed_image_dir, img_path.split("/")[-1].replace(".bmp", ".jpg"))
1019
+ img = cv2.imread(img_path)
1020
+ cv2.imwrite(processed_path, img)
1021
+
1022
+ # Get annotation files
1023
+ anno_dirs = ["PeachLabels", "PearLabels"]
1024
+ anno_paths = []
1025
+ for anno_dir in anno_dirs:
1026
+ anno_paths += [
1027
+ os.path.join(dataset_dir, anno_dir, file_name)
1028
+ for file_name in get_file_list(os.path.join(dataset_dir, anno_dir))
1029
+ ]
1030
+
1031
+ # Transform mask and save to processed directory
1032
+ for anno_path in anno_paths:
1033
+ img = cv2.imread(anno_path, cv2.IMREAD_GRAYSCALE)
1034
+ img = np.where(img[:] == 255, 1, 0)
1035
+ processed_path = os.path.join(processed_annotation_dir, anno_path.split("/")[-1])
1036
+ cv2.imwrite(processed_path, img)
1037
+
1038
+ def ghai_romaine_detection(self, dataset_name):
1039
+ # Create processed directories
1040
+ original_dir = os.path.join(self.data_original_dir, dataset_name)
1041
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
1042
+ processed_image_dir = os.path.join(processed_dir, "images")
1043
+ os.makedirs(processed_image_dir, exist_ok=True)
1044
+
1045
+ # Move images
1046
+ for image in glob.glob(os.path.join(original_dir, "*.jpg")):
1047
+ shutil.move(image, processed_image_dir)
1048
+ shutil.move(
1049
+ os.path.join(original_dir, "coco.json"),
1050
+ os.path.join(processed_dir, "annotations.json"),
1051
+ )
1052
+
1053
+ def ghai_green_cabbage_detection(self, dataset_name):
1054
+ # Create processed directories
1055
+ original_dir = os.path.join(self.data_original_dir, dataset_name)
1056
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
1057
+ processed_image_dir = os.path.join(processed_dir, "images")
1058
+ os.makedirs(processed_image_dir, exist_ok=True)
1059
+
1060
+ # Move images
1061
+ for image in glob.glob(os.path.join(original_dir, "*.jpg")):
1062
+ shutil.move(image, processed_image_dir)
1063
+ shutil.move(
1064
+ os.path.join(original_dir, "coco.json"),
1065
+ os.path.join(processed_dir, "annotations.json"),
1066
+ )
1067
+
1068
+ def ghai_iceberg_lettuce_detection(self, dataset_name):
1069
+ # Create processed directories
1070
+ original_dir = os.path.join(self.data_original_dir, dataset_name)
1071
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
1072
+ processed_image_dir = os.path.join(processed_dir, "images")
1073
+ os.makedirs(processed_image_dir, exist_ok=True)
1074
+
1075
+ # Move images
1076
+ for image in glob.glob(os.path.join(original_dir, "*.jpg")):
1077
+ shutil.move(image, processed_image_dir)
1078
+ shutil.move(
1079
+ os.path.join(original_dir, "coco.json"),
1080
+ os.path.join(processed_dir, "annotations.json"),
1081
+ )
1082
+
1083
+ def riseholme_strawberry_classification_2021(self, dataset_name):
1084
+ # Create processed data directory.
1085
+ original_dir = os.path.join(self.data_original_dir, "Riseholme-2021-main", "Data")
1086
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
1087
+
1088
+ # Load all of the individual images and keep a mapping to their corresponding directory.
1089
+ images = {
1090
+ "anomalous": get_file_list(os.path.join(original_dir, "Anomalous")),
1091
+ **{
1092
+ dir_.lower(): get_file_list(os.path.join(original_dir, "Normal", dir_))
1093
+ for dir_ in os.listdir(os.path.join(original_dir, "Normal"))
1094
+ },
1095
+ }
1096
+
1097
+ # Create the output file structure.
1098
+ for class_name, image_set in images.items():
1099
+ class_dir = os.path.join(processed_dir, class_name)
1100
+ os.makedirs(class_dir, exist_ok=True)
1101
+ for file in image_set:
1102
+ shutil.copyfile(file, os.path.join(class_dir, os.path.basename(file)))
1103
+
1104
+ def ghai_broccoli_detection(self, dataset_name):
1105
+ # Create processed directories
1106
+ original_dir = os.path.join(self.data_original_dir, dataset_name)
1107
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
1108
+ processed_image_dir = os.path.join(processed_dir, "images")
1109
+ os.makedirs(processed_image_dir, exist_ok=True)
1110
+
1111
+ # Move images
1112
+ for image in tqdm(glob.glob(os.path.join(original_dir, "*.jpg"))):
1113
+ shutil.move(image, processed_image_dir)
1114
+ shutil.move(
1115
+ os.path.join(original_dir, "coco.json"),
1116
+ os.path.join(processed_dir, "annotations.json"),
1117
+ )
1118
+
1119
+ def ghai_strawberry_fruit_detection(self, dataset_name):
1120
+ # Create processed directories
1121
+ original_dir = os.path.join(self.data_original_dir, dataset_name)
1122
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
1123
+ processed_image_dir = os.path.join(processed_dir, "images")
1124
+ os.makedirs(processed_image_dir, exist_ok=True)
1125
+
1126
+ # Move images
1127
+ for image in tqdm(glob.glob(os.path.join(original_dir, "*.jpg"))):
1128
+ shutil.move(image, processed_image_dir)
1129
+ shutil.move(
1130
+ os.path.join(original_dir, "coco.json"),
1131
+ os.path.join(processed_dir, "annotations.json"),
1132
+ )
1133
+
1134
+ def vegann_multicrop_presence_segmentation(self, dataset_name):
1135
+ # Create processed directories
1136
+ original_dir = os.path.join(self.data_original_dir, dataset_name)
1137
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
1138
+ processed_image_dir = os.path.join(processed_dir, "images")
1139
+ os.makedirs(processed_image_dir, exist_ok=True)
1140
+ processed_annotation_dir = os.path.join(processed_dir, "annotations")
1141
+ os.makedirs(processed_annotation_dir, exist_ok=True)
1142
+
1143
+ # Move images
1144
+ for image in tqdm(glob.glob(os.path.join(original_dir, "images", "*.png"))):
1145
+ shutil.copyfile(image, os.path.join(processed_image_dir, os.path.basename(image)))
1146
+
1147
+ # Read annotations
1148
+ for annotation_file in tqdm(glob.glob(os.path.join(original_dir, "annotations", "*.png"))):
1149
+ annotation = cv2.imread(annotation_file, cv2.IMREAD_UNCHANGED)
1150
+ annotation = np.where(annotation == 255, 1, 0)
1151
+ cv2.imwrite(
1152
+ os.path.join(processed_annotation_dir, os.path.basename(annotation_file)),
1153
+ annotation,
1154
+ )
1155
+
1156
+ # Read the CSV file containing the splits
1157
+ split_csv = pd.read_csv(os.path.join(original_dir, "VegAnn_dataset.csv"), sep=";")
1158
+
1159
+ # Get the `Name` and `TVT-split{n}` columns for each n, and save the splits to a folder
1160
+ splits_folder = os.path.join(processed_dir, ".splits")
1161
+ os.makedirs(splits_folder, exist_ok=True)
1162
+ column_pairs = [["Name", f"TVT-split{i}"] for i in range(1, 5 + 1)]
1163
+
1164
+ splits = {}
1165
+ for column_pair in column_pairs:
1166
+ columns = split_csv[column_pair]
1167
+ train_images = columns[columns[column_pair[1]] == "Training"]["Name"]
1168
+ test_images = columns[columns[column_pair[1]] == "Test"]["Name"]
1169
+ splits[column_pair[1]] = {
1170
+ "train": {os.path.join("images", i): os.path.join("annotations", i) for i in train_images},
1171
+ "val": {},
1172
+ "test": {os.path.join("images", i): os.path.join("annotations", i) for i in test_images},
1173
+ }
1174
+
1175
+ # Save each split to a JSON file
1176
+ for split_name, split in splits.items():
1177
+ with open(os.path.join(splits_folder, f"{split_name}.json"), "w") as f:
1178
+ json.dump(split, f)
1179
+
1180
+ def embrapa_wgisd_grape_detection(self, dataset_name):
1181
+ """Preprocesses your grape dataset (Chardonnay, PinotGris, PinotNoir) from YOLO format to COCO format."""
1182
+ base_path = os.path.join(self.data_original_dir)
1183
+
1184
+ # Get all the dataset folders (Chardonnay, PinotGris, PinotNoir)
1185
+ dataset_folders = ['Chardonnay', 'PinotGris', 'PinotNoir']
1186
+
1187
+ # Prepare the COCO annotation dictionary
1188
+ coco_annotation = {
1189
+ "images": [],
1190
+ "annotations": [],
1191
+ "categories": [{
1192
+ "id": 1, # Category ID for the object (e.g., grape)
1193
+ "name": "grape", # You can change this to match your dataset's object
1194
+ "supercategory": "fruit"
1195
+ }]
1196
+ }
1197
+
1198
+ annotation_id = 1
1199
+ image_id = 1
1200
+
1201
+ # Create the output directories for processed images and annotations
1202
+ output_base_dir = os.path.join(self.data_processed_dir, dataset_name)
1203
+ output_img_dir = os.path.join(output_base_dir, 'images')
1204
+ os.makedirs(output_img_dir, exist_ok=True)
1205
+
1206
+ # Iterate through each dataset folder (Chardonnay, PinotGris, PinotNoir)
1207
+ for folder in dataset_folders:
1208
+ images_dir = os.path.join(base_path, folder, 'images')
1209
+ labels_dir = os.path.join(base_path, folder, 'labels')
1210
+
1211
+ # Check if the directories exist
1212
+ if not os.path.exists(images_dir) or not os.path.exists(labels_dir):
1213
+ print(f"Error: Images or labels directory does not exist in {images_dir} or {labels_dir}")
1214
+ continue
1215
+
1216
+ # Create output directory for this folder inside the processed folder
1217
+ output_folder_img_dir = os.path.join(output_img_dir, folder)
1218
+ os.makedirs(output_folder_img_dir, exist_ok=True)
1219
+
1220
+ # Process all images and their corresponding YOLO labels
1221
+ for img_file in tqdm(os.listdir(images_dir), desc=f"Processing images for {folder}"):
1222
+ if img_file.endswith(('jpg', 'jpeg', 'png')):
1223
+ image_path = os.path.join(images_dir, img_file)
1224
+ label_file = img_file.replace('.jpg', '.txt').replace('.jpeg', '.txt').replace('.png', '.txt')
1225
+ label_path = os.path.join(labels_dir, label_file)
1226
+
1227
+ # Read the image to get its dimensions
1228
+ img = cv2.imread(image_path)
1229
+ if img is None:
1230
+ print(f"Error: Could not open image file {image_path}")
1231
+ continue
1232
+ img_height, img_width = img.shape[:2]
1233
+
1234
+ # Add image information to COCO structure
1235
+ image_info = {
1236
+ "file_name": f"{folder}/{img_file}", # Include folder name in file path
1237
+ "height": img_height,
1238
+ "width": img_width,
1239
+ "id": image_id
1240
+ }
1241
+ coco_annotation['images'].append(image_info)
1242
+
1243
+ # Read the YOLO label file and convert to COCO format
1244
+ if os.path.exists(label_path):
1245
+ with open(label_path, 'r') as f:
1246
+ lines = f.readlines()
1247
+
1248
+ # Prepare annotations in COCO format
1249
+ for line in lines:
1250
+ elements = line.strip().split()
1251
+ class_id = int(elements[0]) # class_id from YOLO
1252
+ x_center, y_center, bbox_width, bbox_height = map(float, elements[1:])
1253
+
1254
+ # Convert to COCO format bounding box
1255
+ bbox = yolo_to_coco_bbox(x_center, y_center, bbox_width, bbox_height, img_width, img_height)
1256
+
1257
+ annotation = {
1258
+ "image_id": image_id, # Reference to the image ID
1259
+ "bbox": bbox, # COCO bounding box [xmin, ymin, width, height]
1260
+ "category_id": 1, # Assuming a single category "grape"
1261
+ "id": annotation_id, # Unique annotation ID
1262
+ "area": bbox[2] * bbox[3], # width * height
1263
+ "iscrowd": 0,
1264
+ "segmentation": []
1265
+ }
1266
+ coco_annotation['annotations'].append(annotation)
1267
+ annotation_id += 1
1268
+ else:
1269
+ print(f"Warning: No label file found for {img_file}")
1270
+
1271
+ # Copy the image to the processed directory
1272
+ shutil.copyfile(image_path, os.path.join(output_folder_img_dir, img_file))
1273
+
1274
+ # Increment image ID
1275
+ image_id += 1
1276
+
1277
+ # Save the final COCO annotations to the correct path inside the processed folder
1278
+ output_json_file = os.path.join(output_base_dir, 'annotations.json')
1279
+ with open(output_json_file, 'w') as json_file:
1280
+ json.dump(coco_annotation, json_file, indent=4)
1281
+
1282
+ print(f"COCO annotations saved to {output_json_file}")
1283
+
1284
+ def plant_doc_detection(self, dataset_name):
1285
+ # Resize the dataset (if necessary)
1286
+ resize = 1.0
1287
+
1288
+ # Read public_datasources.json to get class information
1289
+ datasource_file = os.path.join(os.path.dirname(__file__), "../_assets/public_datasources.json")
1290
+ with open(datasource_file) as f:
1291
+ data = json.load(f)
1292
+ category_info = data[dataset_name]['classes'] # This will give us the class information
1293
+ labels_str = []
1294
+ labels_ids = []
1295
+ for info in category_info:
1296
+ labels_str.append(category_info[info])
1297
+ labels_ids.append(int(info))
1298
+
1299
+ # No name conversion in this case, unless you need to remap class names
1300
+ name_converter = None
1301
+ label2id = dict(zip(labels_str, labels_ids)) # Map class names to their respective IDs
1302
+
1303
+ # Set paths to dataset and annotations
1304
+ dataset_dir = os.path.join(self.data_original_dir)
1305
+ ann_dir = dataset_dir # Both images and XMLs are in the same directory
1306
+
1307
+ # Get image file and xml file
1308
+ all_files = os.listdir(ann_dir)
1309
+ anno_files = [os.path.join(ann_dir, x) for x in all_files if x.endswith("xml")]
1310
+ img_files = [x.replace(".xml", ".jpg") for x in anno_files] # Assuming images are in JPG format
1311
+
1312
+ # # Process annotation files
1313
+ # save_dir_anno = os.path.join(self.data_processed_dir, dataset_name, 'annotations')
1314
+ # create_dir(save_dir_anno)
1315
+ # output_json_file = os.path.join(save_dir_anno, 'instances.json')
1316
+
1317
+ # Process image files
1318
+ output_img_path = os.path.join(self.data_processed_dir, dataset_name, 'images')
1319
+ create_dir(output_img_path)
1320
+
1321
+ # General information for the COCO JSON format
1322
+ general_info = {
1323
+ "description": "PlantDoc Object Detection Dataset",
1324
+ "url": "https://github.com/pratikkayal/PlantDoc-Object-Detection-Dataset",
1325
+ "version": "1.0",
1326
+ "year": 2024,
1327
+ "contributor": "PlantDoc",
1328
+ "date_created": "2024/10/17"
1329
+ }
1330
+
1331
+ # Save the COCO JSON file directly in the processed directory
1332
+ output_json_file = os.path.join(self.data_processed_dir, dataset_name, 'annotations.json')
1333
+
1334
+ # Convert the XML annotations to COCO format using your existing function
1335
+ convert_xmls_to_cocojson(
1336
+ general_info=general_info,
1337
+ annotation_paths=anno_files,
1338
+ img_paths=img_files,
1339
+ label2id=label2id,
1340
+ name_converter=name_converter,
1341
+ output_jsonpath=output_json_file,
1342
+ output_imgpath=output_img_path,
1343
+ extract_num_from_imgid=False
1344
+ )
1345
+
1346
+ print(f"Preprocessing completed! Annotations saved to {output_json_file}")
1347
+
1348
+ def growliflower_cauliflower_segmentation(self, dataset_name):
1349
+ """
1350
+ Preprocess the cauliflower dataset by merging mask images and moving them to the new directory.
1351
+
1352
+ Args:
1353
+ self: Instance of the class where this function belongs.
1354
+ dataset_name (str): Name of the dataset.
1355
+ """
1356
+ # Paths to the dataset
1357
+ dataset_dir = os.path.join(self.data_original_dir)
1358
+ images_dir = os.path.join(dataset_dir, 'images') # Folder with original images
1359
+ masks_dir = os.path.join(dataset_dir, 'annotations') # Folder with the mask subfolders
1360
+
1361
+ # Output directory where preprocessed data will be stored
1362
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
1363
+ processed_image_dir = os.path.join(processed_dir, 'images')
1364
+ processed_annotation_dir = os.path.join(processed_dir, 'annotations')
1365
+
1366
+ # Create output directories if they don't exist
1367
+ os.makedirs(processed_image_dir, exist_ok=True)
1368
+ os.makedirs(processed_annotation_dir, exist_ok=True)
1369
+
1370
+ # Define the mask class mappings (assign unique labels to each type)
1371
+ mask_classes = {
1372
+ 'maskLeaves': 1,
1373
+ 'maskPlants': 2,
1374
+ 'maskStems': 3,
1375
+ 'maskVoid': 4
1376
+ }
1377
+
1378
+ # Get the list of image filenames
1379
+ image_files = sorted(os.listdir(images_dir))
1380
+
1381
+ for idx, image_file in enumerate(tqdm(image_files, desc="Processing Cauliflower Dataset")):
1382
+ image_name = os.path.splitext(image_file)[0] # Get the base image name without extension
1383
+ image_path = os.path.join(images_dir, image_file)
1384
+
1385
+ # Find the corresponding masks for each class
1386
+ mask_paths = {}
1387
+ for mask_class in mask_classes.keys():
1388
+ # Build mask filenames with both naming conventions
1389
+ mask_filename_no_plants = f"{image_name}_Label_NoPlants_{mask_class}.png"
1390
+ mask_filename = f"{image_name}_Label_{mask_class}.png"
1391
+
1392
+ # Set the correct mask path
1393
+ mask_path_no_plants = os.path.join(masks_dir, mask_class, mask_filename_no_plants)
1394
+ mask_path = os.path.join(masks_dir, mask_class, mask_filename)
1395
+
1396
+ if os.path.exists(mask_path_no_plants):
1397
+ mask_paths[mask_class] = mask_path_no_plants
1398
+ else:
1399
+ mask_paths[mask_class] = mask_path
1400
+
1401
+ # Load the original image to get its shape (we need the height and width)
1402
+ orig_image = cv2.imread(image_path)
1403
+ if orig_image is None:
1404
+ print(f"Warning: Unable to read image {image_path}")
1405
+ continue
1406
+ image_shape = orig_image.shape[:2] # (height, width)
1407
+
1408
+ # Initialize the merged mask as a blank image (background class 0)
1409
+ merged_mask = np.zeros(image_shape, dtype=np.uint8)
1410
+
1411
+ # Visualize and debug individual masks, if needed
1412
+ individual_masks = {}
1413
+
1414
+ for mask_name, class_value in mask_classes.items():
1415
+ mask = cv2.imread(mask_paths[mask_name], cv2.IMREAD_GRAYSCALE)
1416
+ if mask is None:
1417
+ print(f"Warning: Unable to read mask {mask_paths[mask_name]}")
1418
+ continue
1419
+ individual_masks[mask_name] = mask # Store for visualization
1420
+
1421
+ # Debugging: Check unique values in each mask
1422
+ print(f"{mask_name} unique values for {image_name}: {np.unique(mask)}")
1423
+
1424
+ # Binarize the mask for leaf mask based on the value 60
1425
+ if mask_name == 'maskLeaves':
1426
+ binarized_mask = np.where(mask == 60, 1, 0).astype(np.uint8)
1427
+ else:
1428
+ # For other masks, threshold based on the condition > 60
1429
+ binarized_mask = np.where(mask > 60, 1, 0).astype(np.uint8)
1430
+ # Merge logic: void mask only applies where there's no other mask
1431
+ if class_value == 4: # Void class
1432
+ merged_mask[(merged_mask == 0) & (binarized_mask > 0)] = class_value
1433
+ else:
1434
+ merged_mask[(binarized_mask > 0)] = class_value
1435
+
1436
+ # Debugging Step: Check unique values in the merged mask
1437
+ unique_values = np.unique(merged_mask)
1438
+ print(f"Image {idx+1}/{len(image_files)}: {image_file} - Unique mask values: {unique_values}")
1439
+
1440
+ # Save the original image and the merged mask to the new directory
1441
+ out_image_path = os.path.join(processed_image_dir, image_file)
1442
+ out_mask_path = os.path.join(processed_annotation_dir, f"{image_name}.png")
1443
+
1444
+ cv2.imwrite(out_image_path, orig_image)
1445
+ cv2.imwrite(out_mask_path, merged_mask)
1446
+
1447
+ def strawberry_detection_2023(self, dataset_name):
1448
+ # Create processed directories
1449
+ original_dir = os.path.join(self.data_original_dir)
1450
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
1451
+ processed_image_dir = os.path.join(processed_dir, 'images')
1452
+ os.makedirs(processed_image_dir, exist_ok = True)
1453
+ # Move images
1454
+ for image in tqdm(glob.glob(os.path.join(original_dir, '*.jpg'))):
1455
+ shutil.move(image, processed_image_dir)
1456
+ shutil.move(os.path.join(original_dir, 'coco.json'),
1457
+ os.path.join(processed_dir, 'annotations.json'))
1458
+
1459
+ def strawberry_detection_2022(self, dataset_name):
1460
+ # Create processed directories
1461
+ original_dir = os.path.join(self.data_original_dir)
1462
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
1463
+ processed_image_dir = os.path.join(processed_dir, 'images')
1464
+ os.makedirs(processed_image_dir, exist_ok = True)
1465
+ # Move images
1466
+ for image in tqdm(glob.glob(os.path.join(original_dir, '*.jpg'))):
1467
+ shutil.move(image, processed_image_dir)
1468
+ shutil.move(os.path.join(original_dir, 'coco.json'),
1469
+ os.path.join(processed_dir, 'annotations.json'))
1470
+
1471
+ def almond_harvest_2021(self, dataset_name):
1472
+ # Create processed directories
1473
+ original_dir = os.path.join(self.data_original_dir)
1474
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
1475
+ processed_image_dir = os.path.join(processed_dir, 'images')
1476
+ os.makedirs(processed_image_dir, exist_ok = True)
1477
+ # Move images
1478
+ for image in tqdm(glob.glob(os.path.join(original_dir, '*.jpg'))):
1479
+ shutil.move(image, processed_image_dir)
1480
+ shutil.move(os.path.join(original_dir, 'coco.json'),
1481
+ os.path.join(processed_dir, 'annotations.json'))
1482
+
1483
+ def almond_bloom_2023(self, dataset_name):
1484
+ # Create processed directories
1485
+ original_dir = os.path.join(self.data_original_dir)
1486
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
1487
+ processed_image_dir = os.path.join(processed_dir, 'images')
1488
+ os.makedirs(processed_image_dir, exist_ok = True)
1489
+ # Move images
1490
+ for image in tqdm(glob.glob(os.path.join(original_dir, '*.jpg'))):
1491
+ shutil.move(image, processed_image_dir)
1492
+ shutil.move(os.path.join(original_dir, 'coco.json'),
1493
+ os.path.join(processed_dir, 'annotations.json'))
1494
+
1495
+ def gemini_flower_detection_2022(self, dataset_name):
1496
+ original_dir = os.path.join(self.data_original_dir)
1497
+ print(original_dir)
1498
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
1499
+ processed_image_dir = os.path.join(processed_dir, 'images')
1500
+ os.makedirs(processed_image_dir, exist_ok = True)
1501
+ # Move images
1502
+ for image in tqdm(glob.glob(os.path.join(original_dir, 'images', '*.jpg'))):
1503
+ shutil.move(image, processed_image_dir)
1504
+ shutil.move(os.path.join(original_dir, 'coco.json'),
1505
+ os.path.join(processed_dir, 'annotations.json'))
1506
+
1507
+ def gemini_leaf_detection_2022(self, dataset_name):
1508
+ original_dir = os.path.join(self.data_original_dir)
1509
+ print(original_dir)
1510
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
1511
+ processed_image_dir = os.path.join(processed_dir, 'images')
1512
+ os.makedirs(processed_image_dir, exist_ok = True)
1513
+ # Move images
1514
+ for image in tqdm(glob.glob(os.path.join(original_dir, 'images', '*.jpg'))):
1515
+ shutil.move(image, processed_image_dir)
1516
+ shutil.move(os.path.join(original_dir, 'coco.json'),
1517
+ os.path.join(processed_dir, 'annotations.json'))
1518
+
1519
+ def gemini_pod_detection_2022(self, dataset_name):
1520
+ original_dir = os.path.join(self.data_original_dir)
1521
+ print(original_dir)
1522
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
1523
+ processed_image_dir = os.path.join(processed_dir, 'images')
1524
+ os.makedirs(processed_image_dir, exist_ok = True)
1525
+ # Move images
1526
+ for image in tqdm(glob.glob(os.path.join(original_dir, 'images', '*.jpg'))):
1527
+ shutil.move(image, processed_image_dir)
1528
+ shutil.move(os.path.join(original_dir, 'coco.json'),
1529
+ os.path.join(processed_dir, 'annotations.json'))
1530
+
1531
+ def gemini_plant_detection_2022(self, dataset_name):
1532
+ original_dir = os.path.join(self.data_original_dir)
1533
+ print(original_dir)
1534
+ processed_dir = os.path.join(self.data_processed_dir, dataset_name)
1535
+ processed_image_dir = os.path.join(processed_dir, 'images')
1536
+ os.makedirs(processed_image_dir, exist_ok = True)
1537
+ # Move images
1538
+ for image in tqdm(glob.glob(os.path.join(original_dir, 'images', '*.jpg'))):
1539
+ shutil.move(image, processed_image_dir)
1540
+ shutil.move(os.path.join(original_dir, 'coco.json'),
1541
+ os.path.join(processed_dir, 'annotations.json'))
1542
+
1543
+ def paddy_disease_classification(self, dataset_name):
1544
+ pass
1545
+
1546
+ def onion_leaf_classification(self, dataset_name):
1547
+ pass
1548
+
1549
+ def chilli_leaf_classification(self, dataset_name):
1550
+ pass
1551
+
1552
+ def orange_leaf_disease_classification(self, dataset_name):
1553
+ pass
1554
+
1555
+ def papaya_leaf_disease_classification(self, dataset_name):
1556
+ pass
1557
+
1558
+ def blackgram_plant_leaf_disease_classification(self, dataset_name):
1559
+ pass
1560
+
1561
+ def arabica_coffee_leaf_disease_classification(self, dataset_name):
1562
+ pass
1563
+
1564
+ def banana_leaf_disease_classification(self, dataset_name):
1565
+ pass
1566
+
1567
+ def coconut_tree_disease_classification(self, dataset_name):
1568
+ pass
1569
+
1570
+ def rice_leaf_disease_classification(self, dataset_name):
1571
+ pass
1572
+
1573
+ def tea_leaf_disease_classification(self, dataset_name):
1574
+ pass
1575
+
1576
+ def betel_leaf_disease_classification(self, dataset_name):
1577
+ pass
1578
+
1579
+ def java_plum_leaf_disease_classification(self, dataset_name):
1580
+ pass
1581
+
1582
+ def sunflower_disease_classification(self, dataset_name):
1583
+ pass
1584
+
1585
+ def cucumber_disease_classification(self, dataset_name):
1586
+ pass
1587
+
1588
+ def soybean_insect_classification(self, dataset_name):
1589
+ pass
1590
+
1591
+ if __name__ == '__main__':
1592
+ # Initialize program arguments.
1593
+ ap = argparse.ArgumentParser()
1594
+ ap.add_argument(
1595
+ "--data_dir",
1596
+ type=str,
1597
+ default="../../data_new",
1598
+ help="The directory containing two sub-directories, " "`original` and `processed`, with the data.",
1599
+ )
1600
+ ap.add_argument("--dataset", type=str, help="The dataset to process.")
1601
+ args = ap.parse_args()
1602
+
1603
+ # Execute the preprocessing.
1604
+ p = PublicDataPreprocessor(os.path.abspath(args.data_dir))
1605
+ print("Processing dataset")
1606
+ p.preprocess(args.dataset)
1607
+ print("Converting dataset")
1608
+ # os.chdir(f'{args.data_dir}/processed')
1609
+ # os.system(f'zip -r {args.dataset}.zip {args.dataset} -x ".*" -x "__MACOSX"')
AgML/source/agml/_internal/process_utils.py ADDED
@@ -0,0 +1,576 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 UC Davis Plant AI and Biophysics Lab
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """
16
+ Preprocess utilities for the code in `preprocess.py`.
17
+ """
18
+
19
+ import os
20
+ import sys
21
+ import csv
22
+ import json
23
+ import shutil
24
+ from typing import Dict, List
25
+ import xml.etree.ElementTree as ET
26
+
27
+ import cv2
28
+ import numpy as np
29
+
30
+ from PIL import Image
31
+ from skimage import measure
32
+ from shapely.geometry import Polygon, MultiPolygon
33
+
34
+ from agml.utils.logging import tqdm
35
+
36
+
37
+ def read_txt_file(file_name, delimiter=" ", header=False):
38
+ with open(file_name, newline="\n") as txt_file:
39
+ txt_reader = csv.reader(txt_file, delimiter=delimiter)
40
+ txt_lines = []
41
+ headline = None
42
+ for idx, line in enumerate(txt_reader):
43
+ if header and idx == 0:
44
+ headline = line
45
+ continue
46
+ line = [x.strip() for x in line if x.strip()] # To remove blank elements
47
+ txt_lines.append(line)
48
+
49
+ return txt_lines, headline
50
+
51
+
52
+ def get_label2id(labels_str: str) -> Dict[str, int]:
53
+ """id is 1 start"""
54
+ labels_ids = list(range(1, len(labels_str) + 1))
55
+ return dict(zip(labels_str, labels_ids))
56
+
57
+
58
+ def get_annpaths(
59
+ ann_dir_path: str = None, ann_ids_path: str = None, ext: str = "", annpaths_list_path: str = None
60
+ ) -> List[str]:
61
+ # If wanting to use annotation paths list
62
+ if annpaths_list_path is not None:
63
+ with open(annpaths_list_path, "r") as f:
64
+ ann_paths = f.read().split()
65
+ return ann_paths
66
+
67
+ # If wanting to use annotation ids list
68
+ ext_with_dot = "." + ext if ext != "" else ""
69
+ with open(ann_ids_path, "r") as f:
70
+ ann_ids = f.read().split()
71
+ ann_paths = [os.path.join(ann_dir_path, aid + ext_with_dot) for aid in ann_ids]
72
+ return ann_paths
73
+
74
+
75
+ def get_image_info_from_annoline(annotation_root, idx, resize=1.0, add_foldername=False):
76
+ filename = annotation_root[0].split("/")[-1]
77
+ try:
78
+ img = cv2.imread(annotation_root[0])
79
+
80
+ if resize != 1.0:
81
+ dsize = (int(img.shape[1] * resize), int(img.shape[0] * resize))
82
+ img = cv2.resize(img, dsize)
83
+
84
+ size = img.shape
85
+ width = size[1]
86
+ height = size[0]
87
+
88
+ if add_foldername:
89
+ filename = "{folder}_{img_name}".format(
90
+ folder=annotation_root[0].split("/")[-2], img_name=annotation_root[0].split("/")[-1]
91
+ )
92
+
93
+ image_info = {
94
+ "file_name": filename,
95
+ "height": height,
96
+ "width": width,
97
+ "id": idx, # Use image order
98
+ }
99
+
100
+ except Exception as e:
101
+ print(e)
102
+ print("Cannot open {file}".format(file=annotation_root[0]))
103
+ image_info = None
104
+ img = None
105
+
106
+ return image_info, img
107
+
108
+
109
+ def get_image_info(annotation_root, idx, resize=1.0, add_foldername=False):
110
+ path = annotation_root.findtext("path")
111
+ if path is None or True:
112
+ filename = annotation_root.findtext("filename")
113
+ else:
114
+ filename = os.path.basename(path)
115
+
116
+ try:
117
+ img = cv2.imread(filename)
118
+
119
+ if resize != 1.0:
120
+ dsize = [int(img.shape[1] * resize), int(img.shape[0] * resize)]
121
+ img = cv2.resize(img, dsize)
122
+
123
+ size = img.shape
124
+ width = size[1]
125
+ height = size[0]
126
+
127
+ if add_foldername:
128
+ filename = "{folder}_{img_name}".format(folder=filename.split("/")[-2], img_name=filename.split("/")[-1])
129
+
130
+ image_info = {
131
+ "file_name": filename.split("/")[-1],
132
+ "height": height,
133
+ "width": width,
134
+ "id": idx, # Use image order
135
+ }
136
+
137
+ except Exception as e:
138
+ print(e)
139
+ print("Cannot open {file}".format(file=annotation_root[0]))
140
+ image_info = None
141
+ img = None
142
+
143
+ return image_info, img
144
+
145
+
146
+ # Sourced from https://github.com/roboflow-ai/voc2coco.git
147
+ def get_coco_annotation_from_annoline(obj, resize=1.0):
148
+ # Try the sub-label first
149
+ category_id = int(obj[4])
150
+ xmin = int(float(obj[0]) * resize)
151
+ ymin = int(float(obj[1]) * resize)
152
+ xmax = int(float(obj[2]) * resize)
153
+ ymax = int(float(obj[3]) * resize)
154
+ assert xmax > xmin and ymax > ymin, f"Box size error !: (xmin, ymin, xmax, ymax): {xmin, ymin, xmax, ymax}"
155
+ o_width = xmax - xmin + 1
156
+ o_height = ymax - ymin + 1
157
+ return {
158
+ "area": o_width * o_height,
159
+ "iscrowd": 0,
160
+ "bbox": [xmin, ymin, o_width, o_height],
161
+ "category_id": category_id,
162
+ "ignore": 0,
163
+ "segmentation": [], # This script is not for segmentation
164
+ }
165
+
166
+
167
+ def get_coco_annotation_from_obj(obj, label2id, name_converter=None):
168
+ # Try to sub-label first
169
+ label = obj.findtext("subname")
170
+ if label is None:
171
+ label = obj.findtext("name")
172
+ if name_converter:
173
+ if label in name_converter:
174
+ label = name_converter[label]
175
+ if label not in label2id:
176
+ return []
177
+ category_id = label2id[label]
178
+
179
+ bndbox = obj.find("bndbox")
180
+ if bndbox is None:
181
+ bndbox = obj.find("bbox")
182
+ if bndbox is None:
183
+ return None
184
+
185
+ xmin = int(float(bndbox.findtext("xmin"))) - 1
186
+ ymin = int(float(bndbox.findtext("ymin"))) - 1
187
+ xmax = int(float(bndbox.findtext("xmax")))
188
+ ymax = int(float(bndbox.findtext("ymax")))
189
+ assert xmax > xmin and ymax > ymin, f"Box size error !: (xmin, ymin, xmax, ymax): {xmin, ymin, xmax, ymax}"
190
+ o_width = xmax - xmin
191
+ o_height = ymax - ymin
192
+ return {
193
+ "area": o_width * o_height,
194
+ "iscrowd": 0,
195
+ "bbox": [xmin, ymin, o_width, o_height],
196
+ "category_id": category_id,
197
+ "ignore": 0,
198
+ "segmentation": [], # This script is not for segmentation
199
+ }
200
+
201
+
202
+ def convert_bbox_to_coco(
203
+ annotation: List[str],
204
+ label2id: Dict[str, int],
205
+ output_jsonpath: str,
206
+ output_imgpath: str,
207
+ general_info,
208
+ image_id_list=None,
209
+ bnd_id_list=None,
210
+ get_label_from_folder=False,
211
+ resize=1.0,
212
+ add_foldername=False,
213
+ extract_num_from_imgid=False,
214
+ ):
215
+ """
216
+ Annotation Format
217
+ -----------------
218
+ "image name" "the number of bounding boxes(bb)" "x1" "y1" "x2" "y2" "label" "score" "x1" "y1" "x2" "y2" ...
219
+
220
+ For example, the following line:
221
+
222
+ TRAIN_RGB/n12710693_12225.png 5 515 68 759 285 2 1.000 624 347 868 582 2 1.000 480 488 693 712 2 1.000 44 433 268 657 2 1.000 112 198 342 401 2 1.000
223
+
224
+ will become:
225
+
226
+ image name=TRAIN_RGB/n12710693_12225.png
227
+ the number of bb=5
228
+ x1=515
229
+ y1=68
230
+ x2=759
231
+ y2=285
232
+ label=2 "0=background, 1=capsicum, 2=rockmelon..."
233
+ score=1.000
234
+
235
+ Reference: https://drive.google.com/drive/folders/1CmsZb1caggLRN7ANfika8WuPiywo4mBb
236
+ """
237
+ output_json_dict = {"images": [], "type": "instances", "annotations": [], "categories": [], "info": general_info}
238
+
239
+ # TODO: Use multi thread to boost up the speed
240
+ print("Converting annotations into COCO JSON and process the images")
241
+ for img_idx, anno_line in enumerate(tqdm(annotation)):
242
+ if image_id_list:
243
+ img_unique_id = image_id_list[img_idx]
244
+ else:
245
+ if extract_num_from_imgid:
246
+ filename = anno_line[0].split("/")[-1]
247
+ img_unique_id = int("".join(filter(str.isdigit, filename)))
248
+ else:
249
+ img_unique_id = img_idx + 1
250
+
251
+ img_info, img = get_image_info_from_annoline(
252
+ annotation_root=anno_line, idx=img_unique_id, resize=resize, add_foldername=add_foldername
253
+ )
254
+
255
+ if img_info:
256
+ output_json_dict["images"].append(img_info)
257
+
258
+ bbox_cnt = int(anno_line[1])
259
+ if bbox_cnt > 0:
260
+ ann_reshape = np.reshape(anno_line[2:], (bbox_cnt, -1))
261
+ for bnd_idx, obj in enumerate(ann_reshape):
262
+ if get_label_from_folder:
263
+ # Change label based on folder
264
+ try:
265
+ category_name = anno_line[0].split("/")[-3]
266
+ if category_name not in label2id:
267
+ raise
268
+ except:
269
+ try:
270
+ category_name = anno_line[0].split("/")[-2]
271
+ if category_name not in label2id:
272
+ raise
273
+ except Exception as e:
274
+ raise e
275
+
276
+ if len(obj) < 5:
277
+ obj = np.append(obj, label2id[category_name])
278
+ else:
279
+ obj[4] = label2id[category_name]
280
+ else:
281
+ pass
282
+
283
+ try:
284
+ ann = get_coco_annotation_from_annoline(obj=obj, resize=resize)
285
+ except:
286
+ ann = None
287
+
288
+ if ann:
289
+ if bnd_id_list:
290
+ bnd_idx = bnd_id_list[img_idx][bnd_idx]
291
+ else:
292
+ bnd_idx + 1 # noqa
293
+ ann.update({"image_id": img_info["id"], "id": bnd_idx})
294
+ output_json_dict["annotations"].append(ann)
295
+
296
+ img_name = img_info["file_name"]
297
+ dest_path = os.path.join(output_imgpath, img_name)
298
+ try:
299
+ if resize == 1.0:
300
+ shutil.copyfile(anno_line[0], dest_path)
301
+ else:
302
+ cv2.imwrite(dest_path, img)
303
+ except: # Cannot copy the image file
304
+ pass
305
+
306
+ else: # Not valid image => Delete from annotation
307
+ pass
308
+
309
+ for label, label_id in label2id.items():
310
+ category_info = {"supercategory": "none", "id": label_id, "name": label}
311
+ output_json_dict["categories"].append(category_info)
312
+
313
+ with open(output_jsonpath, "w") as f:
314
+ output_json = json.dumps(output_json_dict)
315
+ f.write(output_json)
316
+
317
+ return output_json_dict
318
+
319
+
320
+ def convert_xmls_to_cocojson(
321
+ general_info,
322
+ annotation_paths: List[str],
323
+ img_paths: List[str],
324
+ label2id: Dict[str, int],
325
+ name_converter,
326
+ output_jsonpath: str,
327
+ output_imgpath: str,
328
+ extract_num_from_imgid: bool = True,
329
+ ):
330
+ """Converts the XML annotation format into COCO JSON."""
331
+ output_json_dict = {"images": [], "type": "instances", "annotations": [], "categories": [], "info": general_info}
332
+ bnd_id = 1 # START_BOUNDING_BOX_ID, TODO input as args ?
333
+ print("Start converting !")
334
+ for img_idx, a_path in enumerate(tqdm(annotation_paths)):
335
+ # Read annotation xml
336
+ ann_tree = ET.parse(a_path)
337
+ ann_root = ann_tree.getroot()
338
+
339
+ if extract_num_from_imgid:
340
+ filename = a_path.split("/")[-1]
341
+ img_unique_id = int("".join(filter(str.isdigit, filename)))
342
+ else:
343
+ img_unique_id = img_idx + 1
344
+
345
+ if len(img_paths) == len(annotation_paths):
346
+ ann_root.find("filename").text = img_paths[img_idx]
347
+
348
+ img_info, img = get_image_info(annotation_root=ann_root, idx=img_unique_id, resize=1.0, add_foldername=False)
349
+ output_json_dict["images"].append(img_info)
350
+
351
+ for obj in ann_root.findall("object"):
352
+ ann = get_coco_annotation_from_obj(obj=obj, label2id=label2id, name_converter=name_converter)
353
+ if ann:
354
+ ann.update({"image_id": img_info["id"], "id": bnd_id})
355
+ output_json_dict["annotations"].append(ann)
356
+ bnd_id = bnd_id + 1
357
+
358
+ # Process images
359
+ img_name = img_info["file_name"]
360
+ dest_path = os.path.join(output_imgpath, img_name)
361
+ try:
362
+ cv2.imwrite(dest_path, img)
363
+ except: # Cannot copy the image file
364
+ pass
365
+
366
+ for label, label_id in label2id.items():
367
+ category_info = {"supercategory": "none", "id": label_id, "name": label}
368
+ output_json_dict["categories"].append(category_info)
369
+
370
+ with open(output_jsonpath, "w") as f:
371
+ output_json = json.dumps(output_json_dict)
372
+ f.write(output_json)
373
+
374
+
375
+ # Reference: https://www.immersivelimit.com/create-coco-annotations-from-scratch
376
+ def create_sub_masks(mask_image):
377
+ width, height = mask_image.size
378
+
379
+ # Initialize a dictionary of sub-masks indexed by RGB colors
380
+ sub_masks = {}
381
+ for x in range(width):
382
+ for y in range(height):
383
+ # Get the RGB values of the pixel
384
+ pixel = mask_image.getpixel((x, y))[:3]
385
+
386
+ # If the pixel is not black...
387
+ if pixel != (0, 0, 0):
388
+ # Check to see if we've created a sub-mask...
389
+ pixel_str = str(pixel)
390
+ sub_mask = sub_masks.get(pixel_str)
391
+ if sub_mask is None:
392
+ # Create a sub-mask (one bit per pixel) and add to the dictionary
393
+ # Note: we add 1 pixel of padding in each direction
394
+ # because the contours module doesn't handle cases
395
+ # where pixels bleed to the edge of the image
396
+ sub_masks[pixel_str] = Image.new("1", (width + 2, height + 2))
397
+
398
+ # Set the pixel value to 1 (default is 0), accounting for padding
399
+ sub_masks[pixel_str].putpixel((x + 1, y + 1), 1)
400
+
401
+ return sub_masks
402
+
403
+
404
+ # Reference: https://www.immersivelimit.com/create-coco-annotations-from-scratch
405
+ def create_sub_mask_annotation(sub_mask, image_id, category_id, annotation_id, is_crowd):
406
+ # Find contours (boundary lines) around each sub-mask
407
+ # Note: there could be multiple contours if the object
408
+ # is partially occluded. (E.g. an elephant behind a tree)
409
+ contours = measure.find_contours(sub_mask, 0.5, positive_orientation="low")
410
+
411
+ segmentations = []
412
+ polygons = []
413
+ for contour in contours:
414
+ # Flip from (row, col) representation to (x, y)
415
+ # and subtract the padding pixel
416
+ for i in range(len(contour)):
417
+ row, col = contour[i]
418
+ contour[i] = (col - 1, row - 1)
419
+
420
+ # Make a polygon and simplify it
421
+ poly = Polygon(contour)
422
+ poly = poly.simplify(1.0, preserve_topology=False)
423
+ polygons.append(poly)
424
+ segmentation = np.array(poly.exterior.coords).ravel().tolist()
425
+ segmentations.append(segmentation)
426
+
427
+ # Combine the polygons to calculate the bounding box and area
428
+ multi_poly = MultiPolygon(polygons)
429
+ x, y, max_x, max_y = multi_poly.bounds
430
+ width = max_x - x
431
+ height = max_y - y
432
+ bbox = (x, y, width, height)
433
+ area = multi_poly.area
434
+
435
+ annotation = {
436
+ "segmentation": segmentations,
437
+ "iscrowd": is_crowd,
438
+ "image_id": image_id,
439
+ "category_id": category_id,
440
+ "id": annotation_id,
441
+ "bbox": bbox,
442
+ "area": area,
443
+ }
444
+
445
+ return annotation
446
+
447
+
448
+ def create_sub_mask_annotation_per_bbox(sub_mask, image_id, category_id, annotation_id, is_crowd):
449
+ # Find contours (boundary lines) around each sub-mask
450
+ # Note: there could be multiple contours if the object
451
+ # is partially occluded. (E.g. an elephant behind a tree)
452
+ sub_mask_np = np.array(sub_mask)
453
+ contours = measure.find_contours(sub_mask_np, 0.5, positive_orientation="low")
454
+
455
+ segmentations = []
456
+ polygons = []
457
+ annotations = []
458
+ for idx, contour in enumerate(contours):
459
+ # Flip from (row, col) representation to (x, y)
460
+ # and subtract the padding pixel
461
+ for i in range(len(contour)):
462
+ row, col = contour[i]
463
+ contour[i] = (col - 1, row - 1)
464
+
465
+ # Make a polygon and simplify it
466
+ poly = Polygon(contour)
467
+ poly = poly.simplify(1.0, preserve_topology=False)
468
+ polygons.append(poly)
469
+ segmentation = np.array(poly.exterior.coords).ravel().tolist()
470
+ segmentations.append(segmentation)
471
+
472
+ if poly.area > 0:
473
+ # Combine the polygons to calculate the bounding box and area
474
+ multi_poly = MultiPolygon([poly])
475
+ x, y, max_x, max_y = multi_poly.bounds
476
+ width = max_x - x
477
+ height = max_y - y
478
+ bbox = (x, y, width, height)
479
+ area = multi_poly.area
480
+
481
+ annotations.append(
482
+ {
483
+ "segmentation": [segmentation],
484
+ "iscrowd": is_crowd,
485
+ "image_id": image_id,
486
+ "category_id": category_id,
487
+ "id": annotation_id + idx,
488
+ "bbox": bbox,
489
+ "area": area,
490
+ }
491
+ )
492
+
493
+ return annotations
494
+
495
+
496
+ def mask_annotation_per_bbox(anno_line, image_id, category_id, annotation_id, is_crowd):
497
+ # Find contours (boundary lines) around each sub-mask
498
+ # Note: there could be multiple contours if the object
499
+ # is partially occluded. (E.g. an elephant behind a tree)
500
+ segmentations = []
501
+ polygons = []
502
+ annotations = []
503
+ mask_data = json.loads(anno_line[5])
504
+
505
+ if len(mask_data["all_points_x"]) > 3:
506
+ # Flip from (row, col) representation to (x, y)
507
+ # and subtract the padding pixel
508
+ contour = []
509
+ for i in range(len(mask_data["all_points_x"])):
510
+ contour.append([int(mask_data["all_points_x"][i]), int(mask_data["all_points_y"][i])])
511
+
512
+ contour = np.array(contour)
513
+ # Make a polygon and simplify it
514
+ poly = Polygon(contour)
515
+ poly = poly.simplify(1.0, preserve_topology=False)
516
+ polygons.append(poly)
517
+ # Only need the contours, not the polygon itself:
518
+ # segmentation = np.array(poly.exterior.coords).ravel().tolist()
519
+ segmentation = contour.ravel().tolist()
520
+
521
+ segmentations.append(segmentation)
522
+
523
+ if poly.area > 0:
524
+ # Combine the polygons to calculate the bounding box and area
525
+ x, y, max_x, max_y = poly.bounds
526
+ width = max_x - x
527
+ height = max_y - y
528
+ bbox = (x, y, width, height)
529
+ area = poly.area
530
+
531
+ annotation = {
532
+ "segmentation": [segmentation],
533
+ "iscrowd": is_crowd,
534
+ "image_id": image_id,
535
+ "category_id": category_id,
536
+ "id": annotation_id,
537
+ "bbox": bbox,
538
+ "area": area,
539
+ }
540
+ annotations.append(annotation)
541
+
542
+ return annotations
543
+
544
+
545
+ def move_segmentation_dataset(
546
+ out_dir,
547
+ dataset_name,
548
+ train_images,
549
+ annotation_images,
550
+ train_dir,
551
+ annotation_dir,
552
+ train_preprocess_fn=None,
553
+ annotation_preprocess_fn=None,
554
+ ):
555
+ """Moves segmentation images and annotations to a new location."""
556
+ processed_dir = os.path.join(out_dir, dataset_name)
557
+ os.makedirs(processed_dir, exist_ok=True)
558
+ processed_image_dir = os.path.join(processed_dir, "images")
559
+ os.makedirs(processed_image_dir, exist_ok=True)
560
+ processed_annotation_dir = os.path.join(processed_dir, "annotations")
561
+ os.makedirs(processed_annotation_dir, exist_ok=True)
562
+ for image_path, annotation_path in zip(
563
+ tqdm(train_images, desc="Processing Images", file=sys.stdout), annotation_images
564
+ ):
565
+ orig_image_path = os.path.join(train_dir, image_path)
566
+ orig_annotation_path = os.path.join(annotation_dir, annotation_path)
567
+ out_image_path = os.path.join(processed_image_dir, image_path)
568
+ out_label_path = os.path.join(processed_annotation_dir, annotation_path)
569
+ if train_preprocess_fn is None:
570
+ shutil.copyfile(orig_image_path, out_image_path)
571
+ else:
572
+ train_preprocess_fn(orig_image_path, out_image_path)
573
+ if annotation_preprocess_fn is None:
574
+ shutil.copyfile(orig_annotation_path, out_label_path)
575
+ else:
576
+ annotation_preprocess_fn(orig_annotation_path, out_label_path)
AgML/source/agml/_internal/s3internal.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 UC Davis Plant AI and Biophysics Lab
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """
16
+ Internal class for interacting with the AgML public dataset S3 bucket.
17
+ """
18
+
19
+ import json
20
+ import os
21
+ import sys
22
+ import warnings
23
+ import zipfile
24
+
25
+ import boto3
26
+ import botocore.exceptions
27
+
28
+ from agml.utils.logging import tqdm
29
+
30
+
31
+ class InternalAgMLS3API(object):
32
+ """
33
+ Internal API for interaction with the dataset S3 bucket.
34
+
35
+ *Note*: This class should only be used by the AgML developers.
36
+ """
37
+
38
+ def __init__(self):
39
+ # Read in metadata for data sources file
40
+ self.data_srcs_path = os.path.join(
41
+ os.path.dirname(os.path.dirname(__file__)),
42
+ "_assets",
43
+ "public_datasources.json",
44
+ )
45
+ with open(self.data_srcs_path) as f:
46
+ self.data_srcs = json.load(f)
47
+
48
+ # Define s3 bucket URI
49
+ self.agdata_s3_uri = "https://s3.us-west-1.amazonaws.com/agdata-data/"
50
+
51
+ # Initialize attribute for storing dataset download path
52
+ self.dataset_download_path = None
53
+
54
+ @property
55
+ def data_sources(self):
56
+ """Returns a list of AgML public data sources."""
57
+ return list(self.data_srcs.keys())
58
+
59
+ def upload_dataset(self, dataset_name, dataset_dir):
60
+ """
61
+ Uploads dataset to agdata-data s3 file storage.
62
+
63
+ Parameters
64
+ ----------
65
+ dataset_name : str
66
+ name of dataset (without '.zip') -- for list of datasets run self.data_srcs.keys()
67
+ dataset_dir : str
68
+ path to directory where dataset is stored
69
+ """
70
+ # Establish connection with s3 via boto
71
+ self.s3 = boto3.client("s3")
72
+
73
+ # Setup progress bar
74
+ self.pg = tqdm(
75
+ total=os.stat(os.path.abspath(os.path.join(dataset_dir, dataset_name + ".zip"))).st_size,
76
+ file=sys.stdout,
77
+ desc=f"Uploading {dataset_name}",
78
+ )
79
+
80
+ # Upload data to agdata-data bucket
81
+ try:
82
+ with open(os.path.join(dataset_dir, dataset_name + ".zip"), "rb") as data:
83
+ self.s3.upload_fileobj(
84
+ Fileobj=data,
85
+ Bucket="agdata-data",
86
+ Key="datasets/" + dataset_name + ".zip",
87
+ Callback=lambda x: self.pg.update(x),
88
+ )
89
+ except:
90
+ warnings.warn(
91
+ f"Upload of {dataset_name} unsuccessful. You may not have permission "
92
+ f"to upload to the agdata-data s3 bucket.",
93
+ category=UserWarning,
94
+ )
95
+ finally:
96
+ self.pg.close()
97
+
98
+ def upload_model(self, model_name, model_dir):
99
+ """Uploads model to agdata-data s3 file storage.
100
+
101
+ Parameters
102
+ ----------
103
+ model_name : str
104
+ name of model (without '.pth')
105
+ model_dir : str
106
+ path to directory where model is stored
107
+ """
108
+ # Establish connection with s3 via boto
109
+ self.s3 = boto3.client("s3")
110
+
111
+ # Setup progress bar
112
+ self.pg = tqdm(
113
+ total=os.stat(os.path.abspath(os.path.join(model_dir, model_name + ".pth"))).st_size,
114
+ file=sys.stdout,
115
+ desc=f"Uploading {model_name}",
116
+ )
117
+
118
+ # Upload data to agdata-data bucket
119
+ try:
120
+ with open(os.path.join(model_dir, model_name + ".pth"), "rb") as data:
121
+ self.s3.upload_fileobj(
122
+ Fileobj=data,
123
+ Bucket="agdata-data",
124
+ Key="models/" + model_name + ".pth",
125
+ Callback=lambda x: self.pg.update(x),
126
+ )
127
+ except:
128
+ warnings.warn(
129
+ f"Upload of {model_name} unsuccessful. You may not have permission "
130
+ f"to upload to the agdata-data s3 bucket.",
131
+ category=UserWarning,
132
+ )
133
+ finally:
134
+ self.pg.close()
135
+
136
+ def download_dataset(self, dataset_name, dest_dir):
137
+ """
138
+ Downloads dataset from agdata-data s3 file storage.
139
+
140
+ Parameters
141
+ ----------
142
+ dataset_name : str
143
+ name of dataset to download
144
+ dest_dir : str
145
+ path for saving downloaded dataset
146
+ """
147
+ # Validate the dataset name.
148
+ if dataset_name not in self.data_sources:
149
+ raise ValueError(f"Invalid dataset '{dataset_name}.'")
150
+
151
+ # Establish connection with s3 via boto
152
+ self.s3 = boto3.client("s3")
153
+ self.s3_resource = boto3.resource("s3")
154
+
155
+ # Setup progress bar
156
+ try:
157
+ self.pg = tqdm(
158
+ total=float(self.s3_resource.ObjectSummary(bucket_name="agdata-data", key=dataset_name + ".zip").size),
159
+ file=sys.stdout,
160
+ desc=f"Downloading {dataset_name}",
161
+ )
162
+ except botocore.exceptions.ClientError as ce:
163
+ if "Not Found" in str(ce):
164
+ raise ValueError(
165
+ f"The dataset '{dataset_name}' could not be found in "
166
+ f"the bucket, perhaps it has not been uploaded yet."
167
+ )
168
+ raise ce
169
+
170
+ # File path of zipped dataset
171
+ self.dataset_download_path = os.path.join(dest_dir, dataset_name + ".zip")
172
+
173
+ # Upload data to agdata-data bucket
174
+ with open(self.dataset_download_path, "wb") as data:
175
+ self.s3.download_fileobj(
176
+ Bucket="agdata-data",
177
+ Key=dataset_name + ".zip",
178
+ Fileobj=data,
179
+ Callback=lambda x: self.pg.update(x),
180
+ )
181
+ self.pg.close()
182
+
183
+ # Unzip downloaded dataset
184
+ with zipfile.ZipFile(self.dataset_download_path, "r") as z:
185
+ z.printdir()
186
+ print("Extracting files...")
187
+ z.extractall(path=dest_dir)
188
+ print("Done!")
189
+
190
+ # Delete zipped file
191
+ os.remove(self.dataset_download_path)
AgML/source/agml/_internal/syntheticdata.py ADDED
@@ -0,0 +1,756 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import csv
3
+ import copy
4
+ import os
5
+ import numpy as np
6
+ from dict2xml import dict2xml
7
+ import platform
8
+ import subprocess
9
+ import sys
10
+
11
+ from skimage.io import imread, imshow
12
+ import skimage
13
+ from skimage.morphology import closing
14
+ import imantics
15
+ import pandas as pd
16
+ import matplotlib.pyplot as plt
17
+ import xml.etree.ElementTree as ET
18
+
19
+ HELIOS_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), "_helios/Helios")
20
+
21
+
22
+ class HeliosDataGenerator(object):
23
+ def __init__(self, path_helios_dir=HELIOS_PATH):
24
+ self.path_canopygen_header = os.path.join(path_helios_dir, "plugins/canopygenerator/include/CanopyGenerator.h")
25
+ self.path_canopygen_cpp = os.path.join(path_helios_dir, "plugins/canopygenerator/src/CanopyGenerator.cpp")
26
+ self.path_cmakelists = os.path.join(
27
+ os.path.dirname(os.path.dirname(__file__)),
28
+ "_helios/Helios/projects/SyntheticImageAnnotation/CMakeLists.txt",
29
+ )
30
+ self.path_main_cpp = os.path.join(
31
+ os.path.dirname(os.path.dirname(__file__)), "_helios/Helios/projects/SyntheticImageAnnotation/main.cpp"
32
+ )
33
+
34
+ self.path_lidar_cpp = os.path.join(path_helios_dir, "plugins/lidar/src/LiDAR.cpp")
35
+ self.canopy_types = self.get_canopy_types()
36
+ self.canopy_params = self.get_canopy_params()
37
+ self.canopy_param_ranges = self.set_initial_canopy_param_ranges()
38
+ self.lidar_params = self.get_lidar_params()
39
+ self.lidar_param_ranges = self.set_initial_lidar_param_ranges()
40
+ self.camera_params = self.get_camera_params()
41
+ self.camera_param_ranges = self.set_initial_camera_param_ranges()
42
+
43
+ def get_canopy_types(self):
44
+ """
45
+ Find all occurrences of 'struct' in canopygen_header_txt.
46
+ Parse canopy type from struct occurrences.
47
+ Generate a list of canopy types.
48
+ """
49
+
50
+ # Read CanopyGenerator.h to define potential canopy types
51
+ with open(self.path_canopygen_header) as f:
52
+ canopygen_header_txt = f.readlines()
53
+
54
+ # Generate list of canopy types
55
+ canopy_types = []
56
+ search_term = "struct "
57
+ length_search_term = len(search_term)
58
+ for i, string in enumerate(canopygen_header_txt):
59
+ if search_term in string:
60
+ canopy_types.append(string[length_search_term:].split("Parameters{")[0])
61
+
62
+ return canopy_types
63
+
64
+ def set_seed(self, seed):
65
+ np.random.seed(seed)
66
+
67
+ def get_canopy_params(self):
68
+ # Flag for parsing
69
+ param_flag = 0
70
+
71
+ # Initialize canopy parameters dictionary
72
+ canopy_params = {}
73
+
74
+ # Read CanopyGenerator.cpp to define potential canopy types
75
+ with open(self.path_canopygen_cpp) as f:
76
+ canopygen_header_txt = f.readlines()
77
+
78
+ # Find parameters for each canopy type
79
+ for canopy_type in self.canopy_types:
80
+ # Find first line of parameter definition in cpp file
81
+ search_term = canopy_type + "Parameters::"
82
+
83
+ for i, string in enumerate(canopygen_header_txt):
84
+ if param_flag == 1:
85
+ if string != "\n" and string != "}\n" and string != " \n":
86
+ line = string.split(";\n")[0]
87
+ key, value = line.split(" = ")
88
+
89
+ if "(" in value:
90
+ value = value.split("(")[1].split(")")[0].replace(",", " ")
91
+
92
+ canopy_params[canopy_type][key.strip()] = (
93
+ value.strip().strip('"').replace("\n", "").replace(";", "").replace(".f", "0")
94
+ )
95
+
96
+ if len(canopy_params[canopy_type][key.strip()]) == 1:
97
+ canopy_params[canopy_type][key.strip()] = canopy_params[canopy_type][key.strip()][0]
98
+
99
+ if "M_PI" in value:
100
+ new_val = float(value.split("*")[0]) * 3.14159
101
+ canopy_params[canopy_type][key.strip()] = str(new_val)
102
+
103
+ if search_term in string:
104
+ canopy_params[canopy_type] = {}
105
+ param_flag = 1
106
+
107
+ if "}" in string and param_flag == 1:
108
+ param_flag = 0
109
+
110
+ return canopy_params
111
+
112
+ def get_lidar_params(self):
113
+ # Flag for parsing
114
+ param_flag = 0
115
+
116
+ # Initialize canopy parameters dictionary
117
+ lidar_params = {}
118
+
119
+ # Read LiDAR.cpp to find parameters
120
+ with open(self.path_lidar_cpp) as f:
121
+ LiDAR_txt = f.readlines()
122
+
123
+ # Find Metadata line of parameter definition in cpp file
124
+ search_term = "ScanMetadata::ScanMetadata"
125
+
126
+ for i, string in enumerate(LiDAR_txt):
127
+ if param_flag == 1:
128
+ if (
129
+ string != "\n"
130
+ and string != "}\n"
131
+ and string != " \n"
132
+ and string != " //Copy arguments into structure variables\n"
133
+ ):
134
+ line = string.split(";\n")[0]
135
+ key = line.split(" = ")
136
+ # Initialization of values from Helios -- Need to read this values from c++
137
+ if key[0].strip() == "origin":
138
+ lidar_params[key[0].strip()] = "0 0 0"
139
+ elif key[0].strip() == "Ntheta":
140
+ lidar_params["size"] = "250"
141
+ elif key[0].strip() == "thetaMin":
142
+ lidar_params[key[0].strip()] = "0"
143
+ elif key[0].strip() == "thetaMax":
144
+ lidar_params[key[0].strip()] = "180"
145
+ elif key[0].strip() == "Nphi":
146
+ lidar_params["size"] = lidar_params["size"] + " 450"
147
+ elif key[0].strip() == "phiMin":
148
+ lidar_params[key[0].strip()] = "0"
149
+ elif key[0].strip() == "phiMax":
150
+ lidar_params[key[0].strip()] = "360"
151
+ elif key[0].strip() == "exitDiameter":
152
+ lidar_params[key[0].strip()] = "0"
153
+ elif key[0].strip() == "beamDivergence":
154
+ lidar_params[key[0].strip()] = "0"
155
+ elif key[0].strip() == "columnFormat":
156
+ lidar_params["ASCII_format"] = "x y z"
157
+
158
+ if "ScanMetadata::ScanMetadata" in string:
159
+ param_flag = 1
160
+
161
+ if "}" in string and param_flag == 1:
162
+ param_flag = 0
163
+ return lidar_params
164
+
165
+ def get_camera_params(self):
166
+ # Initialize canopy parameters dictionary
167
+ camera_params = {}
168
+
169
+ # Initialization of image resolution and camera position
170
+ camera_params["image_resolution"] = "600 400"
171
+ camera_params["camera_lookat"] = "0 0 1"
172
+ camera_params["camera_position"] = "0 -2 1"
173
+
174
+ return camera_params
175
+
176
+ def set_initial_lidar_param_ranges(self):
177
+ lidar_param_ranges = copy.deepcopy(self.lidar_params)
178
+
179
+ # Check if parameter is a path or number; this assumes that all strings will be paths
180
+ for i in lidar_param_ranges.keys():
181
+ if i == "ASCII_format":
182
+ val = lidar_param_ranges[i]
183
+ lidar_param_ranges[i] = [(val.split(" ")[j].replace("f", "0")) for j in range(len(val.split()))]
184
+ else:
185
+ val = lidar_param_ranges[i]
186
+ lidar_param_ranges[i] = [float(val.split(" ")[j].replace("f", "0")) for j in range(len(val.split()))]
187
+
188
+ return lidar_param_ranges
189
+
190
+ def set_initial_canopy_param_ranges(self):
191
+ canopy_param_ranges = copy.deepcopy(self.canopy_params)
192
+
193
+ # Check if parameter is a path or number; this assumes that all strings will be paths
194
+ for i in canopy_param_ranges.keys():
195
+ for key in list(canopy_param_ranges[i]):
196
+ val = canopy_param_ranges[i][key]
197
+ if val.isalpha() or "/" in val or ":" in val:
198
+ canopy_param_ranges[i].pop(key)
199
+ else:
200
+ canopy_param_ranges[i][key] = [
201
+ [float(val.split(" ")[j].replace("f", "0")), float(val.split(" ")[j].replace("f", "0"))]
202
+ for j in range(len(val.split()))
203
+ ]
204
+
205
+ return canopy_param_ranges
206
+
207
+ def set_initial_camera_param_ranges(self):
208
+ camera_param_ranges = copy.deepcopy(self.camera_params)
209
+
210
+ # Check if parameter is a path or number; this assumes that all strings will be paths
211
+ for i in camera_param_ranges.keys():
212
+ val = camera_param_ranges[i]
213
+ camera_param_ranges[i] = [float(val.split(" ")[j]) for j in range(len(val.split()))]
214
+
215
+ return camera_param_ranges
216
+
217
+ def generate_one_datapair(self, canopy_type, simulation_type, export_format="xml"):
218
+ """
219
+ Find all occurrences of 'struct' in canopygen_header_txt.
220
+ Parse canopy type from struct occurrences.
221
+ Generate a list of canopy types.
222
+
223
+ Args:
224
+ canopy_type (string): the selected canopy type for the synthetic images
225
+ It should contain a out_channels attribute, which indicates the number of output
226
+ channels that each feature map has (and it should be the same for all feature maps).
227
+ The backbone should return a single Tensor or and OrderedDict[Tensor].
228
+ simulation_type (string): choose between RGB only and Lidar mode using 'rgb' or 'lidar'
229
+ export_format (string): default is xml for Helios
230
+ """
231
+
232
+ assert canopy_type in self.canopy_types, "Canopy type not available."
233
+
234
+ assert export_format in ["xml", "csv", "json"], "Only xml, csv and json export formats are possible."
235
+
236
+ canopy_params_filtered = {k: v for k, v in self.canopy_params.items() if k.startswith(canopy_type)}
237
+
238
+ canopy_params_filtered[canopy_type + "Parameters"] = canopy_params_filtered.pop(canopy_type)
239
+
240
+ canopy_params_filtered["Ground"] = {
241
+ "origin": "0 0 0",
242
+ "extent": "10 10",
243
+ "texture_subtiles": "10 10",
244
+ "texture_subpatches": "1 1",
245
+ "ground_texture_file": "plugins/canopygenerator/textures/dirt.jpg",
246
+ "rotation": "0",
247
+ }
248
+
249
+ canopy_params_filtered = {"canopygenerator": canopy_params_filtered}
250
+
251
+ if simulation_type == "lidar":
252
+ canopy_params_filtered["scan"] = self.lidar_params
253
+
254
+ if simulation_type == "rgb":
255
+ canopy_params_filtered[""] = self.camera_params
256
+
257
+ canopy_params_filtered = {"helios": canopy_params_filtered}
258
+ from pprint import pprint
259
+ # print(canopy_params_filtered)
260
+
261
+ if not os.path.exists(
262
+ os.path.join(
263
+ os.path.dirname(os.path.dirname(__file__)), "_helios/Helios/projects/SyntheticImageAnnotation/xml"
264
+ )
265
+ ):
266
+ os.makedirs(
267
+ os.path.join(
268
+ os.path.dirname(os.path.dirname(__file__)), "_helios/Helios/projects/SyntheticImageAnnotation/xml"
269
+ )
270
+ )
271
+
272
+ if export_format == "xml":
273
+ with open(
274
+ os.path.join(
275
+ os.path.dirname(os.path.dirname(__file__)),
276
+ "_helios/Helios/projects/SyntheticImageAnnotation/xml/tmp_canopy_params_image.xml",
277
+ ),
278
+ "w",
279
+ ) as f:
280
+ f.write(dict2xml(canopy_params_filtered))
281
+
282
+ def generate_data(
283
+ self, n_imgs, canopy_type, simulation_type, annotation_type, label_elements="none", output_directory=".."
284
+ ):
285
+ """
286
+ Given the path to the output of Helios, this method can be used to convert the data to a more standard format such as COCO JSON
287
+
288
+ Args:
289
+ n_imgs (int): The number of images that to be generated
290
+ canopy_type (string): the selected canopy type for the synthetic images
291
+ simulation_type (string): choose between RGB only and Lidar mode using 'rgb' or 'lidar'
292
+ annotation_type (string): choose between semantic segmentation, object detection, and instance segmentation as 'semantic', 'object', and 'instance'.
293
+ output_directory (string) (optional): optionally you may pass in a custom path to save the Helios output to the custom path
294
+
295
+ """
296
+ output_directory = os.path.abspath(output_directory)
297
+
298
+ assert simulation_type in ["rgb", "lidar"], "Simulation type unavailable."
299
+
300
+ assert annotation_type in ["semantic", "object", "instance", "none"], "Annotation type unavailable."
301
+
302
+ Labels = label_elements.replace(" ", "").split(",")
303
+
304
+ for j in range(len(Labels)):
305
+ assert Labels[j] in ["trunk", "leaves", "fruits", "branches", "ground", "none"], "Label type unavailable"
306
+
307
+ param_ranges = self.canopy_param_ranges[canopy_type]
308
+
309
+ camera_ranges = self.camera_param_ranges
310
+
311
+ if simulation_type == "lidar":
312
+ lidar_ranges = copy.deepcopy(self.lidar_param_ranges)
313
+
314
+ # LiDAR parameters
315
+ for key in lidar_ranges:
316
+ # param_vals=lidar_params[key].split(' ')
317
+ arr = [lidar_ranges[key][i] for i in range(len(lidar_ranges[key]))]
318
+ string_arr = [str(a) for a in arr]
319
+ self.lidar_params[key] = " ".join(string_arr)
320
+ # Mutiple LiDAR
321
+ LiDARs = []
322
+ for i in range(len(lidar_ranges["origin"])):
323
+ for key in lidar_ranges:
324
+ if key == "origin":
325
+ arr = [lidar_ranges[key][i] for i in range(len(lidar_ranges[key]))]
326
+ arr = arr[i]
327
+ string_arr = [str(a) for a in arr]
328
+ self.lidar_params[key] = " ".join(string_arr)
329
+ A = copy.deepcopy(self.lidar_params)
330
+ LiDARs.append(A)
331
+ self.lidar_params = LiDARs
332
+
333
+ if simulation_type == "rgb":
334
+ # Camera parameters
335
+ for key in camera_ranges:
336
+ arr = [camera_ranges[key][i] for i in range(len(camera_ranges[key]))]
337
+ string_arr = [str(a).replace(",", "").replace("[", " ").replace("]", " ") for a in arr]
338
+ self.camera_params[key] = " ".join(string_arr)
339
+
340
+ for n in range(n_imgs):
341
+ params = self.canopy_params[canopy_type]
342
+ # lidar_params = self.lidar_params
343
+
344
+ # Context parameters
345
+ for key in param_ranges:
346
+ print(param_ranges[key])
347
+ arr = [
348
+ np.random.uniform(param_ranges[key][i][0], param_ranges[key][i][1])
349
+ for i in range(len(param_ranges[key]))
350
+ ]
351
+ string_arr = [str(a) for a in arr]
352
+ params[key] = " ".join(string_arr)
353
+ self.canopy_params[canopy_type] = params
354
+ print(self.canopy_params[canopy_type])
355
+
356
+ self.generate_one_datapair(canopy_type, simulation_type)
357
+
358
+ # Re-write tags of XML to have the expected Helios input
359
+ tree = ET.parse(
360
+ os.path.join(
361
+ os.path.dirname(os.path.dirname(__file__)),
362
+ "_helios/Helios/projects/SyntheticImageAnnotation/xml",
363
+ "tmp_canopy_params_image.xml",
364
+ )
365
+ )
366
+ root = tree.getroot()
367
+ for child in root:
368
+ if child.tag == "camera_position":
369
+ child.tag = "globaldata_vec3"
370
+ child.set("label", "camera_position")
371
+ if child.tag == "camera_lookat":
372
+ child.tag = "globaldata_vec3"
373
+ child.set("label", "camera_lookat")
374
+ if child.tag == "image_resolution":
375
+ child.tag = "globaldata_int2"
376
+ child.set("label", "image_resolution")
377
+
378
+ tree.write(
379
+ os.path.join(
380
+ os.path.dirname(os.path.dirname(__file__)),
381
+ "_helios/Helios/projects/SyntheticImageAnnotation/xml",
382
+ "tmp_canopy_params_image.xml",
383
+ )
384
+ )
385
+
386
+ # Modify cmake file for rgb versus lidar simulation
387
+ with open(self.path_cmakelists) as f:
388
+ cmakelists_txt = f.readlines()
389
+
390
+ for i, string in enumerate(cmakelists_txt):
391
+ if "set( PLUGINS " in string and simulation_type == "lidar":
392
+ cmakelists_txt[i] = 'set( PLUGINS "lidar;visualizer;canopygenerator;syntheticannotation" )\n'
393
+
394
+ if "set( PLUGINS " in string and simulation_type == "rgb":
395
+ cmakelists_txt[i] = 'set( PLUGINS "visualizer;canopygenerator;syntheticannotation" )\n'
396
+
397
+ # and write everything back
398
+ with open(self.path_cmakelists, "w") as f:
399
+ f.writelines(cmakelists_txt)
400
+
401
+ # Modify maincpp file for rgb versus lidar simulation
402
+ # with open(self.path_main_cpp) as f:
403
+ # main_cpp = f.readlines()
404
+
405
+ # Define paths for CMAKE compilation and output files
406
+ current_directory = os.getcwd()
407
+ build_dir = os.path.join(current_directory, "Helios/projects/SyntheticImageAnnotation/build")
408
+ if output_directory == "":
409
+ output_dir = os.path.join(current_directory, "Helios/projects/SyntheticImageAnnotation/output")
410
+ else:
411
+ assert os.path.isdir(output_directory), "Please, introduce a valid directory"
412
+ output_dir = output_directory + "/output"
413
+ point_cloud_dir = os.path.join(current_directory, output_dir + "/point_cloud/")
414
+ images_dir = os.path.join(current_directory, output_dir + "/images/")
415
+
416
+ if not os.path.exists(build_dir):
417
+ os.makedirs(build_dir)
418
+
419
+ if not os.path.exists(output_dir):
420
+ os.makedirs(output_dir)
421
+
422
+ if simulation_type == "lidar":
423
+ if not os.path.exists(point_cloud_dir):
424
+ os.makedirs(point_cloud_dir)
425
+
426
+ if simulation_type == "rgb":
427
+ if not os.path.exists(images_dir):
428
+ os.makedirs(images_dir)
429
+
430
+ exe = os.path.join(build_dir, "SyntheticImageAnnotation")
431
+
432
+ # Modify main.cpp file for compilation in LIDAR and RGB case
433
+ """
434
+ print('Generation synthetic data: #' + str(n))
435
+ for i, string in enumerate(main_cpp):
436
+
437
+ # Managing annotation types requested by user
438
+ if 'annotation.disableSemanticSegmentation();' in string and 'semantic' not in annotation_type:
439
+ main_cpp[i] = ' annotation.disableSemanticSegmentation();\n'
440
+
441
+ if 'annotation.disableSemanticSegmentation();' in string and 'semantic' in annotation_type:
442
+ main_cpp[i] = ' //annotation.disableSemanticSegmentation();\n'
443
+
444
+ if 'annotation.disableObjectDetection();' in string and 'object' not in annotation_type:
445
+ main_cpp[i] = ' annotation.disableObjectDetection();\n'
446
+
447
+ if 'annotation.disableObjectDetection();' in string and 'object' in annotation_type:
448
+ main_cpp[i] = ' //annotation.disableObjectDetection();\n'
449
+
450
+ if 'annotation.disableInstanceSegmentation();' in string and 'instance' not in annotation_type:
451
+ main_cpp[i] = ' annotation.disableInstanceSegmentation();\n'
452
+
453
+ if 'annotation.disableInstanceSegmentation();' in string and 'instance' in annotation_type:
454
+ main_cpp[i] = ' //annotation.disableInstanceSegmentation();\n'
455
+
456
+ # Managing LiDAR versus rgb request by user
457
+ if '#include "L' in string and simulation_type == 'lidar':
458
+ main_cpp[i] = '#include "LiDAR.h"\n'
459
+
460
+ if '#include "L' in string and simulation_type == 'rgb':
461
+ main_cpp[i] = '//#include "LiDAR.h"\n'
462
+
463
+ if 'flag=' in string and simulation_type == 'lidar':
464
+ main_cpp[i] = 'bool flag=true;\n'
465
+
466
+ if 'flag=' in string and simulation_type == 'rgb':
467
+ main_cpp[i] = 'bool flag=false;\n'
468
+
469
+ if 'LiDARcloud lidarcloud' in string and simulation_type == 'lidar':
470
+ main_cpp[i] = ' LiDARcloud lidarcloud;\n'
471
+
472
+ if 'LiDARcloud lidarcloud' in string and simulation_type == 'rgb':
473
+ main_cpp[i] = ' //LiDARcloud lidarcloud;\n'
474
+
475
+ if 'lidarcloud.loadXML' in string and simulation_type == 'lidar':
476
+ main_cpp[i] = ' lidarcloud.loadXML("../xml/tmp_canopy_params_image.xml");\n'
477
+
478
+ if 'lidarcloud.loadXML' in string and simulation_type == 'rgb':
479
+ main_cpp[i] = ' //lidarcloud.loadXML("../xml/tmp_canopy_params_image.xml");\n'
480
+
481
+ if 'lidarcloud.syntheticScan' in string and simulation_type == 'lidar':
482
+ main_cpp[i] = ' lidarcloud.syntheticScan( &context);\n'
483
+
484
+ if 'lidarcloud.syntheticScan' in string and simulation_type == 'rgb':
485
+ main_cpp[i] = ' //lidarcloud.syntheticScan( &context);\n'
486
+
487
+ #
488
+ if 'lidarcloud.exportPointCloud' in string and simulation_type == 'lidar':
489
+ main_cpp[i] = ' lidarcloud.exportPointCloud( "' + output_dir + '/point_cloud/synthetic_scan_' + str(
490
+ n) + '.xyz" );\n'
491
+
492
+ if 'lidarcloud.exportPointCloud' in string and simulation_type == 'rgb':
493
+ main_cpp[
494
+ i] = ' //lidarcloud.exportPointCloud( "' + output_dir + '/point_cloud/synthetic_scan_' + str(
495
+ n) + '.xyz" );\n'
496
+
497
+ # Change output directory -- if requested
498
+
499
+ if 'annotation.render(' in string and simulation_type == 'rgb':
500
+ main_cpp[i] = ' annotation.render( "' + images_dir + '" );\n'
501
+
502
+ # Choose elements to label
503
+ for k in Labels:
504
+
505
+ if 'annotation.labelPrimitives(cgen.getTrunkUUIDs(p)' in string and simulation_type == 'rgb' and 'trunk' in Labels:
506
+ main_cpp[i] = ' annotation.labelPrimitives(cgen.getTrunkUUIDs(p), "trunks");\n'
507
+ elif 'annotation.labelPrimitives(cgen.getTrunkUUIDs(p)' in string and simulation_type == 'rgb' and 'trunk' not in Labels:
508
+ main_cpp[i] = ' //annotation.labelPrimitives(cgen.getTrunkUUIDs(p), "trunks");\n'
509
+
510
+ if 'annotation.labelPrimitives(cgen.getBranchUUIDs(p)' in string and simulation_type == 'rgb' and 'branches' in Labels:
511
+ main_cpp[i] = ' annotation.labelPrimitives(cgen.getBranchUUIDs(p), "branches");\n'
512
+ elif 'annotation.labelPrimitives(cgen.getBranchUUIDs(p)' in string and simulation_type == 'rgb' and 'branches' not in Labels:
513
+ main_cpp[i] = ' //annotation.labelPrimitives(cgen.getBranchUUIDs(p), "branches");\n'
514
+
515
+ if 'annotation.labelPrimitives(cgen.getLeafUUIDs(p)' in string and simulation_type == 'rgb' and 'leaves' in Labels:
516
+ main_cpp[i] = ' annotation.labelPrimitives(cgen.getLeafUUIDs(p), "leaves");\n'
517
+ elif 'annotation.labelPrimitives(cgen.getLeafUUIDs(p)' in string and simulation_type == 'rgb' and 'leaves' not in Labels:
518
+ main_cpp[i] = ' //annotation.labelPrimitives(cgen.getLeafUUIDs(p), "leaves");\n'
519
+
520
+ if 'annotation.labelPrimitives( flatten(fruitUUIDs.at(c))' in string and simulation_type == 'rgb' and 'fruits' in Labels:
521
+ main_cpp[i] = ' annotation.labelPrimitives( flatten(fruitUUIDs.at(c)), "clusters" );\n'
522
+ elif 'annotation.labelPrimitives( flatten(fruitUUIDs.at(c))' in string and simulation_type == 'rgb' and 'fruits' not in Labels:
523
+ main_cpp[i] = ' //annotation.labelPrimitives( flatten(fruitUUIDs.at(c)), "clusters" );\n'
524
+
525
+
526
+
527
+ # and write everything back
528
+ with open(self.path_main_cpp, 'w') as f:
529
+ f.writelines(main_cpp)
530
+ """
531
+
532
+ default_xml_path = os.path.join(
533
+ os.path.dirname(os.path.dirname(__file__)),
534
+ "_helios/Helios/projects/SyntheticImageAnnotation/xml",
535
+ "tmp_canopy_params_image.xml",
536
+ )
537
+ str_to_write = f"{annotation_type}\n{simulation_type}\n{' '.join(Labels)}\n{default_xml_path}\n{images_dir}"
538
+ with open(
539
+ os.path.join(
540
+ os.path.dirname(os.path.dirname(__file__)),
541
+ "_helios/Helios/projects/SyntheticImageAnnotation/config.txt",
542
+ ),
543
+ "w",
544
+ ) as f:
545
+ f.write(str_to_write)
546
+
547
+ # System call to helios @DARIO
548
+ # current_directory = os.getcwd()
549
+ helios_directory = os.path.join(os.path.dirname(os.path.dirname(__file__)), "_helios")
550
+ build_dir = os.path.join(helios_directory, "Helios/projects/SyntheticImageAnnotation/build")
551
+ output_dir = os.path.join(helios_directory, "Helios/projects/SyntheticImageAnnotation/output")
552
+ point_cloud_dir = os.path.join(
553
+ helios_directory, "Helios/projects/SyntheticImageAnnotation/output/point_cloud/"
554
+ )
555
+ images_dir = os.path.join(helios_directory, "Helios/projects/SyntheticImageAnnotation/output/images/")
556
+
557
+ if not os.path.exists(build_dir):
558
+ os.makedirs(build_dir)
559
+
560
+ if not os.path.exists(output_dir):
561
+ os.makedirs(output_dir)
562
+
563
+ if simulation_type == "lidar":
564
+ if not os.path.exists(point_cloud_dir):
565
+ os.makedirs(point_cloud_dir)
566
+
567
+ if simulation_type == "rgb":
568
+ if not os.path.exists(images_dir):
569
+ os.makedirs(images_dir)
570
+
571
+ exe = os.path.join(build_dir, "SyntheticImageAnnotation")
572
+
573
+ cmake_args = ["-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=" + build_dir]
574
+ cmake_args += ["-G", "Unix Makefiles"]
575
+ # current_directory = os.getcwd()
576
+ helios_directory = os.path.join(os.path.dirname(os.path.dirname(__file__)), "_helios")
577
+ build_dir = os.path.join(helios_directory, "Helios/projects/SyntheticImageAnnotation/build")
578
+ output_dir = os.path.join(helios_directory, "Helios/projects/SyntheticImageAnnotation/output")
579
+ point_cloud_dir = os.path.join(
580
+ helios_directory, "Helios/projects/SyntheticImageAnnotation/output/point_cloud/"
581
+ )
582
+ images_dir = os.path.join(helios_directory, "Helios/projects/SyntheticImageAnnotation/output/images/")
583
+
584
+ if not os.path.exists(build_dir):
585
+ os.makedirs(build_dir)
586
+
587
+ if not os.path.exists(output_dir):
588
+ os.makedirs(output_dir)
589
+
590
+ if simulation_type == "lidar":
591
+ if not os.path.exists(point_cloud_dir):
592
+ os.makedirs(point_cloud_dir)
593
+
594
+ if n == 0:
595
+ subprocess.run(["cmake", ".."] + cmake_args, cwd=build_dir, check=True)
596
+ subprocess.run(["cmake", "--build", "."], cwd=build_dir, check=True)
597
+
598
+ subprocess.run(
599
+ [
600
+ exe,
601
+ "/Users/amoghjoshi/Documents/Amogh/Programs/Python/AgML/agml/_helios/Helios/projects/SyntheticImageAnnotation/config.txt",
602
+ output_dir,
603
+ ],
604
+ cwd=build_dir,
605
+ )
606
+
607
+ # self.convert_data() # Conversion to standard formats @PRANAV
608
+
609
+ def convert_data(self, Frames_path, annotation_format="instance_segmentation"):
610
+ """
611
+ Given the path to the output of Helios, this method can be used to convert the data to a more standard format such as COCO JSON
612
+
613
+ Args:
614
+ annotation_format (string): choose between 'instance_segmentation', 'object_detection', or 'panoptic_segmentation'
615
+ Frames_path (string): Specify the path to the output of Helios
616
+
617
+ """
618
+
619
+ if annotation_format == "object_detection":
620
+ pass # YOLO @PRANAV
621
+ if annotation_format == "semantic_segmentation":
622
+ pass # single channel + mapping @PRANAV
623
+ if annotation_format == "instance_segmentation" or annotation_format == "object_detection":
624
+ # data_path = sorted(os.listdir(data_path))
625
+ frames_view = sorted(os.listdir(Frames_path))
626
+ # Initialize a list of label numpy arrays
627
+ # imgs = []
628
+ # npy_arrs = []
629
+ images = []
630
+ if not os.path.exists("./train_images/"):
631
+ os.mkdir("./train_images/")
632
+ for i, frame in enumerate(frames_view):
633
+ if frame == ".DS_Store":
634
+ continue
635
+
636
+ else:
637
+ # Generate labels and append to list
638
+ image, mask, pixel_ID = self.generate_npy_arr(Frames_path, Frames_path + frame + "/")
639
+ plt.imsave("./train_images/" + frame + ".jpeg", image)
640
+
641
+ strel = skimage.morphology.selem.disk(2)
642
+
643
+ # %% Convert masks to COCO for detectron implementation.
644
+
645
+ mapping = pd.read_csv(
646
+ Frames_path + frame + "/" + "ID_mapping.txt", delim_whitespace=True, names=["ID", "Class"]
647
+ )
648
+
649
+ category = imantics.Category(
650
+ mapping["Class"].iloc[pixel_ID], color=imantics.Color([255, 0, 0])
651
+ ) # color for debug only
652
+ c = 0
653
+
654
+ im2 = imantics.Image(
655
+ image_array=image, path="data/train_images/" + frame + ".jpeg", id=i
656
+ ) # imantics image object
657
+
658
+ for i in range(mask.shape[2]): # create the polygons for each slice
659
+ if annotation_format == "instance_segmentation":
660
+ poly = imantics.Mask(closing(mask[:, :, i], strel)).polygons()
661
+ ann = imantics.Annotation(image=im2, category=category, polygons=poly, id=c)
662
+ box = imantics.Mask(closing(mask[:, :, i], strel)).bbox()
663
+ # print(box)
664
+
665
+ if annotation_format == "object_detection":
666
+ box = imantics.Mask(closing(mask[:, :, i], strel)).bbox()
667
+ ann = imantics.Annotation(image=im2, category=category, bbox=box, id=c)
668
+ # print(box)
669
+ c += 1
670
+
671
+ if np.sum(mask[:, :, i]) > 1300:
672
+ im2.add(ann) # add the polygon to the image object
673
+ print(box)
674
+
675
+ images.append(im2) # collect the image objects after they get polygons
676
+ # %% Create Imantics dataset and export
677
+ ds = imantics.Dataset(name="coco", images=images)
678
+ obj = ds.coco()
679
+ with open("trainval.json", "w") as json_file:
680
+ json.dump(obj, json_file)
681
+ # json_file.close()
682
+ # obj=obj.clear()
683
+ # del obj, ds, json_file, images
684
+
685
+ # COCO json @PRANAV
686
+ if annotation_format == "panoptic_segmentation":
687
+ pass
688
+ if annotation_format == "regression":
689
+ pass
690
+
691
+ def generate_npy_arr(self, Frames_path, frames_view_path):
692
+ Frame_paths = frames_view_path
693
+
694
+ # Grab dimensions
695
+ render = imread(frames_view_path + "/RGB_rendering" + ".jpeg")
696
+ render_xy_shape = (render.shape[0], render.shape[1])
697
+ # print(render_xy_shape)
698
+
699
+ # Number of instances
700
+ frames_list = []
701
+ exclude_files = ["RGB_rendering.jpeg", "pixelID_combined.txt", "ID_mapping.txt"]
702
+ frames_files = [x for x in os.listdir(frames_view_path) if x not in exclude_files]
703
+ frames_list.append(frames_files)
704
+ n_instances = [len(frame) for frame in frames_list]
705
+
706
+ # Initialize numpy array of shape (x, y, n_instances)
707
+ npy_arr = np.zeros(
708
+ shape=(render_xy_shape[0], render_xy_shape[1], n_instances[0]), dtype=np.bool_
709
+ ) # Make boolean dtype
710
+
711
+ # Read frames as a list
712
+ bboxes = []
713
+ frames = []
714
+ i = 0
715
+ for file_grape_arr in os.listdir(frames_view_path):
716
+ if file_grape_arr == ".DS_Store":
717
+ continue
718
+
719
+ if file_grape_arr == "pixelID_combined.txt":
720
+ continue
721
+
722
+ if file_grape_arr == "ID_mapping.txt":
723
+ continue
724
+
725
+ if file_grape_arr == "RGB_rendering.jpeg":
726
+ continue
727
+
728
+ if file_grape_arr == "rectangular_labels.txt":
729
+ continue
730
+
731
+ else:
732
+ # Extract pixel id based off name of the file. We want the number between _ and .
733
+
734
+ pixel_id_tmp = int(file_grape_arr.split("_")[1].split(".")[0])
735
+ # print(pixel_id_tmp)
736
+
737
+ # Load bounding box coordinates from file
738
+ bbox_tmp = np.loadtxt(frames_view_path + file_grape_arr, max_rows=1)
739
+ bbox_tmp = [int(bbox_tmp[i]) for i in range(0, 4)]
740
+ bbox = np.copy(bbox_tmp)
741
+ bbox[3] = render_xy_shape[0] - bbox_tmp[2]
742
+ bbox[2] = render_xy_shape[0] - bbox_tmp[3]
743
+
744
+ # print(bbox_tmp)
745
+
746
+ # Load pixel positions from file
747
+ grape_arr_tmp = np.loadtxt(frames_view_path + file_grape_arr, skiprows=1, ndmin=2)
748
+
749
+ #
750
+ grape_arr_tmp = grape_arr_tmp == pixel_id_tmp
751
+
752
+ npy_arr[bbox[2] : (bbox[3] + 1), bbox[0] : (bbox[1] + 1), i] = grape_arr_tmp
753
+
754
+ i += 1
755
+
756
+ return render, npy_arr, pixel_id_tmp
AgML/source/agml/_internal/utils.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 UC Davis Plant AI and Biophysics Lab
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """
16
+ Methods for checking the dataset information in AgML.
17
+ """
18
+
19
+ import os
20
+ import pickle
21
+
22
+ from agml.utils.io import recursive_dirname
23
+
24
+ _PERSONAL_ACCESS_TOKEN = None
25
+
26
+
27
+ def shape_info_file_contents():
28
+ with open(
29
+ os.path.join(recursive_dirname(__file__, 2), "_assets", "shape_info.pickle"),
30
+ "rb",
31
+ ) as f:
32
+ return pickle.load(f)
33
+
34
+
35
+ def set_git_personal_access_token(token):
36
+ global _PERSONAL_ACCESS_TOKEN
37
+ _PERSONAL_ACCESS_TOKEN = token
38
+
39
+
40
+ def get_personal_access_token():
41
+ global _PERSONAL_ACCESS_TOKEN
42
+ return _PERSONAL_ACCESS_TOKEN
AgML/source/agml/backend/__init__.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 UC Davis Plant AI and Biophysics Lab
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """
16
+ Controls the AgML backend system.
17
+
18
+ The first part of the backend is the backend deep learning library.
19
+ The backend, one of {TensorFlow, PyTorch}, primarily exists for internal
20
+ purposes, e.g. figuring out which methods to use in the data module or
21
+ the actual model configuring/training modules.
22
+
23
+ Secondly, the backend controls the loading/saving procedure for files within
24
+ AgML, specifically data loaded or generated from the data module.
25
+ """
26
+
27
+ from . import experimental
28
+ from .config import (
29
+ clear_all_datasets,
30
+ data_save_path,
31
+ downloaded_datasets,
32
+ model_save_path,
33
+ set_data_save_path,
34
+ set_model_save_path,
35
+ set_synthetic_save_path,
36
+ synthetic_data_save_path,
37
+ )
38
+ from .random import set_seed
39
+ from .tftorch import get_backend, set_backend
AgML/source/agml/backend/config.py ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 UC Davis Plant AI and Biophysics Lab
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import json
16
+ import logging
17
+ import os
18
+ import shutil
19
+ from pathlib import Path
20
+
21
+ from agml.utils.logging import log
22
+
23
+ # The super base save directory for AgML. This is the overriding base
24
+ # save directory and default original save directory. This is saved here
25
+ # because the default path to save datasets to can be overridden by
26
+ # `set_dataset_save_path()`, but we need to keep the super directory to
27
+ # access the config file which stores this information in the first place.
28
+ SUPER_BASE_DIR = Path(os.getenv("AGML_CONFIG_DIR", os.path.join(os.path.expanduser("~"), ".agml")))
29
+
30
+
31
+ # This is the path to the saved datasets. By default, this saves to
32
+ # SUPER_BASE_DIR/datasets, but can be overridden. The value of this
33
+ # is set upon the instantiation of the module (see below code).
34
+ DATASET_SAVE_DIR: str
35
+
36
+
37
+ # This is similar to `DATASET_SAVE_DIR`, but is for synthetically generated
38
+ # datasets using Helios. By default, this will be SUPER_BASE_DIR/synthetic,
39
+ # but it can be overridden. The value is set upon instantiation of the module.
40
+ SYNTHETIC_SAVE_DIR: str
41
+
42
+
43
+ # This is the path to any downloaded models. By default, this saves to
44
+ # SUPER_BASE_DIR/models, but can be overridden. The value is set upon
45
+ # instantiation of the module (see the below code).
46
+ MODEL_SAVE_DIR: str
47
+
48
+
49
+ # Loads the configuration info. We don't cache this since it may
50
+ # change if the user decides to change the path. This method
51
+ # also runs upon the first import of AgML to set it properly.
52
+ def _load_config_info():
53
+ global DATASET_SAVE_DIR, SYNTHETIC_SAVE_DIR, MODEL_SAVE_DIR
54
+ try:
55
+ with open(os.path.join(SUPER_BASE_DIR, "config.json"), "r") as f:
56
+ contents = json.load(f)
57
+ DATASET_SAVE_DIR = contents["data_path"]
58
+ SYNTHETIC_SAVE_DIR = contents["synthetic_data_path"]
59
+ MODEL_SAVE_DIR = contents["model_path"]
60
+ except (OSError, KeyError):
61
+ with open(os.path.join(SUPER_BASE_DIR, "config.json"), "w") as f:
62
+ json.dump(
63
+ {
64
+ "data_path": os.path.join(SUPER_BASE_DIR, "datasets"),
65
+ "synthetic_data_path": os.path.join(SUPER_BASE_DIR, "synthetic"),
66
+ "model_path": os.path.join(SUPER_BASE_DIR, "models"),
67
+ },
68
+ f,
69
+ )
70
+ _load_config_info()
71
+
72
+
73
+ _load_config_info()
74
+
75
+
76
+ def data_save_path():
77
+ """Returns the default dataset save path for AgML."""
78
+ global DATASET_SAVE_DIR
79
+ return DATASET_SAVE_DIR
80
+
81
+
82
+ def set_data_save_path(location=None):
83
+ """Sets the default dataset save path for AgML.
84
+ Changing the data save path using this method permanently changes
85
+ the data save path for all future sessions, until it is changed
86
+ or switched back to the original. If you just want to download one
87
+ dataset to a different path, use the `dataset_path` argument.
88
+ Parameters
89
+ ----------
90
+ location : str
91
+ The location to save the data to.
92
+ Returns
93
+ -------
94
+ The fully expanded location.
95
+ """
96
+ global SUPER_BASE_DIR
97
+ if location is None or location == "reset":
98
+ location = os.path.join(SUPER_BASE_DIR, "datasets")
99
+ location = os.path.expanduser(location)
100
+ if not os.path.exists(location) and not os.path.isdir(location):
101
+ raise NotADirectoryError(f"The provided destination {location} does " f"not exist, or is not a directory.")
102
+ _update_config("data_path", os.path.realpath(os.path.abspath(location)))
103
+ return
104
+
105
+
106
+ def synthetic_data_save_path():
107
+ """Returns the default synthetic data save path for AgML."""
108
+ global SYNTHETIC_SAVE_DIR
109
+ return SYNTHETIC_SAVE_DIR
110
+
111
+
112
+ def set_synthetic_save_path(location=None):
113
+ """Sets the default synthetic data save path for AgML.
114
+ Changing the data save path using this method permanently changes
115
+ the data save path for all future sessions, until it is changed
116
+ or switched back to the original. If you just want to download one
117
+ dataset to a different path, use the `dataset_path` argument.
118
+ Parameters
119
+ ----------
120
+ location : str
121
+ The location to save the data to.
122
+ Returns
123
+ -------
124
+ The fully expanded location.
125
+ """
126
+ global SUPER_BASE_DIR
127
+ if location is None or location == "reset":
128
+ location = os.path.join(SUPER_BASE_DIR, "synthetic")
129
+ location = os.path.expanduser(location)
130
+ if not os.path.exists(location) and not os.path.isdir(location):
131
+ raise NotADirectoryError(f"The provided destination {location} does " f"not exist, or is not a directory.")
132
+ _update_config("synthetic_data_path", os.path.realpath(os.path.abspath(location)))
133
+ return
134
+
135
+
136
+ def model_save_path():
137
+ """Returns the default model save path for AgML."""
138
+ global MODEL_SAVE_DIR
139
+ return MODEL_SAVE_DIR
140
+
141
+
142
+ def set_model_save_path(location=None):
143
+ """Sets the default model save path for AgML.
144
+ Changing the data save path using this method permanently changes
145
+ the data save path for all future sessions, until it is changed
146
+ or switched back to the original.
147
+ Parameters
148
+ ----------
149
+ location : str
150
+ The location to save the model to.
151
+ Returns
152
+ -------
153
+ The fully expanded location.
154
+ """
155
+ global SUPER_BASE_DIR
156
+ if location is None or location == "reset":
157
+ location = os.path.join(SUPER_BASE_DIR, "models")
158
+ location = os.path.expanduser(location)
159
+ if not os.path.exists(location) and not os.path.isdir(location):
160
+ raise NotADirectoryError(f"The provided destination {location} does " f"not exist, or is not a directory.")
161
+ _update_config("model_path", os.path.realpath(os.path.abspath(location)))
162
+ return
163
+
164
+
165
+ def _get_config(param):
166
+ """Update the configuration file with a new parameter."""
167
+ global SUPER_BASE_DIR
168
+ with open(os.path.join(SUPER_BASE_DIR, "config.json"), "r") as f:
169
+ contents = json.load(f)
170
+ if param not in contents.keys():
171
+ return None
172
+ return contents[param]
173
+
174
+
175
+ def _update_config(param, value):
176
+ """Update the configuration file with a new parameter."""
177
+ global SUPER_BASE_DIR
178
+ with open(os.path.join(SUPER_BASE_DIR, "config.json"), "r") as f:
179
+ contents = json.load(f)
180
+ contents[param] = value
181
+ with open(os.path.join(SUPER_BASE_DIR, "config.json"), "w") as f:
182
+ json.dump(contents, f)
183
+
184
+
185
+ def clear_all_datasets():
186
+ """Deletes all of the datasets within the AgML local storage."""
187
+ log("Entering AgML interactive dataset deletion mode.", logging.WARNING)
188
+ msg_format = "\033[91m{0}\033[0m"
189
+ if not input(msg_format.format("Please confirm that you want to delete datasets: [y|n] ")) == "y":
190
+ print("Aborting dataset deletion.")
191
+ local_datasets = os.listdir(data_save_path())
192
+ deleted_datasets = []
193
+ for dataset in local_datasets:
194
+ if not os.path.isdir(os.path.join(data_save_path(), dataset)):
195
+ continue
196
+ if not input(msg_format.format(f"Delete dataset '{dataset}'? [y|n] ")) == "y":
197
+ continue
198
+ shutil.rmtree(os.path.join(data_save_path(), dataset))
199
+ deleted_datasets.append(dataset)
200
+ print(f"Deleted datasets {deleted_datasets}.")
201
+ log("Exiting AgML interactive dataset deletion mode.", logging.WARNING)
202
+
203
+
204
+ def downloaded_datasets():
205
+ """Lists downloaded datasets in ~/.agml/datasets"""
206
+ return [d for d in os.listdir(data_save_path()) if os.path.isdir(os.path.join(data_save_path(), d))]
AgML/source/agml/backend/experimental.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 UC Davis Plant AI and Biophysics Lab
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Experimental features in AgML, these may be added or removed permanently."""
15
+
16
+ from agml.framework import AgMLSerializable
17
+
18
+ __all__ = ["allow_nested_data_splitting"]
19
+
20
+
21
+ class AgMLExperimentalFeatureWrapper(AgMLSerializable):
22
+ """Stores all of the feature values."""
23
+
24
+ _NESTED_SPLITTING = False
25
+
26
+ def __init__(self):
27
+ raise ValueError("This class should not be instantiated!")
28
+
29
+ @classmethod
30
+ def allow_nested_data_splitting(cls, value: bool) -> None:
31
+ """Enables/disables nested splitting of `AgMLDataLoader`s.
32
+
33
+ This method can be used to either enable or disable a feature which
34
+ allows an sub-AgMLDataLoader which has been split to a custom data
35
+ split to be split again. This is an experimental feature, which
36
+ would allow for multiple levels of nested splits (e.g., you could
37
+ have a `loader.train_data.val_data.test_data`).
38
+
39
+ Parameters
40
+ ----------
41
+ value : bool
42
+ Whether to enable or disable the feature.
43
+
44
+ Notes
45
+ -----
46
+ This method must be called at the start of each script in order
47
+ to function, otherwise it will default to `False`.
48
+ """
49
+ if not isinstance(value, bool):
50
+ raise TypeError("Expected either True or False.")
51
+ cls._NESTED_SPLITTING = value
52
+
53
+ @classmethod
54
+ def nested_splitting(cls):
55
+ return cls._NESTED_SPLITTING
56
+
57
+
58
+ # While the `AgMLExperimentalFeatureWrapper` class controls the actual values,
59
+ # we expose each of its toggle methods as part of the `agml.backend` API.
60
+ allow_nested_data_splitting = AgMLExperimentalFeatureWrapper.allow_nested_data_splitting
AgML/source/agml/backend/random.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 UC Davis Plant AI and Biophysics Lab
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from agml.backend.tftorch import get_backend, tf, torch
16
+
17
+
18
+ def set_seed(seed=None):
19
+ """Sets a new random seed. If None, uses a random seed."""
20
+ import random
21
+
22
+ random.seed(seed)
23
+ import numpy as np
24
+
25
+ np.random.seed(seed)
26
+ if get_backend() == "torch":
27
+ torch.random.manual_seed(seed)
28
+ elif get_backend() == "tensorflow":
29
+ tf.random.set_seed(seed)
AgML/source/agml/backend/tftorch.py ADDED
@@ -0,0 +1,334 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 UC Davis Plant AI and Biophysics Lab
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """
16
+ This part of the backend controls the AgML methods where either
17
+ TensorFlow or PyTorch methods can be used, and prevents unnecessary
18
+ importing of either library (which takes a significant amount of time).
19
+ """
20
+
21
+ import functools
22
+ import importlib
23
+ import inspect
24
+ import logging
25
+ import types
26
+
27
+ # Suppress any irrelevant warnings which will pop up from either backend.
28
+ import warnings
29
+
30
+ import numpy as np
31
+
32
+ from agml.utils.image import consistent_shapes
33
+ from agml.utils.logging import log
34
+
35
+ warnings.filterwarnings("ignore", category=UserWarning, message=".*Named tensors.*Triggered internally.*")
36
+
37
+
38
+ class StrictBackendError(ValueError):
39
+ def __init__(self, message=None, change=None, obj=None):
40
+ if message is None:
41
+ message = (
42
+ f"Backend was manually set to "
43
+ f"'{get_backend()}', but got an object "
44
+ f"from backend '{change}': {obj}."
45
+ )
46
+ super(StrictBackendError, self).__init__(message)
47
+
48
+
49
+ # Check if TensorFlow and PyTorch exist in the environment.
50
+ _HAS_TENSORFLOW: bool
51
+ _HAS_TORCH: bool
52
+
53
+
54
+ @functools.lru_cache(maxsize=None)
55
+ def _check_tf_torch():
56
+ global _HAS_TENSORFLOW, _HAS_TORCH
57
+ try:
58
+ import tensorflow
59
+ except ImportError:
60
+ _HAS_TENSORFlOW = False
61
+ else:
62
+ _HAS_TENSORFLOW = True
63
+ try:
64
+ import torch
65
+ except ImportError:
66
+ _HAS_TORCH = False
67
+ else:
68
+ _HAS_TORCH = True
69
+
70
+
71
+ # Default backend is PyTorch.
72
+ _BACKEND = None
73
+ _USER_SET_BACKEND = False
74
+
75
+
76
+ def get_backend():
77
+ """Returns the current AgML backend."""
78
+ return _BACKEND
79
+
80
+
81
+ def set_backend(backend):
82
+ """Change the AgML backend for the current session.
83
+
84
+ By default, AgML uses PyTorch as a backend, but it is
85
+ compatible with both TensorFlow and PyTorch. AgML can
86
+ also automatically inference the backend from the
87
+ different parameters passed into `AgMLDataLoader` and
88
+ other internal library methods.
89
+
90
+ This method allows a user to automatically set the backend.
91
+ """
92
+ global _USER_SET_BACKEND, _BACKEND
93
+ # Check whether the user has modified the backend.
94
+ mod = inspect.getmodule(inspect.stack()[1][0])
95
+ if mod is None: # IPython shell
96
+ _USER_SET_BACKEND = True
97
+ elif "agml." not in mod.__name__:
98
+ _USER_SET_BACKEND = True
99
+
100
+ # If the backend is the same, don't do anything.
101
+ if backend == _BACKEND:
102
+ return
103
+
104
+ _check_tf_torch()
105
+ if backend not in ["tensorflow", "tf", "torch", "pytorch"]:
106
+ raise ValueError(f"Invalid backend: {backend}.")
107
+ if backend in ["tensorflow", "tf"] and _BACKEND != "tensorflow":
108
+ if not _HAS_TENSORFLOW:
109
+ raise ImportError(
110
+ "TensorFlow not found on system, cannot be used as " "backend. Try running `pip install tensorflow`."
111
+ )
112
+ _BACKEND = "tf"
113
+ log("Switched backend to TensorFlow.", level=logging.INFO)
114
+ elif backend in ["torch", "pytorch"] and _BACKEND != "torch":
115
+ if not _HAS_TORCH:
116
+ raise ImportError(
117
+ "PyTorch not found on system, cannot be used as " "backend. Try running `pip install torch`."
118
+ )
119
+ _BACKEND = "torch"
120
+ log("Switched backend to PyTorch.", level=logging.INFO)
121
+
122
+
123
+ def user_changed_backend():
124
+ """Returns whether the backend has been manually changed."""
125
+ return _USER_SET_BACKEND
126
+
127
+
128
+ # Ported from https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/util/lazy_loader.py
129
+ class LazyLoader(types.ModuleType):
130
+ """Lazily import a module, mainly to avoid pulling in large dependencies."""
131
+
132
+ def __init__(self, local_name, parent_module_globals, name):
133
+ self._local_name = local_name
134
+ self._parent_module_globals = parent_module_globals
135
+ super(LazyLoader, self).__init__(name)
136
+
137
+ def _load(self):
138
+ """Load the module and insert it into the parent's globals."""
139
+ # Import the target module and insert it into the parent's namespace.
140
+ module = importlib.import_module(self.__name__)
141
+ self._parent_module_globals[self._local_name] = module
142
+
143
+ # Update this object's dict so that if someone keeps a reference to the
144
+ # LazyLoader, lookups are efficient (__getattr__ is only called on lookups
145
+ # that fail).
146
+ self.__dict__.update(module.__dict__)
147
+ return module
148
+
149
+ def __getattr__(self, item):
150
+ module = self._load()
151
+ return getattr(module, item)
152
+
153
+ def __dir__(self):
154
+ module = self._load()
155
+ return dir(module)
156
+
157
+
158
+ # Load TensorFlow and PyTorch lazily to prevent pulling them in when unnecessary.
159
+ torch = LazyLoader("torch", globals(), "torch")
160
+ torch_data = LazyLoader("torch_data", globals(), "torch.utils.data")
161
+ torchvision = LazyLoader("torchvision", globals(), "torchvision")
162
+ tf = LazyLoader("tensorflow", globals(), "tensorflow")
163
+
164
+
165
+ ######### GENERAL METHODS #########
166
+
167
+
168
+ def _convert_image_to_torch(image):
169
+ """Converts an image (np.ndarray) to a torch Tensor."""
170
+ if isinstance(image, (list, tuple)):
171
+ return torch.tensor(image)
172
+ if isinstance(image, torch.Tensor) or image.ndim == 4:
173
+ if image.shape[0] == 1 and image.shape[-1] <= 3 and image.ndim == 4:
174
+ return torch.from_numpy(image).permute(0, 3, 1, 2).float()
175
+ return image
176
+ if image.ndim == 3:
177
+ if image.shape[0] > image.shape[-1]:
178
+ return torch.from_numpy(image).permute(2, 0, 1).float()
179
+ elif image.ndim == 2:
180
+ return torch.from_numpy(image)
181
+ return torch.from_numpy(image).float()
182
+
183
+
184
+ def _postprocess_torch_annotation(image):
185
+ """Post-processes a spatially augmented torch annotation."""
186
+ try:
187
+ if image.dtype.is_floating_point:
188
+ image = (image * 255).int()
189
+ except AttributeError:
190
+ pass
191
+ return image
192
+
193
+
194
+ def as_scalar(inp):
195
+ """Converts an input value to a scalar."""
196
+ if isinstance(inp, (int, float)):
197
+ return inp
198
+ if np.isscalar(inp):
199
+ return inp.item()
200
+ if isinstance(inp, np.ndarray):
201
+ return inp.item()
202
+ if isinstance(inp, torch.Tensor):
203
+ return inp.item()
204
+ if isinstance(inp, tf.Tensor):
205
+ return inp.numpy()
206
+ raise TypeError(f"Unsupported variable type {type(inp)}.")
207
+
208
+
209
+ def scalar_unpack(inp):
210
+ """Unpacks a 1-d array into a list of scalars."""
211
+ return [as_scalar(item) for item in inp]
212
+
213
+
214
+ def is_array_like(inp, no_list=False):
215
+ """Determines if an input is a np.ndarray, torch.Tensor, or tf.Tensor."""
216
+ if isinstance(inp, (list, tuple)): # no need to import tensorflow for this
217
+ if no_list:
218
+ return False
219
+ return True
220
+ if isinstance(inp, np.ndarray):
221
+ return True
222
+ if isinstance(inp, torch.Tensor):
223
+ return True
224
+ if isinstance(inp, tf.Tensor):
225
+ return True
226
+ return False
227
+
228
+
229
+ def convert_to_batch(images):
230
+ """Converts a set of images to a batch."""
231
+ # If `images` is already an array type, nothing to do.
232
+ if is_array_like(images):
233
+ return images
234
+
235
+ # NumPy Arrays.
236
+ if isinstance(images[0], np.ndarray):
237
+ if not consistent_shapes(images):
238
+ images = np.array(images, dtype=object)
239
+ log(
240
+ "Created a batch of images with different "
241
+ "shapes. If you want the shapes to be consistent, "
242
+ "run `loader.resize_images('auto')`."
243
+ )
244
+ else:
245
+ images = np.array(images)
246
+ return images
247
+
248
+ # Torch Tensors.
249
+ if isinstance(images[0], torch.Tensor):
250
+ if not consistent_shapes(images):
251
+ images = [image.numpy() for image in images]
252
+ images = np.array(images, dtype=object)
253
+ log(
254
+ "Created a batch of images with different "
255
+ "shapes. If you want the shapes to be consistent, "
256
+ "run `loader.resize_images('auto')`."
257
+ )
258
+ else:
259
+ images = torch.stack(images)
260
+ return images
261
+
262
+ # TensorFlow Tensors.
263
+ if isinstance(images[0], tf.Tensor):
264
+ if not consistent_shapes(images):
265
+ images = tf.ragged.stack(images)
266
+ log(
267
+ "Created a batch of images with different "
268
+ "shapes. If you want the shapes to be consistent, "
269
+ "run `loader.resize_images('auto')`."
270
+ )
271
+ else:
272
+ images = tf.stack(images)
273
+ return images
274
+
275
+
276
+ ######### AGMLDATALOADER METHODS #########
277
+
278
+
279
+ class AgMLObject(object):
280
+ """Base class for the `AgMLDataLoader` to enable inheritance.
281
+
282
+ This class solves a bug which arises when trying to dynamically
283
+ inherit from `tf.keras.utils.Sequence` and/or `torch.utils.data.Dataset`.
284
+ The fact that the `AgMLDataLoader` has this `AgMLObject` as a subclass
285
+ enables it to be able to handle dynamic inheritance. This is the sole
286
+ purpose of this subclass, it does not have any features.
287
+ """
288
+
289
+
290
+ def _add_dataset_to_mro(inst, mode):
291
+ """Adds the relevant backend class to the `AgMLDataLoader` MRO.
292
+
293
+ This allows for the loader to dynamically inherent from the
294
+ `tf.keras.utils.Sequence` and `torch.utils.data.Dataset`.
295
+ """
296
+ if mode == "tf":
297
+ if not get_backend() == "tf":
298
+ if user_changed_backend():
299
+ raise StrictBackendError(change="tf", obj=inst)
300
+ set_backend("tf")
301
+ if tf.keras.utils.Sequence not in inst.__class__.__bases__:
302
+ inst.__class__.__bases__ += (tf.keras.utils.Sequence,)
303
+ if mode == "torch":
304
+ if not get_backend() == "torch":
305
+ if user_changed_backend():
306
+ raise StrictBackendError(change="torch", obj=inst)
307
+ if torch_data.Dataset not in inst.__class__.__bases__:
308
+ inst.__class__.__bases__ += (torch_data.Dataset,)
309
+
310
+
311
+ def collate_fn_basic(batch):
312
+ images = torch.stack([i[0] for i in batch], dim=0)
313
+ coco = tuple(zip(*[i[1] for i in batch]))
314
+ return images, coco
315
+
316
+
317
+ def collate_fn_efficientdet(batch):
318
+ """Collates items together into a batch."""
319
+ images, targets = tuple(zip(*batch))
320
+ images = torch.stack(images)
321
+ images = images.float()
322
+
323
+ boxes = [target["bboxes"].float() for target in targets]
324
+ labels = [target["labels"].float() for target in targets]
325
+ img_size = torch.stack([target["img_size"] for target in targets]).float()
326
+ img_scale = torch.stack([target["img_scale"] for target in targets]).float()
327
+
328
+ annotations = {
329
+ "bbox": boxes,
330
+ "cls": labels,
331
+ "img_size": img_size,
332
+ "img_scale": img_scale,
333
+ }
334
+ return images, annotations, targets
AgML/source/agml/data/__init__.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 UC Davis Plant AI and Biophysics Lab
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from . import experimental, exporters, extensions
16
+ from .image_loader import ImageLoader
17
+ from .loader import AgMLDataLoader
18
+ from .point_cloud import PointCloud
19
+ from .public import download_public_dataset, public_data_sources, source
20
+ from .tools import coco_to_bboxes, convert_bbox_format
AgML/source/agml/data/builder.py ADDED
@@ -0,0 +1,326 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 UC Davis Plant AI and Biophysics Lab
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import json
16
+ import os
17
+ import re
18
+ import sys
19
+
20
+ from agml.backend.config import data_save_path
21
+ from agml.framework import AgMLSerializable
22
+ from agml.utils.downloads import download_dataset
23
+ from agml.utils.io import get_dir_list, get_file_list, is_image_file
24
+
25
+
26
+ class DataBuilder(AgMLSerializable):
27
+ """Builds an internal representation format of AgML data.
28
+
29
+ This class doesn't affect the state of the `AgMLDataLoader`, but
30
+ instead generates an internal representation of the data content
31
+ of a dataset. This allows for all data to be loaded in a standard
32
+ format and streamlined into the `DataManager`.
33
+
34
+ Primarily, this class attempts to locate the dataset and if unable
35
+ to do so, it downloads the dataset from the public bucket. Then,
36
+ it creates a mapping between the images and annotations which is
37
+ used by the `DataManager` inside the `AgMLDataLoader`.
38
+ """
39
+
40
+ serializable = frozenset(("name", "labels_for_image", "data", "info", "dataset_root", "data_length"))
41
+
42
+ def __init__(self, info, dataset_path, overwrite):
43
+ # Attempt to locate or download the dataset.
44
+ self._info = info
45
+ self._name = info.name
46
+ self._configure_dataset(dataset_path=dataset_path, overwrite=overwrite)
47
+ self._data = None
48
+ self._data_length = None
49
+ self._labels_for_image = None
50
+
51
+ @classmethod
52
+ def from_data(cls, contents, info, root, builder=None):
53
+ """Initializes the `DataBuilder` from a pre-built set of data.
54
+
55
+ This is mainly used when running `AgMLDataLoader.split`, to create
56
+ a `DataManager` with a split of data. Functionally, it disables
57
+ all of the actual generation protocols of the `DataBuilder` and
58
+ just pre-assigns all of the values to it.
59
+ """
60
+ obj = super(DataBuilder, cls).__new__(cls)
61
+ obj._name = info.name
62
+ obj._info = info
63
+ obj._dataset_root = root
64
+ obj._data = contents[0]
65
+ obj._data_length = len(obj._data)
66
+ obj._labels_for_image = contents[1]
67
+ if builder is not None:
68
+ if hasattr(builder, "_default_coco_annotations"):
69
+ obj._default_coco_annotations = DataBuilder._regenerate_coco_annotations(
70
+ builder._default_coco_annotations, obj._data
71
+ )
72
+ return obj
73
+
74
+ @staticmethod
75
+ def _regenerate_coco_annotations(coco, data):
76
+ """Regenerates COCO annotations from a reduced set."""
77
+ image_coco = []
78
+ annotation_coco = []
79
+ image_id_tracker = 0
80
+
81
+ # Update the list of image paths.
82
+ for c in coco["images"]:
83
+ if c["file_name"] in list(data.keys()):
84
+ c["image_id"] = image_id_tracker
85
+ image_coco.append(c)
86
+ image_id_tracker += 1
87
+
88
+ # Update the image IDs for the annotations
89
+ for image_id, (_, annotation) in enumerate(data.items()):
90
+ for a in annotation:
91
+ a["id"] = image_id
92
+ annotation_coco.extend(annotation)
93
+
94
+ # Update the complete COCO dictionary.
95
+ ret_coco = coco.copy()
96
+ ret_coco["images"] = image_coco
97
+ ret_coco["annotations"] = annotation_coco
98
+ return ret_coco
99
+
100
+ @property
101
+ def dataset_root(self):
102
+ return self._dataset_root
103
+
104
+ def _configure_dataset(self, **kwargs):
105
+ """Finds and configures the dataset into the loader."""
106
+ # Check if the user wants to overwrite the existing dataset,
107
+ # and resolve the potentially provided custom dataset path.
108
+ overwrite = kwargs.get("overwrite", False)
109
+ if kwargs.get("dataset_path", None):
110
+ kwargs["dataset_path"] = os.path.realpath(os.path.expanduser(kwargs["dataset_path"]))
111
+
112
+ # If a custom dataset has been provided, then we assume the directory
113
+ # provided is the root of the dataset, e.g., if /root/datasets/dir
114
+ # is provided, then dir doesn't necessarily need to be the name of
115
+ # the dataset, but it is assumed to be its root. Note that if no path
116
+ # is given, then we check similarly to the default path for datasets,
117
+ # e.g., if there is a directory with the provided name in the custom path.
118
+ #
119
+ # No downloading phase for custom datasets, as there is nothing to do.
120
+ if self._info.is_custom_dataset:
121
+ if kwargs.get("dataset_path", False):
122
+ path = kwargs.get("dataset_path")
123
+ if os.path.exists(path):
124
+ self._dataset_root = path
125
+ return
126
+
127
+ elif os.path.exists(os.path.join(data_save_path(), self._name)):
128
+ self._dataset_root = os.path.join(data_save_path(), self._name)
129
+ return
130
+
131
+ # Otherwise, we don't know what to do.
132
+ raise OSError(
133
+ f"Could not find a directory for the dataset {self._name}. "
134
+ f"Dataset path given: {kwargs.get('dataset_path', None)}"
135
+ )
136
+
137
+ # If the user doesn't want to overwrite the existing contents,
138
+ # first check whether the dataset already exists. If so, then set
139
+ # the dataset root and return without doing any downloading.
140
+ #
141
+ # Note that the `dataset_path` is resolved as follows: If the path
142
+ # ends with the name of the dataset, e.g., `/root/datasets/<name>`,
143
+ # then we pop '<name>' from the end of the path in order to prevent
144
+ # the dataset from being downloaded to `/root/datasets/<name>/<name>`.
145
+ if not overwrite:
146
+ if kwargs.get("dataset_path", False):
147
+ path = kwargs.get("dataset_path")
148
+ if (os.path.basename(path) == self._name and os.path.exists(path)) or os.path.exists(
149
+ os.path.join(path, self._name)
150
+ ):
151
+ if os.path.basename(path) != self._name:
152
+ path = os.path.join(path, self._name)
153
+ self._dataset_root = path
154
+ return
155
+
156
+ elif os.path.exists(os.path.join(data_save_path(), self._name)):
157
+ self._dataset_root = os.path.join(data_save_path(), self._name)
158
+ return
159
+
160
+ # If the user wants to overwrite, or the dataset doesn't exist
161
+ # at the path, then we create the root in the same way, except
162
+ # we just also download the dataset to the path.
163
+ else:
164
+ if kwargs.get("dataset_path", False):
165
+ path = kwargs.get("dataset_path")
166
+ if (os.path.basename(path) == self._name and os.path.exists(path)) or os.path.exists(
167
+ os.path.join(path, self._name)
168
+ ):
169
+ if os.path.basename(path) != self._name:
170
+ path = os.path.join(path, self._name)
171
+ print(f"[AgML Download]: Overwriting dataset at " f"{os.path.join(path)}")
172
+ if os.path.basename(path) != self._name:
173
+ path = os.path.join(path, self._name)
174
+ self._dataset_root = path
175
+
176
+ elif os.path.exists(os.path.join(data_save_path(), self._name)):
177
+ self._dataset_root = os.path.join(data_save_path(), self._name)
178
+ sys.stderr.write(f"[AgML Download]: Overwriting dataset at " f"{os.path.join(self._dataset_root)}")
179
+
180
+ # Performs the actual downloading of the dataset.
181
+ if kwargs.get("dataset_path", False):
182
+ download_path = kwargs["dataset_path"]
183
+ if os.path.basename(download_path) != self._name:
184
+ download_path = os.path.join(download_path, self._name)
185
+ else:
186
+ download_path = os.path.join(data_save_path(), self._name)
187
+ sys.stderr.write(f"[AgML Download]: Downloading dataset " f"`{self._name}` to {download_path}.")
188
+ download_dataset(self._name, download_path)
189
+ self._dataset_root = download_path
190
+
191
+ def _generate_contents(self, task):
192
+ """Dispatches to a content generation method for the provided task."""
193
+ if self._data is not None:
194
+ return
195
+ if task == "image_classification":
196
+ self._generate_image_classification_data()
197
+ elif task == "image_regression":
198
+ self._generate_image_regression_data()
199
+ elif task == "object_detection":
200
+ self._generate_object_detection_data()
201
+ else:
202
+ self._generate_semantic_segmentation_data()
203
+
204
+ def get_contents(self):
205
+ """Extracts the internal representation of the data content."""
206
+ # Create the internal content representation of the dataset.
207
+ self._generate_contents(self._info.tasks.ml)
208
+
209
+ # Update the length of the data in the metadata dictionary for
210
+ # custom datasets, since they may not be providing the total number.
211
+ self._data_length = len(self._data)
212
+ if self._info._metadata.get("n_images", None) is None:
213
+ self._info._metadata["n_images"] = str(self._data_length)
214
+ return self._data
215
+
216
+ def export_contents(self, export_format):
217
+ """Returns the raw contents of the loader."""
218
+ # We start by constructing a default mapping.
219
+ contents = self.get_contents()
220
+
221
+ # For a COCO JSON dictionary, we have to make the full paths.
222
+ if self._info.tasks.ml == "object_detection":
223
+ paths, coco = contents.keys(), contents.values()
224
+ paths = [os.path.join(self._dataset_root, "images", i) for i in paths]
225
+ contents = dict({k: v for k, v in zip(paths, coco)})
226
+
227
+ # If the export format is `None`, we return the default mapping.
228
+ if export_format is None:
229
+ return contents
230
+
231
+ # If the export format is `arrays`, return the keys and
232
+ # the values of the mapping as two independent arrays.
233
+ if export_format == "arrays":
234
+ return list(contents.keys()), list(contents.values())
235
+
236
+ # A special case for COCO JSON dictionaries.
237
+ if export_format == "coco":
238
+ if self._info.tasks.ml != "object_detection":
239
+ raise ValueError("The `coco` export format is " "only for object detection tasks.")
240
+ return self._default_coco_annotations
241
+
242
+ # The following methods actually generate the content mappings for
243
+ # the different tasks. In essence, for each image path, `image`, a
244
+ # mapping is generated with a corresponding annotation, such as a
245
+ # label for image classification, mask for semantic segmentation, or
246
+ # a COCO JSON dictionary for object detection.
247
+
248
+ def _generate_image_classification_data(self):
249
+ """Loads image classification data for the `directory_names` format.
250
+
251
+ In this format, images are organized by class where the directory
252
+ they are placed in corresponds to their label in the dataset.
253
+ """
254
+ image_label_mapping = {}
255
+ candidate_dirs = get_dir_list(self._dataset_root)
256
+ for dir_ in candidate_dirs:
257
+ if dir_.startswith("."):
258
+ continue
259
+ dir_path = os.path.join(self._dataset_root, dir_)
260
+ if len(get_file_list(dir_path)) == 0:
261
+ continue
262
+ for file_ in get_file_list(dir_path):
263
+ file_ = os.path.join(dir_path, file_)
264
+ image_label_mapping[file_] = self._info.class_to_num[dir_]
265
+ self._data = image_label_mapping
266
+
267
+ def _generate_image_regression_data(self):
268
+ """Loads image regression data for the loader.
269
+
270
+ In this format, there are input images in an `images` folder as
271
+ well as other image formats in other various `*_images` folders,
272
+ and an `annotations.json` file containing the regression outputs.
273
+ """
274
+ with open(os.path.join(self._dataset_root, "annotations.json"), "r") as f:
275
+ annotations = json.load(f)
276
+ content_mapping = {"inputs": [], "outputs": []}
277
+ annotation_types = set(list(self._info.class_to_num.keys()))
278
+ annotation_types.remove("regression")
279
+ for sample in annotations:
280
+ for k, v in sample.items():
281
+ if is_image_file(v):
282
+ sample[k] = os.path.join(self._dataset_root, f"{k}s", v)
283
+ content_mapping["inputs"].append({k: v for k, v in sample.items() if re.match("(.*?)image", k)})
284
+ out = {"regression": list(sample["outputs"]["regression"].values())}
285
+ out.update(
286
+ {k: self._info.class_to_num[k][v] for k, v in sample["outputs"].items() if k in annotation_types}
287
+ )
288
+ content_mapping["outputs"].append(out)
289
+ self._data = content_mapping
290
+
291
+ def _generate_semantic_segmentation_data(self):
292
+ """Loads semantic segmentation data for the loader.
293
+
294
+ Image data is loaded from an `images` directory, and pixel-wise
295
+ annotated images are loaded from an `annotations` directory.
296
+ """
297
+ image_dir = os.path.join(self._dataset_root, "images")
298
+ annotation_dir = os.path.join(self._dataset_root, "annotations")
299
+ images, annotations = (
300
+ sorted(get_file_list(image_dir)),
301
+ sorted(get_file_list(annotation_dir)),
302
+ )
303
+ image_annotation_map = {}
304
+ for image_path, annotation_path in zip(images, annotations):
305
+ image_annotation_map[os.path.join(image_dir, image_path)] = os.path.join(annotation_dir, annotation_path)
306
+ self._data = image_annotation_map
307
+
308
+ def _generate_object_detection_data(self):
309
+ """Generates object detection data for the loader.
310
+
311
+ Image data is loaded from an `images` directory, and the COCO
312
+ JSON annotations are loaded from an `annotations.json` file.
313
+ """
314
+ with open(os.path.join(self._dataset_root, "annotations.json")) as f:
315
+ self._default_coco_annotations = json.load(f)
316
+ coco_annotations = self._default_coco_annotations
317
+ image_id_mapping = {}
318
+ for img_meta in coco_annotations["images"]:
319
+ image_id_mapping[img_meta["id"]] = img_meta["file_name"]
320
+ coco_map = {fname: [] for fname in image_id_mapping.values()}
321
+ image_category_map = {k: [] for k in coco_map.keys()}
322
+ for a_meta in coco_annotations["annotations"]:
323
+ image_category_map[image_id_mapping[a_meta["image_id"]]].append(a_meta["category_id"])
324
+ coco_map[image_id_mapping[a_meta["image_id"]]].append(a_meta)
325
+ self._labels_for_image = image_category_map
326
+ self._data = coco_map
AgML/source/agml/data/experimental.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 UC Davis Plant AI and Biophysics Lab
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """
16
+ Experimental data utilities that are in development.
17
+ """
18
+
19
+ import functools
20
+
21
+ import numpy as np
22
+
23
+ __all__ = ["generate_keras_segmentation_dual_transform"]
24
+
25
+
26
+ def generate_keras_segmentation_dual_transform(*layers):
27
+ """Generates a `dual_transform` pipeline from Keras preprocessing layers.
28
+
29
+ This method takes in Keras preprocessing layers and generates a
30
+ transformation pipeline for the `dual_transform` argument in
31
+ *semantic segmentation* loaders, which applies the transform in the
32
+ same fashion to both the image and annotation.
33
+
34
+ This is due to the fact that TensorFlow has its operation-level
35
+ random states different than its module-level random state, so
36
+ the layers need to have their seeds manually set in order to work.
37
+
38
+ In essence, for each of the preprocessing layers passed, this
39
+ method conducts the following operations:
40
+
41
+ > def preprocessing_transform(image, annotation):
42
+ > layer = functools.partial(KerasPreprocessingLayer, **kwargs)
43
+ > seed = np.random.randint(BUFFER_SIZE) # up to sys.maxsize
44
+ > image = layer(image, seed = seed)
45
+ > annotation = layer(annotation, seed = seed)
46
+ > return image, annotation
47
+
48
+ It then repeats this transform for all of the preprocessing layers
49
+ passed, and returns a method which has this behavior wrapped into
50
+ it and can perform it when the preprocessing is actually conducted.
51
+
52
+ Parameters
53
+ ----------
54
+ layers : Any
55
+ Either a Sequential model with preprocessing layers, or a
56
+ set of instantiated preprocessing layers.
57
+
58
+ Returns
59
+ -------
60
+ """
61
+ import tensorflow as tf
62
+
63
+ if len(layers) == 1:
64
+ if isinstance(layers[0], tf.keras.Sequential):
65
+ layers = layers[0].layers
66
+
67
+ # These methods perform the behavior indicated in the
68
+ # code snippet above (for each of the layers given).
69
+ def _single_preprocessing_layer_base(layer_, build_dict):
70
+ def _internal(image, annotation, seed):
71
+ instantiated_layer = functools.partial(layer_, **build_dict)
72
+ seed_update = {}
73
+ if seed is not None:
74
+ seed_update["seed"] = seed
75
+ image = instantiated_layer(**seed_update)(image)
76
+ annotation = instantiated_layer(**seed_update)(annotation)
77
+ return image, annotation
78
+
79
+ return _internal
80
+
81
+ preprocessing_methods, use_seeds = [], []
82
+ for layer in layers:
83
+ config = layer.get_config()
84
+ if "seed" in config:
85
+ config.pop("seed")
86
+ use_seeds.append(True)
87
+ else:
88
+ use_seeds.append(False)
89
+ preprocessing_methods.append(_single_preprocessing_layer_base(layer.__class__, config))
90
+
91
+ def _execute_preprocessing(layers_, use_seeds_):
92
+ def _execute(image, annotation):
93
+ for p_layer, seed_ in zip(layers_, use_seeds_):
94
+ seed = np.random.randint(2147483647) if seed_ else None
95
+ image, annotation = p_layer(image, annotation, seed=seed)
96
+ return image, annotation
97
+
98
+ return _execute
99
+
100
+ return _execute_preprocessing(preprocessing_methods, use_seeds)
AgML/source/agml/data/exporters/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .yolo import export_yolo
AgML/source/agml/data/exporters/tensorflow.py ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 UC Davis Plant AI and Biophysics Lab
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from agml.backend.tftorch import tf
16
+ from agml.data.managers.training import TrainState
17
+ from agml.data.object import DataObject
18
+ from agml.utils.logging import log
19
+
20
+
21
+ class TFExporter(object):
22
+ """Exports an `AgMLDataLoader` as a `tf.data.Dataset`.
23
+
24
+ This class manages the methods and conversions between `AgMLDataLoader`
25
+ parameters, managers, and methods, into TensorFlow native methods.
26
+ """
27
+
28
+ def __init__(self, task, builder):
29
+ self._task = task
30
+ self._builder = builder
31
+
32
+ def digest_transforms(self, transforms, resizing):
33
+ """Parses the transforms for the `AgMLDataLoader`."""
34
+ self._transforms = {k: state for k, state in transforms.items()}
35
+ self._size = resizing if resizing is not None else (512, 512)
36
+
37
+ def assign_state(self, state):
38
+ """Updates the training state for the `tf.data.Dataset`."""
39
+ if state in [
40
+ TrainState.EVAL,
41
+ TrainState.EVAL_TF,
42
+ TrainState.EVAL_TORCH,
43
+ TrainState.FALSE,
44
+ ]:
45
+ self._state = "eval"
46
+ else:
47
+ self._state = "train"
48
+
49
+ def _build_from_contents_by_type(self, builder):
50
+ """Parses the provided mapping into a valid set of contents."""
51
+ if self._task == "image_classification":
52
+ ds = self._build_image_classification(builder)
53
+ return ds.map(self._image_classification_load)
54
+ elif self._task == "semantic_segmentation":
55
+ ds = self._build_semantic_segmentation(builder)
56
+ return ds.map(self._semantic_segmentation_load)
57
+ else:
58
+ ds = self._build_object_detection(builder)
59
+ return ds.map(self._object_detection_load)
60
+
61
+ def _apply_resizing_by_type(self, ds):
62
+ """Applies resizing based on the task."""
63
+ if self._task == "image_classification":
64
+ return ds.map(self._image_classification_resize)
65
+ elif self._task == "semantic_segmentation":
66
+ return ds.map(self._semantic_segmentation_resize)
67
+ else:
68
+ return ds.map(self._object_detection_resize)
69
+
70
+ def build(self, batch_size=None):
71
+ """Builds the `tf.data.Dataset` using the provided parameters."""
72
+ # Construct the dataset from the contents.
73
+ ds = self._build_from_contents_by_type(self._builder)
74
+
75
+ # Apply the digested transforms and resizing.
76
+ ds = self._apply_resizing_by_type(ds)
77
+ if self._state != "eval":
78
+ # No transforms for object detection, since it is near impossible
79
+ # for TensorFlow's graph mode to use COCO JSON dictionaries.
80
+ if self._task == "object_detection":
81
+ if len(self._transforms) != 0:
82
+ log(
83
+ "Got transforms when exporting an `AgMLDataLoader`"
84
+ "to a `tf.data.Dataset`. These transforms will not be "
85
+ "applied. To use transforms in TensorFlow, use the "
86
+ "`as_keras_sequence()` method instead."
87
+ )
88
+ else:
89
+ tfm = self._apply_transforms
90
+ if len(self._transforms) != 0:
91
+ ds = ds.map(tfm)
92
+
93
+ # Apply batching and prefetching, then return the dataset.
94
+ if batch_size is not None:
95
+ ds = ds.batch(batch_size)
96
+ ds = ds.prefetch(batch_size)
97
+ return ds
98
+
99
+ # The following methods are used to parse the input
100
+ # contents into valid methods for the loaders.
101
+
102
+ @staticmethod
103
+ def _build_image_classification(builder):
104
+ images, labels = builder.export_contents(export_format="arrays")
105
+ images, labels = tf.constant(images), tf.constant(labels)
106
+ ds = tf.data.Dataset.from_tensor_slices((images, labels))
107
+ return ds.shuffle(len(images))
108
+
109
+ @staticmethod
110
+ def _build_semantic_segmentation(builder):
111
+ images, masks = builder.export_contents(export_format="arrays")
112
+ images, masks = tf.constant(images), tf.constant(masks)
113
+ ds = tf.data.Dataset.from_tensor_slices((images, masks))
114
+ return ds.shuffle(len(images))
115
+
116
+ @staticmethod
117
+ def _build_object_detection(builder):
118
+ images, annotations = builder.export_contents(export_format="arrays")
119
+ images = tf.constant(images)
120
+ processed_annotations = [DataObject._parse_coco(a) for a in annotations]
121
+ features = {
122
+ "bbox": [],
123
+ "category_id": [],
124
+ "area": [],
125
+ "image_id": [],
126
+ "iscrowd": [],
127
+ "segmentation": [],
128
+ }
129
+ for a_set in processed_annotations:
130
+ for feature in features.keys():
131
+ features[feature].append(a_set[feature]) # noqa
132
+ for feature in features.keys():
133
+ features[feature] = tf.ragged.constant(features[feature])
134
+ feature_ds = tf.data.Dataset.from_tensor_slices(features)
135
+ ds = tf.data.Dataset.zip((tf.data.Dataset.from_tensor_slices(images), feature_ds))
136
+ return ds.shuffle(len(images))
137
+
138
+ # The following methods are used for loading images and
139
+ # annotations for each of the different types of tasks.
140
+
141
+ @staticmethod
142
+ @tf.function
143
+ def _image_classification_load(image, label):
144
+ image = tf.cast(tf.image.decode_jpeg(tf.io.read_file(image)), tf.float32) / 255.0
145
+ return image, tf.convert_to_tensor(label)
146
+
147
+ @staticmethod
148
+ @tf.function
149
+ def _semantic_segmentation_load(image, mask):
150
+ image = tf.cast(tf.image.decode_jpeg(tf.io.read_file(image)), tf.float32) / 255.0
151
+ mask = tf.image.decode_jpeg(tf.io.read_file(mask))
152
+ return image, mask
153
+
154
+ @staticmethod
155
+ @tf.function
156
+ def _object_detection_load(image, coco):
157
+ image = tf.cast(tf.image.decode_jpeg(tf.io.read_file(image)), tf.float32) / 255.0
158
+ ret_coco = coco.copy()
159
+ for key in coco.keys():
160
+ try:
161
+ ret_coco[key] = coco[key].to_tensor()
162
+ except:
163
+ pass
164
+ return image, ret_coco
165
+
166
+ # The following methods apply resizing to the data.
167
+
168
+ def _image_classification_resize(self, image, label):
169
+ image = (tf.image.resize(image, self._size, method="nearest"), tf.float32)
170
+ return image, label
171
+
172
+ def _semantic_segmentation_resize(self, image, mask):
173
+ image = tf.cast(tf.image.resize(image, self._size, method="nearest"), tf.float32)
174
+ mask = tf.cast(tf.image.resize(mask, self._size, method="nearest"), tf.float32)
175
+ return image, mask
176
+
177
+ def _object_detection_resize(self, image, coco):
178
+ # Helper for the `tf.py_function` for object detection.
179
+ def _resize_image_and_bboxes(image, coco_boxes):
180
+ nonlocal size
181
+ y_scale, x_scale = image.shape[0:2]
182
+ stack_boxes = tf.stack(
183
+ [
184
+ coco_boxes[:, 0] / x_scale,
185
+ coco_boxes[:, 1] / y_scale,
186
+ coco_boxes[:, 2] / x_scale,
187
+ coco_boxes[:, 3] / y_scale,
188
+ ],
189
+ axis=-1,
190
+ )
191
+ image = tf.cast(tf.image.resize(image, size), tf.float32)
192
+ y_new, x_new = image.shape[0:2]
193
+ new_stack = tf.cast(
194
+ tf.stack(
195
+ [
196
+ stack_boxes[:, 0] * x_new,
197
+ stack_boxes[:, 1] * y_new,
198
+ stack_boxes[:, 2] * x_new,
199
+ stack_boxes[:, 3] * y_new,
200
+ ],
201
+ axis=-1,
202
+ ),
203
+ tf.int32,
204
+ )
205
+ areas = new_stack[:, 2] * new_stack[:, 3]
206
+ return image, new_stack, areas
207
+
208
+ # The actual resizing can't take place in graph mode, so we
209
+ # dispatch to a `tf.py_function` to do the resizing, then
210
+ # re-assign the values back to the COCO JSON dictionary.
211
+ size = self._size
212
+ image, ret_coco_boxes, ret_areas = tf.py_function(
213
+ _resize_image_and_bboxes,
214
+ [image, coco["bbox"]],
215
+ [tf.float32, tf.int32, tf.int32],
216
+ )
217
+ coco["bbox"] = ret_coco_boxes
218
+ coco["area"] = ret_areas
219
+ return image, coco
220
+
221
+ # The following method manages the application of transforms.
222
+
223
+ def _apply_transforms(self, image, annotation):
224
+ # Helper for the `tf.py_function` for most transforms.
225
+ def _py_apply(img, ann):
226
+ nonlocal transforms
227
+ img, ann = img.numpy(), ann.numpy()
228
+ for key, state in transforms.items():
229
+ if key == "transform":
230
+ for t in state:
231
+ img = t(img)
232
+ elif key == "target_transform":
233
+ for t in state:
234
+ ann = t(ann)
235
+ else:
236
+ for t in state:
237
+ img, ann = t(img, ann)
238
+
239
+ return img, ann
240
+
241
+ # The actual transforming can't take place in graph mode
242
+ # (in most cases), so we dispatch and reassign.
243
+ transforms = self._transforms
244
+ image, annotation = tf.py_function(_py_apply, [image, annotation], [tf.float32, tf.int32])
245
+ return image, annotation
AgML/source/agml/data/exporters/yolo.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import shutil
3
+
4
+ import yaml
5
+
6
+ import agml
7
+ from agml.utils.general import flatten
8
+ from agml.utils.logging import log
9
+
10
+
11
+ def convert_annotations_to_yolo_list(annotation_dict, image_shape, annotation_remapper):
12
+ yolo_list = []
13
+ height, width = image_shape
14
+ for annotation in annotation_dict:
15
+ x, y, w, h = annotation["bbox"]
16
+ x_center = x + w / 2
17
+ y_center = y + h / 2
18
+
19
+ # scale XYWH to [0, 1]
20
+ yolo_list.append(
21
+ [
22
+ annotation_remapper[annotation["category_id"]],
23
+ x_center / width,
24
+ y_center / height,
25
+ w / width,
26
+ h / width,
27
+ ]
28
+ )
29
+ return yolo_list
30
+
31
+
32
+ def export_yolo(dataset, yolo_path=None):
33
+ """Exports an object detection dataset to YOLO format, ready-to-use for YOLO training.
34
+
35
+ This method will export an AgML dataset to the YOLO format, given its name and the
36
+ desired output location. This is so that the data is prepared within the YOLO format -
37
+ however, this does not translate to *integration* with the YOLO training pipeline.
38
+ Instead, this will simply enable you to add the dataset path to the YOLO training
39
+ configuration (and all other preprocessing steps are abstracted away in that same
40
+ pipeline, not requiring AgML).
41
+
42
+ Note that you can also use this function to export a custom dataset - in this case,
43
+ rather than passing in the name of a dataset simply pass in an AgMLDataLoader with
44
+ the dataset (and this will work for multi-dataset datasets, as well as dataset splits).
45
+
46
+ If you provide a path to a YOLO implementation, this function will automatically write
47
+ the corresponding files (including the `dataset.yaml` file). However, if an empty path,
48
+ no path, or a non-YOLO path is provided, the function will simply create a new directory
49
+ and write the files within a `metadata` directory there.
50
+
51
+ Additionally, if the data is already split, then `train.txt`, `val.txt`, and `test.txt`
52
+ files will be written to the data directory, which contain the split paths.
53
+
54
+ Parameters
55
+ ----------
56
+ dataset : {str, AgMLDataLoader}
57
+ The name of the dataset to export to YOLO format, or an AgMLDataLoader object.
58
+ yolo_path : str
59
+ The path to the directory where the YOLO-formatted dataset will be saved.
60
+
61
+ """
62
+ # if a name is provided, load the dataset
63
+ if isinstance(dataset, str):
64
+ loader = agml.data.AgMLDataLoader(dataset)
65
+ else:
66
+ loader = dataset
67
+
68
+ if yolo_path is None:
69
+ yolo_path = os.path.join(os.getcwd(), f"{loader.name}_yolo_export")
70
+ if not os.path.exists(os.path.join(yolo_path, "data")):
71
+ log("YOLO Export Tool did not receive a valid YOLO path. Creating a new directory for the export.")
72
+
73
+ yolo_data_path = os.path.join(yolo_path, "datasets", loader.name)
74
+ output_image_dir = os.path.join(yolo_data_path, "images")
75
+ output_annotation_dir = os.path.join(yolo_data_path, "labels")
76
+ os.makedirs(output_image_dir, exist_ok=True)
77
+ os.makedirs(output_annotation_dir, exist_ok=True)
78
+
79
+ # create the dataset YAML
80
+ yaml_dump = {"path": yolo_data_path, "names": loader.num_to_class}
81
+
82
+ # AgML indexes object detection datasets from 1 -> N, so reindex from 0 -> N-1
83
+ class_mapper = {i: i - 1 for i in yaml_dump["names"]}
84
+ yaml_dump["names"] = {i - 1: name for i, name in yaml_dump["names"].items()}
85
+
86
+ # get the height/width for all the images for normalization purposes
87
+ if loader.IS_MULTI_DATASET:
88
+ image_info = flatten(
89
+ [sub_loader._builder._default_coco_annotations["images"] for sub_loader in loader._loaders]
90
+ ) # noqa
91
+ else:
92
+ image_info = loader._builder._default_coco_annotations["images"]
93
+ image_info = {image["file_name"]: (image["height"], image["width"]) for image in image_info}
94
+
95
+ # compatibility for content format for multi-dataset loaders
96
+ if not loader.IS_MULTI_DATASET:
97
+ loader_contents = {loader.name: loader.export_contents(export_format=None)}
98
+ else:
99
+ loader_contents = loader.export_contents(export_format=None)
100
+
101
+ # check for data splits and get a list of images in each split
102
+ all_split_images = {}
103
+ if loader._is_split_generated():
104
+ for split_name in ["train", "val", "test"]:
105
+ split_content = getattr(loader, f"_{split_name}_content")
106
+ if not loader.IS_MULTI_DATASET:
107
+ split_content = {loader.name: split_content}
108
+
109
+ # skip empty splits by checking the contents
110
+ if all(i is None for i in split_content.values()):
111
+ continue
112
+
113
+ split_images = flatten(
114
+ [
115
+ [f"{curr_name}_{os.path.basename(image)}" for image in curr_split]
116
+ for curr_name, curr_split in split_content.items()
117
+ ]
118
+ )
119
+ all_split_images[split_name] = split_images
120
+ else:
121
+ # if no split generated, put all in `train`
122
+ all_split_images["train"] = flatten(
123
+ [
124
+ [f"{loader.name}_{os.path.basename(image)}" for image in curr_split]
125
+ for curr_split in loader_contents.values()
126
+ ]
127
+ )
128
+
129
+ # write the text files containing the split contents
130
+ for split_name, split_images in all_split_images.items():
131
+ split_images = [os.path.join(output_image_dir, image) for image in split_images]
132
+ with open(os.path.join(yolo_data_path, f"{split_name}.txt"), "w") as f:
133
+ f.write("\n".join(split_images))
134
+
135
+ # update the YAML file with the corresponding locs of train/val/test
136
+ yaml_dump[split_name] = f"{split_name}.txt"
137
+
138
+ for loader_name, loader_content in loader_contents.items():
139
+ for image, annotation_set in loader_content.items():
140
+ # convert from the default AgML format (COCO: [x, y, w, h])
141
+ # to YOLO format: (normalized [x_center, y_center, w, h])
142
+ image_shape = image_info[os.path.basename(image)]
143
+ new_annotation_set = convert_annotations_to_yolo_list(annotation_set, image_shape, class_mapper)
144
+ text_content = "\n".join([" ".join(map(str, annotation)) for annotation in new_annotation_set])
145
+
146
+ # the name of the new image is {dataset_name}_{old_name}.{ext}
147
+ image_name = f"{loader_name}_{os.path.basename(image)}"
148
+ txt_name = os.path.splitext(image_name)[0] + ".txt"
149
+
150
+ # save the image and the text file
151
+ shutil.copy(image, os.path.join(output_image_dir, image_name))
152
+ with open(os.path.join(output_annotation_dir, txt_name), "w") as f:
153
+ f.write(text_content)
154
+
155
+ # write the dataset.yaml file
156
+ os.makedirs(os.path.join(yolo_path, "data"), exist_ok=True)
157
+ with open(os.path.join(yolo_path, "data", "dataset.yaml"), "w") as f:
158
+ yaml.safe_dump(yaml_dump, f)
159
+
160
+ # log information about the dataset location
161
+ information = """
162
+ Dataset successfully exported to YOLO format.
163
+
164
+ You can find the dataset at the following location: {0}
165
+ The dataset metadata is stored at {1}
166
+ """.format(yolo_data_path, os.path.join(yolo_path, "data", "dataset.yaml"))
167
+ log(information)
168
+
169
+ return {
170
+ "dataset_path": yolo_data_path,
171
+ "metadata_path": os.path.join(yolo_path, "data", "dataset.yaml"),
172
+ }
AgML/source/agml/data/extensions/__init__.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 UC Davis Plant AI and Biophysics Lab
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from .cvat import restructure_cvat_annotations
AgML/source/agml/data/extensions/cvat.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 UC Davis Plant AI and Biophysics Lab
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import json
16
+ import os
17
+ import shutil
18
+
19
+
20
+ def restructure_cvat_annotations(image_dir, cvat_dir, dataset_name, output_dir=None):
21
+ """
22
+ Restructure CVAT annotations and images into the AgML dataset format.
23
+
24
+ Parameters
25
+ ----------
26
+ image_dir : str
27
+ Path to the directory containing the images
28
+ cvat_dir : str
29
+ Path to the directory containing the CVAT annotations
30
+ dataset_name : str
31
+ Name of the new dataset
32
+ output_dir : str
33
+ Directory where the dataset will be saved. By default, saves to `~/.agml/datasets`
34
+
35
+ Returns
36
+ -------
37
+ The path to the dataset
38
+ """
39
+ image_dir = os.path.expanduser(image_dir)
40
+ cvat_dir = os.path.expanduser(cvat_dir)
41
+
42
+ # Determine the dataset path
43
+ if output_dir is None:
44
+ import agml.backend # Ensure that agml is imported
45
+
46
+ dataset_path = os.path.join(agml.backend.data_save_path(), dataset_name)
47
+ else:
48
+ dataset_path = os.path.join(output_dir, dataset_name)
49
+
50
+ images_output_dir = os.path.join(dataset_path, "images")
51
+ os.makedirs(images_output_dir, exist_ok=True)
52
+
53
+ # Copy images to the new dataset directory
54
+ for image_file in os.listdir(image_dir):
55
+ src_image_path = os.path.join(image_dir, image_file)
56
+ dst_image_path = os.path.join(images_output_dir, image_file)
57
+ shutil.copy2(src_image_path, dst_image_path)
58
+
59
+ # Load the CVAT annotations
60
+ cvat_annotations_path = os.path.join(cvat_dir, "instances_default.json")
61
+ with open(cvat_annotations_path, "r") as f:
62
+ coco = json.load(f)
63
+
64
+ # Build a mapping from base image names to actual filenames (with extensions)
65
+ image_files = os.listdir(image_dir)
66
+ base_name_to_file_name = {}
67
+ for file_name in image_files:
68
+ base_name, ext = os.path.splitext(file_name)
69
+ base_name_to_file_name[base_name] = file_name
70
+
71
+ # Update the 'file_name' field in each image entry to match the actual filenames
72
+ for image in coco.get("images", []):
73
+ original_file_name = image["file_name"]
74
+ base_name, _ = os.path.splitext(original_file_name)
75
+ actual_file_name = base_name_to_file_name.get(base_name)
76
+
77
+ if actual_file_name:
78
+ image["file_name"] = actual_file_name
79
+ else:
80
+ print(f"Warning: No matching image file found for '{original_file_name}'.")
81
+ # Optionally, handle this case as needed (e.g., remove the image from the list)
82
+
83
+ # Save the updated annotations to 'annotations.json' in the new dataset directory
84
+ annotations_output_path = os.path.join(dataset_path, "annotations.json")
85
+ with open(annotations_output_path, "w") as f:
86
+ json.dump(coco, f)
87
+
88
+ print(f"Dataset '{dataset_name}' has been created successfully at '{dataset_path}'.")