From d8b59d06a42a7a54904a232682c0fff568d551bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Fernando=20S=C3=A1nchez?= Date: Tue, 28 Feb 2017 04:28:54 +0100 Subject: [PATCH] Converted Ekman2VAD to centroids * Changed the way modules are imported -> we can now use dotted notation (e.g. senpy.plugins.conversion.centroids) * Refactored ekman2vad's plugin -> generic centroids * Added some basic tests --- Makefile | 6 +- senpy/extensions.py | 18 ++++-- senpy/{plugins.py => plugins/__init__.py} | 2 +- senpy/plugins/conversion/__init__.py | 0 senpy/plugins/conversion/centroids.py | 52 +++++++++++++++++ senpy/plugins/conversion/emotion/ekman2vad.py | 58 ------------------- .../conversion/emotion/ekman2vad.senpy | 5 +- 7 files changed, 72 insertions(+), 69 deletions(-) rename senpy/{plugins.py => plugins/__init__.py} (99%) create mode 100644 senpy/plugins/conversion/__init__.py create mode 100644 senpy/plugins/conversion/centroids.py delete mode 100644 senpy/plugins/conversion/emotion/ekman2vad.py diff --git a/Makefile b/Makefile index e3ad1dd..fc24281 100644 --- a/Makefile +++ b/Makefile @@ -41,12 +41,12 @@ build-%: version Dockerfile-% quick_test: $(addprefix test-,$(PYMAIN)) dev-%: - @docker start $(NAME)-dev || (\ + @docker start $(NAME)-dev$* || (\ $(MAKE) build-$*; \ - docker run -d -w /usr/src/app/ -v $$PWD:/usr/src/app --entrypoint=/bin/bash -p 5000:5000 -ti --name $(NAME)-dev '$(IMAGEWTAG)-python$*'; \ + docker run -d -w /usr/src/app/ -v $$PWD:/usr/src/app --entrypoint=/bin/bash -ti --name $(NAME)-dev$* '$(IMAGEWTAG)-python$*'; \ )\ - docker exec -ti $(NAME)-dev bash + docker exec -ti $(NAME)-dev$* bash dev: dev-$(PYMAIN) diff --git a/senpy/extensions.py b/senpy/extensions.py index 861dc89..3686ae7 100644 --- a/senpy/extensions.py +++ b/senpy/extensions.py @@ -17,7 +17,7 @@ import copy import fnmatch import inspect import sys -import imp +import importlib import logging import traceback import yaml @@ -181,7 +181,7 @@ class Senpy(object): newentries = [] for i in resp.entries: if output == "full": - newemotions = copy.copy(i.emotions) + newemotions = copy.deepcopy(i.emotions) else: newemotions = [] for j in i.emotions: @@ -303,6 +303,13 @@ class Senpy(object): logger.info('Installing requirements: ' + str(requirements)) pip.main(pip_args) + @classmethod + def _load_module(cls, name, root): + sys.path.append(root) + tmp = importlib.import_module(name) + sys.path.remove(root) + return tmp + @classmethod def _load_plugin_from_info(cls, info, root): if not cls.validate_info(info): @@ -310,11 +317,10 @@ class Senpy(object): return None, None module = info["module"] name = info["name"] - sys.path.append(root) - (fp, pathname, desc) = imp.find_module(module, [root, ]) + cls._install_deps(info) - tmp = imp.load_module(module, fp, pathname, desc) - sys.path.remove(root) + tmp = cls._load_module(module, root) + candidate = None for _, obj in inspect.getmembers(tmp): if inspect.isclass(obj) and inspect.getmodule(obj) == tmp: diff --git a/senpy/plugins.py b/senpy/plugins/__init__.py similarity index 99% rename from senpy/plugins.py rename to senpy/plugins/__init__.py index 693cb43..fdc6882 100644 --- a/senpy/plugins.py +++ b/senpy/plugins/__init__.py @@ -7,7 +7,7 @@ import pickle import logging import tempfile import copy -from . import models +from .. import models logger = logging.getLogger(__name__) diff --git a/senpy/plugins/conversion/__init__.py b/senpy/plugins/conversion/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/senpy/plugins/conversion/centroids.py b/senpy/plugins/conversion/centroids.py new file mode 100644 index 0000000..0d0b899 --- /dev/null +++ b/senpy/plugins/conversion/centroids.py @@ -0,0 +1,52 @@ +from senpy.plugins import EmotionConversionPlugin +from senpy.models import EmotionSet, Emotion, Error + +import logging +logger = logging.getLogger(__name__) + + +class CentroidConversion(EmotionConversionPlugin): + + def _forward_conversion(self, original): + """Sum the VAD value of all categories found.""" + res = Emotion() + for e in original.onyx__hasEmotion: + category = e.onyx__hasEmotionCategory + if category in self.centroids: + for dim, value in self.centroids[category].iteritems(): + try: + res[dim] += value + except Exception: + res[dim] = value + return res + + def _backwards_conversion(self, original): + """Find the closest category""" + dimensions = list(self.centroids.values())[0] + + def distance(e1, e2): + return sum((e1[k] - e2.get(self.aliases[k], 0)) for k in dimensions) + + emotion = '' + mindistance = 10000000000000000000000.0 + for state in self.centroids: + d = distance(self.centroids[state], original) + if d < mindistance: + mindistance = d + emotion = state + result = Emotion(onyx__hasEmotionCategory=emotion) + return result + + def convert(self, emotionSet, fromModel, toModel, params): + + cf, ct = self.centroids_direction + logger.debug('{}\n{}\n{}\n{}'.format(emotionSet, fromModel, toModel, params)) + e = EmotionSet() + if fromModel == cf: + e.onyx__hasEmotion.append(self._forward_conversion(emotionSet)) + elif fromModel == ct: + for i in emotionSet.onyx__hasEmotion: + e.onyx__hasEmotion.append(self._backwards_conversion(i)) + else: + raise Error('EMOTION MODEL NOT KNOWN') + yield e diff --git a/senpy/plugins/conversion/emotion/ekman2vad.py b/senpy/plugins/conversion/emotion/ekman2vad.py deleted file mode 100644 index f38f83d..0000000 --- a/senpy/plugins/conversion/emotion/ekman2vad.py +++ /dev/null @@ -1,58 +0,0 @@ -from senpy.plugins import EmotionConversionPlugin -from senpy.models import EmotionSet, Emotion, Error - -import logging -logger = logging.getLogger(__name__) - -import math - - -class WNA2VAD(EmotionConversionPlugin): - - def _ekman_to_vad(self, ekmanSet): - """Sum the VAD value of all categories found.""" - valence = 0 - arousal = 0 - dominance = 0 - for e in ekmanSet.onyx__hasEmotion: - category = e.onyx__hasEmotionCategory - centroid = self.centroids[category] - valence += centroid['V'] - arousal += centroid['A'] - dominance += centroid['D'] - e = Emotion({'emoml:valence': valence, - 'emoml:arousal': arousal, - 'emoml:potency': dominance}) - return e - - def _vad_to_ekman(self, VADEmotion): - """Find the closest category""" - V = VADEmotion['emoml:valence'] - A = VADEmotion['emoml:arousal'] - D = VADEmotion['emoml:potency'] - emotion = '' - value = 10000000000000000000000.0 - for state in self.centroids: - valence = V - self.centroids[state]['V'] - arousal = A - self.centroids[state]['A'] - dominance = D - self.centroids[state]['D'] - new_value = math.sqrt((valence**2) + - (arousal**2) + - (dominance**2)) - if new_value < value: - value = new_value - emotion = state - result = Emotion(onyx__hasEmotionCategory=emotion) - return result - - def convert(self, emotionSet, fromModel, toModel, params): - logger.debug('{}\n{}\n{}\n{}'.format(emotionSet, fromModel, toModel, params)) - e = EmotionSet() - if fromModel == 'emoml:big6': - e.onyx__hasEmotion.append(self._ekman_to_vad(emotionSet)) - elif fromModel == 'emoml:fsre-dimensions': - for i in emotionSet.onyx__hasEmotion: - e.onyx__hasEmotion.append(self._vad_to_ekman(i)) - else: - raise Error('EMOTION MODEL NOT KNOWN') - yield e diff --git a/senpy/plugins/conversion/emotion/ekman2vad.senpy b/senpy/plugins/conversion/emotion/ekman2vad.senpy index 80cb0fc..4246ba7 100644 --- a/senpy/plugins/conversion/emotion/ekman2vad.senpy +++ b/senpy/plugins/conversion/emotion/ekman2vad.senpy @@ -1,6 +1,6 @@ --- name: Ekman2VAD -module: ekman2vad +module: senpy.plugins.conversion.centroids description: Plugin to convert emotion sets from Ekman to VAD version: 0.1 onyx:doesConversion: @@ -29,6 +29,9 @@ centroids: A: 5.21 D: 2.82 V: 2.21 +centroids_direction: + - emoml:big6 + - emoml:fsre-dimensions aliases: A: emoml:arousal V: emoml:valence