geebeam 0.2.2__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {geebeam-0.2.2 → geebeam-0.3.2}/PKG-INFO +27 -7
- {geebeam-0.2.2 → geebeam-0.3.2}/README.md +1 -1
- {geebeam-0.2.2 → geebeam-0.3.2}/_version.py +2 -2
- {geebeam-0.2.2 → geebeam-0.3.2}/pyproject.toml +22 -3
- geebeam-0.3.2/src/geebeam/__init__.py +10 -0
- geebeam-0.3.2/src/geebeam/ee_utils.py +94 -0
- {geebeam-0.2.2 → geebeam-0.3.2}/src/geebeam/pipeline.py +69 -99
- geebeam-0.3.2/src/geebeam/tf_utils.py +73 -0
- geebeam-0.3.2/src/geebeam/tfds_writer.py +229 -0
- geebeam-0.3.2/src/geebeam/tfrecord_writer.py +84 -0
- geebeam-0.3.2/src/geebeam/tiff_writer.py +195 -0
- geebeam-0.3.2/src/geebeam/transforms.py +146 -0
- {geebeam-0.2.2 → geebeam-0.3.2}/tests/test_ee_utils.py +11 -7
- geebeam-0.3.2/tests/test_tf_utils.py +34 -0
- {geebeam-0.2.2 → geebeam-0.3.2}/tests/test_transforms.py +9 -40
- geebeam-0.2.2/src/geebeam/__init__.py +0 -17
- geebeam-0.2.2/src/geebeam/ee_utils.py +0 -40
- geebeam-0.2.2/src/geebeam/transforms.py +0 -219
- {geebeam-0.2.2 → geebeam-0.3.2}/.gitignore +0 -0
- {geebeam-0.2.2 → geebeam-0.3.2}/LICENSE +0 -0
- {geebeam-0.2.2 → geebeam-0.3.2}/src/geebeam/climate_indices.py +0 -0
- {geebeam-0.2.2 → geebeam-0.3.2}/src/geebeam/sampler.py +0 -0
- {geebeam-0.2.2 → geebeam-0.3.2}/tests/test_climate_indices.py +0 -0
- {geebeam-0.2.2 → geebeam-0.3.2}/tests/test_pipeline.py +0 -0
- {geebeam-0.2.2 → geebeam-0.3.2}/tests/test_sampler.py +0 -0
|
@@ -1,19 +1,39 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: geebeam
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Earth Engine + Apache Beam
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Requires-Python: >=3.10
|
|
8
|
-
Requires-Dist: apache-beam[gcp]
|
|
8
|
+
Requires-Dist: apache-beam[gcp]==2.72.0
|
|
9
|
+
Requires-Dist: crcmod>=1.7
|
|
9
10
|
Requires-Dist: dill>=0.4.1
|
|
10
11
|
Requires-Dist: earthengine-api>=1.7.10
|
|
11
12
|
Requires-Dist: geopandas>=1.0.1
|
|
12
13
|
Requires-Dist: grpcio>=1.62.3
|
|
13
|
-
Requires-Dist:
|
|
14
|
-
|
|
15
|
-
Requires-Dist:
|
|
16
|
-
Requires-Dist:
|
|
14
|
+
Requires-Dist: rasterio>=1.3.10
|
|
15
|
+
Provides-Extra: all
|
|
16
|
+
Requires-Dist: importlib-resources>=6.5.2; extra == 'all'
|
|
17
|
+
Requires-Dist: pytest; extra == 'all'
|
|
18
|
+
Requires-Dist: ruff; extra == 'all'
|
|
19
|
+
Requires-Dist: tensorflow-data-validation>=1.16; extra == 'all'
|
|
20
|
+
Requires-Dist: tensorflow-datasets>=4.9.9; extra == 'all'
|
|
21
|
+
Requires-Dist: tensorflow-metadata>=1.16; extra == 'all'
|
|
22
|
+
Requires-Dist: tensorflow>=2.16; extra == 'all'
|
|
23
|
+
Requires-Dist: tfx-bsl>=1.16; extra == 'all'
|
|
24
|
+
Provides-Extra: build
|
|
25
|
+
Requires-Dist: hatch; extra == 'build'
|
|
26
|
+
Provides-Extra: lint
|
|
27
|
+
Requires-Dist: ruff; extra == 'lint'
|
|
28
|
+
Provides-Extra: tensorflow
|
|
29
|
+
Requires-Dist: importlib-resources>=6.5.2; extra == 'tensorflow'
|
|
30
|
+
Requires-Dist: tensorflow-data-validation>=1.16; extra == 'tensorflow'
|
|
31
|
+
Requires-Dist: tensorflow-datasets>=4.9.9; extra == 'tensorflow'
|
|
32
|
+
Requires-Dist: tensorflow-metadata>=1.16; extra == 'tensorflow'
|
|
33
|
+
Requires-Dist: tensorflow>=2.16; extra == 'tensorflow'
|
|
34
|
+
Requires-Dist: tfx-bsl>=1.16; extra == 'tensorflow'
|
|
35
|
+
Provides-Extra: test
|
|
36
|
+
Requires-Dist: pytest; extra == 'test'
|
|
17
37
|
Description-Content-Type: text/markdown
|
|
18
38
|
|
|
19
39
|
# GeeBeam
|
|
@@ -114,7 +134,7 @@ python examples/geebeam_run.py \
|
|
|
114
134
|
--experiments=use_runner_v2 \
|
|
115
135
|
--temp_location=gs://[your-bucket]/[path_to_temp_dir]
|
|
116
136
|
--machine_type=n2-highmem-2 \
|
|
117
|
-
--sdk_container_image=
|
|
137
|
+
--sdk_container_image=kysolvik/geebeam:latest
|
|
118
138
|
```
|
|
119
139
|
|
|
120
140
|
Note in this case your `output_path` in run_pipeline() should be a Google Cloud Storage path. If you're running an older version of geebeam, replace "latest" in the sdk_container_image URI with the version number (e.g. v0.1.2). You can also build your own Docker image to run on. More info in the [DataFlow docs](https://docs.cloud.google.com/dataflow/docs/guides/using-custom-containers).
|
|
@@ -96,7 +96,7 @@ python examples/geebeam_run.py \
|
|
|
96
96
|
--experiments=use_runner_v2 \
|
|
97
97
|
--temp_location=gs://[your-bucket]/[path_to_temp_dir]
|
|
98
98
|
--machine_type=n2-highmem-2 \
|
|
99
|
-
--sdk_container_image=
|
|
99
|
+
--sdk_container_image=kysolvik/geebeam:latest
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
Note in this case your `output_path` in run_pipeline() should be a Google Cloud Storage path. If you're running an older version of geebeam, replace "latest" in the sdk_container_image URI with the version number (e.g. v0.1.2). You can also build your own Docker image to run on. More info in the [DataFlow docs](https://docs.cloud.google.com/dataflow/docs/guides/using-custom-containers).
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.
|
|
22
|
-
__version_tuple__ = version_tuple = (0,
|
|
21
|
+
__version__ = version = '0.3.2'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 3, 2)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -10,15 +10,35 @@ description = "Earth Engine + Apache Beam"
|
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.10"
|
|
12
12
|
dependencies = [
|
|
13
|
-
"apache-beam[gcp]
|
|
13
|
+
"apache-beam[gcp]==2.72.0",
|
|
14
|
+
"crcmod>=1.7",
|
|
14
15
|
"dill>=0.4.1",
|
|
15
16
|
"earthengine-api>=1.7.10",
|
|
16
17
|
"geopandas>=1.0.1",
|
|
18
|
+
"grpcio>=1.62.3",
|
|
19
|
+
"rasterio>=1.3.10",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.optional-dependencies]
|
|
23
|
+
test = ["pytest"]
|
|
24
|
+
lint = ["ruff"]
|
|
25
|
+
build = ["hatch"]
|
|
26
|
+
tensorflow = [
|
|
17
27
|
"tensorflow>=2.16",
|
|
18
28
|
"tensorflow-data-validation>=1.16",
|
|
19
29
|
"tensorflow-metadata>=1.16",
|
|
20
30
|
"tfx-bsl>=1.16",
|
|
21
|
-
"
|
|
31
|
+
"tensorflow-datasets>=4.9.9",
|
|
32
|
+
"importlib-resources>=6.5.2",
|
|
33
|
+
]
|
|
34
|
+
all = [
|
|
35
|
+
"geebeam[tensorflow,test,lint]"
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
# Pin upper version of python, since apache-beam only supports to 3.12
|
|
39
|
+
[tool.uv]
|
|
40
|
+
environments = [
|
|
41
|
+
"python_version < '3.13'"
|
|
22
42
|
]
|
|
23
43
|
|
|
24
44
|
# Version control
|
|
@@ -28,7 +48,6 @@ version-file = "_version.py"
|
|
|
28
48
|
[tool.hatch.version]
|
|
29
49
|
source = "vcs"
|
|
30
50
|
|
|
31
|
-
|
|
32
51
|
[tool.hatch.build.targets.sdist]
|
|
33
52
|
include = [
|
|
34
53
|
"src",
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utilities for cleaning, combining, and serializing/deserializing EE objects.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import io
|
|
6
|
+
|
|
7
|
+
import ee
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
def deserialize(obj_json):
|
|
11
|
+
"""Deserialize Earth Engine JSON DAG"""
|
|
12
|
+
return ee.deserializer.fromJSON(obj_json)
|
|
13
|
+
|
|
14
|
+
def serialize(obj_ee):
|
|
15
|
+
"""Serialize Earth Engine object to JSON for Dataflow workers"""
|
|
16
|
+
return ee.serializer.toJSON(obj_ee)
|
|
17
|
+
|
|
18
|
+
def get_band_names(input_list):
|
|
19
|
+
"""Get simplified band_names for output (without prefixed image_id)
|
|
20
|
+
|
|
21
|
+
TODO: Add year to distinguish multiple years?
|
|
22
|
+
"""
|
|
23
|
+
return [
|
|
24
|
+
image.bandNames()
|
|
25
|
+
for image in input_list
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
def build_prepped_image(input_list, split_processing=False):
|
|
29
|
+
"""Combine a list of EE images into single image"""
|
|
30
|
+
band_names = get_band_names(input_list)
|
|
31
|
+
band_names_flat = ee.List(band_names).flatten()
|
|
32
|
+
|
|
33
|
+
if split_processing:
|
|
34
|
+
band_groups = band_names
|
|
35
|
+
else:
|
|
36
|
+
band_groups = [band_names_flat]
|
|
37
|
+
# Final prepped image
|
|
38
|
+
prepped_im = ee.ImageCollection(input_list).toBands().rename(band_names_flat)
|
|
39
|
+
return prepped_im, band_groups, band_names_flat.getInfo()
|
|
40
|
+
|
|
41
|
+
def list_to_im(input_list):
|
|
42
|
+
prepped_im, _ = build_prepped_image(input_list)
|
|
43
|
+
return prepped_im
|
|
44
|
+
|
|
45
|
+
def get_pixels(im, point, patch_size, scale_x, scale_y, crs_code):
|
|
46
|
+
# Make a request object.
|
|
47
|
+
request = {
|
|
48
|
+
'expression': im,
|
|
49
|
+
'fileFormat': 'NPY',
|
|
50
|
+
'grid': {
|
|
51
|
+
'dimensions': {
|
|
52
|
+
'width': patch_size,
|
|
53
|
+
'height': patch_size
|
|
54
|
+
},
|
|
55
|
+
'affineTransform': {
|
|
56
|
+
'scaleX': scale_x,
|
|
57
|
+
'shearX': 0,
|
|
58
|
+
'translateX': point['x'],
|
|
59
|
+
'shearY': 0,
|
|
60
|
+
'scaleY': scale_y,
|
|
61
|
+
'translateY': point['y']
|
|
62
|
+
},
|
|
63
|
+
'crsCode': crs_code
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
raw = ee.data.computePixels(request)
|
|
68
|
+
|
|
69
|
+
if not raw:
|
|
70
|
+
raise RuntimeError("Empty EE response")
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
arr = np.load(io.BytesIO(raw), allow_pickle=True)
|
|
74
|
+
except Exception as e:
|
|
75
|
+
raise RuntimeError(f"Failed to load NPY for coords {point['id']}") from e
|
|
76
|
+
|
|
77
|
+
return arr
|
|
78
|
+
|
|
79
|
+
def get_pixels_allbands(im, band_groups, point, patch_size, scale_x, scale_y, crs_code):
|
|
80
|
+
"""Get pixels for all groups of bands"""
|
|
81
|
+
out_ars = []
|
|
82
|
+
for band_list in band_groups:
|
|
83
|
+
prepped_image = im.select(band_list)
|
|
84
|
+
out_ars.append(
|
|
85
|
+
get_pixels(
|
|
86
|
+
im=prepped_image,
|
|
87
|
+
point=point,
|
|
88
|
+
patch_size=patch_size,
|
|
89
|
+
scale_x=scale_x,
|
|
90
|
+
scale_y=scale_y,
|
|
91
|
+
crs_code=crs_code
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
return out_ars
|
|
@@ -5,12 +5,7 @@ import ee
|
|
|
5
5
|
import geopandas as gpd
|
|
6
6
|
import pandas as pd
|
|
7
7
|
import numpy as np
|
|
8
|
-
import apache_beam as beam
|
|
9
8
|
from apache_beam.options.pipeline_options import PipelineOptions
|
|
10
|
-
import tensorflow_data_validation as tfdv
|
|
11
|
-
from tfx_bsl.coders import example_coder
|
|
12
|
-
import os
|
|
13
|
-
from google.protobuf.json_format import MessageToJson
|
|
14
9
|
|
|
15
10
|
from geebeam import ee_utils, sampler, transforms
|
|
16
11
|
|
|
@@ -42,6 +37,7 @@ def run_pipeline(
|
|
|
42
37
|
patch_size: int,
|
|
43
38
|
scale: float,
|
|
44
39
|
n_sample: int,
|
|
40
|
+
output_type: str = 'tiff',
|
|
45
41
|
sampling_region: str | gpd.GeoDataFrame | ee.Geometry | None = None,
|
|
46
42
|
sampling_points: pd.DataFrame | gpd.GeoDataFrame | ee.Geometry | None = None,
|
|
47
43
|
validation_ratio: float = 0.2,
|
|
@@ -49,7 +45,9 @@ def run_pipeline(
|
|
|
49
45
|
random_seed: int = None,
|
|
50
46
|
split_processing: bool = False,
|
|
51
47
|
extra_metadata: dict = {},
|
|
52
|
-
beam_options: dict[str] | list[str] | None = None
|
|
48
|
+
beam_options: dict[str] | list[str] | None = None,
|
|
49
|
+
dataset_version: str = '1.0.0',
|
|
50
|
+
dataset_name: str = 'geebeam_dataset'
|
|
53
51
|
) -> None:
|
|
54
52
|
"""Run a Beam pipeline to download image chips from Earth Engine.
|
|
55
53
|
|
|
@@ -58,6 +56,7 @@ def run_pipeline(
|
|
|
58
56
|
sampling_region: The region for sampling images.
|
|
59
57
|
sampling_points: Locations to sample from, specifying upper-left of box.
|
|
60
58
|
output_path: The path where output will be saved.
|
|
59
|
+
output_type: 'tfrecord' (raw tfrecords) or 'tfds' (tensorflow-dataset).
|
|
61
60
|
project: The Google Cloud project ID.
|
|
62
61
|
patch_size: The size of the patches to be processed.
|
|
63
62
|
scale: The scale factor for image processing.
|
|
@@ -121,110 +120,81 @@ def run_pipeline(
|
|
|
121
120
|
# Pre-run info:
|
|
122
121
|
scale_x, scale_y = _prepare_run_metadata(config)
|
|
123
122
|
|
|
124
|
-
#
|
|
125
|
-
|
|
126
|
-
# Check if a local runner
|
|
123
|
+
# Check if a local runner. If so, add longer job timeout to fix grpcio timeout issue
|
|
127
124
|
is_local = _check_if_localrunner(pipeline_options)
|
|
125
|
+
if is_local:
|
|
126
|
+
logging.warning('Running on local runner. Setting beam job_server_timeout'
|
|
127
|
+
' to 9999999 seconds to avoid grpcio timeout errors.')
|
|
128
|
+
pipeline_options_dict = pipeline_options.get_all_options(drop_default=True)
|
|
129
|
+
pipeline_options_dict['job_server_timeout'] = 9999999
|
|
130
|
+
pipeline_options = PipelineOptions.from_dictionary(pipeline_options_dict)
|
|
128
131
|
|
|
129
132
|
# Prepare and serialize inputs
|
|
130
133
|
# band_groups is a list of lists containing bands to export
|
|
131
134
|
# if split_processing = False, will contain one list with all bands
|
|
132
135
|
# if split_processing = True, contains separate band_lists for each image in image_list
|
|
133
|
-
prepped_image, band_groups = ee_utils.build_prepped_image(image_list, split_processing=split_processing)
|
|
136
|
+
prepped_image, band_groups, all_bands = ee_utils.build_prepped_image(image_list, split_processing=split_processing)
|
|
134
137
|
serialized_image = ee_utils.serialize(prepped_image)
|
|
135
138
|
|
|
136
|
-
# Execute pipeline
|
|
137
|
-
with beam.Pipeline(options=pipeline_options) as pipeline:
|
|
138
|
-
|
|
139
|
-
points = pipeline | 'Create points' >> beam.Create(input_records)
|
|
140
139
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
140
|
+
# Execute pipeline based on output type:
|
|
141
|
+
if output_type == 'tfrecord':
|
|
142
|
+
try:
|
|
143
|
+
from geebeam import tfrecord_writer
|
|
144
|
+
except ImportError:
|
|
145
|
+
raise ImportError(
|
|
146
|
+
"Missing dependencies for tfrecord writer. "
|
|
147
|
+
"Install them with `pip install geebeam[tensorflow]`"
|
|
148
148
|
)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
# Convert to TF examples
|
|
166
|
-
training_examples = (
|
|
167
|
-
training_data
|
|
168
|
-
| 'Train to tf.Example' >> beam.Map(transforms.dict_to_example)
|
|
149
|
+
# Write sidecar schema before pipeline execution
|
|
150
|
+
extra_keys = list(extra_metadata.keys())
|
|
151
|
+
transforms.write_sidecar_schema(output_path, all_bands, extra_keys,
|
|
152
|
+
is_gcs=output_path.startswith('gs://'))
|
|
153
|
+
tfrecord_writer.run_tfrecord_export(
|
|
154
|
+
input_records=input_records,
|
|
155
|
+
output_path=output_path,
|
|
156
|
+
config=config,
|
|
157
|
+
serialized_image=serialized_image,
|
|
158
|
+
band_groups=band_groups,
|
|
159
|
+
scale_x=scale_x,
|
|
160
|
+
scale_y=scale_y,
|
|
161
|
+
extra_metadata=extra_metadata,
|
|
162
|
+
pipeline_options=pipeline_options
|
|
169
163
|
)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
164
|
+
elif output_type == 'tfds':
|
|
165
|
+
try:
|
|
166
|
+
from geebeam import tfds_writer
|
|
167
|
+
except ImportError:
|
|
168
|
+
raise ImportError(
|
|
169
|
+
"Missing dependencies for TFDS writer. "
|
|
170
|
+
"Install them with `pip install geebeam[tensorflow]`"
|
|
171
|
+
)
|
|
172
|
+
tfds_writer.run_tfds_export(
|
|
173
|
+
input_records=input_records,
|
|
174
|
+
output_path=output_path,
|
|
175
|
+
config=config,
|
|
176
|
+
serialized_image=serialized_image,
|
|
177
|
+
band_groups=band_groups,
|
|
178
|
+
all_bands=all_bands,
|
|
179
|
+
scale_x=scale_x,
|
|
180
|
+
scale_y=scale_y,
|
|
181
|
+
extra_metadata=extra_metadata,
|
|
182
|
+
pipeline_options=pipeline_options,
|
|
183
|
+
dataset_name=dataset_name,
|
|
184
|
+
dataset_version=dataset_version
|
|
179
185
|
)
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
186
|
+
elif output_type == 'tif' or output_type == 'tiff':
|
|
187
|
+
from geebeam import tiff_writer
|
|
188
|
+
tiff_writer.run_tiff_export(
|
|
189
|
+
input_records=input_records,
|
|
190
|
+
output_path=output_path,
|
|
191
|
+
config=config,
|
|
192
|
+
serialized_image=serialized_image,
|
|
193
|
+
band_groups=band_groups,
|
|
194
|
+
scale_x=scale_x,
|
|
195
|
+
scale_y=scale_y,
|
|
196
|
+
extra_metadata=extra_metadata,
|
|
197
|
+
pipeline_options=pipeline_options
|
|
187
198
|
)
|
|
188
|
-
if config['validation_ratio'] > 0:
|
|
189
|
-
validation_examples = (
|
|
190
|
-
validation_data
|
|
191
|
-
| 'Val to tf.Example' >> beam.Map(transforms.dict_to_example)
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
(validation_examples
|
|
195
|
-
| 'Write validation' >> transforms.WriteTFExample(
|
|
196
|
-
os.path.join(output_path, 'validation'))
|
|
197
|
-
)
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
# NOTE: testing a few different formats.
|
|
201
|
-
# Need to simplify once we decide on one
|
|
202
|
-
# Infer schema
|
|
203
|
-
stats = tfdv.load_statistics(
|
|
204
|
-
os.path.join(output_path, 'stats.tfrecord')
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
schema = tfdv.infer_schema(stats)
|
|
208
|
-
|
|
209
|
-
tfdv.write_schema_text(
|
|
210
|
-
schema,
|
|
211
|
-
os.path.join(output_path, 'schema.pbtxt')
|
|
212
|
-
)
|
|
213
|
-
|
|
214
|
-
# Also write as pbtxt, easier to read
|
|
215
|
-
tfdv.write_stats_text(
|
|
216
|
-
stats,
|
|
217
|
-
os.path.join(output_path, 'stats.pbtxt')
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
# Also write stats and schema as jsons
|
|
221
|
-
out_schema_json = os.path.join(output_path, 'schema.json')
|
|
222
|
-
out_stats_json = os.path.join(output_path, 'stats.json')
|
|
223
|
-
if output_path.startswith('gs://'):
|
|
224
|
-
transforms.write_json_to_gcs(MessageToJson(schema), out_schema_json)
|
|
225
|
-
transforms.write_json_to_gcs(MessageToJson(stats), out_stats_json)
|
|
226
199
|
else:
|
|
227
|
-
|
|
228
|
-
transforms.write_json_to_local(MessageToJson(stats), out_stats_json)
|
|
229
|
-
|
|
230
|
-
|
|
200
|
+
raise ValueError(f"output_type {output_type} not implemented. Options are ['tfds', 'tfrecord', 'tiff']")
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Helpers for tensorflow"""
|
|
2
|
+
|
|
3
|
+
import tensorflow as tf
|
|
4
|
+
import apache_beam as beam
|
|
5
|
+
|
|
6
|
+
from geebeam import transforms
|
|
7
|
+
|
|
8
|
+
def _bytes_feature(value):
|
|
9
|
+
"""Returns a bytes_list from a string / byte."""
|
|
10
|
+
return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))
|
|
11
|
+
|
|
12
|
+
def _float_feature(value):
|
|
13
|
+
"""Returns a float_list from a float / double."""
|
|
14
|
+
return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))
|
|
15
|
+
|
|
16
|
+
def _int64_feature(value):
|
|
17
|
+
"""Returns an int64_list from a bool / enum / int / uint."""
|
|
18
|
+
return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))
|
|
19
|
+
|
|
20
|
+
def dict_to_example(element):
|
|
21
|
+
""""Convert structured numpy array to tf.Example proto."""
|
|
22
|
+
# First add metadata
|
|
23
|
+
md_dict = {
|
|
24
|
+
'md_id': _int64_feature(element['metadata']['id']),
|
|
25
|
+
'md_y': _float_feature(element['metadata']['y']),
|
|
26
|
+
'md_x': _float_feature(element['metadata']['x']),
|
|
27
|
+
}
|
|
28
|
+
for md_key in element['metadata'].keys():
|
|
29
|
+
if md_key not in ['id','y','x', 'split']:
|
|
30
|
+
md_dict['md_' + md_key] = tf.train.Feature(float_list=
|
|
31
|
+
tf.train.FloatList(
|
|
32
|
+
value = transforms.convert_to_iterable(element['metadata'][md_key])
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Build image feature with named bands
|
|
37
|
+
array_dict = {}
|
|
38
|
+
for im_feat in element['array'].keys():#.dtype.names:
|
|
39
|
+
array_dict['im_'+im_feat] = tf.train.Feature(
|
|
40
|
+
float_list = tf.train.FloatList(
|
|
41
|
+
value = element['array'][im_feat].flatten()))
|
|
42
|
+
|
|
43
|
+
# Combine
|
|
44
|
+
feature = {**md_dict, **array_dict}
|
|
45
|
+
|
|
46
|
+
# Build example and serialize
|
|
47
|
+
return tf.train.Example(
|
|
48
|
+
features = tf.train.Features(feature = feature)).SerializeToString()
|
|
49
|
+
|
|
50
|
+
def array_to_example(structured_array):
|
|
51
|
+
""""Convert structured numpy array to tf.Example proto."""
|
|
52
|
+
feature = {}
|
|
53
|
+
for f in structured_array.dtype.names:
|
|
54
|
+
feature[f] = tf.train.Feature(
|
|
55
|
+
float_list = tf.train.FloatList(
|
|
56
|
+
value = structured_array[f].flatten()))
|
|
57
|
+
return tf.train.Example(
|
|
58
|
+
features = tf.train.Features(feature = feature))
|
|
59
|
+
|
|
60
|
+
class WriteTFExample(beam.PTransform):
|
|
61
|
+
"""Write example"""
|
|
62
|
+
def __init__(self, output_dir, file_name_suffix='.tfrecord.gz'):
|
|
63
|
+
self.output_dir = output_dir
|
|
64
|
+
self.file_name_suffix = file_name_suffix
|
|
65
|
+
|
|
66
|
+
def expand(self, pcoll):
|
|
67
|
+
return (
|
|
68
|
+
pcoll
|
|
69
|
+
| 'Write to TFRecord' >> beam.io.WriteToTFRecord(
|
|
70
|
+
self.output_dir,
|
|
71
|
+
file_name_suffix=self.file_name_suffix
|
|
72
|
+
)
|
|
73
|
+
)
|