diff --git a/indicoio/__init__.py b/indicoio/__init__.py index 72245de..afffc63 100644 --- a/indicoio/__init__.py +++ b/indicoio/__init__.py @@ -19,6 +19,7 @@ from indicoio.text.keywords import keywords from indicoio.text.ner import named_entities from indicoio.images.fer import fer from indicoio.images.features import facial_features +from indicoio.images.faciallocalization import facial_localization from indicoio.images.features import image_features from indicoio.images.filtering import content_filtering from indicoio.utils.multi import predict_image, predict_text diff --git a/indicoio/images/faciallocalization.py b/indicoio/images/faciallocalization.py new file mode 100644 index 0000000..827836e --- /dev/null +++ b/indicoio/images/faciallocalization.py @@ -0,0 +1,31 @@ +import requests + +from indicoio.utils.image import image_preprocess +from indicoio.utils.api import api_handler + + +def facial_localization(image, cloud=None, batch=False, api_key=None, **kwargs): + """ + Given an image, returns a list of faces found within the image. + For each face, we return a dictionary containing the upper left corner and lower right corner. + If crop is True, the cropped face is included in the dictionary. + Input should be in a numpy ndarray or a filename. + + Example usage: + + .. code-block:: python + + >>> from indicoio import facial_localization + >>> import numpy as np + >>> img = np.zeros([image of a face]) + >>> faces = facial_localization(img) + >>> len(faces) + 1 + + :param image: The image to be analyzed. + :type image: filepath or ndarray + :rtype: List of faces (dict) found. + """ + image = image_preprocess(image, batch=batch) + url_params = {"batch": batch, "api_key": api_key} + return api_handler(image, cloud=cloud, api="faciallocalization", url_params=url_params, **kwargs) \ No newline at end of file diff --git a/indicoio/images/features.py b/indicoio/images/features.py index 75482bd..d96b85c 100644 --- a/indicoio/images/features.py +++ b/indicoio/images/features.py @@ -16,7 +16,7 @@ def facial_features(image, cloud=None, batch=False, api_key=None, **kwargs): >>> from indicoio import facial_features >>> import numpy as np - >>> face = np.zeros((48,48)).tolist() + >>> face = np.zeros((48,48)) >>> features = facial_features(face) >>> len(features) 48 @@ -25,7 +25,7 @@ def facial_features(image, cloud=None, batch=False, api_key=None, **kwargs): :type image: list of lists :rtype: List containing feature responses """ - image = image_preprocess(image, batch=batch) + image = image_preprocess(image, batch=batch, size=(48,48)) url_params = {"batch": batch, "api_key": api_key} return api_handler(image, cloud=cloud, api="facialfeatures", url_params=url_params, **kwargs) diff --git a/indicoio/images/filtering.py b/indicoio/images/filtering.py index 3eb697b..d5e51cf 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, min_axis=128) + image = image_preprocess(image, batch=batch, 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/image.py b/indicoio/utils/image.py index 14e4f2a..06b7174 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), min_axis=None, batch=False): +def image_preprocess(image, size=None, min_axis=None, batch=False): """ Takes an image and prepares it for sending to the api including resizing and image data/structure standardizing. diff --git a/tests/test_remote.py b/tests/test_remote.py index e0c645a..228b70f 100644 --- a/tests/test_remote.py +++ b/tests/test_remote.py @@ -6,7 +6,7 @@ from requests import ConnectionError from nose.plugins.skip import Skip, SkipTest from indicoio import config -from indicoio import political, sentiment, fer, facial_features, content_filtering, language, image_features, text_tags +from indicoio import political, sentiment, fer, facial_features, facial_localization, content_filtering, language, image_features, text_tags from indicoio import batch_political, batch_sentiment, batch_fer, batch_content_filtering, batch_facial_features from indicoio import batch_language, batch_image_features, batch_text_tags from indicoio import keywords, batch_keywords @@ -355,6 +355,24 @@ class FullAPIRun(unittest.TestCase): self.assertTrue(isinstance(response, dict)) self.assertEqual(fer_set, set(response.keys())) + def test_facial_localization(self): + test_face = os.path.normpath(os.path.join(DIR, "data/happy.png")) + res = facial_localization(test_face)[0] + self.assertTrue(res["top_left_corner"][0] < res["bottom_right_corner"][0]) + self.assertTrue(res["top_left_corner"][1] < res["bottom_right_corner"][1]) + + def test_facial_localization_sensitivity(self): + test_face = os.path.normpath(os.path.join(DIR, "data/happy.png")) + low_sens = facial_localization(test_face, sensitivity=0.1) + high_sens = facial_localization(test_face, sensitivity=0.9) + self.assertEqual(len(low_sens), 1) + self.assertTrue(len(high_sens) > 1) + + def test_facial_localization_crop(self): + test_face = os.path.normpath(os.path.join(DIR, "data/happy.png")) + res = facial_localization(test_face, crop=True)[0] + self.assertTrue(res.get("image")) + def test_safe_content_filtering(self): test_face = os.path.normpath(os.path.join(DIR, "data/happy.png")) response = content_filtering(test_face)