[mousetrap/gnome3-wip: 214/240] Add FeatureDetector caching.



commit 5a0a51b3fdaa2050a6ac6747d8301e92e170da14
Author: Stoney Jackson <dr stoney gmail com>
Date:   Sun Jun 29 14:01:00 2014 -0400

    Add FeatureDetector caching.

 src/mousetrap/image.py        |    6 ++++
 src/mousetrap/mousetrap.yaml  |    1 +
 src/mousetrap/plugins/eyes.py |    6 ++--
 src/mousetrap/plugins/nose.py |    4 +-
 src/mousetrap/vision.py       |   57 ++++++++++++++++++++++++++++++++++++-----
 5 files changed, 62 insertions(+), 12 deletions(-)
---
diff --git a/src/mousetrap/image.py b/src/mousetrap/image.py
index 02a714b..1eed2be 100644
--- a/src/mousetrap/image.py
+++ b/src/mousetrap/image.py
@@ -36,6 +36,12 @@ class Image(object):
     def get_height(self):
         return self._image_cv.shape[1]
 
+    def __hash__(self):
+        return id(self)
+
+    def __eq__(self, other):
+        return hash(self) == hash(other)
+
 
 def _cv_rgb_to_cv_grayscale(image):
     return cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
diff --git a/src/mousetrap/mousetrap.yaml b/src/mousetrap/mousetrap.yaml
index d9dcf0a..052c358 100644
--- a/src/mousetrap/mousetrap.yaml
+++ b/src/mousetrap/mousetrap.yaml
@@ -5,6 +5,7 @@ assembly:
 - mousetrap.plugins.display.DisplayPlugin
 - mousetrap.plugins.nose.NoseJoystickPlugin
 - mousetrap.plugins.eyes.EyesPlugin
+- mousetrap.vision.FeatureDetectorClearCachePlugin
 
 
 # camera - Configuration for the built-in camera.
diff --git a/src/mousetrap/plugins/eyes.py b/src/mousetrap/plugins/eyes.py
index bf380d4..25018fb 100644
--- a/src/mousetrap/plugins/eyes.py
+++ b/src/mousetrap/plugins/eyes.py
@@ -62,19 +62,19 @@ class LeftEyeLocator(object):
 
     def __init__(self, config):
         self._config = config
-        self._face_detector = FeatureDetector(
+        self._face_detector = FeatureDetector.get_detector(
             config,
             "face",
             scale_factor=config[self]['face_detector']['scale_factor'],
             min_neighbors=config[self]['face_detector']['min_neighbors'],
         )
-        self._open_eye_detector = FeatureDetector(
+        self._open_eye_detector = FeatureDetector.get_detector(
             config,
             "open_eye",
             scale_factor=config[self]['open_eye_detector']['scale_factor'],
             min_neighbors=config[self]['open_eye_detector']['min_neighbors'],
         )
-        self._left_eye_detector = FeatureDetector(
+        self._left_eye_detector = FeatureDetector.get_detector(
             config,
             "left_eye",
             scale_factor=config[self]['left_eye_detector']['scale_factor'],
diff --git a/src/mousetrap/plugins/nose.py b/src/mousetrap/plugins/nose.py
index 4192419..12283bc 100644
--- a/src/mousetrap/plugins/nose.py
+++ b/src/mousetrap/plugins/nose.py
@@ -98,13 +98,13 @@ class NoseJoystickPlugin(interface.Plugin):
 class NoseLocator(object):
     def __init__(self, config):
         self._config = config
-        self._face_detector = FeatureDetector(
+        self._face_detector = FeatureDetector.get_detector(
             config,
             'face',
             scale_factor=config[self]['face_detector']['scale_factor'],
             min_neighbors=config[self]['face_detector']['min_neighbors'],
         )
-        self._nose_detector = FeatureDetector(
+        self._nose_detector = FeatureDetector.get_detector(
             config,
             'nose',
             scale_factor=config[self]['nose_detector']['scale_factor'],
diff --git a/src/mousetrap/vision.py b/src/mousetrap/vision.py
index 0957f4f..3ea0cf8 100644
--- a/src/mousetrap/vision.py
+++ b/src/mousetrap/vision.py
@@ -6,6 +6,7 @@ import cv2
 import cv
 from mousetrap.i18n import _
 from mousetrap.image import Image
+import mousetrap.plugins.interface as interface
 
 import logging
 LOGGER = logging.getLogger(__name__)
@@ -87,6 +88,23 @@ class HaarNameError(Exception):
 
 
 class FeatureDetector(object):
+
+    _INSTANCES = {}
+
+    @classmethod
+    def get_detector(cls, config, name, scale_factor=1.1, min_neighbors=3):
+        if name in cls._INSTANCES:
+            LOGGER.info("Reusing %s detector." % name)
+            return cls._INSTANCES[name]
+        cls._INSTANCES[name] = FeatureDetector(
+                config, name, scale_factor, min_neighbors)
+        return cls._INSTANCES[name]
+
+    @classmethod
+    def clear_all_detection_caches(cls):
+        for name, instance in cls._INSTANCES.items():
+            instance.clear_cache()
+
     def __init__(self, config, name, scale_factor=1.1, min_neighbors=3):
         '''
         name - name of feature to detect
@@ -97,6 +115,7 @@ class FeatureDetector(object):
         min_neighbors - how many neighbors each candidate rectangle should have
                 to retain it. Default 3.
         '''
+        LOGGER.info("Building %s detector." % name)
         self._config = config
         self._name = name
         self._single = None
@@ -106,15 +125,28 @@ class FeatureDetector(object):
         self._scale_factor = scale_factor
         self._min_neighbors = min_neighbors
         self._last_attempt_successful = False
+        self._detect_cache = {}
 
     def detect(self, image):
-        self._image = image
-        self._detect_plural()
-        self._exit_if_none_detected()
-        self._unpack_first()
-        self._extract_image()
-        self._calculate_center()
-        return self._single
+        if image in self._detect_cache:
+            LOGGER.debug("Detection cache hit: %(image)d -> %(result)s" %
+                    {'image':id(image), 'result':self._detect_cache[image]}
+                    )
+            if isinstance(self._detect_cache[image], FeatureNotFoundException):
+                raise FeatureNotFoundException(str(self._detect_cache[image]))
+            return self._detect_cache[image]
+        try:
+            self._image = image
+            self._detect_plural()
+            self._exit_if_none_detected()
+            self._unpack_first()
+            self._extract_image()
+            self._calculate_center()
+            self._detect_cache[image] = self._single
+            return self._detect_cache[image]
+        except FeatureNotFoundException as exception:
+            self._detect_cache[image] = exception
+            raise
 
     def _detect_plural(self):
         self._plural = self._cascade.detectMultiScale(
@@ -157,6 +189,17 @@ class FeatureDetector(object):
             is_grayscale=True,
         )
 
+    def clear_cache(self):
+        self._detect_cache.clear()
+
+
+class FeatureDetectorClearCachePlugin(interface.Plugin):
+    def __init__(self, config):
+        self._config = config
+
+    def run(self, app):
+        FeatureDetector.clear_all_detection_caches()
+
 
 class FeatureNotFoundException(Exception):
     pass


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]