diff --git a/CHANGES.txt b/CHANGES.txt index 581a418..fa5f937 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -31,4 +31,5 @@ v0.7.4 Mon Jun 22 -- Fix for setup.py issues v0.7.5 Wed Jul 1 -- Public access to sentimentHQ api v0.7.6 Tue Jul 7 -- Add Keywords API v0.8.0 Fri Jul 10 -- Add Content Filtering API, Named Entities API, Facial Emotion with Localization -v0.8.1 Wed Fri 22 -- Add Sentiment HQ to predict_text API +v0.8.1 Wed Jul 22 -- Add Sentiment HQ to predict_text API +v0.9.0 Tue Jul 28 -- Deprecate batch function calls in favor of type inference diff --git a/README.md b/README.md index b90a5ad..f01b5ef 100644 --- a/README.md +++ b/README.md @@ -83,11 +83,11 @@ Examples Batch API --------- -Each `indicoio` function has a corresponding batch function for analyzing many examples with a single request. Simply pass in a list of inputs and receive a list of results in return. +Each `indicoio` function can process many examples with a single request. Simply pass in a list of inputs and receive a list of results in return. ```python ->>> from indicoio import batch_sentiment +>>> from indicoio import sentiment ->>> batch_sentiment(['Best day ever', 'Worst day ever']) +>>> sentiment(['Best day ever', 'Worst day ever']) [0.9899001220871786, 0.005709885173415242] ``` @@ -101,12 +101,12 @@ Accepted text API names: `text_tags, political, sentiment, language` Accepted image API names: `fer, facial_features, image_features` ```python ->>> from indicoio import predict_text, predict_image, batch_predict_text, batch_predict_image +>>> from indicoio import predict_text, predict_image, predict_text, predict_image >>> predict_text('Best day ever', apis=["sentiment", "language"]) {'sentiment': 0.9899001220871786, 'language': {u'Swedish': 0.0022464881013042294, u'Vietnamese': 9.887170914498351e-05, ...}} ->>> batch_predict_text(['Best day ever', 'Worst day ever'], apis=["sentiment", "language"]) +>>> predict_text(['Best day ever', 'Worst day ever'], apis=["sentiment", "language"]) {'sentiment': [0.9899001220871786, 0.005709885173415242], 'language': [{u'Swedish': 0.0022464881013042294, u'Vietnamese': 9.887170914498351e-05, u'Romanian': 0.00010661175919993216, ...}, {u'Swedish': 0.4924352805804646, u'Vietnamese': 0.028574824174911372, u'Romanian': 0.004185623723173551, u'Dutch': 0.000717033819689362, u'Korean': 0.0030093489153785826, ...}]} >>> import numpy as np @@ -116,6 +116,6 @@ Accepted image API names: `fer, facial_features, image_features` >>> predict_image(test_face, apis=["fer", "facial_features"]) {'facial_features': [0.0, -0.026176479280200796, 0.20707644777495776, ...], 'fer': {u'Angry': 0.08877494466353497, u'Sad': 0.3933999409104264, u'Neutral': 0.1910612654566151, u'Surprise': 0.0346146405941845, u'Fear': 0.17682159820518667, u'Happy': 0.11532761017005204}} ->>> batch_predict_image([test_face, test_face], apis=["fer", "facial_features"]) +>>> predict_image([test_face, test_face], apis=["fer", "facial_features"]) {'facial_features': [[0.0, -0.026176479280200796, 0.20707644777495776, ...], [0.0, -0.026176479280200796, 0.20707644777495776, ...]], 'fer': [{u'Angry': 0.08877494466353497, u'Sad': 0.3933999409104264, u'Neutral': 0.1910612654566151, u'Surprise': 0.0346146405941845, u'Fear': 0.17682159820518667, u'Happy': 0.11532761017005204}, { u'Angry': 0.08877494466353497, u'Sad': 0.3933999409104264, u'Neutral': 0.1910612654566151, u'Surprise': 0.0346146405941845, u'Fear': 0.17682159820518667, u'Happy': 0.11532761017005204}]} ``` diff --git a/README.rst b/README.rst index 0ea9ef4..c3cde27 100644 --- a/README.rst +++ b/README.rst @@ -91,15 +91,15 @@ Examples Batch API --------- -Each ``indicoio`` function has a corresponding batch function for -analyzing many examples with a single request. Simply pass in a list of -inputs and receive a list of results in return. +Each ``indicoio`` function can process many examples with a single +request. Simply pass in a list of inputs and receive a list of results +in return. .. code:: python - >>> from indicoio import batch_sentiment + >>> from indicoio import sentiment - >>> batch_sentiment(['Best day ever', 'Worst day ever']) + >>> sentiment(['Best day ever', 'Worst day ever']) [0.9899001220871786, 0.005709885173415242] Calling multiple APIs with a single function @@ -119,12 +119,12 @@ Accepted image API names: ``fer, facial_features, image_features`` .. code:: python - >>> from indicoio import predict_text, predict_image, batch_predict_text, batch_predict_image + >>> from indicoio import predict_text, predict_image, predict_text, predict_image >>> predict_text('Best day ever', apis=["sentiment", "language"]) {'sentiment': 0.9899001220871786, 'language': {u'Swedish': 0.0022464881013042294, u'Vietnamese': 9.887170914498351e-05, ...}} - >>> batch_predict_text(['Best day ever', 'Worst day ever'], apis=["sentiment", "language"]) + >>> predict_text(['Best day ever', 'Worst day ever'], apis=["sentiment", "language"]) {'sentiment': [0.9899001220871786, 0.005709885173415242], 'language': [{u'Swedish': 0.0022464881013042294, u'Vietnamese': 9.887170914498351e-05, u'Romanian': 0.00010661175919993216, ...}, {u'Swedish': 0.4924352805804646, u'Vietnamese': 0.028574824174911372, u'Romanian': 0.004185623723173551, u'Dutch': 0.000717033819689362, u'Korean': 0.0030093489153785826, ...}]} >>> import numpy as np @@ -134,6 +134,6 @@ Accepted image API names: ``fer, facial_features, image_features`` >>> predict_image(test_face, apis=["fer", "facial_features"]) {'facial_features': [0.0, -0.026176479280200796, 0.20707644777495776, ...], 'fer': {u'Angry': 0.08877494466353497, u'Sad': 0.3933999409104264, u'Neutral': 0.1910612654566151, u'Surprise': 0.0346146405941845, u'Fear': 0.17682159820518667, u'Happy': 0.11532761017005204}} - >>> batch_predict_image([test_face, test_face], apis=["fer", "facial_features"]) + >>> predict_image([test_face, test_face], apis=["fer", "facial_features"]) {'facial_features': [[0.0, -0.026176479280200796, 0.20707644777495776, ...], [0.0, -0.026176479280200796, 0.20707644777495776, ...]], 'fer': [{u'Angry': 0.08877494466353497, u'Sad': 0.3933999409104264, u'Neutral': 0.1910612654566151, u'Surprise': 0.0346146405941845, u'Fear': 0.17682159820518667, u'Happy': 0.11532761017005204}, { u'Angry': 0.08877494466353497, u'Sad': 0.3933999409104264, u'Neutral': 0.1910612654566151, u'Surprise': 0.0346146405941845, u'Fear': 0.17682159820518667, u'Happy': 0.11532761017005204}]} diff --git a/indicoio/__init__.py b/indicoio/__init__.py index 9db9de6..1225c9c 100644 --- a/indicoio/__init__.py +++ b/indicoio/__init__.py @@ -1,6 +1,7 @@ -from functools import partial +from functools import wraps, partial +import warnings -Version, version, __version__, VERSION = ('0.8.1',) * 4 +Version, version, __version__, VERSION = ('0.9.0',) * 4 JSON_HEADERS = { 'Content-type': 'application/json', @@ -23,9 +24,26 @@ from indicoio.utils.multi import predict_image, predict_text from indicoio.config import API_NAMES +def deprecation_decorator(f, api): + @wraps(f) + def wrapper(*args, **kwargs): + warnings.warn( + "'batch_" + api + "' will be deprecated in the next major update. Please call '" + api + "' instead with the same arguments.", + DeprecationWarning + ) + return f(*args, **kwargs) + return wrapper +def detect_batch_decorator(f): + @wraps(f) + def wrapper(*args, **kwargs): + if isinstance(args[0], list): + kwargs['batch'] = True + return f(*args, **kwargs) + return wrapper + apis = dict((api, globals().get(api)) for api in API_NAMES) for api in apis: - globals()[api] = partial(apis[api]) - globals()['batch_' + api] = partial(apis[api], batch=True) + globals()[api] = partial(detect_batch_decorator(apis[api])) + globals()['batch_' + api] = partial(deprecation_decorator(apis[api], api), batch=True) diff --git a/indicoio/images/filtering.py b/indicoio/images/filtering.py index 3851b8f..3eb697b 100644 --- a/indicoio/images/filtering.py +++ b/indicoio/images/filtering.py @@ -24,6 +24,6 @@ def content_filtering(image, cloud=None, batch=False, api_key=None, **kwargs): :type image: list of lists :rtype: float of nsfwness """ - image = image_preprocess(image, batch=batch, size=None) + image = image_preprocess(image, batch=batch, size=None, min_axis=128) url_params = {"batch": batch, "api_key": api_key} return api_handler(image, cloud=cloud, api="contentfiltering", url_params=url_params, **kwargs) diff --git a/indicoio/utils/api.py b/indicoio/utils/api.py index 836b243..141cc0f 100644 --- a/indicoio/utils/api.py +++ b/indicoio/utils/api.py @@ -8,12 +8,15 @@ from indicoio.utils.errors import IndicoError, DataStructureException from indicoio import JSON_HEADERS from indicoio import config -def api_handler(arg, cloud, api, url_params = {"batch":False, "api_key":None}, **kwargs): +def api_handler(arg, cloud, api, url_params=None, **kwargs): + if url_params is None: + url_params = {"api_key":None, batch:False } + data = {'data': arg} data.update(**kwargs) json_data = json.dumps(data) if not cloud: - cloud=config.cloud + cloud = config.cloud if cloud: host = "%s.indico.domains" % cloud diff --git a/indicoio/utils/image.py b/indicoio/utils/image.py index f0ab106..8db17d7 100644 --- a/indicoio/utils/image.py +++ b/indicoio/utils/image.py @@ -10,7 +10,7 @@ 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), batch=False): +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. @@ -26,14 +26,8 @@ def image_preprocess(image, size=(48,48), batch=False): elif B64_PATTERN.match(b64_str) is not None: return b64_str else: - raise IndicoError("Snose tring provided must be a valid filepath or base64 encoded string") + raise IndicoError("String 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 - ) - out_image = process_list_image(image) elif isinstance(image, Image.Image): out_image = image elif type(image).__name__ == "ndarray": # image is from numpy/scipy @@ -47,9 +41,7 @@ def image_preprocess(image, size=(48,48), batch=False): else: raise IndicoError("Image must be a filepath, base64 encoded string, or a numpy array") - # image resizing - if size: - out_image = out_image.resize(size) + out_image = resize_image(out_image, size, min_axis) # convert to base64 temp_output = StringIO.StringIO() @@ -60,6 +52,25 @@ def image_preprocess(image, size=(48,48), batch=False): return base64.b64encode(output_s) +def resize_image(image, size, min_axis): + if size: + image = image.resize(size) + 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)) + + return image + + def get_list_dimensions(_list): """ Takes a nested list and returns the size of each dimension followed @@ -80,38 +91,3 @@ def get_element_type(_list, 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 = [] - - 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] - 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(tuple(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) - out_image.putdata(data = seq_obj) - - return out_image diff --git a/setup.py b/setup.py index b34cec7..cbdc19d 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ except ImportError: setup( name="IndicoIo", - version="0.8.1", + version="0.9.0", packages=[ "indicoio", "indicoio.text", diff --git a/tests/data/48by48.png b/tests/data/48by48.png new file mode 100644 index 0000000..2d893b9 Binary files /dev/null and b/tests/data/48by48.png differ diff --git a/tests/data/48by48rgb.png b/tests/data/48by48rgb.png new file mode 100644 index 0000000..99e612a Binary files /dev/null and b/tests/data/48by48rgb.png differ diff --git a/tests/data/48by48rgba.png b/tests/data/48by48rgba.png new file mode 100644 index 0000000..91ffaf8 Binary files /dev/null and b/tests/data/48by48rgba.png differ diff --git a/tests/data/64by64.png b/tests/data/64by64.png new file mode 100644 index 0000000..44b5961 Binary files /dev/null and b/tests/data/64by64.png differ diff --git a/tests/test_remote.py b/tests/test_remote.py index 85c45b5..f41f31a 100644 --- a/tests/test_remote.py +++ b/tests/test_remote.py @@ -31,74 +31,74 @@ class BatchAPIRun(unittest.TestCase): def test_batch_texttags(self): test_data = ["On Monday, president Barack Obama will be..."] - response = batch_text_tags(test_data, api_key=self.api_key) + response = text_tags(test_data, api_key=self.api_key) self.assertTrue(isinstance(response, list)) def test_batch_keywords(self): test_data = ["A working api is key to the success of our young company"] words = [set(text.lower().split()) for text in test_data] - response = batch_keywords(test_data, api_key=self.api_key) + response = keywords(test_data, api_key=self.api_key) self.assertTrue(isinstance(response, list)) self.assertTrue(set(response[0].keys()).issubset(words[0])) def test_batch_posneg(self): test_data = ['Worst song ever', 'Best song ever'] - response = batch_sentiment(test_data, api_key=self.api_key) + response = sentiment(test_data, api_key=self.api_key) self.assertTrue(isinstance(response, list)) self.assertTrue(response[0] < 0.5) def test_batch_sentiment_hq(self): test_data = ['Worst song ever', 'Best song ever'] - response = batch_sentiment_hq(test_data, api_key=self.api_key) + response = sentiment_hq(test_data, api_key=self.api_key) self.assertTrue(isinstance(response, list)) self.assertTrue(response[0] < 0.5) def test_batch_political(self): test_data = ["Guns don't kill people, people kill people."] - response = batch_political(test_data, api_key=self.api_key) + response = political(test_data, api_key=self.api_key) self.assertTrue(isinstance(response, list)) def test_batch_fer(self): - test_data = [generate_array((48,48))] - response = batch_fer(test_data, api_key=self.api_key) + test_data = [os.path.normpath(os.path.join(DIR, "data/48by48.png"))] + response = fer(test_data, api_key=self.api_key) self.assertTrue(isinstance(response, list)) self.assertTrue(isinstance(response[0], dict)) def test_batch_content_filtering(self): - test_data = [generate_array((48,48))] - response = batch_content_filtering(test_data, api_key=self.api_key) + test_data = [os.path.normpath(os.path.join(DIR, "data/48by48.png"))] + response = content_filtering(test_data, api_key=self.api_key) self.assertTrue(isinstance(response, list)) self.assertTrue(isinstance(response[0], float)) def test_batch_fer_bad_b64(self): test_data = ["$bad#FI jeaf9(#0"] - self.assertRaises(IndicoError, batch_fer, test_data, api_key=self.api_key) + self.assertRaises(IndicoError, fer, test_data, api_key=self.api_key) def test_batch_fer_good_b64(self): test_data = ["iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAg5JREFUeNrEV4uNgzAMpegGyAgZgQ3KBscIjMAGx03QEdqbgG5AOwG3AWwAnSCXqLZkuUkwhfYsvaLm5xc7sZ1dIhdtUVjsLZRFTvp+LSaLq8UZ/s+KMSbZCcY5RV9E4QQKHG7QtgeCGv4PFt8WpzkCcztu3TiL0eJgkQmsVFn0MK+LzYkRKEGpG1GDyZdKRdaolhAoJewXnJsO1jtKCFDlChZAFxyJj2PnBRU20KZg7oMlOAENijpi8hwmGkKkZW2GzONtVLA/DxHAhTO2I7MCVBSQ6nGDlEBJDhyVYiUBHXBxzQm0wE4FzPYsGs856dA9SAAP2oENzFYqR6iAFQpHIAUzO/nxnOgthF/lM3w/3U8KYXTwxG/1IgIulF+wPQUXDMl75UoJZIHstRWpaGb8IGYqwBoKlG/lgpzoUEBoj50p8QtVrmHgaaXyC/H3BFC+e9kGFlCB0CtBF7FifQ8D9zjQQHj0pdOM3F1pUBoFKdxtqkMClScHJCSDlSxhHSNRT5K+FaZnHglrz+AGoxZLKNLYH6s3CkkuyJlp58wviZ4PuSCWDXl5hmjZtxcSCGbDUD3gK7EMOZBLCETrgVBF5K0lI5bIZ0wfrYh8NWHIAiNTPHpuTOKpCes1VTFaiNaFdGwPfdmaqlj6LmjJbgoSSfUW74K3voz+/W0oIeB7HWu2s+dfx3N+eLX8CTAAwUmKjK/dHS4AAAAASUVORK5CYII="] - response = batch_fer(test_data, api_key=self.api_key) + response = fer(test_data, api_key=self.api_key) self.assertTrue(isinstance(response, list)) self.assertTrue(isinstance(response[0], dict)) def test_batch_fer_filepath(self): test_data = [os.path.normpath(os.path.join(DIR, "data/fear.png"))] - response = batch_fer(test_data, api_key=self.api_key) + response = fer(test_data, api_key=self.api_key) self.assertTrue(isinstance(response, list)) self.assertTrue(isinstance(response[0], dict)) def test_batch_fer_pil_image(self): test_data = [Image.open(os.path.normpath(os.path.join(DIR, "data/fear.png")))] - response = batch_fer(test_data, api_key=self.api_key) + response = fer(test_data, api_key=self.api_key) self.assertTrue(isinstance(response, list)) self.assertTrue(isinstance(response[0], dict)) def test_batch_fer_nonexistant_filepath(self): test_data = ["data/unhappy.png"] - self.assertRaises(IndicoError, batch_fer, test_data, api_key=self.api_key) + self.assertRaises(IndicoError, fer, test_data, api_key=self.api_key) def test_batch_facial_features(self): - test_data = [generate_array((48,48))] - response = batch_facial_features(test_data, api_key=self.api_key) + test_data = [os.path.normpath(os.path.join(DIR, "data/48by48.png"))] + response = facial_features(test_data, api_key=self.api_key) self.assertTrue(isinstance(response, list)) self.assertTrue(isinstance(response[0], list)) self.assertEqual(len(response[0]), 48) @@ -117,22 +117,22 @@ class BatchAPIRun(unittest.TestCase): # have decided how we are dealing with them def test_batch_image_features_greyscale(self): - test_data = [generate_array((48,48))] - response = batch_image_features(test_data, api_key=self.api_key) + test_data = [os.path.normpath(os.path.join(DIR, "data/48by48.png"))] + response = image_features(test_data, api_key=self.api_key) self.assertTrue(isinstance(response, list)) self.assertTrue(isinstance(response[0], list)) self.assertEqual(len(response[0]), 2048) def test_batch_image_features_rgb(self): - test_data = [generate_array((48,48))] - response = batch_image_features(test_data, api_key=self.api_key) + test_data = [os.path.normpath(os.path.join(DIR, "data/48by48rgb.png"))] + response = image_features(test_data, api_key=self.api_key) self.assertTrue(isinstance(response, list)) self.assertTrue(isinstance(response[0], list)) self.assertEqual(len(response[0]), 2048) def test_batch_language(self): test_data = ['clearly an english sentence'] - response = batch_language(test_data, api_key=self.api_key) + response = language(test_data, api_key=self.api_key) self.assertTrue(isinstance(response, list)) self.assertTrue(response[0]['English'] > 0.25) @@ -140,14 +140,15 @@ class BatchAPIRun(unittest.TestCase): batch = ["London Underground's boss Mike Brown warned that the strike ..."] expected_entities = ("London Underground", "Mike Brown") expected_keys = set(["categories", "confidence"]) - entities = batch_named_entities(batch)[0] + entities = named_entities(batch)[0] for entity in expected_entities: assert entity in expected_entities assert not (set(entities[entity]) - expected_keys) def test_batch_multi_api_image(self): - test_data = [generate_array((48,48)), generate_int_array((48,48))] - response = batch_predict_image(test_data, apis=config.IMAGE_APIS, api_key=self.api_key) + test_data = [os.path.normpath(os.path.join(DIR, "data/48by48.png")), + os.path.normpath(os.path.join(DIR, "data/48by48.png"))] + response = predict_image(test_data, apis=config.IMAGE_APIS, api_key=self.api_key) self.assertTrue(isinstance(response, dict)) self.assertTrue(set(response.keys()) == set(config.IMAGE_APIS)) @@ -155,21 +156,21 @@ class BatchAPIRun(unittest.TestCase): def test_batch_multi_api_text(self): test_data = ['clearly an english sentence'] - response = batch_predict_text(test_data, apis=config.TEXT_APIS, api_key=self.api_key) + response = predict_text(test_data, apis=config.TEXT_APIS, api_key=self.api_key) self.assertTrue(isinstance(response, dict)) self.assertTrue(set(response.keys()) == set(config.TEXT_APIS)) def test_default_multi_api_text(self): test_data = ['clearly an english sentence'] - response = batch_predict_text(test_data, api_key=self.api_key) + response = predict_text(test_data, api_key=self.api_key) self.assertTrue(isinstance(response, dict)) self.assertTrue(set(response.keys()) == set(config.TEXT_APIS)) def test_multi_api_bad_api(self): self.assertRaises(IndicoError, - batch_predict_text, + predict_text, "this shouldn't work", apis=["sentiment", "somethingbad"]) @@ -180,14 +181,14 @@ class BatchAPIRun(unittest.TestCase): apis=["fer", "sentiment", "facial_features"]) def test_batch_multi_bad_mixed_api(self): self.assertRaises(IndicoError, - batch_predict_text, + predict_text, ["this shouldn't work"], apis=["fer", "sentiment", "facial_features"]) def test_batch_set_cloud(self): test_data = ['clearly an english sentence'] self.assertRaises(ConnectionError, - batch_language, + language, test_data, api_key=self.api_key, cloud='invalid/cloud') @@ -290,7 +291,7 @@ class FullAPIRun(unittest.TestCase): def test_good_fer(self): fer_set = set(['Angry', 'Sad', 'Neutral', 'Surprise', 'Fear', 'Happy']) - test_face = generate_array((48,48)) + test_face = os.path.normpath(os.path.join(DIR, "data/48by48.png")) response = fer(test_face) self.assertTrue(isinstance(response, dict)) @@ -298,14 +299,14 @@ class FullAPIRun(unittest.TestCase): def test_good_int_array_fer(self): fer_set = set(['Angry', 'Sad', 'Neutral', 'Surprise', 'Fear', 'Happy']) - test_face = generate_int_array((48,48)) + test_face = os.path.normpath(os.path.join(DIR, "data/48by48.png")) response = fer(test_face) self.assertTrue(isinstance(response, dict)) self.assertEqual(fer_set, set(response.keys())) def test_happy_fer(self): - test_face = self.load_image("data/happy.png", as_grey=True) + test_face = os.path.normpath(os.path.join(DIR, "data/happy.png")) response = fer(test_face) self.assertTrue(isinstance(response, dict)) self.assertTrue(response['Happy'] > 0.5) @@ -317,26 +318,31 @@ class FullAPIRun(unittest.TestCase): self.assertTrue(response['Happy'] > 0.5) def test_fear_fer(self): - test_face = self.load_image("data/fear.png", as_grey=True) + test_face = os.path.normpath(os.path.join(DIR, "data/fear.png")) response = fer(test_face) self.assertTrue(isinstance(response, dict)) self.assertTrue(response['Fear'] > 0.25) def test_bad_fer(self): fer_set = set(['Angry', 'Sad', 'Neutral', 'Surprise', 'Fear', 'Happy']) - test_face = generate_array((56, 56)) + test_face = os.path.normpath(os.path.join(DIR, "data/64by64.png")) response = fer(test_face) self.assertTrue(isinstance(response, dict)) self.assertEqual(fer_set, set(response.keys())) def test_safe_content_filtering(self): - test_face = self.load_image("data/happy.png", as_grey=True) + test_face = os.path.normpath(os.path.join(DIR, "data/happy.png")) response = content_filtering(test_face) self.assertTrue(response < 0.5) + def test_resize_content_filtering(self): + test_face = os.path.normpath(os.path.join(DIR, "data/happy.png")) + response = content_filtering(test_face) + self.assertTrue(isinstance(response, float)) + def test_good_facial_features(self): - test_face = generate_array((48,48)) + test_face = os.path.normpath(os.path.join(DIR, "data/48by48.png")) response = facial_features(test_face) self.assertTrue(isinstance(response, list)) @@ -344,7 +350,7 @@ class FullAPIRun(unittest.TestCase): self.check_range(response) def test_rgba_int_array_facial_features(self): - test_face = generate_rgba_int_array((48, 48)) + test_face = os.path.normpath(os.path.join(DIR, "data/48by48rgba.png")) response = facial_features(test_face) self.assertTrue(isinstance(response, list)) @@ -353,7 +359,7 @@ class FullAPIRun(unittest.TestCase): def test_good_int_array_facial_features(self): fer_set = set(['Angry', 'Sad', 'Neutral', 'Surprise', 'Fear', 'Happy']) - test_face = generate_int_array((48,48)) + test_face = os.path.normpath(os.path.join(DIR, "data/48by48.png")) response = facial_features(test_face) self.assertTrue(isinstance(response, list)) @@ -371,7 +377,7 @@ class FullAPIRun(unittest.TestCase): # self.check_range(response) def test_good_image_features_greyscale(self): - test_image = generate_array((48,48)) + test_image = os.path.normpath(os.path.join(DIR, "data/48by48.png")) response = image_features(test_image) self.assertTrue(isinstance(response, list)) @@ -379,7 +385,7 @@ class FullAPIRun(unittest.TestCase): self.check_range(response) def test_good_image_features_rgb(self): - test_image = [[(random.random(),) * 3 for _ in xrange(48)] for _ in xrange(48)] + test_image = os.path.normpath(os.path.join(DIR, "data/48by48rgb.png")) response = image_features(test_image) self.assertTrue(isinstance(response, list)) @@ -387,7 +393,7 @@ class FullAPIRun(unittest.TestCase): self.check_range(response) def test_multi_api_image(self): - test_data = generate_array((48,48)) + test_data = os.path.normpath(os.path.join(DIR, "data/48by48.png")) response = predict_image(test_data, apis=config.IMAGE_APIS, api_key=self.api_key) self.assertTrue(isinstance(response, dict)) @@ -550,6 +556,11 @@ class NumpyImagesRun(FullAPIRun): test_image = np.random.randint(255, 300, size=(48,48, 5)) self.assertRaises(IndicoError, image_features, test_image) + def test_resize_content_filtering_numpy_arrays(self): + test_image = np.random.randint(0, 255, size=(480,248, 3)) + response = content_filtering(test_image) + self.assertTrue(isinstance(response, float)) + def flatten(container): for i in container: if isinstance(i, list) or isinstance(i, tuple): @@ -558,15 +569,5 @@ def flatten(container): else: yield i -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, 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__": unittest.main() diff --git a/tests/unit_test.py b/tests/unit_test.py new file mode 100644 index 0000000..437c62e --- /dev/null +++ b/tests/unit_test.py @@ -0,0 +1,16 @@ +from indicoio.utils.image import image_preprocess +from PIL import Image +import os, unittest, base64, StringIO + +DIR = os.path.dirname(os.path.realpath(__file__)) + +class ResizeTests(unittest.TestCase): + """ + test image resizing + """ + def test_min_axis_resize(self): + test_image = os.path.normpath(os.path.join(DIR, "data/fear.png")) + resized_image = image_preprocess(test_image, min_axis=360) + image_string = StringIO.StringIO(base64.b64decode(resized_image)) + image = Image.open(image_string) + self.assertEqual(image.size, (360.0, 360.0))