Module delta.imagery.imagery_config
Configuration options specific to imagery.
Expand source code
# Copyright © 2020, United States Government, as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All rights reserved.
#
# The DELTA (Deep Earth Learning, Tools, and Analysis) platform is
# licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Configuration options specific to imagery.
"""
import os
import os.path
import numpy as np
import appdirs
from delta.config import config, DeltaConfigComponent, validate_path, validate_positive
from delta.config.extensions import image_reader, preprocess_function
from . import disk_folder_cache
class ImageSet:
"""
Specifies a set of image files.
The images can be accessed by using the `ImageSet` as an iterable.
"""
def __init__(self, images, image_type, preprocess=None, nodata_value=None):
"""
Parameters
----------
images: Iterator[str]
Image filenames
image_type: str
The image type as a string (i.e., tiff, worldview, landsat). Must have
been previously registered with `delta.config.extensions.register_image_reader`.
preprocess: Callable
Optional preprocessing function to apply to the image
following the signature in `delta.imagery.delta_image.DeltaImage.set_preprocess`.
nodata_value: image dtype
A no data value for pixels to disregard
"""
self._images = images
self._image_type = image_type
self._preprocess = preprocess
self._nodata_value = nodata_value
def type(self):
"""
Returns
-------
str:
The type of the image
"""
return self._image_type
def preprocess(self):
"""
Returns
-------
Callable:
The preprocessing function
"""
return self._preprocess
def nodata_value(self):
"""
Returns
-------
image dtype:
Value of pixels to disregard.
"""
return self._nodata_value
def set_nodata_value(self, nodata):
"""
Set the pixel value to disregard.
Parameters
----------
nodata: image dtype
The pixel value to set as nodata
"""
self._nodata_value = nodata
def load(self, index):
"""
Loads the image of the given index.
Parameters
----------
index: int
Index of the image to load.
Returns
-------
`delta.imagery.delta_image.DeltaImage`:
The image
"""
img = image_reader(self.type())(self[index], self.nodata_value())
if self._preprocess:
img.set_preprocess(self._preprocess)
return img
def __len__(self):
return len(self._images)
def __getitem__(self, index):
if index < 0 or index >= len(self):
raise IndexError('Index %s out of range.' % (index))
return self._images[index]
def __iter__(self):
return self._images.__iter__()
__DEFAULT_EXTENSIONS = {'tiff' : '.tiff',
'worldview' : '.zip',
'landsat' : '.zip',
'npy' : '.npy',
'sentinel1' : '.zip'}
def __extension(conf):
if conf['extension'] == 'default':
return __DEFAULT_EXTENSIONS.get(conf['type'])
return conf['extension']
def __find_images(conf, matching_images=None, matching_conf=None):
'''
Find the images specified by a given configuration, returning a list of images.
If matching_images and matching_conf are specified, we find the labels matching these images.
'''
images = []
if conf['type'] not in __DEFAULT_EXTENSIONS:
raise ValueError('Unexpected image type %s.' % (conf['type']))
if conf['files']:
assert conf['file_list'] is None and conf['directory'] is None, 'Only one image specification allowed.'
images = conf['files']
for (i, im) in enumerate(images):
images[i] = os.path.normpath(im)
elif conf['file_list']:
assert conf['directory'] is None, 'Only one image specification allowed.'
with open(conf['file_list'], 'r') as f:
for line in f:
images.append(os.path.normpath(line.strip()))
elif conf['directory']:
extension = __extension(conf)
if not os.path.exists(conf['directory']):
raise ValueError('Supplied images directory %s does not exist.' % (conf['directory']))
if matching_images is None:
for root, _, filenames in os.walk(conf['directory'],
followlinks=True):
for filename in filenames:
if filename.endswith(extension):
images.append(os.path.join(root, filename))
else:
# find matching labels
for m in matching_images:
rel_path = os.path.relpath(m, matching_conf['directory'])
label_path = os.path.join(conf['directory'], rel_path)
if matching_conf['directory'] is None:
images.append(os.path.splitext(label_path)[0] + extension)
else:
# if custom extension, remove it
label_path = label_path[:-len(__extension(matching_conf))]
images.append(label_path + extension)
for img in images:
if not os.path.exists(img):
raise ValueError('Image file %s does not exist.' % (img))
return images
def load_images_labels(images_comp, labels_comp, classes_comp):
'''
Takes two configuration subsections and returns (image set, label set). Also takes classes
configuration to apply preprocessing function to labels.
'''
images_dict = images_comp._config_dict #pylint:disable=protected-access
labels_dict = labels_comp._config_dict #pylint:disable=protected-access
images = __find_images(images_dict)
if images_dict['directory']:
if labels_dict['files'] or labels_dict['file_list']:
raise ValueError('Image directory only supported with label directory.')
if labels_dict['directory']:
# remove images in same directory ending with label's extension (can have .tiff and _label.tiff in same dir)
if os.path.realpath(labels_dict['directory']).startswith(os.path.realpath(images_dict['directory'])):
label_extension = __extension(labels_dict)
images = [img for img in images if not img.endswith(label_extension)]
pre = images_comp.preprocess_function()
imageset = ImageSet(images, images_dict['type'], pre, images_dict['nodata_value'])
if (labels_dict['files'] is None) and (labels_dict['file_list'] is None) and (labels_dict['directory'] is None):
return (imageset, None)
labels = __find_images(labels_dict, images, images_dict)
if len(labels) != len(images):
raise ValueError('%d images found, but %d labels found.' % (len(images), len(labels)))
labels_nodata = labels_dict['nodata_value']
pre_orig = labels_comp.preprocess_function()
# we shift the label images to always be 0...n[+1], Class 1, Class 2, ... Class N, [nodata]
def class_shift(data, _, dummy):
if pre_orig is not None:
data = pre_orig(data, _, dummy)
# set any nodata values to be past the expected range
if labels_nodata is not None:
nodata_indices = (data == labels_nodata)
conv = classes_comp.classes_to_indices_func()
if conv is not None:
data = conv(data)
if labels_nodata is not None:
data[nodata_indices] = len(classes_comp)
return data
return (imageset, ImageSet(labels, labels_dict['type'],
class_shift, len(classes_comp) if labels_nodata is not None else None))
class ImagePreprocessConfig(DeltaConfigComponent):
"""
Configuration for image preprocessing.
Expects a list of preprocessing functions registered
with `delta.config.extensions.register_preprocess`.
"""
def __init__(self):
super().__init__()
self._functions = []
def _load_dict(self, d, base_dir):
if d is None:
self._functions = []
return
if not d:
return
self._functions = []
assert isinstance(d, list), 'preprocess should be list of commands'
for func in d:
if isinstance(func, str):
self._functions.append((func, {}))
else:
assert isinstance(func, dict), 'preprocess items must be strings or dicts'
assert len(func) == 1, 'One preprocess item per list entry.'
name = list(func.keys())[0]
self._functions.append((name, func[name]))
def function(self, image_type):
"""
Parameters
----------
image_type: str
Type of the image
Returns
-------
Callable:
The specified preprocessing function to apply to the image.
"""
prep = lambda data, _, dummy: data
for (name, args) in self._functions:
t = preprocess_function(name)
assert t is not None, 'Preprocess function %s not found.' % (name)
p = t(image_type=image_type, **args)
def helper(cur, prev):
return lambda data, roi, bands: cur(prev(data, roi, bands), roi, bands)
prep = helper(p, prep)
return prep
def _validate_paths(paths, base_dir):
out = []
for p in paths:
out.append(validate_path(p, base_dir))
return out
class ImageSetConfig(DeltaConfigComponent):
"""
Configuration for a set of images.
Used for images, labels, and validation images and labels.
"""
def __init__(self, name=None):
super().__init__()
self.register_field('type', str, 'type', None, 'Image type.')
self.register_field('files', list, None, _validate_paths, 'List of image files.')
self.register_field('file_list', str, None, validate_path, 'File listing image files.')
self.register_field('directory', str, None, validate_path, 'Directory of image files.')
self.register_field('extension', str, None, None, 'Image file extension.')
self.register_field('nodata_value', (float, int), None, None, 'Value of pixels to ignore.')
if name:
self.register_arg('type', '--' + name + '-type', name + '_type')
self.register_arg('file_list', '--' + name + '-file-list', name + '_file_list')
self.register_arg('directory', '--' + name + '-dir', name + '_directory')
self.register_arg('extension', '--' + name + '-extension', name + '_extension')
self.register_component(ImagePreprocessConfig(), 'preprocess')
self._name = name
def preprocess_function(self):
"""
Returns
-------
Callable:
Preprocessing function for the set of images.
"""
return self._components['preprocess'].function(self._config_dict['type'])
def setup_arg_parser(self, parser, components = None) -> None:
if self._name is None:
return
super().setup_arg_parser(parser, components)
parser.add_argument("--" + self._name, dest=self._name, required=False,
help="Specify a single image file.")
def parse_args(self, options):
if self._name is None:
return
super().parse_args(options)
if hasattr(options, self._name) and getattr(options, self._name) is not None:
self._config_dict['files'] = [getattr(options, self._name)]
self._config_dict['directory'] = None
self._config_dict['file_list'] = None
class LabelClass:
"""
Label configuration.
"""
def __init__(self, value, name=None, color=None, weight=None):
"""
Parameters
----------
value: int
Pixel of the label
name: str
Name of the class to display
color: int
In visualizations, set the class to this RGB color.
weight: float
During training weight this class by this amount.
"""
color_order = [0x1f77b4, 0xff7f0e, 0x2ca02c, 0xd62728, 0x9467bd, 0x8c564b, \
0xe377c2, 0x7f7f7f, 0xbcbd22, 0x17becf]
if name is None:
name = 'Class ' + str(value)
if color is None:
color = color_order[value] if value < len(color_order) else 0
self.value = value
self.name = name
self.color = color
self.weight = weight
self.end_value = None
def __repr__(self):
return 'Color: ' + self.name
class ClassesConfig(DeltaConfigComponent):
"""
Configuration for classes.
Specify either a number of classes or list of classes with details.
"""
def __init__(self):
super().__init__()
self._classes = []
self._conversions = []
def __iter__(self):
return self._classes.__iter__()
def __getitem__(self, key):
return self._classes[key]
def __len__(self):
return len(self._classes)
# overwrite model entirely if updated (don't want combined layers from multiple files)
def _load_dict(self, d : dict, base_dir):
if not d:
return
self._config_dict = d
self._classes = []
if isinstance(d, int):
for i in range(d):
self._classes.append(LabelClass(i))
elif isinstance(d, list):
for (i, c) in enumerate(d):
if isinstance(c, int): # just pixel value
self._classes.append(LabelClass(i))
else:
keys = c.keys()
assert len(keys) == 1, 'Dict should have name of pixel value.'
k = next(iter(keys))
assert isinstance(k, int), 'Class label value must be int.'
inner_dict = c[k]
self._classes.append(LabelClass(k, str(inner_dict.get('name')),
inner_dict.get('color'), inner_dict.get('weight')))
elif isinstance(d, dict):
for k in d:
assert isinstance(k, int), 'Class label value must be int.'
self._classes.append(LabelClass(k, str(d[k].get('name')),
d[k].get('color'), d[k].get('weight')))
else:
raise ValueError('Expected classes to be an int or list in config, was ' + str(d))
# make sure the order is consistent for same values, and create preprocessing function
self._conversions = []
self._classes = sorted(self._classes, key=lambda x: x.value)
for (i, v) in enumerate(self._classes):
if v.value != i:
self._conversions.append(v.value)
v.end_value = i
def class_id(self, class_name):
"""
Parameters
----------
class_name: int or str
Either the original pixel value in images (int) or the name (str) of a class.
The special value 'nodata' will give the nodata class, if any.
Returns
-------
int:
the ID of the class in the labels after default image preprocessing (labels are arranged
to a canonical order, with nodata always coming after them.)
"""
if class_name == len(self._classes) or class_name == 'nodata':
return len(self._classes)
for (i, c) in enumerate(self._classes):
if class_name in (c.value, c.name):
return i
raise ValueError('Class ' + str(class_name) + ' not found.')
def weights(self):
"""
Returns
-------
List[float]
List of class weights for use in training, if specified.
"""
weights = []
for c in self._classes:
if c.weight is not None:
weights.append(c.weight)
if not weights:
return None
assert len(weights) == len(self._classes), 'For class weights, either all or none must be specified.'
return weights
def classes_to_indices_func(self):
"""
Returns
-------
Callable[[numpy.ndarray], numpy.ndarray]:
Function to convert label image to canonical form
"""
if not self._conversions:
return None
def convert(data):
assert isinstance(data, np.ndarray)
for (i, c) in enumerate(self._conversions):
data[data == c] = i
return data
return convert
def indices_to_classes_func(self):
"""
Returns
-------
Callable[[numpy.ndarray], numpy.ndarray]:
Reverse of `classes_to_indices_func`.
"""
if not self._conversions:
return None
def convert(data):
assert isinstance(data, np.ndarray)
for (i, c) in reversed(list(enumerate(self._conversions))):
data[data == i] = c
return data
return convert
class DatasetConfig(DeltaConfigComponent):
"""
Configuration for a dataset.
"""
def __init__(self):
super().__init__('Dataset')
self.register_component(ImageSetConfig('image'), 'images', '__image_comp')
self.register_component(ImageSetConfig('label'), 'labels', '__label_comp')
self.__images = None
self.__labels = None
self.register_component(ClassesConfig(), 'classes')
def reset(self):
super().reset()
self.__images = None
self.__labels = None
def images(self) -> ImageSet:
"""
Returns
-------
ImageSet:
the training images
"""
if self.__images is None:
(self.__images, self.__labels) = load_images_labels(self._components['images'],
self._components['labels'],
self._components['classes'])
return self.__images
def labels(self) -> ImageSet:
"""
Returns
-------
ImageSet:
the label images
"""
if self.__labels is None:
(self.__images, self.__labels) = load_images_labels(self._components['images'],
self._components['labels'],
self._components['classes'])
return self.__labels
class CacheConfig(DeltaConfigComponent):
"""
Configuration for cache.
"""
def __init__(self):
super().__init__()
self.register_field('dir', str, None, validate_path, 'Cache directory.')
self.register_field('limit', int, None, validate_positive, 'Number of items to cache.')
self._cache_manager = None
def reset(self):
super().reset()
self._cache_manager = None
def manager(self) -> disk_folder_cache.DiskCache:
"""
Returns
-------
`disk_folder_cache.DiskCache`:
the object to manage the cache
"""
if self._cache_manager is None:
# Auto-populating defaults here is a workaround so small tools can skip the full
# command line config setup. Could be improved!
if 'dir' not in self._config_dict:
self._config_dict['dir'] = 'default'
if 'limit' not in self._config_dict:
self._config_dict['limit'] = 8
cdir = self._config_dict['dir']
if cdir == 'default':
cdir = appdirs.AppDirs('delta', 'nasa').user_cache_dir
self._cache_manager = disk_folder_cache.DiskCache(cdir, self._config_dict['limit'])
return self._cache_manager
def _validate_tile_size(size, _):
assert len(size) == 2, 'Size must have two components.'
assert isinstance(size[0], int) and isinstance(size[1], int), 'Size must be integer.'
assert size[0] > 0 and size[1] > 1, 'Size must be positive.'
return size
class IOConfig(DeltaConfigComponent):
"""
Configuration for I/O.
"""
def __init__(self):
super().__init__('IO')
self.register_field('threads', int, None, None, 'Number of threads to use.')
self.register_field('tile_size', list, 'tile_size', _validate_tile_size,
'Size of an image tile to load in memory at once.')
self.register_field('interleave_blocks', int, 'interleave_blocks', None,
'Number of blocks to interleave at a time.')
self.register_arg('threads', '--threads')
self.register_component(CacheConfig(), 'cache')
def threads(self):
"""
Returns
-------
int:
number of threads to use for I/O
"""
if 'threads' in self._config_dict and self._config_dict['threads']:
return self._config_dict['threads']
return min(1, os.cpu_count() // 2)
def register():
"""
Registers imagery config options with the global config manager.
cmd_args enables command line options if set to true.
"""
config.register_component(DatasetConfig(), 'dataset')
config.register_component(IOConfig(), 'io')
Functions
def load_images_labels(images_comp, labels_comp, classes_comp)
-
Takes two configuration subsections and returns (image set, label set). Also takes classes configuration to apply preprocessing function to labels.
Expand source code
def load_images_labels(images_comp, labels_comp, classes_comp): ''' Takes two configuration subsections and returns (image set, label set). Also takes classes configuration to apply preprocessing function to labels. ''' images_dict = images_comp._config_dict #pylint:disable=protected-access labels_dict = labels_comp._config_dict #pylint:disable=protected-access images = __find_images(images_dict) if images_dict['directory']: if labels_dict['files'] or labels_dict['file_list']: raise ValueError('Image directory only supported with label directory.') if labels_dict['directory']: # remove images in same directory ending with label's extension (can have .tiff and _label.tiff in same dir) if os.path.realpath(labels_dict['directory']).startswith(os.path.realpath(images_dict['directory'])): label_extension = __extension(labels_dict) images = [img for img in images if not img.endswith(label_extension)] pre = images_comp.preprocess_function() imageset = ImageSet(images, images_dict['type'], pre, images_dict['nodata_value']) if (labels_dict['files'] is None) and (labels_dict['file_list'] is None) and (labels_dict['directory'] is None): return (imageset, None) labels = __find_images(labels_dict, images, images_dict) if len(labels) != len(images): raise ValueError('%d images found, but %d labels found.' % (len(images), len(labels))) labels_nodata = labels_dict['nodata_value'] pre_orig = labels_comp.preprocess_function() # we shift the label images to always be 0...n[+1], Class 1, Class 2, ... Class N, [nodata] def class_shift(data, _, dummy): if pre_orig is not None: data = pre_orig(data, _, dummy) # set any nodata values to be past the expected range if labels_nodata is not None: nodata_indices = (data == labels_nodata) conv = classes_comp.classes_to_indices_func() if conv is not None: data = conv(data) if labels_nodata is not None: data[nodata_indices] = len(classes_comp) return data return (imageset, ImageSet(labels, labels_dict['type'], class_shift, len(classes_comp) if labels_nodata is not None else None))
def register()
-
Registers imagery config options with the global config manager.
cmd_args enables command line options if set to true.
Expand source code
def register(): """ Registers imagery config options with the global config manager. cmd_args enables command line options if set to true. """ config.register_component(DatasetConfig(), 'dataset') config.register_component(IOConfig(), 'io')
Classes
class CacheConfig
-
Configuration for cache.
Parameters
section_header
:Optional[str]
- The title of the section for command line arguments in the help.
Expand source code
class CacheConfig(DeltaConfigComponent): """ Configuration for cache. """ def __init__(self): super().__init__() self.register_field('dir', str, None, validate_path, 'Cache directory.') self.register_field('limit', int, None, validate_positive, 'Number of items to cache.') self._cache_manager = None def reset(self): super().reset() self._cache_manager = None def manager(self) -> disk_folder_cache.DiskCache: """ Returns ------- `disk_folder_cache.DiskCache`: the object to manage the cache """ if self._cache_manager is None: # Auto-populating defaults here is a workaround so small tools can skip the full # command line config setup. Could be improved! if 'dir' not in self._config_dict: self._config_dict['dir'] = 'default' if 'limit' not in self._config_dict: self._config_dict['limit'] = 8 cdir = self._config_dict['dir'] if cdir == 'default': cdir = appdirs.AppDirs('delta', 'nasa').user_cache_dir self._cache_manager = disk_folder_cache.DiskCache(cdir, self._config_dict['limit']) return self._cache_manager
Ancestors
Methods
def manager(self) ‑> DiskCache
-
Returns
disk_folder_cache.DiskCache
: the object to manage the cacheExpand source code
def manager(self) -> disk_folder_cache.DiskCache: """ Returns ------- `disk_folder_cache.DiskCache`: the object to manage the cache """ if self._cache_manager is None: # Auto-populating defaults here is a workaround so small tools can skip the full # command line config setup. Could be improved! if 'dir' not in self._config_dict: self._config_dict['dir'] = 'default' if 'limit' not in self._config_dict: self._config_dict['limit'] = 8 cdir = self._config_dict['dir'] if cdir == 'default': cdir = appdirs.AppDirs('delta', 'nasa').user_cache_dir self._cache_manager = disk_folder_cache.DiskCache(cdir, self._config_dict['limit']) return self._cache_manager
Inherited members
class ClassesConfig
-
Configuration for classes.
Specify either a number of classes or list of classes with details.
Parameters
section_header
:Optional[str]
- The title of the section for command line arguments in the help.
Expand source code
class ClassesConfig(DeltaConfigComponent): """ Configuration for classes. Specify either a number of classes or list of classes with details. """ def __init__(self): super().__init__() self._classes = [] self._conversions = [] def __iter__(self): return self._classes.__iter__() def __getitem__(self, key): return self._classes[key] def __len__(self): return len(self._classes) # overwrite model entirely if updated (don't want combined layers from multiple files) def _load_dict(self, d : dict, base_dir): if not d: return self._config_dict = d self._classes = [] if isinstance(d, int): for i in range(d): self._classes.append(LabelClass(i)) elif isinstance(d, list): for (i, c) in enumerate(d): if isinstance(c, int): # just pixel value self._classes.append(LabelClass(i)) else: keys = c.keys() assert len(keys) == 1, 'Dict should have name of pixel value.' k = next(iter(keys)) assert isinstance(k, int), 'Class label value must be int.' inner_dict = c[k] self._classes.append(LabelClass(k, str(inner_dict.get('name')), inner_dict.get('color'), inner_dict.get('weight'))) elif isinstance(d, dict): for k in d: assert isinstance(k, int), 'Class label value must be int.' self._classes.append(LabelClass(k, str(d[k].get('name')), d[k].get('color'), d[k].get('weight'))) else: raise ValueError('Expected classes to be an int or list in config, was ' + str(d)) # make sure the order is consistent for same values, and create preprocessing function self._conversions = [] self._classes = sorted(self._classes, key=lambda x: x.value) for (i, v) in enumerate(self._classes): if v.value != i: self._conversions.append(v.value) v.end_value = i def class_id(self, class_name): """ Parameters ---------- class_name: int or str Either the original pixel value in images (int) or the name (str) of a class. The special value 'nodata' will give the nodata class, if any. Returns ------- int: the ID of the class in the labels after default image preprocessing (labels are arranged to a canonical order, with nodata always coming after them.) """ if class_name == len(self._classes) or class_name == 'nodata': return len(self._classes) for (i, c) in enumerate(self._classes): if class_name in (c.value, c.name): return i raise ValueError('Class ' + str(class_name) + ' not found.') def weights(self): """ Returns ------- List[float] List of class weights for use in training, if specified. """ weights = [] for c in self._classes: if c.weight is not None: weights.append(c.weight) if not weights: return None assert len(weights) == len(self._classes), 'For class weights, either all or none must be specified.' return weights def classes_to_indices_func(self): """ Returns ------- Callable[[numpy.ndarray], numpy.ndarray]: Function to convert label image to canonical form """ if not self._conversions: return None def convert(data): assert isinstance(data, np.ndarray) for (i, c) in enumerate(self._conversions): data[data == c] = i return data return convert def indices_to_classes_func(self): """ Returns ------- Callable[[numpy.ndarray], numpy.ndarray]: Reverse of `classes_to_indices_func`. """ if not self._conversions: return None def convert(data): assert isinstance(data, np.ndarray) for (i, c) in reversed(list(enumerate(self._conversions))): data[data == i] = c return data return convert
Ancestors
Methods
def class_id(self, class_name)
-
Parameters
class_name
:int
orstr
- Either the original pixel value in images (int) or the name (str) of a class. The special value 'nodata' will give the nodata class, if any.
Returns
int:
- the ID of the class in the labels after default image preprocessing (labels are arranged to a canonical order, with nodata always coming after them.)
Expand source code
def class_id(self, class_name): """ Parameters ---------- class_name: int or str Either the original pixel value in images (int) or the name (str) of a class. The special value 'nodata' will give the nodata class, if any. Returns ------- int: the ID of the class in the labels after default image preprocessing (labels are arranged to a canonical order, with nodata always coming after them.) """ if class_name == len(self._classes) or class_name == 'nodata': return len(self._classes) for (i, c) in enumerate(self._classes): if class_name in (c.value, c.name): return i raise ValueError('Class ' + str(class_name) + ' not found.')
def classes_to_indices_func(self)
-
Returns
Callable[[numpy.ndarray], numpy.ndarray]:
- Function to convert label image to canonical form
Expand source code
def classes_to_indices_func(self): """ Returns ------- Callable[[numpy.ndarray], numpy.ndarray]: Function to convert label image to canonical form """ if not self._conversions: return None def convert(data): assert isinstance(data, np.ndarray) for (i, c) in enumerate(self._conversions): data[data == c] = i return data return convert
def indices_to_classes_func(self)
-
Returns
Callable[[numpy.ndarray], numpy.ndarray]:
- Reverse of
classes_to_indices_func
.
Expand source code
def indices_to_classes_func(self): """ Returns ------- Callable[[numpy.ndarray], numpy.ndarray]: Reverse of `classes_to_indices_func`. """ if not self._conversions: return None def convert(data): assert isinstance(data, np.ndarray) for (i, c) in reversed(list(enumerate(self._conversions))): data[data == i] = c return data return convert
def weights(self)
-
Returns
List[float]
- List of class weights for use in training, if specified.
Expand source code
def weights(self): """ Returns ------- List[float] List of class weights for use in training, if specified. """ weights = [] for c in self._classes: if c.weight is not None: weights.append(c.weight) if not weights: return None assert len(weights) == len(self._classes), 'For class weights, either all or none must be specified.' return weights
Inherited members
class DatasetConfig
-
Configuration for a dataset.
Parameters
section_header
:Optional[str]
- The title of the section for command line arguments in the help.
Expand source code
class DatasetConfig(DeltaConfigComponent): """ Configuration for a dataset. """ def __init__(self): super().__init__('Dataset') self.register_component(ImageSetConfig('image'), 'images', '__image_comp') self.register_component(ImageSetConfig('label'), 'labels', '__label_comp') self.__images = None self.__labels = None self.register_component(ClassesConfig(), 'classes') def reset(self): super().reset() self.__images = None self.__labels = None def images(self) -> ImageSet: """ Returns ------- ImageSet: the training images """ if self.__images is None: (self.__images, self.__labels) = load_images_labels(self._components['images'], self._components['labels'], self._components['classes']) return self.__images def labels(self) -> ImageSet: """ Returns ------- ImageSet: the label images """ if self.__labels is None: (self.__images, self.__labels) = load_images_labels(self._components['images'], self._components['labels'], self._components['classes']) return self.__labels
Ancestors
Methods
def images(self) ‑> ImageSet
-
Returns
Imageset
the training images
Expand source code
def images(self) -> ImageSet: """ Returns ------- ImageSet: the training images """ if self.__images is None: (self.__images, self.__labels) = load_images_labels(self._components['images'], self._components['labels'], self._components['classes']) return self.__images
def labels(self) ‑> ImageSet
-
Returns
Imageset
the label images
Expand source code
def labels(self) -> ImageSet: """ Returns ------- ImageSet: the label images """ if self.__labels is None: (self.__images, self.__labels) = load_images_labels(self._components['images'], self._components['labels'], self._components['classes']) return self.__labels
Inherited members
class IOConfig
-
Configuration for I/O.
Parameters
section_header
:Optional[str]
- The title of the section for command line arguments in the help.
Expand source code
class IOConfig(DeltaConfigComponent): """ Configuration for I/O. """ def __init__(self): super().__init__('IO') self.register_field('threads', int, None, None, 'Number of threads to use.') self.register_field('tile_size', list, 'tile_size', _validate_tile_size, 'Size of an image tile to load in memory at once.') self.register_field('interleave_blocks', int, 'interleave_blocks', None, 'Number of blocks to interleave at a time.') self.register_arg('threads', '--threads') self.register_component(CacheConfig(), 'cache') def threads(self): """ Returns ------- int: number of threads to use for I/O """ if 'threads' in self._config_dict and self._config_dict['threads']: return self._config_dict['threads'] return min(1, os.cpu_count() // 2)
Ancestors
Methods
def threads(self)
-
Returns
int:
- number of threads to use for I/O
Expand source code
def threads(self): """ Returns ------- int: number of threads to use for I/O """ if 'threads' in self._config_dict and self._config_dict['threads']: return self._config_dict['threads'] return min(1, os.cpu_count() // 2)
Inherited members
class ImagePreprocessConfig
-
Configuration for image preprocessing.
Expects a list of preprocessing functions registered with
register_preprocess()
.Parameters
section_header
:Optional[str]
- The title of the section for command line arguments in the help.
Expand source code
class ImagePreprocessConfig(DeltaConfigComponent): """ Configuration for image preprocessing. Expects a list of preprocessing functions registered with `delta.config.extensions.register_preprocess`. """ def __init__(self): super().__init__() self._functions = [] def _load_dict(self, d, base_dir): if d is None: self._functions = [] return if not d: return self._functions = [] assert isinstance(d, list), 'preprocess should be list of commands' for func in d: if isinstance(func, str): self._functions.append((func, {})) else: assert isinstance(func, dict), 'preprocess items must be strings or dicts' assert len(func) == 1, 'One preprocess item per list entry.' name = list(func.keys())[0] self._functions.append((name, func[name])) def function(self, image_type): """ Parameters ---------- image_type: str Type of the image Returns ------- Callable: The specified preprocessing function to apply to the image. """ prep = lambda data, _, dummy: data for (name, args) in self._functions: t = preprocess_function(name) assert t is not None, 'Preprocess function %s not found.' % (name) p = t(image_type=image_type, **args) def helper(cur, prev): return lambda data, roi, bands: cur(prev(data, roi, bands), roi, bands) prep = helper(p, prep) return prep
Ancestors
Methods
def function(self, image_type)
-
Parameters
image_type
:str
- Type of the image
Returns
Callable
The specified preprocessing function to apply to the image.
Expand source code
def function(self, image_type): """ Parameters ---------- image_type: str Type of the image Returns ------- Callable: The specified preprocessing function to apply to the image. """ prep = lambda data, _, dummy: data for (name, args) in self._functions: t = preprocess_function(name) assert t is not None, 'Preprocess function %s not found.' % (name) p = t(image_type=image_type, **args) def helper(cur, prev): return lambda data, roi, bands: cur(prev(data, roi, bands), roi, bands) prep = helper(p, prep) return prep
Inherited members
class ImageSet (images, image_type, preprocess=None, nodata_value=None)
-
Specifies a set of image files.
The images can be accessed by using the
ImageSet
as an iterable.Parameters
images
:Iterator[str]
- Image filenames
image_type
:str
- The image type as a string (i.e., tiff, worldview, landsat). Must have
been previously registered with
register_image_reader()
. preprocess
:Callable
- Optional preprocessing function to apply to the image
following the signature in
DeltaImage.set_preprocess()
. nodata_value
:image dtype
- A no data value for pixels to disregard
Expand source code
class ImageSet: """ Specifies a set of image files. The images can be accessed by using the `ImageSet` as an iterable. """ def __init__(self, images, image_type, preprocess=None, nodata_value=None): """ Parameters ---------- images: Iterator[str] Image filenames image_type: str The image type as a string (i.e., tiff, worldview, landsat). Must have been previously registered with `delta.config.extensions.register_image_reader`. preprocess: Callable Optional preprocessing function to apply to the image following the signature in `delta.imagery.delta_image.DeltaImage.set_preprocess`. nodata_value: image dtype A no data value for pixels to disregard """ self._images = images self._image_type = image_type self._preprocess = preprocess self._nodata_value = nodata_value def type(self): """ Returns ------- str: The type of the image """ return self._image_type def preprocess(self): """ Returns ------- Callable: The preprocessing function """ return self._preprocess def nodata_value(self): """ Returns ------- image dtype: Value of pixels to disregard. """ return self._nodata_value def set_nodata_value(self, nodata): """ Set the pixel value to disregard. Parameters ---------- nodata: image dtype The pixel value to set as nodata """ self._nodata_value = nodata def load(self, index): """ Loads the image of the given index. Parameters ---------- index: int Index of the image to load. Returns ------- `delta.imagery.delta_image.DeltaImage`: The image """ img = image_reader(self.type())(self[index], self.nodata_value()) if self._preprocess: img.set_preprocess(self._preprocess) return img def __len__(self): return len(self._images) def __getitem__(self, index): if index < 0 or index >= len(self): raise IndexError('Index %s out of range.' % (index)) return self._images[index] def __iter__(self): return self._images.__iter__()
Methods
def load(self, index)
-
Loads the image of the given index.
Parameters
index
:int
- Index of the image to load.
Returns
DeltaImage
: The imageExpand source code
def load(self, index): """ Loads the image of the given index. Parameters ---------- index: int Index of the image to load. Returns ------- `delta.imagery.delta_image.DeltaImage`: The image """ img = image_reader(self.type())(self[index], self.nodata_value()) if self._preprocess: img.set_preprocess(self._preprocess) return img
def nodata_value(self)
-
Returns
image dtype:
- Value of pixels to disregard.
Expand source code
def nodata_value(self): """ Returns ------- image dtype: Value of pixels to disregard. """ return self._nodata_value
def preprocess(self)
-
Returns
Callable
The preprocessing function
Expand source code
def preprocess(self): """ Returns ------- Callable: The preprocessing function """ return self._preprocess
def set_nodata_value(self, nodata)
-
Set the pixel value to disregard.
Parameters
nodata
:image dtype
- The pixel value to set as nodata
Expand source code
def set_nodata_value(self, nodata): """ Set the pixel value to disregard. Parameters ---------- nodata: image dtype The pixel value to set as nodata """ self._nodata_value = nodata
def type(self)
-
Returns
str:
- The type of the image
Expand source code
def type(self): """ Returns ------- str: The type of the image """ return self._image_type
class ImageSetConfig (name=None)
-
Configuration for a set of images.
Used for images, labels, and validation images and labels.
Parameters
section_header
:Optional[str]
- The title of the section for command line arguments in the help.
Expand source code
class ImageSetConfig(DeltaConfigComponent): """ Configuration for a set of images. Used for images, labels, and validation images and labels. """ def __init__(self, name=None): super().__init__() self.register_field('type', str, 'type', None, 'Image type.') self.register_field('files', list, None, _validate_paths, 'List of image files.') self.register_field('file_list', str, None, validate_path, 'File listing image files.') self.register_field('directory', str, None, validate_path, 'Directory of image files.') self.register_field('extension', str, None, None, 'Image file extension.') self.register_field('nodata_value', (float, int), None, None, 'Value of pixels to ignore.') if name: self.register_arg('type', '--' + name + '-type', name + '_type') self.register_arg('file_list', '--' + name + '-file-list', name + '_file_list') self.register_arg('directory', '--' + name + '-dir', name + '_directory') self.register_arg('extension', '--' + name + '-extension', name + '_extension') self.register_component(ImagePreprocessConfig(), 'preprocess') self._name = name def preprocess_function(self): """ Returns ------- Callable: Preprocessing function for the set of images. """ return self._components['preprocess'].function(self._config_dict['type']) def setup_arg_parser(self, parser, components = None) -> None: if self._name is None: return super().setup_arg_parser(parser, components) parser.add_argument("--" + self._name, dest=self._name, required=False, help="Specify a single image file.") def parse_args(self, options): if self._name is None: return super().parse_args(options) if hasattr(options, self._name) and getattr(options, self._name) is not None: self._config_dict['files'] = [getattr(options, self._name)] self._config_dict['directory'] = None self._config_dict['file_list'] = None
Ancestors
Methods
def preprocess_function(self)
-
Returns
Callable
Preprocessing function for the set of images.
Expand source code
def preprocess_function(self): """ Returns ------- Callable: Preprocessing function for the set of images. """ return self._components['preprocess'].function(self._config_dict['type'])
Inherited members
class LabelClass (value, name=None, color=None, weight=None)
-
Label configuration.
Parameters
value
:int
- Pixel of the label
name
:str
- Name of the class to display
color
:int
- In visualizations, set the class to this RGB color.
weight
:float
- During training weight this class by this amount.
Expand source code
class LabelClass: """ Label configuration. """ def __init__(self, value, name=None, color=None, weight=None): """ Parameters ---------- value: int Pixel of the label name: str Name of the class to display color: int In visualizations, set the class to this RGB color. weight: float During training weight this class by this amount. """ color_order = [0x1f77b4, 0xff7f0e, 0x2ca02c, 0xd62728, 0x9467bd, 0x8c564b, \ 0xe377c2, 0x7f7f7f, 0xbcbd22, 0x17becf] if name is None: name = 'Class ' + str(value) if color is None: color = color_order[value] if value < len(color_order) else 0 self.value = value self.name = name self.color = color self.weight = weight self.end_value = None def __repr__(self): return 'Color: ' + self.name