Created
July 26, 2018 18:42
-
-
Save thejohnhoffer/8ada13099c3a36ced0ed2dfbae65bea7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from warnings import warn | |
from multiprocessing.pool import ThreadPool | |
from io import BytesIO | |
import numpy as np | |
import boto3 | |
import cv2 | |
import os | |
###### | |
# Copyright (C) 2011, the scikit-image team | |
# All rights reserved. | |
# | |
# Redistribution and use in source and binary forms, with or without | |
# modification, are permitted provided that the following conditions are | |
# met: | |
# | |
# 1. Redistributions of source code must retain the above copyright | |
# notice, this list of conditions and the following disclaimer. | |
# 2. Redistributions in binary form must reproduce the above copyright | |
# notice, this list of conditions and the following disclaimer in | |
# the documentation and/or other materials provided with the | |
# distribution. | |
# 3. Neither the name of skimage nor the names of its contributors may be | |
# used to endorse or promote products derived from this software without | |
# specific prior written permission. | |
# | |
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, | |
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | |
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
# POSSIBILITY OF SUCH DAMAGE. | |
# From skikit-image: https://github.com/scikit-image/scikit-image/tree/cc7b116cdbb9f9981d4c7b9cd01a201489e4dc6e # noqa: E501 | |
# skimage.util.dtype._integer_types|_integer_ranges|dtype_range | |
_integer_types = (np.byte, np.ubyte, # 8 bits | |
np.short, np.ushort, # 16 bits | |
np.intc, np.uintc, # 16 or 32 or 64 bits | |
np.int_, np.uint, # 32 or 64 bits | |
np.longlong, np.ulonglong) # 64 bits | |
_integer_ranges = {t: (np.iinfo(t).min, np.iinfo(t).max) | |
for t in _integer_types} | |
dtype_range = {np.bool_: (False, True), | |
np.bool8: (False, True), | |
np.float16: (-1, 1), | |
np.float32: (-1, 1), | |
np.float64: (-1, 1)} | |
dtype_range.update(_integer_ranges) | |
# skimage.exposure.DTYPE_RANGE | |
DTYPE_RANGE = dtype_range.copy() | |
DTYPE_RANGE.update((d.__name__, limits) for d, limits in dtype_range.items()) | |
DTYPE_RANGE.update({'uint10': (0, 2 ** 10 - 1), | |
'uint12': (0, 2 ** 12 - 1), | |
'uint14': (0, 2 ** 14 - 1), | |
'bool': dtype_range[np.bool_], | |
'float': dtype_range[np.float64]}) | |
# skimage.util.dtype._supported_types | |
_supported_types = list(dtype_range.keys()) | |
# skimage.util.dtype.dtype_limits | |
def dtype_limits(image, clip_negative=False): | |
"""Return intensity limits, i.e. (min, max) tuple, of the image's dtype. | |
Parameters | |
---------- | |
image : ndarray | |
Input image. | |
clip_negative : bool, optional | |
If True, clip the negative range (i.e. return 0 for min intensity) | |
even if the image dtype allows negative values. | |
Returns | |
------- | |
imin, imax : tuple | |
Lower and upper intensity limits. | |
""" | |
imin, imax = dtype_range[image.dtype.type] | |
if clip_negative: | |
imin = 0 | |
return imin, imax | |
# skimage.exposure.exposure._assert_non_negative | |
def _assert_non_negative(image): | |
if np.any(image < 0): | |
raise ValueError('Image Correction methods work correctly only on ' | |
'images with non-negative values. Use ' | |
'skimage.exposure.rescale_intensity.') | |
# skimage.exposure.exposure.intensity_range | |
def intensity_range(image, range_values='image', clip_negative=False): | |
"""Return image intensity range (min, max) based on desired value type. | |
Parameters | |
---------- | |
image : array | |
Input image. | |
range_values : str or 2-tuple | |
The image intensity range is configured by this parameter. | |
The possible values for this parameter are enumerated below. | |
'image' | |
Return image min/max as the range. | |
'dtype' | |
Return min/max of the image's dtype as the range. | |
dtype-name | |
Return intensity range based on desired `dtype`. Must be valid key | |
in `DTYPE_RANGE`. Note: `image` is ignored for this range type. | |
2-tuple | |
Return `range_values` as min/max intensities. Note that there's no | |
reason to use this function if you just want to specify the | |
intensity range explicitly. This option is included for functions | |
that use `intensity_range` to support all desired range types. | |
clip_negative : bool | |
If True, clip the negative range (i.e. return 0 for min intensity) | |
even if the image dtype allows negative values. | |
""" | |
if range_values == 'dtype': | |
range_values = image.dtype.type | |
if range_values == 'image': | |
i_min = np.min(image) | |
i_max = np.max(image) | |
elif range_values in DTYPE_RANGE: | |
i_min, i_max = DTYPE_RANGE[range_values] | |
if clip_negative: | |
i_min = 0 | |
else: | |
i_min, i_max = range_values | |
return i_min, i_max | |
# skimage.util.dtype.convert | |
def convert(image, dtype, force_copy=False, uniform=False): | |
""" | |
Convert an image to the requested data-type. | |
Warnings are issued in case of precision loss, or when negative values | |
are clipped during conversion to unsigned integer types (sign loss). | |
Floating point values are expected to be normalized and will be clipped | |
to the range [0.0, 1.0] or [-1.0, 1.0] when converting to unsigned or | |
signed integers respectively. | |
Numbers are not shifted to the negative side when converting from | |
unsigned to signed integer types. Negative values will be clipped when | |
converting to unsigned integers. | |
Parameters | |
---------- | |
image : ndarray | |
Input image. | |
dtype : dtype | |
Target data-type. | |
force_copy : bool, optional | |
Force a copy of the data, irrespective of its current dtype. | |
uniform : bool, optional | |
Uniformly quantize the floating point range to the integer range. | |
By default (uniform=False) floating point values are scaled and | |
rounded to the nearest integers, which minimizes back and forth | |
conversion errors. | |
References | |
---------- | |
.. [1] DirectX data conversion rules. | |
http://msdn.microsoft.com/en-us/library/windows/desktop/dd607323%28v=vs.85%29.aspx | |
.. [2] Data Conversions. In "OpenGL ES 2.0 Specification v2.0.25", | |
pp 7-8. Khronos Group, 2010. | |
.. [3] Proper treatment of pixels as integers. A.W. Paeth. | |
In "Graphics Gems I", pp 249-256. Morgan Kaufmann, 1990. | |
.. [4] Dirty Pixels. J. Blinn. In "Jim Blinn's corner: Dirty Pixels", | |
pp 47-57. Morgan Kaufmann, 1998. | |
""" | |
image = np.asarray(image) | |
dtypeobj_in = image.dtype | |
dtypeobj_out = np.dtype(dtype) | |
dtype_in = dtypeobj_in.type | |
dtype_out = dtypeobj_out.type | |
kind_in = dtypeobj_in.kind | |
kind_out = dtypeobj_out.kind | |
itemsize_in = dtypeobj_in.itemsize | |
itemsize_out = dtypeobj_out.itemsize | |
# Below, we do an `issubdtype` check. Its purpose is to find out | |
# whether we can get away without doing any image conversion. This happens | |
# when: | |
# | |
# - the output and input dtypes are the same or | |
# - when the output is specified as a type, and the input dtype | |
# is a subclass of that type (e.g. `np.floating` will allow | |
# `float32` and `float64` arrays through) | |
type_out = dtype if isinstance(dtype, type) else dtypeobj_out | |
if np.issubdtype(dtypeobj_in, type_out): | |
if force_copy: | |
image = image.copy() | |
return image | |
if not (dtype_in in _supported_types and dtype_out in _supported_types): | |
raise ValueError("Can not convert from {} to {}." | |
.format(dtypeobj_in, dtypeobj_out)) | |
def sign_loss(): | |
warn("Possible sign loss when converting negative image of type " | |
"{} to positive image of type {}." | |
.format(dtypeobj_in, dtypeobj_out)) | |
def prec_loss(): | |
warn("Possible precision loss when converting from {} to {}" | |
.format(dtypeobj_in, dtypeobj_out)) | |
def _dtype_itemsize(itemsize, *dtypes): | |
# Return first of `dtypes` with itemsize greater than `itemsize` | |
return next(dt for dt in dtypes if np.dtype(dt).itemsize >= itemsize) | |
def _dtype_bits(kind, bits, itemsize=1): | |
# Return dtype of `kind` that can store a `bits` wide unsigned int | |
def compare(x, y, kind='u'): | |
if kind == 'u': | |
return x <= y | |
else: | |
return x < y | |
s = next(i for i in (itemsize, ) + (2, 4, 8) if compare(bits, i * 8, | |
kind=kind)) | |
return np.dtype(kind + str(s)) | |
def _scale(a, n, m, copy=True): | |
"""Scale an array of unsigned/positive integers from `n` to `m` bits. | |
Numbers can be represented exactly only if `m` is a multiple of `n`. | |
Parameters | |
---------- | |
a : ndarray | |
Input image array. | |
n : int | |
Number of bits currently used to encode the values in `a`. | |
m : int | |
Desired number of bits to encode the values in `out`. | |
copy : bool, optional | |
If True, allocates and returns new array. Otherwise, modifies | |
`a` in place. | |
Returns | |
------- | |
out : array | |
Output image array. Has the same kind as `a`. | |
""" | |
kind = a.dtype.kind | |
if n > m and a.max() < 2 ** m: | |
mnew = int(np.ceil(m / 2) * 2) | |
if mnew > m: | |
dtype = "int{}".format(mnew) | |
else: | |
dtype = "uint{}".format(mnew) | |
n = int(np.ceil(n / 2) * 2) | |
warn("Downcasting {} to {} without scaling because max " | |
"value {} fits in {}".format(a.dtype, dtype, a.max(), dtype)) | |
return a.astype(_dtype_bits(kind, m)) | |
elif n == m: | |
return a.copy() if copy else a | |
elif n > m: | |
# downscale with precision loss | |
prec_loss() | |
if copy: | |
b = np.empty(a.shape, _dtype_bits(kind, m)) | |
np.floor_divide(a, 2**(n - m), out=b, dtype=a.dtype, | |
casting='unsafe') | |
return b | |
else: | |
a //= 2**(n - m) | |
return a | |
elif m % n == 0: | |
# exact upscale to a multiple of `n` bits | |
if copy: | |
b = np.empty(a.shape, _dtype_bits(kind, m)) | |
np.multiply(a, (2**m - 1) // (2**n - 1), out=b, dtype=b.dtype) | |
return b | |
else: | |
a = a.astype(_dtype_bits(kind, m, a.dtype.itemsize), | |
copy=False) | |
a *= (2**m - 1) // (2**n - 1) | |
return a | |
else: | |
# upscale to a multiple of `n` bits, | |
# then downscale with precision loss | |
prec_loss() | |
o = (m // n + 1) * n | |
if copy: | |
b = np.empty(a.shape, _dtype_bits(kind, o)) | |
np.multiply(a, (2**o - 1) // (2**n - 1), out=b, dtype=b.dtype) | |
b //= 2**(o - m) | |
return b | |
else: | |
a = a.astype(_dtype_bits(kind, o, a.dtype.itemsize), | |
copy=False) | |
a *= (2**o - 1) // (2**n - 1) | |
a //= 2**(o - m) | |
return a | |
if kind_in in 'ui': | |
imin_in = np.iinfo(dtype_in).min | |
imax_in = np.iinfo(dtype_in).max | |
if kind_out in 'ui': | |
imin_out = np.iinfo(dtype_out).min | |
imax_out = np.iinfo(dtype_out).max | |
# any -> binary | |
if kind_out == 'b': | |
if kind_in in "fi": | |
sign_loss() | |
prec_loss() | |
return image > dtype_in(dtype_range[dtype_in][1] / 2) | |
# binary -> any | |
if kind_in == 'b': | |
result = image.astype(dtype_out) | |
if kind_out != 'f': | |
result *= dtype_out(dtype_range[dtype_out][1]) | |
return result | |
# float -> any | |
if kind_in == 'f': | |
if np.min(image) < -1.0 or np.max(image) > 1.0: | |
raise ValueError("Images of type float must be between -1 and 1.") | |
if kind_out == 'f': | |
# float -> float | |
if itemsize_in > itemsize_out: | |
prec_loss() | |
return image.astype(dtype_out) | |
# floating point -> integer | |
prec_loss() | |
# use float type that can represent output integer type | |
computation_type = _dtype_itemsize(itemsize_out, dtype_in, | |
np.float32, np.float64) | |
if not uniform: | |
if kind_out == 'u': | |
image_out = np.multiply(image, imax_out, | |
dtype=computation_type) | |
else: | |
image_out = np.multiply(image, (imax_out - imin_out) / 2, | |
dtype=computation_type) | |
image_out -= 1.0 / 2. | |
np.rint(image_out, out=image_out) | |
np.clip(image_out, imin_out, imax_out, out=image_out) | |
elif kind_out == 'u': | |
image_out = np.multiply(image, imax_out + 1, | |
dtype=computation_type) | |
np.clip(image_out, 0, imax_out, out=image_out) | |
else: | |
image_out = np.multiply(image, (imax_out - imin_out + 1.0) / 2.0, | |
dtype=computation_type) | |
np.floor(image_out, out=image_out) | |
np.clip(image_out, imin_out, imax_out, out=image_out) | |
return image_out.astype(dtype_out) | |
# signed/unsigned int -> float | |
if kind_out == 'f': | |
if itemsize_in >= itemsize_out: | |
prec_loss() | |
# use float type that can exactly represent input integers | |
computation_type = _dtype_itemsize(itemsize_in, dtype_out, | |
np.float32, np.float64) | |
if kind_in == 'u': | |
# using np.divide or np.multiply doesn't copy the data | |
# until the computation time | |
image = np.multiply(image, 1. / imax_in, | |
dtype=computation_type) | |
# DirectX uses this conversion also for signed ints | |
# if imin_in: | |
# np.maximum(image, -1.0, out=image) | |
else: | |
image = np.add(image, 0.5, dtype=computation_type) | |
image *= 2 / (imax_in - imin_in) | |
return np.asarray(image, dtype_out) | |
# unsigned int -> signed/unsigned int | |
if kind_in == 'u': | |
if kind_out == 'i': | |
# unsigned int -> signed int | |
image = _scale(image, 8 * itemsize_in, 8 * itemsize_out - 1) | |
return image.view(dtype_out) | |
else: | |
# unsigned int -> unsigned int | |
return _scale(image, 8 * itemsize_in, 8 * itemsize_out) | |
# signed int -> unsigned int | |
if kind_out == 'u': | |
sign_loss() | |
image = _scale(image, 8 * itemsize_in - 1, 8 * itemsize_out) | |
result = np.empty(image.shape, dtype_out) | |
np.maximum(image, 0, out=result, dtype=image.dtype, casting='unsafe') | |
return result | |
# signed int -> signed int | |
if itemsize_in > itemsize_out: | |
return _scale(image, 8 * itemsize_in - 1, 8 * itemsize_out - 1) | |
image = image.astype(_dtype_bits('i', itemsize_out * 8)) | |
image -= imin_in | |
image = _scale(image, 8 * itemsize_in, 8 * itemsize_out, copy=False) | |
image += imin_out | |
return image.astype(dtype_out) | |
# skimage.util.dtype.img_as_float | |
def img_as_float(image, force_copy=False): | |
"""Convert an image to floating point format. | |
This function is similar to `img_as_float64`, but will not convert | |
lower-precision floating point arrays to `float64`. | |
Parameters | |
---------- | |
image : ndarray | |
Input image. | |
force_copy : bool, optional | |
Force a copy of the data, irrespective of its current dtype. | |
Returns | |
------- | |
out : ndarray of float | |
Output image. | |
Notes | |
----- | |
The range of a floating point image is [0.0, 1.0] or [-1.0, 1.0] when | |
converting from unsigned or signed datatypes, respectively. | |
If the input image has a float type, intensity values are not modified | |
and can be outside the ranges [0.0, 1.0] or [-1.0, 1.0]. | |
""" | |
return convert(image, np.floating, force_copy) | |
# skimage.exposure.exposure.rescale_intensity | |
def rescale_intensity(image, in_range='image', out_range='dtype'): | |
"""Return image after stretching or shrinking its intensity levels. | |
The desired intensity range of the input and output, `in_range` and | |
`out_range` respectively, are used to stretch or shrink the intensity range | |
of the input image. See examples below. | |
Parameters | |
---------- | |
image : array | |
Image array. | |
in_range, out_range : str or 2-tuple | |
Min and max intensity values of input and output image. | |
The possible values for this parameter are enumerated below. | |
'image' | |
Use image min/max as the intensity range. | |
'dtype' | |
Use min/max of the image's dtype as the intensity range. | |
dtype-name | |
Use intensity range based on desired `dtype`. Must be valid key | |
in `DTYPE_RANGE`. | |
2-tuple | |
Use `range_values` as explicit min/max intensities. | |
Returns | |
------- | |
out : array | |
Image array after rescaling its intensity. This image is the same dtype | |
as the input image. | |
See Also | |
-------- | |
equalize_hist | |
Examples | |
-------- | |
By default, the min/max intensities of the input image are stretched to | |
the limits allowed by the image's dtype, since `in_range` defaults to | |
'image' and `out_range` defaults to 'dtype': | |
>>> image = np.array([51, 102, 153], dtype=np.uint8) | |
>>> rescale_intensity(image) | |
array([ 0, 127, 255], dtype=uint8) | |
It's easy to accidentally convert an image dtype from uint8 to float: | |
>>> 1.0 * image | |
array([ 51., 102., 153.]) | |
Use `rescale_intensity` to rescale to the proper range for float dtypes: | |
>>> image_float = 1.0 * image | |
>>> rescale_intensity(image_float) | |
array([ 0. , 0.5, 1. ]) | |
To maintain the low contrast of the original, use the `in_range` parameter: | |
>>> rescale_intensity(image_float, in_range=(0, 255)) | |
array([ 0.2, 0.4, 0.6]) | |
If the min/max value of `in_range` is more/less than the min/max image | |
intensity, then the intensity levels are clipped: | |
>>> rescale_intensity(image_float, in_range=(0, 102)) | |
array([ 0.5, 1. , 1. ]) | |
If you have an image with signed integers but want to rescale the image to | |
just the positive range, use the `out_range` parameter: | |
>>> image = np.array([-10, 0, 10], dtype=np.int8) | |
>>> rescale_intensity(image, out_range=(0, 127)) | |
array([ 0, 63, 127], dtype=int8) | |
""" | |
dtype = image.dtype.type | |
imin, imax = intensity_range(image, in_range) | |
omin, omax = intensity_range(image, out_range, clip_negative=(imin >= 0)) | |
image = np.clip(image, imin, imax) | |
image = (image - imin) / float(imax - imin) | |
return np.array(image * (omax - omin) + omin, dtype=dtype) | |
# skimage.exposure.exposure.adjust_gamma | |
def adjust_gamma(image, gamma=1, gain=1): | |
"""Performs Gamma Correction on the input image. | |
Also known as Power Law Transform. | |
This function transforms the input image pixelwise according to the | |
equation ``O = I**gamma`` after scaling each pixel to the range 0 to 1. | |
Parameters | |
---------- | |
image : ndarray | |
Input image. | |
gamma : float | |
Non negative real number. Default value is 1. | |
gain : float | |
The constant multiplier. Default value is 1. | |
Returns | |
------- | |
out : ndarray | |
Gamma corrected output image. | |
See Also | |
-------- | |
adjust_log | |
Notes | |
----- | |
For gamma greater than 1, the histogram will shift towards left and | |
the output image will be darker than the input image. | |
For gamma less than 1, the histogram will shift towards right and | |
the output image will be brighter than the input image. | |
References | |
---------- | |
.. [1] http://en.wikipedia.org/wiki/Gamma_correction | |
Examples | |
-------- | |
>>> from skimage import data, exposure, img_as_float | |
>>> image = img_as_float(data.moon()) | |
>>> gamma_corrected = exposure.adjust_gamma(image, 2) | |
>>> # Output is darker for gamma > 1 | |
>>> image.mean() > gamma_corrected.mean() | |
True | |
""" | |
_assert_non_negative(image) | |
dtype = image.dtype.type | |
if gamma < 0: | |
raise ValueError("Gamma should be a non-negative real number.") | |
scale = float(dtype_limits(image, True)[1] - dtype_limits(image, True)[0]) | |
out = ((image / scale) ** gamma) * scale * gain | |
return dtype(out) | |
# blend.py | |
# | |
# from Minerva Library version 0.0.1 | |
# https://github.com/sorgerlab/minerva-lib-python/ | |
### | |
def composite_channel(target, image, color, range_min, range_max, out=None): | |
''' Render _image_ in pseudocolor and composite into _target_ | |
By default, a new output array will be allocated to hold | |
the result of the composition operation. To update _target_ | |
in place instead, specify the same array for _target_ and _out_. | |
Args: | |
target: Numpy array containing composition target image | |
image: Numpy array of image to render and composite | |
color: Color as r, g, b float array within 0, 1 | |
range_min: Threshhold range minimum, float within 0, 1 | |
range_max: Threshhold range maximum, float within 0, 1 | |
out: Optional output numpy array in which to place the result. | |
Returns: | |
A numpy array with the same shape as the composited image. | |
If an output array is specified, a reference to _out_ is returned. | |
''' | |
if out is None: | |
out = target.copy() | |
# Rescale the new channel to a float64 between 0 and 1 | |
f64_range = (range_min, range_max) | |
f64_image = img_as_float(image) | |
f64_image = rescale_intensity(f64_image, f64_range) | |
# Colorize and add the new channel to composite image | |
for i, component in enumerate(color): | |
out[:, :, i] += f64_image * component | |
return out | |
def composite_channels(channels): | |
'''Render each image in _channels_ additively into a composited image | |
Args: | |
channels: List of dicts for channels to blend. Each dict in the | |
list must have the following rendering settings: | |
{ | |
image: Numpy 2D image data of any type | |
color: Color as r, g, b float array within 0, 1 | |
min: Threshhold range minimum, float within 0, 1 | |
max: Threshhold range maximum, float within 0, 1 | |
} | |
Returns: | |
For input images with shape `(n,m)`, | |
returns a float32 RGB color image with shape | |
`(n,m,3)` and values in the range 0 to 1 | |
''' | |
num_channels = len(channels) | |
# Must be at least one channel | |
if num_channels < 1: | |
raise ValueError('At least one channel must be specified') | |
# Ensure that dimensions of all channels are equal | |
shape = channels[0]['image'].shape | |
for channel in channels: | |
if channel['image'].shape != shape: | |
raise ValueError('All channel images must have equal dimensions') | |
# Shape of 3 color image | |
shape_color = shape + (3,) | |
# Final buffer for blending | |
out_buffer = np.zeros(shape_color, dtype=np.float32) | |
# rescaled images and normalized colors | |
for channel in channels: | |
# Add all three channels to output buffer | |
args = map(channel.get, ['image', 'color', 'min', 'max']) | |
composite_channel(out_buffer, *args, out=out_buffer) | |
# Return gamma correct image within 0, 1 | |
np.clip(out_buffer, 0, 1, out=out_buffer) | |
return adjust_gamma(out_buffer, 1 / 2.2) | |
###### | |
# Entrypoint | |
### | |
def _s3_get(bucket, key): | |
'''Fetch a specific PNG from S3 and decode''' | |
obj = boto3.resource('s3').Object(bucket, key) | |
body = obj.get()['Body'] | |
data = body.read() | |
stream = BytesIO(data) | |
image = cv2.imdecode(np.fromstring(stream.getvalue(), dtype=np.uint8), 0) | |
return image | |
def format_input(args): | |
''' Combine all parameters | |
''' | |
image_, color_, range_ = args | |
return { | |
'image': image_, | |
'color': color_, | |
'min': range_[0], | |
'max': range_[1], | |
} | |
def main(): | |
""" Crop a region | |
""" | |
out = './output' | |
os.makedirs(out) | |
out_path_format = out + '/T{0:}-Z{2:}-L{1:}-Y{3:}-X{4:}.png' | |
# Important parameters | |
channels = [0] | |
ranges = np.array([ | |
[0.01068132, 0.0717174] | |
]) | |
colors = np.array([ | |
[0, 1, 0] | |
]) | |
x = 0 | |
y = 0 | |
z = 0 | |
t = 0 | |
level = 0 | |
uuid = 'a7db6f10-8a27-4c40-9cab-bc30c6abfb59' | |
bucket = 'minerva-test-cf-common-tilebucket-yhuku9umej1s' | |
key_base = ( | |
f'{uuid}/' | |
'C{}' | |
f'-T{t}-Z{z}-L{level}-Y{y}-X{x}.png' | |
) | |
# Prepare for blending | |
args = [(bucket, key_base.format(c)) for c in channels] | |
# Fetch raw tiles in parallel | |
pool = ThreadPool(processes=len(channels)) | |
images = pool.starmap(_s3_get, args) | |
pool.close() | |
all_in = zip(images, colors, ranges) | |
inputs = list(map(format_input, all_in)) | |
img_buffer = 255*composite_channels(inputs) | |
# Write the image buffer to a file | |
out_file = out_path_format.format(t, level, z, y, x) | |
try: | |
cv2.imwrite(out_file, img_buffer) | |
except OSError as o_e: | |
print(o_e) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
usage
python composite_s3.py