Skip to content

Commit

Permalink
Merge pull request #153 from developmentseed/enh/152-auth
Browse files Browse the repository at this point in the history
HTTP authentication
  • Loading branch information
wronk authored Nov 12, 2019
2 parents 089ce95 + 8b8663a commit eb7146e
Show file tree
Hide file tree
Showing 12 changed files with 49 additions and 33 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: 2
jobs:
build:
docker:
- image: circleci/python:3.5
- image: circleci/python:3.6-stretch

working_directory: ~/label-maker
steps:
Expand Down
8 changes: 5 additions & 3 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
0.6.0 (2018-11-06)
0.6.1 (2019-11-11)
------------------
- Added ability to use HTTP Authentication for TMS endpoints (#152)

0.6.0 (2019-11-06)
------------------
- Use sys.exectuable in place of python string (#124)
- Correct script reference to fix bug in skynet train example (#129)
- Add s3 requirement to rasterio (#137)
- users can split data into more groups than train and test, for example train/test/validate, and specify the ratio for
each split (#149)



0.5.1 (2018-11-12)
------------------
- Skip invalid or empty geometries which prevent segmentation rendering (#118)
Expand Down
3 changes: 3 additions & 0 deletions docs/parameters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ Here is the full list of configuration parameters you can specify in a ``config.

Remote files like a `WMS endpoint <http://www.opengeospatial.org/standards/wms>`_ ``GetMap`` request. Fill out all necessary parameters except ``bbox`` which should be set as ``{bbox}``. Ex: ``'https://basemap.nationalmap.gov/arcgis/services/USGSImageryOnly/MapServer/WMSServer?SERVICE=WMS&REQUEST=GetMap&VERSION=1.1.1&LAYERS=0&STYLES=&FORMAT=image%2Fjpeg&TRANSPARENT=false&HEIGHT=256&WIDTH=256&SRS=EPSG%3A3857&BBOX={bbox}'``

**http_auth**: list
Optional parameter to specify a username and password for restricted WMS services. For example, ``['my_username', 'my_password']``.

**background_ratio**: float
Specify how many background (or "negative") training examples to create when there is only one class specified with the ``classes`` parameter. Label Maker will generate ``background_ratio`` times the number of images matching the one class.

Expand Down
2 changes: 1 addition & 1 deletion label_maker/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,4 @@ def class_test(value):
image_function = get_image_function(imagery)

for tile in tiles:
image_function(tile, imagery, tiles_dir, imagery_offset)
image_function(tile, imagery, tiles_dir, imagery_offset, kwargs)
4 changes: 4 additions & 0 deletions label_maker/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ def cli():
config['country'] = op.splitext(op.basename(config.get('geojson')))[0]
config['bounding_box'] = get_bounds(json.load(open(config.get('geojson'), 'r')))

# Convert HTTP auth from list to tuple if it exists
if 'http_auth' in config.keys():
config['http_auth'] = tuple(config['http_auth'])

if cmd == 'download':
download_mbtiles(dest_folder=dest_folder, **config)
elif cmd == 'labels':
Expand Down
38 changes: 21 additions & 17 deletions label_maker/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,43 +9,46 @@
from label_maker.utils import is_tif


def package_directory(dest_folder, classes, imagery, ml_type, seed=False, split_names=['train', 'test'],
split_vals=[0.8, .2], **kwargs):
def package_directory(dest_folder, classes, imagery, ml_type, seed=False,
split_names=('train', 'test'), split_vals=(0.8, .2),
**kwargs):
"""Generate an .npz file containing arrays for training machine learning algorithms
Parameters
------------
dest_folder: str
Folder to save labels, tiles, and final numpy arrays into
classes: list
A list of classes for machine learning training. Each class is defined as a dict
with two required properties:
A list of classes for machine learning training. Each class is defined
as a dict with two required properties:
- name: class name
- filter: A Mapbox GL Filter.
See the README for more details
imagery: str
Imagery template to download satellite images from.
Ex: http://a.tiles.mapbox.com/v4/mapbox.satellite/{z}/{x}/{y}.jpg?access_token=ACCESS_TOKEN
ml_type: str
Defines the type of machine learning. One of "classification", "object-detection", or "segmentation"
Defines the type of machine learning. One of "classification",
"object-detection", or "segmentation"
seed: int
Random generator seed. Optional, use to make results reproducible.
split_vals: list
Default: [0.8, 0.2]
Percentage of data to put in each catagory listed in split_names.
Must be floats and must sum to one.
split_names: list
Default: ['train', 'test']
split_vals: tuple
Percentage of data to put in each catagory listed in split_names. Must
be floats and must sum to one. Default: (0.8, 0.2)
split_names: tupel
Default: ('train', 'test')
List of names for each subset of the data.
**kwargs: dict
Other properties from CLI config passed as keywords to other utility functions
Other properties from CLI config passed as keywords to other utility
functions.
"""
# if a seed is given, use it
if seed:
np.random.seed(seed)

if len(split_names) != len(split_vals):
raise ValueError('`split_names` and `split_vals` must be the same length. Please update your config.')
raise ValueError('`split_names` and `split_vals` must be the same '
'length. Please update your config.')
if not np.isclose(sum(split_vals), 1):
raise ValueError('`split_vals` must sum to one. Please update your config.')

Expand Down Expand Up @@ -105,7 +108,8 @@ def package_directory(dest_folder, classes, imagery, ml_type, seed=False, split_
split_n_samps = [len(x_vals) * val for val in split_vals]

if np.any(split_n_samps == 0):
raise ValueError('split must not generate zero samples per partition, change ratio of values in config file.')
raise ValueError('Split must not generate zero samples per partition. '
'Change ratio of values in config file.')

# Convert into a cumulative sum to get indices
split_inds = np.cumsum(split_n_samps).astype(np.integer)
Expand All @@ -117,8 +121,8 @@ def package_directory(dest_folder, classes, imagery, ml_type, seed=False, split_
save_dict = {}

for si, split_name in enumerate(split_names):
save_dict[f'x_{split_name}'] = split_arrs_x[si]
save_dict[f'y_{split_name}'] = split_arrs_y[si]
save_dict['x_{}'.format(split_name)] = split_arrs_x[si]
save_dict['y_{}'.format(split_name)] = split_arrs_y[si]

np.savez(op.join(dest_folder, 'data.npz'), **save_dict)
print('Saving packaged file to {}'.format(op.join(dest_folder, 'data.npz')))
print('Saving packaged file to {}'.format(op.join(dest_folder, 'data.npz')))
3 changes: 2 additions & 1 deletion label_maker/preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ def preview(dest_folder, number, classes, imagery, ml_type, imagery_offset=False
if n >= number:
break

tile_img = image_function(tile, imagery, class_dir, imagery_offset)
tile_img = image_function(tile, imagery, class_dir, imagery_offset,
kwargs)

if ml_type == 'object-detection':
img = Image.open(tile_img)
Expand Down
11 changes: 6 additions & 5 deletions label_maker/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,18 @@ def class_match(ml_type, label, i):
return np.count_nonzero(label == i)
return None

def download_tile_tms(tile, imagery, folder, *args):
def download_tile_tms(tile, imagery, folder, kwargs):
"""Download a satellite image tile from a tms endpoint"""
o = urlparse(imagery)
_, image_format = op.splitext(o.path)
r = requests.get(url(tile.split('-'), imagery))
r = requests.get(url(tile.split('-'), imagery),
auth=kwargs.get('http_auth'))
tile_img = op.join(folder, '{}{}'.format(tile, image_format))
with open(tile_img, 'wb')as w:
w.write(r.content)
return tile_img

def get_tile_tif(tile, imagery, folder, imagery_offset):
def get_tile_tif(tile, imagery, folder, imagery_offset, kwargs):
"""
Read a GeoTIFF with a window corresponding to a TMS tile
Expand Down Expand Up @@ -87,7 +88,7 @@ def get_tile_tif(tile, imagery, folder, imagery_offset):

return tile_img

def get_tile_wms(tile, imagery, folder, imagery_offset):
def get_tile_wms(tile, imagery, folder, imagery_offset, kwargs):
"""
Read a WMS endpoint with query parameters corresponding to a TMS tile
Expand Down Expand Up @@ -118,7 +119,7 @@ def get_tile_wms(tile, imagery, folder, imagery_offset):

# request the image with the transformed bounding box and save
wms_url = imagery.replace('{bbox}', ','.join([str(b) for b in bbox]))
r = requests.get(wms_url)
r = requests.get(wms_url, auth=kwargs.get('http_auth'))
tile_img = op.join(folder, '{}.{}'.format(tile, image_format))
with open(tile_img, 'wb') as w:
w.write(r.content)
Expand Down
1 change: 1 addition & 0 deletions label_maker/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
'zoom': {'type': 'integer', 'required': True},
'classes': {'type': 'list', 'schema': class_schema, 'required': True},
'imagery': {'type': 'string', 'required': True},
'http_auth': {'type': 'list', 'schema': {'type': 'string'}},
'background_ratio': {'type': 'float'},
'ml_type': {'allowed': ['classification', 'object-detection', 'segmentation'], 'required': True},
'seed': {'type': 'integer'},
Expand Down
2 changes: 1 addition & 1 deletion label_maker/version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
"""Library verison"""
__version__ = '0.6.0'
__version__ = '0.6.1'
Binary file modified test/fixtures/146-195-9.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions test/unit/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def test_get_tile_tif(self):
if not op.isdir(tiles_dir):
makedirs(tiles_dir)

get_tile_tif(tile, 'test/fixtures/drone.tif', tiles_dir, None)
get_tile_tif(tile, 'test/fixtures/drone.tif', tiles_dir, None, {})
test_tile = Image.open('test/tiles/{}.jpg'.format(tile))
fixture_tile = Image.open('test/fixtures/{}.jpg'.format(tile))
self.assertEqual(test_tile, fixture_tile)
Expand All @@ -95,7 +95,7 @@ def test_get_tile_tif_offset(self):
if not op.isdir(tiles_dir):
makedirs(tiles_dir)

get_tile_tif(tile, 'test/fixtures/drone.tif', tiles_dir, [128, 64])
get_tile_tif(tile, 'test/fixtures/drone.tif', tiles_dir, [128, 64], {})
test_tile = Image.open('test/tiles/{}.jpg'.format(tile))
fixture_tile = Image.open('test/fixtures/{}_offset.jpg'.format(tile))
self.assertEqual(test_tile, fixture_tile)
Expand All @@ -109,7 +109,7 @@ def test_get_tile_vrt(self):
if not op.isdir(tiles_dir):
makedirs(tiles_dir)

get_tile_tif(tile, 'test/fixtures/drone.vrt', tiles_dir, None)
get_tile_tif(tile, 'test/fixtures/drone.vrt', tiles_dir, None, {})
test_tile = Image.open('test/tiles/{}.jpg'.format(tile))
fixture_tile = Image.open('test/fixtures/{}.jpg'.format(tile))
self.assertEqual(test_tile, fixture_tile)
Expand All @@ -125,7 +125,7 @@ def test_get_tile_wms(self):

nasa_url = 'https://gibs.earthdata.nasa.gov/wms/epsg4326/best/wms.cgi?SERVICE=WMS&REQUEST=GetMap&layers=MODIS_Aqua_CorrectedReflectance_TrueColor&version=1.3.0&crs=EPSG:4326&transparent=false&width=256&height=256&bbox={bbox}&format=image/jpeg&time=2019-03-05'

get_tile_wms(tile, nasa_url, tiles_dir, None)
get_tile_wms(tile, nasa_url, tiles_dir, None, {})
test_tile = Image.open('test/tiles/{}.jpeg'.format(tile))
fixture_tile = Image.open('test/fixtures/{}.jpeg'.format(tile))
self.assertEqual(test_tile, fixture_tile)
Expand Down

0 comments on commit eb7146e

Please sign in to comment.