[Python-modules-commits] [willow] 01/05: Initial commit

Christopher Stuart Hoskin mans0954 at moszumanska.debian.org
Tue Dec 5 23:12:17 UTC 2017


This is an automated email from the git hooks/post-receive script.

mans0954 pushed a commit to tag v0.1
in repository willow.

commit de3f0d3b16205a63ee9b3a3f30b63bfb85d3d605
Author: Karl Hobley <karl at torchbox.com>
Date:   Fri Oct 31 17:06:44 2014 +0000

    Initial commit
---
 .gitignore                                         |     6 +
 .travis.yml                                        |    17 +
 LICENSE                                            |    27 +
 readme.rst                                         |    60 +
 runtests.py                                        |     7 +
 setup.py                                           |    51 +
 tests/__init__.py                                  |     0
 tests/test_image.py                                |   680 +
 willow/__init__.py                                 |     3 +
 willow/backends/__init__.py                        |     0
 willow/backends/base.py                            |    32 +
 .../haarcascade_frontalface_alt2.xml               | 23550 +++++++++++++++++++
 willow/backends/opencv.py                          |    93 +
 willow/backends/pillow.py                          |    70 +
 willow/backends/wand.py                            |    56 +
 willow/image.py                                    |   183 +
 16 files changed, 24835 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0a7b971
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+*.pyc
+*.swp
+/build
+/Willow.egg-info
+/dist
+/venv
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..fdffd97
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,17 @@
+language: python
+
+# Test matrix
+python:
+    - 2.6
+    - 2.7
+    - 3.2
+    - 3.4
+
+# Package installation
+install:
+  - pip install unittest2
+  - python setup.py install
+
+# Run the tests
+script:
+  python runtests.py
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8c6aacc
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2014 Torchbox Ltd and individual contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice,
+       this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+       notice, this list of conditions and the following disclaimer in the
+       documentation and/or other materials provided with the distribution.
+
+    3. Neither the name of Torchbox nor the names of its contributors may be used
+       to endorse or promote products derived from this software without
+       specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/readme.rst b/readme.rst
new file mode 100644
index 0000000..9d5dcc6
--- /dev/null
+++ b/readme.rst
@@ -0,0 +1,60 @@
+.. image:: https://travis-ci.org/kaedroho/Willow.svg?branch=master
+    :target: https://travis-ci.org/kaedroho/Willow
+
+
+Willow imaging library
+======================
+
+Willow provides a unified and extensible interface to the many imaging libraries that are available for Python. It can also automatically switch between libraries at runtime allowing you to use features in all of the imaging libraries with any image. 
+
+It includes backends for Pillow, Wand and OpenCV out of the box. It is easy to add new operations to existing backends or roll your own backend to add support for other imaging libraries.
+
+
+Features
+--------
+
+* Simple, extensible API
+* Basic resize and crop operations
+* Animated GIFs
+* Face and feature detection
+* Supports Python 2.6, 2.7, 3.2, 3.3 and 3.4
+
+
+Examples
+--------
+
+Resizing a PNG file
+```````````````````
+
+This will open the image file with Pillow or Wand (if Pillow is unavailable).
+
+It will then resize it to 100x100 pixels and save it back out as a PNG file.
+
+.. code-block:: python
+
+   from willow.image import Image
+
+   img = Image.open("test.png")
+
+   # Resize the image to 100x100 pixels
+   img.resize(100, 100)
+
+   # Save it
+   img.save_as_png("test_thumbnail.png")
+
+
+Detecting faces
+```````````````
+
+Like above, the image file will be loaded with either Pillow or Wand.
+
+As neither Pillow nor Wand support detecting faces, Willow would automatically convert the image to OpenCV and use that to perform the detection.
+
+.. code-block:: python
+
+   from willow.image import Image
+
+   img = Image.open("photo.png")
+
+   # Find faces
+   faces = img.detect_faces()
diff --git a/runtests.py b/runtests.py
new file mode 100644
index 0000000..c42898f
--- /dev/null
+++ b/runtests.py
@@ -0,0 +1,7 @@
+import unittest
+
+from tests.test_image import *
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..9a9fef7
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+
+import sys, os
+
+try:
+    from setuptools import setup, find_packages
+except ImportError:
+    from distutils.core import setup
+
+
+# Hack to prevent "TypeError: 'NoneType' object is not callable" error
+# in multiprocessing/util.py _exit_function when setup.py exits
+# (see http://www.eby-sarna.com/pipermail/peak/2010-May/003357.html)
+try:
+    import multiprocessing
+except ImportError:
+    pass
+
+
+
+setup(
+    name='Willow',
+    version='0.1a1',
+    description='',
+    author='Karl Hobley',
+    author_email='karlhobley10 at gmail.com',
+    url='',
+    packages=find_packages(),
+    include_package_data=True,
+    license='BSD',
+    classifiers=[
+        'Development Status :: 2 - Pre-Alpha',
+        'Topic :: Multimedia :: Graphics',
+        'Topic :: Multimedia :: Graphics :: Graphics Conversion',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: BSD License',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python',
+        'Programming Language :: Python :: 2',
+        'Programming Language :: Python :: 2.6',
+        'Programming Language :: Python :: 2.7',
+        'Programming Language :: Python :: 3',
+        'Programming Language :: Python :: 3.2',
+        'Programming Language :: Python :: 3.3',
+        'Programming Language :: Python :: 3.4',
+    ],
+    install_requires=[
+        'six>=1.7.0',
+    ],
+    zip_safe=False,
+)
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_image.py b/tests/test_image.py
new file mode 100644
index 0000000..ba45572
--- /dev/null
+++ b/tests/test_image.py
@@ -0,0 +1,680 @@
+import types
+import unittest2
+
+from willow.image import Image, setup
+
+from willow.backends.base import ImageBackend
+
+
+class ImageTestCase(unittest2.TestCase):
+    def setUp(self):
+        # Reset Image class
+        self.reset()
+
+        # Make two fake backends and a bad backend
+        class FakeBackend(ImageBackend):
+            def to_buffer(self):
+                pass
+
+            @classmethod
+            def from_buffer(cls, buf):
+                return cls()
+
+        class AnotherFakeBackend(ImageBackend):
+            def to_buffer(self):
+                pass
+
+            @classmethod
+            def from_buffer(cls, buf):
+                return cls()
+
+        class BadFakeBackend(ImageBackend):
+            @classmethod
+            def check(cls):
+                raise ImportError("Bad image backend")
+
+        self.FakeBackend = FakeBackend
+        self.AnotherFakeBackend = AnotherFakeBackend
+        self.BadFakeBackend = BadFakeBackend
+
+        Image.register_backend(FakeBackend)
+        Image.register_backend(AnotherFakeBackend)
+        Image.register_backend(BadFakeBackend)
+
+    @staticmethod
+    def tearDownClass():
+        # Make sure that Image is returned to its
+        # default state after all tests have been run
+        ImageTestCase.reset()
+        setup(Image)
+
+    @staticmethod
+    def reset():
+        Image.backends = []
+        Image.loaders = {}
+
+
+class TestRegisterLoader(ImageTestCase):
+    """
+    Tests the register_loader method on Image
+    """
+    def test_register_loader(self):
+        """
+        Tests basic usage of register_loader
+        """
+        Image.register_loader('.jpg', self.FakeBackend)
+
+        self.assertEqual(Image.loaders, {
+            '.jpg': [
+                (0, self.FakeBackend),
+            ],
+        })
+
+    def test_register_loader_priority(self):
+        """
+        Tests that register_loader saves priority
+        """
+        Image.register_loader('.jpg', self.FakeBackend, priority=100)
+
+        self.assertEqual(Image.loaders, {
+            '.jpg': [
+                (100, self.FakeBackend),
+            ],
+        })
+
+    def test_register_loader_priority_multiple(self):
+        """
+        Tests that register_loader keeps the loaders list sorted by priority
+        """
+        Image.register_loader('.jpg', self.FakeBackend, priority=100)
+        Image.register_loader('.jpg', self.AnotherFakeBackend, priority=200)
+
+        self.assertEqual(Image.loaders, {
+            '.jpg': [
+                (100, self.FakeBackend),
+                (200, self.AnotherFakeBackend),
+            ],
+        })
+
+    def test_register_loader_priority_multiple_other_way(self):
+        """
+        Tests that register_loader keeps the loaders list sorted by priority
+
+        Same as above test, just inserting in opposite way to make sure the
+        loaders are being sorted by priority (and not insertion order)
+        """
+        Image.register_loader('.jpg', self.FakeBackend, priority=200)
+        Image.register_loader('.jpg', self.AnotherFakeBackend, priority=100)
+
+        self.assertEqual(Image.loaders, {
+            '.jpg': [
+                (100, self.AnotherFakeBackend),
+                (200, self.FakeBackend),
+            ],
+        })
+
+    def test_register_loader_different_extension(self):
+        """
+        Tests that register_loader stores loaders for different extensions
+        separately
+        """
+        Image.register_loader('.jpg', self.FakeBackend)
+        Image.register_loader('.gif', self.FakeBackend)
+
+        self.assertEqual(Image.loaders, {
+            '.jpg': [
+                (0, self.FakeBackend),
+            ],
+            '.gif': [
+                (0, self.FakeBackend),
+            ],
+        })
+
+    def test_register_loader_different_extension_at_same_time(self):
+        """
+        Tests that a single backend can be assigned to load two extensions
+        with a single call to register_loader
+        """
+        Image.register_loader(['.jpg', '.gif'], self.FakeBackend)
+
+        self.assertEqual(Image.loaders, {
+            '.jpg': [
+                (0, self.FakeBackend),
+            ],
+            '.gif': [
+                (0, self.FakeBackend),
+            ],
+        })
+
+    def test_register_loader_different_extension_at_same_time_tuple(self):
+        """
+        Tests that a single backend can be assigned to load two extensions with
+        a single call to register_loader using a tuple
+        """
+        Image.register_loader(('.jpg', '.gif'), self.FakeBackend)
+
+        self.assertEqual(Image.loaders, {
+            '.jpg': [
+                (0, self.FakeBackend),
+            ],
+            '.gif': [
+                (0, self.FakeBackend),
+            ],
+        })
+
+    def test_register_loader_different_extension_at_same_time_with_priority(self):
+        """
+        Tests that register_loader saves priority when using multipe extensions
+        """
+        Image.register_loader(['.jpg', '.gif'], self.FakeBackend, priority=100)
+
+        self.assertEqual(Image.loaders, {
+            '.jpg': [
+                (100, self.FakeBackend),
+            ],
+            '.gif': [
+                (100, self.FakeBackend),
+            ],
+        })
+
+    def test_register_loader_priority_same(self):
+        """
+        Tests that new loaders are placed after loaders that have the same
+        priority
+        """
+        Image.register_loader('.jpg', self.FakeBackend)
+        Image.register_loader('.jpg', self.AnotherFakeBackend)
+
+        self.assertEqual(Image.loaders, {
+            '.jpg': [
+                (0, self.FakeBackend),
+                (0, self.AnotherFakeBackend),
+            ],
+        })
+
+    def test_register_loader_priority_same_other_way(self):
+        """
+        If multiple backends are inserted with the same priority, the last one
+        should be placed last in the list
+
+        Same as above test, just inserting in opposite way to make sure the
+        loaders are being sorted by insertion time (and not name)
+        """
+        Image.register_loader('.jpg', self.AnotherFakeBackend)
+        Image.register_loader('.jpg', self.FakeBackend)
+
+        self.assertEqual(Image.loaders, {
+            '.jpg': [
+                (0, self.AnotherFakeBackend),
+                (0, self.FakeBackend),
+            ],
+        })
+
+
+class TestFindLoader(ImageTestCase):
+    """
+    Tests the find_loader method on Image
+    """
+    def test_find_loader(self):
+        """
+        Tests basic usage of find_loader
+        """
+        Image.loaders = {
+            '.jpg': [
+                (0, self.FakeBackend),
+            ],
+        }
+
+        self.assertEqual(Image.find_loader('.jpg'), self.FakeBackend)
+
+    def test_find_loader_unknown_extension(self):
+        """
+        Tests that a LoaderError is raised when find_loader is called with an
+        unknown extension
+        """
+        Image.loaders = {
+            '.jpg': [
+                (0, self.FakeBackend),
+            ],
+        }
+
+        self.assertRaises(Image.LoaderError, Image.find_loader, '.jpeg')
+
+    def test_find_loader_multiple_extensions(self):
+        """
+        Tests that find_loader works well when multiple extensions are
+        registered
+        """
+        Image.loaders = {
+            '.jpg': [
+                (0, self.FakeBackend),
+            ],
+            '.gif': [
+                (100, self.AnotherFakeBackend),
+            ],
+        }
+
+        self.assertEqual(Image.find_loader('.jpg'), self.FakeBackend)
+
+    def test_find_loader_priority(self):
+        """
+        Tests that find_loader gets the backend with the highest priority
+        """
+        Image.loaders = {
+            '.jpg': [
+                (100, self.FakeBackend),
+                (200, self.AnotherFakeBackend),
+            ],
+        }
+
+        self.assertEqual(Image.find_loader('.jpg'), self.AnotherFakeBackend)
+
+    def test_find_loader_priority_same(self):
+        """
+        Tests that find_loader picks the last backend when there are multiple
+        backends with the same priority
+        """
+        Image.loaders = {
+            '.jpg': [
+                (0, self.FakeBackend),
+                (0, self.AnotherFakeBackend),
+            ],
+        }
+
+        self.assertEqual(Image.find_loader('.jpg'), self.AnotherFakeBackend)
+
+    def test_find_loader_priority_same_other_way(self):
+        """
+        Tests that find_loader picks the last backend when there are multiple
+        backends with the same priority
+        """
+        Image.loaders = {
+            '.jpg': [
+                (0, self.AnotherFakeBackend),
+                (0, self.FakeBackend),
+            ],
+        }
+
+        self.assertEqual(Image.find_loader('.jpg'), self.FakeBackend)
+
+    def test_find_loader_with_bad_image_backend(self):
+        """
+        Tests that find_loader ignores bad backends even if they have a higher
+        priority
+        """
+        Image.loaders = {
+            '.jpg': [
+                (0, self.FakeBackend),
+                (100, self.BadFakeBackend),
+            ],
+        }
+
+        self.assertEqual(Image.find_loader('.jpg'), self.FakeBackend)
+
+    def test_find_loader_with_only_bad_image_backend(self):
+        """
+        Tests that find_loader raises a LoaderError when there are no good
+        backends available
+        """
+        Image.loaders = {
+            '.jpg': [
+                (100, self.BadFakeBackend),
+            ],
+        }
+
+        self.assertRaises(Image.LoaderError, Image.find_loader, '.jpg')
+
+
+class TestLoaderDefaultConfiguration(ImageTestCase):
+    pass
+
+    # TODO
+
+
+class TestRegisterOperation(ImageTestCase):
+    """
+    Tests the register_operation method on Image
+    """
+    def test_register_operation(self):
+        """
+        Tests basic usage of register_operation
+        """
+        @self.FakeBackend.register_operation('test')
+        def myop(backend):
+            pass
+
+        self.assertEqual(self.FakeBackend.operations, {
+            'test': myop,
+        })
+
+    def test_register_operation_multiple(self):
+        """
+        Tests that register_operation can register multiple operations
+        """
+        @self.FakeBackend.register_operation('test')
+        def myop(backend):
+            pass
+
+        @self.FakeBackend.register_operation('test2')
+        def myotherop(backend):
+            pass
+
+        self.assertEqual(self.FakeBackend.operations, {
+            'test': myop,
+            'test2': myotherop,
+        })
+
+    def test_register_operation_multiple_samename(self):
+        """
+        Tests that register_operation will replace a previously registered
+        operation if they have the same name
+        """
+        @self.FakeBackend.register_operation('test')
+        def myop(backend):
+            pass
+
+        @self.FakeBackend.register_operation('test')
+        def myotherop(backend):
+            pass
+
+        self.assertEqual(self.FakeBackend.operations, {
+            'test': myotherop,
+        })
+
+    def test_register_operation_multiple_samename_other_way(self):
+        """
+        Tests that register_operation will replace a previously registered
+        operation if they have the same name
+        """
+        @self.FakeBackend.register_operation('test')
+        def myotherop(backend):
+            pass
+
+        @self.FakeBackend.register_operation('test')
+        def myop(backend):
+            pass
+
+        self.assertEqual(self.FakeBackend.operations, {
+            'test': myop,
+        })
+
+
+class TestFindOperation(ImageTestCase):
+    """
+    Tests the find_operation method on Image
+    """
+    def test_find_operation(self):
+        """
+        Tests basic usage of find_operation
+        """
+        def myop(backend):
+            pass
+
+        self.FakeBackend.operations = {
+            'test': myop,
+        }
+
+        self.assertEqual(Image.find_operation('test'), (
+            self.FakeBackend,
+            myop,
+        ))
+
+    def test_find_operation_unknown_operation(self):
+        """
+        Tests find_operation returns None when the operation name is not
+        registered
+        """
+        def myop(backend):
+            pass
+
+        self.FakeBackend.operations = {
+            'test': myop,
+        }
+
+        self.assertIsNone(Image.find_operation('test2'))
+
+    def test_find_operation_multiple_backends(self):
+        """
+        Tests find_operation picks the first available backend if the operation
+        has been registered by multiple backends
+        """
+        def myop(backend):
+            pass
+
+        self.FakeBackend.operations = {
+            'test': myop,
+        }
+
+        self.AnotherFakeBackend.operations = {
+            'test': myop,
+        }
+
+        self.assertEqual(Image.find_operation('test'), (
+            self.FakeBackend,
+            myop,
+        ))
+
+    def test_find_operation_multiple_backends_preferred(self):
+        """
+        Tests find_operation will priorities the preferred backend
+        """
+        def myop(backend):
+            pass
+
+        self.FakeBackend.operations = {
+            'test': myop,
+        }
+
+        self.AnotherFakeBackend.operations = {
+            'test': myop,
+        }
+
+        self.assertEqual(Image.find_operation('test', preferred_backend=self.AnotherFakeBackend), (
+            self.AnotherFakeBackend,
+            myop,
+        ))
+
+    def test_find_operation_with_bad_backend(self):
+        """
+        Tests that find_operation ignores bad backends
+        """
+        def myop(backend):
+            pass
+
+        self.BadFakeBackend.operations = {
+            'test': myop,
+        }
+
+        self.AnotherFakeBackend.operations = {
+            'test': myop,
+        }
+
+        self.assertEqual(Image.find_operation('test'), (
+            self.AnotherFakeBackend,
+            myop,
+        ))
+
+    def test_find_operation_with_only_bad_backend(self):
+        """
+        Tests that find_operation raises a RuntimeError when there are no good
+        backends available
+        """
+        def myop(backend):
+            pass
+
+        self.BadFakeBackend.operations = {
+            'test': myop,
+        }
+
+        self.assertRaises(RuntimeError, Image.find_operation, 'test')
+
+
+class TestGetAttribute(ImageTestCase):
+    """
+    This tests the __getattr__ method on Image
+    """
+    def test_getattr_operation(self):
+        """
+        Tests that __getattr__ looks up operations correctly
+        """
+        def myop(backend):
+            pass
+
+        self.FakeBackend.operations = {
+            'test': myop,
+        }
+
+        image = Image(self.FakeBackend())
+
+        self.assertIsInstance(image.test, types.FunctionType)
+
+    def test_getattr_operation_unknown(self):
+        """
+        Tests that __getattr__ raises an AttributeError when the requested
+        attribute is not an operation
+        """
+        def myop(backend):
+            pass
+
+        self.FakeBackend.operations = {
+            'test': myop,
+        }
+
+        image = Image(self.FakeBackend())
+
+        self.assertRaises(AttributeError, getattr, image, 'test2')
+
+
+class TestCallOperation(ImageTestCase):
+    """
+    Tests calling an operation that has been registered in Image
+    """
+    def test_calls_function(self):
+        """
+        Tests that calling the operation calls the underlying function
+        """
+        def myop(backend):
+            backend.func_called = True
+
+        self.FakeBackend.operations = {
+            'test': myop,
+        }
+
+        image = Image(self.FakeBackend())
+        image.backend.func_called = False
+        image.test()
+
+        self.assertTrue(image.backend.func_called)
+
+    def test_args_get_passed_through(self):
+        """
+        Tests that args get passed through to the underlying function
+        """
+        def myop(backend, *args, **kwargs):
+            backend.passed_args = args
+            backend.passed_kwargs = kwargs
+
+        self.FakeBackend.operations = {
+            'test': myop,
+        }
+
+        image = Image(self.FakeBackend())
+        image.backend.passed_args = None
+        image.backend.passed_kwargs = None
+        image.test('Hello', 'World', name="Karl")
+
+        self.assertEqual(image.backend.passed_args, ('Hello', 'World'))
+        self.assertEqual(image.backend.passed_kwargs, {'name': "Karl"})
+
+    def test_return_value_gets_passed_back(self):
+        """
+        Tests that the return value of the underlying function gets passed back
+        to the caller
+        """
+        def myop(backend):
+            return "Hello world!"
+
+        self.FakeBackend.operations = {
+            'test': myop,
+        }
+
+        image = Image(self.FakeBackend())
+
+        self.assertEqual(image.test(), "Hello world!")
+
+    def test_switches_backend(self):
+        """
+        Tests that calling an operation will switch backend if the current
+        backend doesn't support it
+        """
+        def say_hello(backend):
+            return "Hello world!"
+
+        self.FakeBackend.operations = {
+            'say_hello': say_hello,
+        }
+
+        def say_goodbye(backend):
+            return "Goodbye!"
+
+        self.AnotherFakeBackend.operations = {
+            'say_goodbye': say_goodbye,
+        }
+
+        image = Image(self.FakeBackend())
+        self.assertIsInstance(image.backend, self.FakeBackend)
+
+        image.say_goodbye()
+        self.assertIsInstance(image.backend, self.AnotherFakeBackend)
+
+        image.say_hello()
+        self.assertIsInstance(image.backend, self.FakeBackend)
+
+
+class TestRegisterBackend(ImageTestCase):
+    """
+    Tests the register_backend method on Image
+    """
+    def setUp(self):
+        super(TestRegisterBackend, self).setUp()
+        self.reset()
+
+    def test_register_backend(self):
+        """
+        Tests basic usage of register_backend
+        """
+        Image.register_backend(self.FakeBackend)
+
+        self.assertEqual(Image.backends, [self.FakeBackend])
+
+    def test_register_backend_multiple(self):
+        """
+        Tests register_backend can register multiple backends
+        """
+        Image.register_backend(self.FakeBackend)
+        Image.register_backend(self.AnotherFakeBackend)
+
+        self.assertEqual(Image.backends, [self.FakeBackend, self.AnotherFakeBackend])
+
+    def test_register_backend_removes_duplicates(self):
+        """
+        Tests register_backend won't insert the same backend more than once
+        """
+        Image.register_backend(self.FakeBackend)
+        Image.register_backend(self.FakeBackend)
+
+        self.assertEqual(Image.backends, [self.FakeBackend])
+
+
+class TestCheckBackends(ImageTestCase):
+    pass
+
+
+class TestSwitchBackend(ImageTestCase):
+    pass
+
+    # TODO
+
+
+if __name__ == '__main__':
+    unittest2.main()
diff --git a/willow/__init__.py b/willow/__init__.py
new file mode 100644
index 0000000..f86fa09
--- /dev/null
+++ b/willow/__init__.py
@@ -0,0 +1,3 @@
+from willow.image import Image
+
+__version__ = '0.1a1'
diff --git a/willow/backends/__init__.py b/willow/backends/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/willow/backends/base.py b/willow/backends/base.py
new file mode 100644
index 0000000..6a3861c
--- /dev/null
+++ b/willow/backends/base.py
@@ -0,0 +1,32 @@
+import six
+
+
+class ImageBackendBase(type):
+    def __init__(cls, name, bases, dct):
+        super(ImageBackendBase, cls).__init__(name, bases, dct)
+
+        # Make sure all backends have their own operations attribute
+        cls.operations = {}
+
+    def __lt__(cls, other):
+        # As we insert backend classes into ordered lists, we need to define an
+        # ordering for backend classes. This is used incase two priorities are
+        # the same leading to the following comparison:
+        # (0, BackendA) > (0, BackendB)
+        # So this method is simply to prevent a crash in this situation
+        return False
+
+
+class ImageBackend(six.with_metaclass(ImageBackendBase)):
+    @classmethod
+    def register_operation(cls, operation_name):
+        def wrapper(func):
+            cls.operations[operation_name] = func
+
+            return func
+
+        return wrapper
+
+    @classmethod
+    def check(cls):
+        pass
diff --git a/willow/backends/face_detection/haarcascade_frontalface_alt2.xml b/willow/backends/face_detection/haarcascade_frontalface_alt2.xml
new file mode 100644
index 0000000..caa86f6
--- /dev/null
+++ b/willow/backends/face_detection/haarcascade_frontalface_alt2.xml
@@ -0,0 +1,23550 @@
+<?xml version="1.0"?>
+<!--
+    Tree-based 20x20 gentle adaboost frontal face detector.
+    Created by Rainer Lienhart.
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
+
+  By downloading, copying, installing or using the software you agree to this license.
+  If you do not agree to this license, do not download, install,
+  copy or use the software.
+
+
+                        Intel License Agreement
+                For Open Source Computer Vision Library
+
+ Copyright (C) 2000, Intel Corporation, all rights reserved.
+ Third party copyrights are property of their respective owners.
+
+ Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+
+   * Redistribution's of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+   * Redistribution's in binary form must reproduce the above copyright notice,
... 23949 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/willow.git



More information about the Python-modules-commits mailing list