diff --git a/CHANGES.txt b/CHANGES.txt index f48e333..b8d3766 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -26,3 +26,5 @@ v0.6.0, Thu May 29 -- Remove numpy / scipy dependency in favor of Pillow v0.7.0, Tue Jun 9 -- Added support for calling multiple APIs in a single function and accepting filenames as image API inputs v0.7.1 Thu Jun 11 -- High quality sentiment API for private beta, fix for multi API support v0.7.2 Thu Jun 11 -- Remove sentiment_hq from text apis by default +v0.7.3 Wed Jun 17 -- Fixes for handling of specific image types +v0.7.4 Mon Jun 22 -- Fix for setup.py issues diff --git a/indicoio/__init__.py b/indicoio/__init__.py index ed19a5c..162ede4 100644 --- a/indicoio/__init__.py +++ b/indicoio/__init__.py @@ -1,6 +1,6 @@ from functools import partial -Version, version, __version__, VERSION = ('0.7.2',) * 4 +Version, version, __version__, VERSION = ('0.7.4',) * 4 JSON_HEADERS = { 'Content-type': 'application/json', diff --git a/indicoio/utils/image.py b/indicoio/utils/image.py index 5fc9d1b..70fb19e 100644 --- a/indicoio/utils/image.py +++ b/indicoio/utils/image.py @@ -22,7 +22,7 @@ def image_preprocess(image, size=(48,48), batch=False): b64_str = re.sub('^data:image/.+;base64,', '', image) if os.path.isfile(image): # check type of element - outImage = Image.open(image) + out_image = Image.open(image) elif B64_PATTERN.match(b64_str) is not None: return b64_str else: @@ -33,20 +33,26 @@ def image_preprocess(image, size=(48,48), batch=False): "Input as lists of pixels will be deprecated in the next major update", DeprecationWarning ) - outImage = process_list_image(image) + out_image = process_list_image(image) elif isinstance(image, Image.Image): - outImage = image + out_image = image elif type(image).__name__ == "ndarray": # image is from numpy/scipy - out_image = Image.fromarray(image) + 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") # image resizing - outImage = outImage.resize(size) + out_image = out_image.resize(size) # convert to base64 temp_output = StringIO.StringIO() - outImage.save(temp_output, format='PNG') + out_image.save(temp_output, format='PNG') temp_output.seek(0) output_s = temp_output.read() @@ -87,7 +93,7 @@ def process_list_image(_list): seq_obj = [] - outImage = Image.new("RGB", (dimens[0], dimens[1])) + out_image = Image.new("RGB", (dimens[0], dimens[1])) for i in xrange(dimens[0]): for j in xrange(dimens[1]): elem = _list[i][j] @@ -96,7 +102,7 @@ def process_list_image(_list): 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]) + seq_obj.append(tuple(elem[0:3])) elif data_type == float: #Grayscale 0 - 1.0f seq_obj.append((int(elem * 255), ) * 3) @@ -105,6 +111,6 @@ def process_list_image(_list): seq_obj.append((elem, ) * 3) #Needs to be 0 - 255 in flattened list of (R, G, B) - outImage.putdata(data = seq_obj) + out_image.putdata(data = seq_obj) - return outImage + return out_image diff --git a/setup.py b/setup.py index 5dbf095..3dca9cc 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,6 @@ """ Setup for indico apis """ -from indicoio import VERSION try: from setuptools import setup @@ -10,7 +9,7 @@ except ImportError: setup( name="IndicoIo", - version=VERSION, + version="0.7.4", packages=[ "indicoio", "indicoio.text", diff --git a/tests/test_remote.py b/tests/test_remote.py index ef66a96..b4b262d 100644 --- a/tests/test_remote.py +++ b/tests/test_remote.py @@ -214,7 +214,7 @@ class FullAPIRun(unittest.TestCase): response = political(test_string) self.assertTrue(isinstance(response, dict)) - assert response['Liberal'] > 0.25 + assert response['Libertarian'] > 0.25 def test_posneg(self): test_string = "Worst song ever." @@ -291,6 +291,14 @@ class FullAPIRun(unittest.TestCase): self.assertEqual(len(response), 48) self.check_range(response) + def test_rgba_int_array_facial_features(self): + test_face = generate_rgba_int_array((48, 48)) + response = facial_features(test_face) + + self.assertTrue(isinstance(response, list)) + self.assertEqual(len(response), 48) + self.check_range(response) + def test_good_int_array_facial_features(self): fer_set = set(['Angry', 'Sad', 'Neutral', 'Surprise', 'Fear', 'Happy']) test_face = generate_int_array((48,48)) @@ -421,6 +429,75 @@ class FullAPIRun(unittest.TestCase): config.api_key = temp_api_key + +class NumpyImagesRun(FullAPIRun): + """ + Testing numpy array as images + """ + def setUp(self): + self.api_key = config.api_key + try: + import numpy as np + globals()["np"] = np + except ImportError: + self.skipTest("Numpy is not installed!") + + def test_float_numpy_arrays(self): + test_image = np.random.random(size=(48,48)) + response = image_features(test_image) + + self.assertTrue(isinstance(response, list)) + self.assertEqual(len(response), 2048) + self.check_range(response) + + def test_float_RGB_numpy_arrays(self): + test_image = np.random.random(size=(48,48,3)) + response = image_features(test_image) + + self.assertTrue(isinstance(response, list)) + self.assertEqual(len(response), 2048) + self.check_range(response) + + def test_float_RGBA_numpy_arrays(self): + test_image = np.random.random(size=(48,48,4)) + response = image_features(test_image) + + self.assertTrue(isinstance(response, list)) + self.assertEqual(len(response), 2048) + self.check_range(response) + + def test_int_numpy_arrays(self): + test_image = np.random.randint(0, 255, size=(48,48)) + response = image_features(test_image) + + self.assertTrue(isinstance(response, list)) + self.assertEqual(len(response), 2048) + self.check_range(response) + + def test_int_RGB_numpy_arrays(self): + test_image = np.random.randint(0, 255, size=(48,48, 3)) + response = image_features(test_image) + + self.assertTrue(isinstance(response, list)) + self.assertEqual(len(response), 2048) + self.check_range(response) + + def test_int_RGBA_numpy_arrays(self): + test_image = np.random.randint(0, 255, size=(48,48, 3)) + response = image_features(test_image) + + self.assertTrue(isinstance(response, list)) + self.assertEqual(len(response), 2048) + self.check_range(response) + + def test_invalid_int_numpy_arrays(self): + test_image = np.random.randint(255, 300, size=(48,48, 3)) + self.assertRaises(IndicoError, image_features, test_image) + + def test_invalid_int_numpy_arrays(self): + test_image = np.random.randint(255, 300, size=(48,48, 5)) + self.assertRaises(IndicoError, image_features, test_image) + def flatten(container): for i in container: if isinstance(i, list) or isinstance(i, tuple): @@ -433,7 +510,10 @@ def generate_array(size): return [[random.random() for _ in xrange(size[0])] for _ in xrange(size[1])] def generate_int_array(size): - return [[random.randint(0, 50) for _ in xrange(size[0])] for _ in xrange(size[1])] + return [[random.randint(0, 255) for _ in xrange(size[0])] for _ in xrange(size[1])] + +def generate_rgba_int_array(size): + return [[[random.randint(0, 255) for _ in xrange(3)] for _ in xrange(size[0])] for _ in xrange(size[1])] if __name__ == "__main__":