Merge pull request #52 from IndicoDataSolutions/Chris/remove-scinumpy

Remove scipy/numpy dependencies
This commit is contained in:
Madison May
2015-05-28 15:48:07 -04:00
9 changed files with 167 additions and 102 deletions
+95 -62
View File
@@ -1,11 +1,13 @@
import inspect, json, getpass, os
import inspect, json, getpass, os.path, base64, StringIO, re, warnings
import requests
import numpy as np
from skimage.transform import resize
from PIL import Image
from indicoio import JSON_HEADERS
from indicoio import config
B64_PATTERN = re.compile("^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)")
def api_handler(arg, cloud, api, batch=False, api_key=None, **kwargs):
data = {'data': arg}
data.update(**kwargs)
@@ -81,69 +83,102 @@ class DataStructureException(Exception):
""" % (self.callback, self.structure, str(self.accepted))
@TypeCheck((list, dict, np.ndarray), 'array')
def normalize(array, distribution=1, norm_range=(0, 1), **kwargs):
"""
First arg is an array, whether that's in the form of a numpy array,
a list, or a dictionary that contains the data in its values.
Second arg is the desired distribution which would be applied before
normalization.
Supports linear, exponential, logarithmic and raising to whatever
power specified (in which case you just put a number)
Third arg is the range across which you want the data normalized
"""
# Handling dictionary array input
# Note: lists and numpy arrays behave the same in this program
dict_array = isinstance(array, dict)
if dict_array:
keys = array.keys()
array = np.array(array.values()).astype('float')
else: # Decorator errors if this isn't a list or a numpy array
array = np.array(array).astype('float')
# Handling various distributions
if type(distribution) in [float, int]:
array = np.power(array, distribution)
else:
array = getattr(np, distribution)(array, **kwargs)
# Prep for normalization
x_max, x_min = (np.max(array), np.min(array))
def norm(element,x_min,x_max):
base_span = (element - x_min)*(norm_range[-1] - norm_range[0])
return norm_range[0] + base_span / (x_max - x_min)
norm_array = np.vectorize(norm)(array, x_min, x_max)
if dict_array:
return dict(zip(keys, norm_array))
return norm_array
def image_preprocess(image, batch=False):
def image_preprocess(image, size=(48,48), batch=False):
"""
Takes an image and prepares it for sending to the api including
resizing and image data/structure standardizing.
"""
if batch:
return [image_preprocess(img, batch=False) for img in image]
if isinstance(image,list):
image = np.asarray(image)
if type(image).__module__ != np.__name__:
raise ValueError('Image was not of type numpy.ndarray or list.')
if str(image.dtype) in ['int64','uint8']:
image = image/255.
if len(image.shape) == 2:
image = np.dstack((image,image,image))
if len(image.shape) == 4:
image = image[:,:,:3]
image = resize(image,(64,64))
image = image.tolist()
return image
if isinstance(image, basestring):
b64_str = re.sub('^data:image/.+;base64,', '', image)
if os.path.isfile(image):
# check type of element
outImage = Image.open(image)
elif B64_PATTERN.match(b64_str) is not None:
return b64_str
else:
raise ValueError("Snose tring provided must be a valid filepath or base64 encoded string")
elif isinstance(image, list): # image passed in is a list and not np.array
warnings.warn(
"Input as lists of pixels will be deprecated in the next major update",
DeprecationWarning
)
outImage = process_list_image(image)
elif type(image).__name__ == "ndarray": # image is from numpy/scipy
out_image = Image.fromarray(image)
else:
raise ValueError("Image must be a filepath, base64 encoded string, or a numpy array")
# image resizing
outImage = outImage.resize(size)
# convert to base64
temp_output = StringIO.StringIO()
outImage.save(temp_output, format='PNG')
temp_output.seek(0)
output_s = temp_output.read()
return base64.b64encode(output_s)
def get_list_dimensions(_list):
"""
Takes a nested list and returns the size of each dimension followed
by the element type in the list
"""
if isinstance(_list, list) or isinstance(_list, tuple):
return [len(_list)] + get_list_dimensions(_list[0])
return []
def get_element_type(_list, dimens):
"""
Given the dimensions of a nested list and the list, returns the type of the
elements in the inner list.
"""
elem = _list
for _ in xrange(len(dimens)):
elem = elem[0]
return type(elem)
def process_list_image(_list):
"""
Processes list to be [[(int, int, int), ...]]
"""
# Check if list is empty
if not _list:
return _list
dimens = get_list_dimensions(_list)
data_type = get_element_type(_list, dimens)
seq_obj = []
outImage = Image.new("RGB", (dimens[0], dimens[1]))
for i in xrange(dimens[0]):
for j in xrange(dimens[1]):
elem = _list[i][j]
if len(dimens) >= 3:
#RGB(A)
if data_type == float:
seq_obj.append((int(elem[0] * 255), int(elem[1] * 255), int(elem[2] * 255)))
else:
seq_obj.append(elem[0:3])
elif data_type == float:
#Grayscale 0 - 1.0f
seq_obj.append((int(elem * 255), ) * 3)
else:
#Grayscale 0 - 255
seq_obj.append((elem, ) * 3)
#Needs to be 0 - 255 in flattened list of (R, G, B)
outImage.putdata(data = seq_obj)
return outImage
def is_url(data, batch=False):
@@ -152,5 +187,3 @@ def is_url(data, batch=False):
if not batch and isinstance(data, basestring):
return True
return False