""" Image Utils Handles preprocessing images before they are sent to the server """ import os.path, base64, StringIO, re, warnings from PIL import Image from indicoio.utils.errors import IndicoError, DataStructureException 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 image_preprocess(image, size=(48,48), min_axis=None, 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, basestring): b64_str = re.sub('^data:image/.+;base64,', '', image) if os.path.isfile(image): # check type of element out_image = Image.open(image) elif B64_PATTERN.match(b64_str) is not None: return b64_str else: raise IndicoError("String provided must be a valid filepath or base64 encoded string") elif isinstance(image, Image.Image): out_image = image elif type(image).__name__ == "ndarray": # image is from numpy/scipy if "float" in str(image.dtype) and image.min() >= 0 and image.max() <= 1: image *= 255. try: out_image = Image.fromarray(image.astype("uint8")) except TypeError as e: raise IndicoError("Please ensure the numpy array is acceptable by PIL. Values must be between 0 and 1 or between 0 and 255 in greyscale, rgb, or rgba format.") else: raise IndicoError("Image must be a filepath, base64 encoded string, or a numpy array") if size or min_axis: out_image = resize_image(out_image, size, min_axis) # convert to base64 temp_output = StringIO.StringIO() out_image.save(temp_output, format='PNG') temp_output.seek(0) output_s = temp_output.read() return base64.b64encode(output_s) def resize_image(image, size, min_axis): if min_axis: min_idx, other_idx = (0,1) if image.size[0] < image.size[1] else (1,0) aspect = image.size[other_idx]/float(image.size[min_idx]) if aspect > 10: warnings.warn( "An aspect ratio greater than 10:1 is not recommended", Warning ) size_arr = [0,0] size_arr[min_idx] = min_axis size_arr[other_idx] = int(min_axis * aspect) image = image.resize(tuple(size_arr)) elif size: image = image.resize(size) return image 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)