Skip to content

Commit 6eb28cf

Browse files
authored
Merge pull request #48 from Project-AgML/dev
Preparation for v0.5.0
2 parents 7278383 + 97bef96 commit 6eb28cf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+2433
-1154
lines changed

.github/workflows/update-datasets.yml

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,8 @@ on:
77
push:
88
paths:
99
- agml/_assets/public_datasources.json
10-
pull_request:
11-
paths:
12-
- agml/_assets/public_datasources.json
1310

14-
permissions: read-all|write-all
11+
permissions: write-all
1512

1613
jobs:
1714
build:
@@ -20,6 +17,8 @@ jobs:
2017

2118
steps:
2219
- uses: actions/checkout@v3
20+
with:
21+
ref: dev
2322
- name: Set up Python 3.8
2423
uses: actions/setup-python@v3
2524
with:
@@ -28,14 +27,17 @@ jobs:
2827
run: |
2928
python -m pip install --upgrade pip
3029
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
30+
python3 setup.py install
3131
- name: Configure Git
3232
run: |
33-
git config user.name "AgML Dataset Update Bot"
34-
git config user.email "joshi.amoghn@gmail.com"
33+
git config user.name 'github-actions[bot]'
34+
git config user.email 'github-actions[bot]@users.noreply.github.com'
3535
- name: Update Datasets
3636
run: |
3737
python scripts/run_full_dataset_update.py
38-
git add agml/_assets/*
38+
git add -f agml/_assets/*
39+
git add -f docs/
40+
git add README.md
3941
git commit -m "Updated information for new dataset"
4042
git push origin dev
41-
43+

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ include agml/_assets/shape_info.pickle
44
include agml/_helios/helios_install.sh
55
include agml/synthetic/synthetic_data_generation/*
66
prune agml/_internal
7+
prune .github

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@ You're now ready to use AgML for training your own models!
131131
[peachpear_flower_segmentation](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/peachpear_flower_segmentation.md) | Semantic Segmentation | 42 |
132132
[red_grapes_and_leaves_segmentation](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/red_grapes_and_leaves_segmentation.md) | Semantic Segmentation | 258 |
133133
[white_grapes_and_leaves_segmentation](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/white_grapes_and_leaves_segmentation.md) | Semantic Segmentation | 273 |
134+
[ghai_romaine_detection](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/ghai_romaine_detection.md) | Object Detection | 500 |
135+
[ghai_green_cabbage_detection](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/ghai_green_cabbage_detection.md) | Object Detection | 500 |
136+
[ghai_iceberg_lettuce_detection](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/ghai_iceberg_lettuce_detection.md) | Object Detection | 500 |
137+
[riseholme_strawberry_classification_2021](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/riseholme_strawberry_classification_2021.md) | Image Classification | 3520 |
138+
[ghai_broccoli_detection](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/ghai_broccoli_detection.md) | Object Detection | 500 |
139+
[bean_synthetic_earlygrowth_aerial](https://github.com/Project-AgML/AgML/blob/main/docs/datasets/bean_synthetic_earlygrowth_aerial.md) | Semantic Segmentation | 2500 |
134140

135141
## Usage Information
136142

@@ -161,4 +167,4 @@ a bug or feature that you would like to see implemented, please don't hesitate t
161167
See the [contributing guidelines](/CONTRIBUTING.md) for more information.
162168

163169
## Funding
164-
This project is partly funded by the [National AI Institute for Food Systems (AIFS)](https://aifs.ucdavis.edu).
170+
This project is partly funded by the [National AI Institute for Food Systems (AIFS)](https://aifs.ucdavis.edu

agml/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414

1515
__version__ = '0.4.7'
16-
__all__ = ['data', 'synthetic', 'backend', 'viz']
16+
__all__ = ['data', 'synthetic', 'backend', 'viz', 'io']
1717

1818

1919
# If AgML is being imported for the first time, then we need to setup
@@ -25,12 +25,13 @@ def _setup():
2525
_os.makedirs(_os.path.expanduser('~/.agml'))
2626
with open(_os.path.join(
2727
_os.path.expanduser('~/.agml/config.json')), 'w') as f:
28-
_json.dump({'data_path': _os.path.expanduser('~/.agml/datasets')}, f)
28+
_json.dump({'data_path': _os.path.expanduser('~/.agml/datasets'),
29+
'viz_backend': 'matplotlib'}, f)
2930
_setup(); del _setup # noqa
3031

3132

3233
# There are no top-level imported functions or classes, only the modules.
33-
from . import data, backend, synthetic, viz
34+
from . import data, backend, synthetic, viz, io
3435

3536

3637

agml/_assets/public_datasources.json

Lines changed: 127 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -627,8 +627,8 @@
627627
"ml_task": "object_detection",
628628
"ag_task": "fruit_detection",
629629
"location": {
630-
"continent": "north_america",
631-
"country": "usa"
630+
"continent": "digital",
631+
"country": "digital"
632632
},
633633
"sensor_modality": "rgb",
634634
"real_synthetic": "synthetic",
@@ -1105,5 +1105,130 @@
11051105
0.276553213596344
11061106
]
11071107
}
1108+
},
1109+
"ghai_iceberg_lettuce_detection": {
1110+
"classes": {
1111+
"1": "iceberg_lettuce"
1112+
},
1113+
"ml_task": "object_detection",
1114+
"ag_task": "crop_detection",
1115+
"location": {
1116+
"continent": "north_america",
1117+
"country": "usa"
1118+
},
1119+
"sensor_modality": "rgb",
1120+
"real_synthetic": "real",
1121+
"platform": "handheld/ground",
1122+
"input_data_format": "jpg",
1123+
"annotation_format": "coco_json",
1124+
"n_images": "500",
1125+
"docs_url": "https://github.com/AxisAg/GHAIDatasets/blob/main/datasets/iceberg.md",
1126+
"external_image_sources": [],
1127+
"stats": {
1128+
"mean": [
1129+
0.3193310499191284,
1130+
0.4143226742744446,
1131+
0.3003973960876465
1132+
],
1133+
"std": [
1134+
0.2828165590763092,
1135+
0.2841264605522156,
1136+
0.25660744309425354
1137+
]
1138+
}
1139+
},
1140+
"riseholme_strawberry_classification_2021": {
1141+
"classes": {
1142+
"0": "anomalous",
1143+
"1": "occluded",
1144+
"2": "ripe",
1145+
"3": "unripe"
1146+
},
1147+
"ml_task": "image_classification",
1148+
"ag_task": "disease_classification",
1149+
"sensor_modality": "rgb",
1150+
"real_synthetic": "real",
1151+
"platform": "ground",
1152+
"input_data_format": "png",
1153+
"annotation_format": "directory_names",
1154+
"n_images": "3520",
1155+
"docs_url": "https://github.com/ctyeong/Riseholme-2021",
1156+
"external_image_sources": [],
1157+
"stats": {
1158+
"mean": [
1159+
0.5753855109214783,
1160+
0.4637872874736786,
1161+
0.3637043833732605
1162+
],
1163+
"std": [
1164+
0.1983788013458252,
1165+
0.19222092628479004,
1166+
0.1773509532213211
1167+
]
1168+
}
1169+
},
1170+
"ghai_broccoli_detection": {
1171+
"classes": {
1172+
"1": "canopy",
1173+
"2": "crown"
1174+
},
1175+
"ml_task": "object_detection",
1176+
"ag_task": "crop_detection",
1177+
"location": {
1178+
"continent": "north_america",
1179+
"country": "usa"
1180+
},
1181+
"sensor_modality": "rgb",
1182+
"real_synthetic": "real",
1183+
"platform": "handheld/ground",
1184+
"input_data_format": "jpg",
1185+
"annotation_format": "coco_json",
1186+
"n_images": "500",
1187+
"docs_url": "https://github.com/AxisAg/GHAIDatasets/blob/main/datasets/broccoli.md",
1188+
"external_image_sources": [],
1189+
"stats": {
1190+
"mean": [
1191+
0.42832377552986145,
1192+
0.45015057921409607,
1193+
0.43816232681274414
1194+
],
1195+
"std": [
1196+
0.24546495079994202,
1197+
0.23975253105163574,
1198+
0.23829665780067444
1199+
]
1200+
}
1201+
},
1202+
"bean_synthetic_earlygrowth_aerial": {
1203+
"classes": {
1204+
"1": "leaves",
1205+
"2": "branches"
1206+
},
1207+
"ml_task": "semantic_segmentation",
1208+
"ag_task": "plant_segmentation",
1209+
"location": {
1210+
"continent": "digital",
1211+
"country": "digital"
1212+
},
1213+
"sensor_modality": "rgb",
1214+
"real_synthetic": "synthetic",
1215+
"platform": "aerial",
1216+
"input_data_format": "jpg",
1217+
"annotation_format": "image",
1218+
"n_images": "2500",
1219+
"docs_url": "https://github.com/Project-AgML/AgML/blob/main/docs/datasets/bean_synthetic_earlygrowth_aerial.md",
1220+
"external_image_sources": [],
1221+
"stats": {
1222+
"mean": [
1223+
0.24804304540157318,
1224+
0.2218194305896759,
1225+
0.16611839830875397
1226+
],
1227+
"std": [
1228+
0.10789012163877487,
1229+
0.12054657936096191,
1230+
0.08992248773574829
1231+
]
1232+
}
11081233
}
11091234
}

agml/_assets/shape_info.pickle

66.3 KB
Binary file not shown.

agml/_assets/source_citations.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,5 +122,21 @@
122122
"ghai_green_cabbage_detection": {
123123
"license": "CC BY-SA 4.0",
124124
"citation": ""
125+
},
126+
"ghai_iceberg_lettuce_detection": {
127+
"license": "CC BY-SA 4.0",
128+
"citation": ""
129+
},
130+
"riseholme_strawberry_classification_2021": {
131+
"license": "",
132+
"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}"
133+
},
134+
"ghai_broccoli_detection": {
135+
"license": "CC BY-SA 4.0",
136+
"citation": ""
137+
},
138+
"bean_synthetic_earlygrowth_aerial": {
139+
"license": "MIT",
140+
"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}"
125141
}
126142
}

agml/_internal/preprocess.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -970,11 +970,57 @@ def ghai_green_cabbage_detection(self, dataset_name):
970970
shutil.move(os.path.join(original_dir, 'coco.json'),
971971
os.path.join(processed_dir, 'annotations.json'))
972972

973+
def ghai_iceberg_lettuce_detection(self, dataset_name):
974+
# Create processed directories
975+
original_dir = os.path.join(self.data_original_dir, dataset_name)
976+
processed_dir = os.path.join(self.data_processed_dir, dataset_name)
977+
processed_image_dir = os.path.join(processed_dir, 'images')
978+
os.makedirs(processed_image_dir, exist_ok = True)
979+
980+
# Move images
981+
for image in glob.glob(os.path.join(original_dir, '*.jpg')):
982+
shutil.move(image, processed_image_dir)
983+
shutil.move(os.path.join(original_dir, 'coco.json'),
984+
os.path.join(processed_dir, 'annotations.json'))
985+
986+
def riseholme_strawberry_classification_2021(self, dataset_name):
987+
# Create processed data directory.
988+
original_dir = os.path.join(
989+
self.data_original_dir, 'Riseholme-2021-main', 'Data')
990+
processed_dir = os.path.join(self.data_processed_dir, dataset_name)
991+
992+
# Load all of the individual images and keep a mapping to their corresponding directory.
993+
images = {
994+
'anomalous': get_file_list(os.path.join(original_dir, 'Anomalous')),
995+
**{dir_.lower(): get_file_list(os.path.join(original_dir, 'Normal', dir_))
996+
for dir_ in os.listdir(os.path.join(original_dir, 'Normal'))}
997+
}
998+
999+
# Create the output file structure.
1000+
for class_name, image_set in images.items():
1001+
class_dir = os.path.join(processed_dir, class_name)
1002+
os.makedirs(class_dir, exist_ok = True)
1003+
for file in image_set:
1004+
shutil.copyfile(file, os.path.join(class_dir, os.path.basename(file)))
1005+
1006+
def ghai_broccoli_detection(self, dataset_name):
1007+
# Create processed directories
1008+
original_dir = os.path.join(self.data_original_dir, dataset_name)
1009+
processed_dir = os.path.join(self.data_processed_dir, dataset_name)
1010+
processed_image_dir = os.path.join(processed_dir, 'images')
1011+
os.makedirs(processed_image_dir, exist_ok = True)
1012+
1013+
# Move images
1014+
for image in tqdm(glob.glob(os.path.join(original_dir, '*.jpg'))):
1015+
shutil.move(image, processed_image_dir)
1016+
shutil.move(os.path.join(original_dir, 'coco.json'),
1017+
os.path.join(processed_dir, 'annotations.json'))
1018+
9731019

9741020
if __name__ == '__main__':
9751021
# Initialize program arguments.
9761022
ap = argparse.ArgumentParser()
977-
ap.add_argument("--data_dir", type = str, default = '../../data',
1023+
ap.add_argument("--data_dir", type = str, default = '../../data_new',
9781024
help = "The directory containing two sub-directories, "
9791025
"`original` and `processed`, with the data.")
9801026
ap.add_argument("--dataset", type = str,
@@ -983,4 +1029,10 @@ def ghai_green_cabbage_detection(self, dataset_name):
9831029

9841030
# Execute the preprocessing.
9851031
p = PublicDataPreprocessor(os.path.abspath(args.data_dir))
1032+
print("Processing dataset")
9861033
p.preprocess(args.dataset)
1034+
print("Converting dataset")
1035+
os.chdir(f'{args.data_dir}/processed')
1036+
os.system(f'zip -r {args.dataset}.zip {args.dataset} -x ".*" -x "__MACOSX"')
1037+
1038+

agml/backend/config.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
DATASET_SAVE_DIR: str
3535

3636

37-
3837
# This is similar to `DATASET_SAVE_DIR`, but is for synthetically generated
3938
# datasets using Helios. By default, this will be SUPER_BASE_DIR/synthetic,
4039
# but it can be overridden. The value is set upon instantiation of the module.
@@ -96,11 +95,7 @@ def set_data_save_path(location = None):
9695
raise NotADirectoryError(
9796
f"The provided destination {location} does "
9897
f"not exist, or is not a directory.")
99-
with open(os.path.join(SUPER_BASE_DIR, 'config.json'), 'r') as f:
100-
contents = json.load(f)
101-
contents['data_path'] = os.path.realpath(os.path.abspath(location))
102-
with open(os.path.join(SUPER_BASE_DIR, 'config.json'), 'w') as f:
103-
json.dump(contents, f)
98+
_update_config('data_path', os.path.realpath(os.path.abspath(location)))
10499
return
105100

106101

@@ -132,11 +127,7 @@ def set_synthetic_save_path(location = None):
132127
raise NotADirectoryError(
133128
f"The provided destination {location} does "
134129
f"not exist, or is not a directory.")
135-
with open(os.path.join(SUPER_BASE_DIR, 'config.json'), 'r') as f:
136-
contents = json.load(f)
137-
contents['synthetic_data_path'] = os.path.realpath(os.path.abspath(location))
138-
with open(os.path.join(SUPER_BASE_DIR, 'config.json'), 'w') as f:
139-
json.dump(contents, f)
130+
_update_config('synthetic_data_path', os.path.realpath(os.path.abspath(location)))
140131
return
141132

142133

@@ -167,13 +158,28 @@ def set_model_save_path(location = None):
167158
raise NotADirectoryError(
168159
f"The provided destination {location} does "
169160
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
170178
with open(os.path.join(SUPER_BASE_DIR, 'config.json'), 'r') as f:
171179
contents = json.load(f)
172-
contents['model_path'] = os.path.realpath(os.path.abspath(location))
180+
contents[param] = value
173181
with open(os.path.join(SUPER_BASE_DIR, 'config.json'), 'w') as f:
174182
json.dump(contents, f)
175-
return
176-
177183

178184

179185
def clear_all_datasets():

0 commit comments

Comments
 (0)