Skip to content

Commit 83e8d26

Browse files
committed
Merge branch 'master' of github.com:developmentseed/label-maker
2 parents 17a0da9 + 1bed60e commit 83e8d26

File tree

10 files changed

+91
-23
lines changed

10 files changed

+91
-23
lines changed

.circleci/config.yml

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,34 @@ jobs:
2525
name: Integration Tests
2626
command: |
2727
sudo pip install .
28-
mkdir integration
29-
cp test/fixtures/integration/portugal-z17.mbtiles integration/portugal-z17.mbtiles
30-
cp -r test/fixtures/integration/tiles integration/tiles
28+
29+
## Classification integration tests
30+
mkdir integration-cl
31+
cp test/fixtures/integration/portugal-z17.mbtiles integration-cl/portugal-z17.mbtiles
32+
cp -r test/fixtures/integration/tiles integration-cl/tiles
3133
3234
# Test `label-maker labels --sparse`; must be run before `label-maker labels` so `verify-package.py` passes
33-
label-maker labels --dest integration --config test/fixtures/integration/config.integration_sparse.json --sparse > stdout_sparse
35+
label-maker labels --dest integration-cl --config test/fixtures/integration/config.integration_sparse.json --sparse > stdout_sparse
3436
python test/verify-sparse-labels.py
3537
# Test `label-maker labels`
36-
label-maker labels --dest integration --config test/fixtures/integration/config.integration.json > stdout
37-
python test/verify-labels.py
38+
label-maker labels --dest integration-cl --config test/fixtures/integration/config.integration.json > stdout
39+
python test/verify-labels-cl.py
3840
# Test `label-maker package`
39-
label-maker package --dest integration --config test/fixtures/integration/config.integration.json
40-
python test/verify-package.py
41+
label-maker package --dest integration-cl --config test/fixtures/integration/config.integration.json
42+
python test/verify-package-cl.py
43+
44+
# segmentation integration tests
45+
mkdir integration-sg
46+
cp test/fixtures/integration/portugal-z17.mbtiles integration-sg/portugal-z17.mbtiles
47+
cp -r test/fixtures/integration/tiles integration-sg/tiles
48+
label-maker labels --dest integration-sg --config test/fixtures/integration/config.integration.segmentation.json
49+
python test/verify-labels-sg.py
4150
4251
# Retest that `label-maker labels` works, but from outside the module directory
4352
cd ~
44-
label-maker labels --dest label-maker/integration --config label-maker/test/fixtures/integration/config.integration.json > stdout
45-
cd ~/label-maker
46-
python test/verify-labels.py
53+
label-maker labels --dest label-maker/integration-cl --config label-maker/test/fixtures/integration/config.integration.json > stdout
54+
cd ~/label-maker
55+
python test/verify-labels-cl.py
56+
57+
- store_artifacts:
58+
path: ~/label-maker/integration-sg/labels/

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ MANIFEST
1010
.cache
1111
data
1212
config.json
13+
stdout*
14+
/integration*

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ Before running any commands, it is necessary to create a `config.json` file to s
4141
- `bounding_box`: The bounding box to create images from. This should be given in the form: `[xmin, ymin, xmax, ymax]` as longitude and latitude values between `[-180, 180]` and `[-90, 90]` respectively. Values should use the WGS84 datum, with longitude and latitude units of decimal degrees.
4242
- `zoom`: The [zoom level](http://wiki.openstreetmap.org/wiki/Zoom_levels) to create images as. This functions as a rough proxy for resolution. Values should be given as integers.
4343
- `classes`: An array of classes for machine learning training. Each class is defined as an object with two required properties:
44-
- `name`: class name
44+
- `name`: The class name
4545
- `filter`: A [Mapbox GL Filter](https://www.mapbox.com/mapbox-gl-js/style-spec#other-filter) to define any vector features matching this class. Filters are applied with the standalone [featureFilter](https://github.com/mapbox/mapbox-gl-js/tree/master/src/style-spec/feature_filter) from Mapbox GL JS.
46+
- `buffer`: The number of pixels to buffer the geometry by. This is an optional parameter to buffer the label for `object-detection` and `segmentation` tasks. Accepts any number (positive or negative). It uses [Shapely `object.buffer`](https://shapely.readthedocs.io/en/latest/manual.html#object.buffer) to calculate the final geometry. You can verify that your buffer options create the desired labels by inspecting the files created in `data/labels/` after running the `labels` command.
4647
- `imagery`: A template string for a tiled imagery service. Note that you will generally need an API key to obtain images and there may be associated costs. The above example requires a [Mapbox access token](https://www.mapbox.com/help/how-access-tokens-work/)
4748
- `background_ratio`: For single-class classification problems, we need to download images with no matching class. We will download `background_ratio` times the number of images matching the one class.
4849
- `ml_type`: One of `"classification"`, `"object-detection"`, or `"segmentation"`. For the final label numpy arrays (`y_train` and `y_test`), we will produce a different label depending upon the `type`.

label_maker/label.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# pylint: disable=unused-argument
1+
# pylint: disable=unused-argument,too-many-nested-blocks
22
"""Create label data from OSM QA tiles for specified classes"""
33

44
from os import makedirs, path as op
@@ -196,7 +196,9 @@ def _mapper(x, y, z, data, args):
196196
for i, cl in enumerate(classes):
197197
ff = create_filter(cl.get('filter'))
198198
if ff(feat):
199-
geo = shape(feat['geometry']).buffer(cl.get('buffer', 0))
199+
geo = shape(feat['geometry'])
200+
if cl.get('buffer'):
201+
geo = geo.buffer(cl.get('buffer'), 4)
200202
bb = _pixel_bbox(geo.bounds) + [i + 1]
201203
bboxes = np.append(bboxes, np.array([bb]), axis=0)
202204
return ('{!s}-{!s}-{!s}'.format(x, y, z), bboxes)
@@ -207,7 +209,9 @@ def _mapper(x, y, z, data, args):
207209
ff = create_filter(cl.get('filter'))
208210
if ff(feat):
209211
feat['geometry']['coordinates'] = _convert_coordinates(feat['geometry']['coordinates'])
210-
geo = shape(feat['geometry']).buffer(cl.get('buffer', 0))
212+
geo = shape(feat['geometry'])
213+
if cl.get('buffer'):
214+
geo = geo.buffer(cl.get('buffer'), 4)
211215
geos.append((mapping(geo), i + 1))
212216
result = rasterize(geos, out_shape=(256, 256))
213217
return ('{!s}-{!s}-{!s}'.format(x, y, z), result)
@@ -277,6 +281,10 @@ def _tile_results_summary(ml_type, classes):
277281
class_tile_counts = list(np.sum(labels, axis=0))
278282
for i, cl in enumerate(classes):
279283
print('{}: {} tiles'.format(cl.get('name'), int(class_tile_counts[i + 1])))
284+
elif ml_type == 'segmentation':
285+
for i, cl in enumerate(classes):
286+
count = len([l for l in labels if class_match(ml_type, l, i + 1)])
287+
print('{}: {} tiles'.format(cl.get('name'), count))
280288

281289
print('Total tiles: {}'.format(len(all_tiles)))
282290

label_maker/validate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
'type': 'dict',
1515
'schema': {'name': {'type': 'string', 'required': True},
1616
'filter': {'type': 'list', 'required': True},
17-
'buffer': {'type': 'integer'}}
17+
'buffer': {'type': 'float'}}
1818
}
1919

2020
lat_schema = {'type': 'float', 'min': -90, 'max': 90}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"country": "portugal",
3+
"bounding_box": [
4+
-9.4575,
5+
38.8467,
6+
-9.4510,
7+
38.8513
8+
],
9+
"zoom": 17,
10+
"classes": [
11+
{ "name": "Water Tower", "filter": ["==", "man_made", "water_tower"], "buffer": 5 },
12+
{ "name": "Building", "filter": ["has", "building"] },
13+
{ "name": "Farmland", "filter": ["==", "landuse", "farmland"] },
14+
{ "name": "Ruins", "filter": ["==", "historic", "ruins"] },
15+
{ "name": "Parking", "filter": ["==", "amenity", "parking"] },
16+
{ "name": "Roads", "filter": ["has", "highway"], "buffer": 3 }
17+
],
18+
"imagery": "https://api.mapbox.com/v4/mapbox.satellite/{z}/{x}/{y}.jpg?access_token=ACCESS_TOKEN",
19+
"background_ratio": 1,
20+
"ml_type": "segmentation",
21+
"seed": 19
22+
}

test/verify-labels.py renamed to test/verify-labels-cl.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@
1515
'62093-50163-17': np.array([0, 0, 0, 0, 1, 1, 1])
1616
}
1717

18-
labels = np.load('integration/labels.npz')
18+
labels = np.load('integration-cl/labels.npz')
1919
assert len(labels.files) == len(expected_labels.keys()) # First check number of tiles
20+
2021
for tile in labels.files:
2122
assert np.array_equal(expected_labels[tile], labels[tile]) # Now, content
2223

2324
# our GeoJSON looks like the fixture
2425
expected_geojson = json.load(open('test/fixtures/integration/classification.geojson'))
25-
geojson = json.load(open('integration/classification.geojson'))
26+
geojson = json.load(open('integration-cl/classification.geojson'))
2627

2728
for feature in geojson['features']:
2829
assert feature in expected_geojson['features']
@@ -37,7 +38,7 @@
3738
Parking: 1 tiles
3839
Roads: 8 tiles
3940
Total tiles: 9
40-
Writing out labels to integration/labels.npz
41+
Writing out labels to integration-cl/labels.npz
4142
"""
4243

4344
with open('stdout', 'r') as output:

test/verify-labels-sg.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"""Validate that the output produced by integration testing on 'label-maker labels'
2+
matches our expectations, specifically for segmentation tasks"""
3+
import numpy as np
4+
5+
# our labels should look like this
6+
expected_sums = {
7+
'62092-50162-17': 0,
8+
'62092-50163-17': 2760,
9+
'62092-50164-17': 13728,
10+
'62093-50162-17': 38004,
11+
'62093-50164-17': 2688,
12+
'62094-50162-17': 21630,
13+
'62094-50164-17': 19440,
14+
'62094-50163-17': 21895,
15+
'62093-50163-17': 32198
16+
}
17+
18+
labels = np.load('integration-sg/labels.npz')
19+
assert len(labels.files) == len(expected_sums.keys()) # First check number of tiles
20+
21+
for tile in labels.files:
22+
assert expected_sums[tile] == np.sum(labels[tile])

test/verify-package.py renamed to test/verify-package-cl.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Validate that the output produced by integration testing on 'label-maker package' matches our expectations"""
22
import numpy as np
33

4-
data = np.load('integration/data.npz')
4+
data = np.load('integration-cl/data.npz')
55
# validate our image data with sums and shapes
66
assert np.sum(data['x_train']) == 144752757
77
assert np.sum(data['x_test']) == 52758414

test/verify-sparse-labels.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414
'62093-50163-17': np.array([0, 0, 0, 0, 1, 1, 1])
1515
}
1616

17-
labels = np.load('integration/labels.npz')
17+
labels = np.load('integration-cl/labels.npz')
1818
assert len(labels.files) == len(expected_labels.keys()) # First check number of tiles
1919
for tile in labels.files:
2020
assert np.array_equal(expected_labels[tile], labels[tile]) # Now, content
2121

2222
# our GeoJSON looks like the fixture
2323
expected_geojson = json.load(open('test/fixtures/integration/classification.geojson'))
24-
geojson = json.load(open('integration/classification.geojson'))
24+
geojson = json.load(open('integration-cl/classification.geojson'))
2525

2626
for feature in geojson['features']:
2727
assert feature in expected_geojson['features']
@@ -37,7 +37,7 @@
3737
Roads: 8 tiles
3838
Total tiles: 9
3939
Using sparse mode; subselected 0 background tiles
40-
Writing out labels to integration/labels.npz
40+
Writing out labels to integration-cl/labels.npz
4141
"""
4242

4343
with open('stdout_sparse', 'r') as output:

0 commit comments

Comments
 (0)