Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Code reformatting

  • Loading branch information...
commit 8a50bfba9443e8d2a1a691ab20eeb525815be597 1 parent 750795d
@heynemann heynemann authored
Showing with 644 additions and 449 deletions.
  1. +3 −0  Makefile
  2. +1 −1  tests/__init__.py
  3. +1 −2  tests/fixtures/conf.py
  4. +1 −1  tests/fixtures/conf1.py
  5. +1 −1  tests/fixtures/default.py
  6. +1 −1  tests/fixtures/file_loader_conf.py
  7. +1 −1  tests/fixtures/file_storage_conf.py
  8. +1 −1  tests/fixtures/file_storage_conf_2.py
  9. +1 −1  tests/fixtures/file_storage_conf_3.py
  10. +1 −1  tests/fixtures/jsonp.py
  11. +1 −1  tests/fixtures/mongo_storage_conf.py
  12. +1 −1  tests/fixtures/mysql_storage_conf.py
  13. +1 −1  tests/fixtures/no_storage_conf.py
  14. +1 −1  tests/test_app.py
  15. +1 −0  tests/test_drop_shadow_filter.py
  16. +2 −1  tests/test_file_loader.py
  17. +6 −3 tests/test_focal_points.py
  18. +2 −2 tests/test_meta_transform.py
  19. +0 −1  tests/test_urls.py
  20. +1 −2  tests/transform_helper.py
  21. +1 −1  tests/visual_test/__init__.py
  22. +3 −1 tests/visual_test/compare_engines.py
  23. +4 −3 tests/visual_test/detect.py
  24. +7 −7 thumbor/console.py
  25. +5 −4 thumbor/context.py
  26. +23 −19 thumbor/crypto.py
  27. +1 −1  thumbor/detectors/__init__.py
  28. +5 −3 thumbor/detectors/face_detector/__init__.py
  29. +3 −3 thumbor/detectors/feature_detector/__init__.py
  30. +2 −1  thumbor/detectors/glasses_detector/__init__.py
  31. +5 −4 thumbor/detectors/local_detector.py
  32. +2 −1  thumbor/detectors/profile_detector/__init__.py
  33. +17 −3 thumbor/detectors/queued_detector/__init__.py
  34. +11 −0 thumbor/detectors/queued_detector/queued_complete_detector.py
  35. +11 −0 thumbor/detectors/queued_detector/queued_face_detector.py
  36. +11 −0 thumbor/detectors/queued_detector/queued_feature_detector.py
  37. +18 −1 thumbor/detectors/queued_sqs_detector/__init__.py
  38. +4 −2 thumbor/engines/__init__.py
  39. +175 −187 thumbor/engines/extensions/pil.py
  40. +3 −2 thumbor/engines/graphicsmagick.py
  41. +5 −5 thumbor/engines/json_engine.py
  42. +4 −2 thumbor/engines/opencv.py
  43. +1 −1  thumbor/engines/pil.py
  44. +1 −0  thumbor/filters/brightness.py
  45. +1 −0  thumbor/filters/contrast.py
  46. +1 −1  thumbor/filters/equalize.py
  47. +5 −2 thumbor/filters/frame.py
  48. +1 −0  thumbor/filters/noise.py
  49. +1 −0  thumbor/filters/quality.py
  50. +5 −3 thumbor/filters/redeye.py
  51. +1 −0  thumbor/filters/rgb.py
  52. +5 −1 thumbor/filters/round_corner.py
  53. +5 −1 thumbor/filters/sharpen.py
  54. +1 −0  thumbor/filters/strip_icc.py
  55. +2 −1  thumbor/filters/watermark.py
  56. +1 −1  thumbor/handlers/healthcheck.py
  57. +1 −1  thumbor/handlers/image.py
  58. +3 −5 thumbor/handlers/images.py
  59. +5 −1 thumbor/handlers/imaging.py
  60. +10 −6 thumbor/handlers/upload.py
  61. +0 −1  thumbor/lib/__init__.py
  62. +1 −2  thumbor/loaders/__init__.py
  63. +11 −7 thumbor/point.py
  64. +1 −0  thumbor/storages/memcache_storage.py
  65. +3 −1 thumbor/storages/mixed_storage.py
  66. +0 −1  thumbor/storages/mongo_storage.py
  67. +2 −2 thumbor/storages/no_storage.py
  68. +2 −1  thumbor/storages/redis_storage.py
  69. +2 −4 thumbor/transformer.py
  70. +1 −1  thumbor/url.py
  71. +16 −16 thumbor/url_composer.py
  72. +1 −1  vows/app_vows.py
  73. +2 −1  vows/cascade_loader_detector_vows.py
  74. +1 −1  vows/config_vows.py
  75. +1 −0  vows/console_vows.py
  76. +2 −1  vows/context_vows.py
  77. +2 −1  vows/crypto_vows.py
  78. +2 −1  vows/detector_vows.py
  79. +3 −1 vows/file_storage_vows.py
  80. +2 −2 vows/fill_filter_vows.py
  81. +11 −0 vows/fixtures/detection_error_detector.py
  82. +11 −1 vows/fixtures/max_age_conf.py
  83. +11 −0 vows/fixtures/prevent_result_storage_detector.py
  84. +0 −1  vows/handler_images_vows.py
  85. +1 −1  vows/healthcheck_vows.py
  86. +9 −1 vows/importer_vows.py
  87. +16 −17 vows/json_engine_vows.py
  88. +0 −2  vows/max_age_vows.py
  89. +1 −2  vows/meta_vows.py
  90. +3 −1 vows/mixed_storage_vows.py
  91. +23 −7 vows/mongo_storage_vows.py
  92. +1 −3 vows/no_storage_vows.py
  93. +4 −2 vows/pil_engine_vows.py
  94. +1 −1  vows/point_vows.py
  95. +1 −0  vows/quality_filter_vows.py
  96. +16 −5 vows/redis_storage_vows.py
  97. +10 −4 vows/transformer_test_data.py
  98. +17 −14 vows/transformer_vows.py
  99. +18 −17 vows/translate_coordinates_vows.py
  100. +12 −7 vows/upload_api_vows.py
  101. +11 −12 vows/upload_vows.py
  102. +1 −1  vows/url_composer_vows.py
  103. +10 −5 vows/url_vows.py
  104. +3 −3 vows/util_vows.py
View
3  Makefile
@@ -31,3 +31,6 @@ kill_redis:
redis: kill_redis
@redis-server redis.conf ; sleep 1
@redis-cli -p 7778 -a hey_you info
+
+flake:
+ @flake8 . --ignore=W801,E501
View
2  tests/__init__.py
@@ -4,7 +4,7 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
View
3  tests/fixtures/conf.py
@@ -4,7 +4,6 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
-
View
2  tests/fixtures/conf1.py
@@ -4,6 +4,6 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
View
2  tests/fixtures/default.py
@@ -4,7 +4,7 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
View
2  tests/fixtures/file_loader_conf.py
@@ -4,7 +4,7 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
View
2  tests/fixtures/file_storage_conf.py
@@ -4,7 +4,7 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
View
2  tests/fixtures/file_storage_conf_2.py
@@ -4,7 +4,7 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
View
2  tests/fixtures/file_storage_conf_3.py
@@ -4,7 +4,7 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
View
2  tests/fixtures/jsonp.py
@@ -4,7 +4,7 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
View
2  tests/fixtures/mongo_storage_conf.py
@@ -4,7 +4,7 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
View
2  tests/fixtures/mysql_storage_conf.py
@@ -4,7 +4,7 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
View
2  tests/fixtures/no_storage_conf.py
@@ -4,7 +4,7 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
View
2  tests/test_app.py
@@ -4,7 +4,7 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
View
1  tests/test_drop_shadow_filter.py
@@ -12,6 +12,7 @@
from thumbor.filters.drop_shadow import Filter
+
def test_drop_shadow_filter_regex():
reg = Filter.regex
View
3  tests/test_file_loader.py
@@ -4,7 +4,7 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
@@ -18,6 +18,7 @@
fixtures_folder = join(abspath(dirname(__file__)), 'fixtures')
+
class FileLoaderTest(AsyncHTTPTestCase):
def get_app(self):
View
9 tests/test_focal_points.py
@@ -4,12 +4,13 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
from thumbor.point import FocalPoint
+
def test_focal_point():
point = FocalPoint(x=10.0, y=20.0, weight=3.4)
@@ -17,17 +18,20 @@ def test_focal_point():
assert point.y == 20.0
assert point.weight == 3.4
+
def test_default_weight_is_one():
point = FocalPoint(x=10.0, y=20.0)
assert point.weight == 1.0
+
def test_from_square():
point = FocalPoint.from_square(x=10.0, y=20.0, width=100, height=200)
assert point.x == 55.0
assert point.y == 110.0
assert point.weight == 20000.0
+
def test_focal_points_alignments():
for point in [
('left', 'top', 100, 200, 0.0, 0.0),
@@ -42,11 +46,10 @@ def test_focal_points_alignments():
]:
yield assert_point_from_alignment, point
+
def assert_point_from_alignment(point):
comp_point = FocalPoint.from_alignment(point[0], point[1], width=point[2], height=point[3])
assert comp_point.x == point[4], "Expected x => %.2f Got x => %.2f" % (point[4], comp_point.x)
assert comp_point.y == point[5], "Expected y => %.2f Got y => %.2f" % (point[5], comp_point.y)
assert comp_point.weight == 1.0
-
-
View
4 tests/test_meta_transform.py
@@ -18,6 +18,7 @@
get_conf_path = lambda filename: abspath(join(dirname(__file__), 'fixtures', filename))
+
class MetaHandlerTestCase(AsyncHTTPTestCase):
def get_app(self):
@@ -173,6 +174,7 @@ def test_meta_returns_proper_json_for_flip(self):
assert operations[2]['type'] == 'flip_horizontally'
assert operations[3]['type'] == 'flip_vertically'
+
class MetaHandlerJSONPTestCase(AsyncHTTPTestCase):
def get_app(self):
@@ -187,5 +189,3 @@ def test_meta_returns_proper_json_for_no_ops_with_callback(self):
assert text.strip().startswith('callback({')
assert text.strip().endswith('});')
-
-
View
1  tests/test_urls.py
@@ -7,4 +7,3 @@
# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
-
View
3  tests/transform_helper.py
@@ -4,7 +4,6 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
-
View
2  tests/visual_test/__init__.py
@@ -4,6 +4,6 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
View
4 tests/visual_test/compare_engines.py
@@ -17,11 +17,13 @@
REPETITIONS = 200
IMAGE_NAME = 'fred'
+
def get_engine(engine_name):
module_name = 'thumbor.engines.%s' % engine_name
module = __import__(module_name)
return reduce(getattr, module_name.split('.')[1:], module).Engine
+
def main():
root = abspath(dirname(__file__))
image = join(root, '%s.jpg' % IMAGE_NAME)
@@ -80,7 +82,7 @@ def main():
engine.load(source, '.jpg')
engine.crop(100, 100, 200, 200)
engine.resize(50, 50)
- result = engine.read('.jpg')
+ engine.read('.jpg')
times[key] = "%.6f" % (time.time() - start)
View
7 tests/visual_test/detect.py
@@ -4,7 +4,7 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
@@ -39,7 +39,7 @@
loaded_cascade_file = cv.Load(cascade_file)
for image_path in images_path:
-
+
with tempfile.NamedTemporaryFile() as temp_file:
file_name = temp_file.name
@@ -53,7 +53,8 @@
grayscale = cv.LoadImageM(file_name, cv.CV_LOAD_IMAGE_GRAYSCALE)
- faces = cv.HaarDetectObjects(grayscale, loaded_cascade_file,
+ faces = cv.HaarDetectObjects(
+ grayscale, loaded_cascade_file,
cv.CreateMemStorage(), 1.1, 3, cv.CV_HAAR_DO_CANNY_PRUNING, (30, 30))
if faces:
View
14 thumbor/console.py
@@ -13,14 +13,15 @@
from thumbor.context import ServerParameters
from thumbor import __version__
+
def get_server_parameters(arguments=None):
parser = optparse.OptionParser(usage="thumbor or type thumbor -h (--help) for help", description=__doc__, version=__version__)
- parser.add_option("-p", "--port", type="int", dest="port", default=8888, help = "The port to run this thumbor instance at [default: %default]." )
- parser.add_option("-i", "--ip", dest="ip", default="0.0.0.0", help = "The host address to run this thumbor instance at [default: %default]." )
- parser.add_option("-c", "--conf", dest="conf", default="", help = "The path of the configuration file to use for this thumbor instance [default: %default]." )
- parser.add_option("-k", "--keyfile", dest="keyfile", default="", help = "The path of the security key file to use for this thumbor instance [default: %default]." )
- parser.add_option("-l", "--log-level", dest="log_level", default="warning", help = "The log level to be used. Possible values are: debug, info, warning, error, critical or notset. [default: %default]." )
- parser.add_option("-a", "--app", dest="app", default='thumbor.app.ThumborServiceApp', help = "A custom app to use for this thumbor server in case you subclassed ThumborServiceApp [default: %default]." )
+ parser.add_option("-p", "--port", type="int", dest="port", default=8888, help="The port to run this thumbor instance at [default: %default].")
+ parser.add_option("-i", "--ip", dest="ip", default="0.0.0.0", help="The host address to run this thumbor instance at [default: %default].")
+ parser.add_option("-c", "--conf", dest="conf", default="", help="The path of the configuration file to use for this thumbor instance [default: %default].")
+ parser.add_option("-k", "--keyfile", dest="keyfile", default="", help="The path of the security key file to use for this thumbor instance [default: %default].")
+ parser.add_option("-l", "--log-level", dest="log_level", default="warning", help="The log level to be used. Possible values are: debug, info, warning, error, critical or notset. [default: %default].")
+ parser.add_option("-a", "--app", dest="app", default='thumbor.app.ThumborServiceApp', help="A custom app to use for this thumbor server in case you subclassed ThumborServiceApp [default: %default].")
(options, args) = parser.parse_args(arguments)
@@ -36,4 +37,3 @@ def get_server_parameters(arguments=None):
keyfile=keyfile,
log_level=log_level,
app_class=options.app)
-
View
9 thumbor/context.py
@@ -108,10 +108,11 @@ def __init__(self,
'bottom': self.int_or_0(crop_bottom)
}
- self.should_crop = self.crop['left'] > 0 or \
- self.crop['top'] > 0 or \
- self.crop['right'] > 0 or \
- self.crop['bottom'] > 0
+ self.should_crop = \
+ self.crop['left'] > 0 or \
+ self.crop['top'] > 0 or \
+ self.crop['right'] > 0 or \
+ self.crop['bottom'] > 0
self.adaptive = bool(adaptive)
self.fit_in = bool(fit_in)
View
42 thumbor/crypto.py
@@ -16,11 +16,12 @@
from thumbor.url import Url
+
class Cryptor(object):
def __init__(self, security_key):
self.security_key = (security_key * 16)[:16]
- def encrypt(self,
+ def encrypt(self,
width,
height,
smart,
@@ -38,23 +39,26 @@ def encrypt(self,
filters,
image):
- url = "%s/%s" % (Url.generate_options(width=width,
- height=height,
- smart=smart,
- meta=False,
- adaptive=adaptive,
- fit_in=fit_in,
- horizontal_flip=flip_horizontal,
- vertical_flip=flip_vertical,
- halign=halign,
- valign=valign,
- trim=trim,
- crop_left=crop_left,
- crop_top=crop_top,
- crop_right=crop_right,
- crop_bottom=crop_bottom,
- filters=filters),
- hashlib.md5(image).hexdigest())
+ generated_url = Url.generate_options(
+ width=width,
+ height=height,
+ smart=smart,
+ meta=False,
+ adaptive=adaptive,
+ fit_in=fit_in,
+ horizontal_flip=flip_horizontal,
+ vertical_flip=flip_vertical,
+ halign=halign,
+ valign=valign,
+ trim=trim,
+ crop_left=crop_left,
+ crop_top=crop_top,
+ crop_right=crop_right,
+ crop_bottom=crop_bottom,
+ filters=filters
+ )
+
+ url = "%s/%s" % (generated_url, hashlib.md5(image).hexdigest())
pad = lambda s: s + (16 - len(s) % 16) * "{"
cipher = AES.new(self.security_key)
@@ -111,6 +115,7 @@ def decrypt(self, encrypted):
return result
+
class Signer:
def __init__(self, security_key):
self.security_key = security_key
@@ -121,4 +126,3 @@ def validate(self, actual_signature, url):
def signature(self, url):
return base64.urlsafe_b64encode(hmac.new(self.security_key, unicode(url).encode('utf-8'), hashlib.sha1).digest())
-
View
2  thumbor/detectors/__init__.py
@@ -8,6 +8,7 @@
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
+
class BaseDetector(object):
def __init__(self, context, index, detectors):
@@ -25,4 +26,3 @@ def next(self, callback):
next_detector = self.detectors[self.index + 1](self.context, self.index + 1, self.detectors)
next_detector.detect(callback)
-
View
8 thumbor/detectors/face_detector/__init__.py
@@ -4,7 +4,7 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
@@ -13,6 +13,7 @@
HAIR_OFFSET = 0.12
+
class Detector(CascadeLoaderDetector):
def __init__(self, context, index, detectors):
@@ -29,8 +30,9 @@ def detect(self, callback):
if features:
for (left, top, width, height), neighbors in features:
top = self.__add_hair_offset(top, height)
- self.context.request.focal_points.append(FocalPoint.from_square(left, top, width, height, origin="Face Detection"))
+ self.context.request.focal_points.append(
+ FocalPoint.from_square(left, top, width, height, origin="Face Detection")
+ )
callback()
else:
self.next(callback)
-
View
6 thumbor/detectors/feature_detector/__init__.py
@@ -4,7 +4,7 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
@@ -27,7 +27,7 @@ def detect(self, callback):
image_mode, image_data = engine.convert_to_rgb()
cv.SetData(image, image_data)
- gray_image = cv.CreateImage(engine.size, 8, 1);
+ gray_image = cv.CreateImage(engine.size, 8, 1)
convert_mode = getattr(cv, 'CV_%s2GRAY' % image_mode)
cv.CvtColor(image, gray_image, convert_mode)
image = gray_image
@@ -36,7 +36,7 @@ def detect(self, callback):
eig_image = cv.CreateMat(rows, cols, cv.CV_32FC1)
temp_image = cv.CreateMat(rows, cols, cv.CV_32FC1)
- points = cv.GoodFeaturesToTrack(image, eig_image, temp_image, 20, 0.04, 1.0, useHarris = False)
+ points = cv.GoodFeaturesToTrack(image, eig_image, temp_image, 20, 0.04, 1.0, useHarris=False)
if points:
for x, y in points:
View
3  thumbor/detectors/glasses_detector/__init__.py
@@ -4,7 +4,7 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
@@ -14,6 +14,7 @@
define('GLASSES_DETECTOR_CASCADE_FILE', default='haarcascade_eye_tree_eyeglasses.xml')
+
class Detector(CascadeLoaderDetector):
def __init__(self, context, index, detectors):
View
9 thumbor/detectors/local_detector.py
@@ -53,10 +53,11 @@ def get_features(self):
cv.EqualizeHist(gray, gray)
- faces = cv.HaarDetectObjects(gray,
- self.__class__.cascade, cv.CreateMemStorage(0),
- haar_scale, min_neighbors,
- cv.CV_HAAR_DO_CANNY_PRUNING, min_size)
+ faces = cv.HaarDetectObjects(
+ gray,
+ self.__class__.cascade, cv.CreateMemStorage(0),
+ haar_scale, min_neighbors,
+ cv.CV_HAAR_DO_CANNY_PRUNING, min_size)
faces_scaled = []
View
3  thumbor/detectors/profile_detector/__init__.py
@@ -4,7 +4,7 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
@@ -14,6 +14,7 @@
define('PROFILE_DETECTOR_CASCADE_FILE', default='haarcascade_profileface.xml')
+
class Detector(CascadeLoaderDetector):
def __init__(self, context, index, detectors):
View
20 thumbor/detectors/queued_detector/__init__.py
@@ -1,9 +1,21 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# thumbor imaging service
+# https://github.com/globocom/thumbor/wiki
+
+# Licensed under the MIT license:
+# http://www.opensource.org/licenses/mit-license
+# Copyright (c) 2011 globo.com timehome@corp.globo.com
+
+
from redis import Redis, RedisError
from remotecv.unique_queue import UniqueQueue
from thumbor.detectors import BaseDetector
from thumbor.utils import logger
+
class QueuedDetector(BaseDetector):
redis = None
@@ -17,9 +29,11 @@ def detect(self, callback):
password=self.context.config.REDIS_QUEUE_SERVER_PASSWORD)
queue = UniqueQueue(server=QueuedDetector.redis)
- queue.enqueue_unique_from_string('remotecv.pyres_tasks.DetectTask', 'Detect',
- args=[self.detection_type, self.context.request.image_url],
- key=self.context.request.image_url)
+ queue.enqueue_unique_from_string(
+ 'remotecv.pyres_tasks.DetectTask', 'Detect',
+ args=[self.detection_type, self.context.request.image_url],
+ key=self.context.request.image_url
+ )
except RedisError:
self.context.request.detection_error = True
QueuedDetector.redis = None
View
11 thumbor/detectors/queued_detector/queued_complete_detector.py
@@ -1,5 +1,16 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# thumbor imaging service
+# https://github.com/globocom/thumbor/wiki
+
+# Licensed under the MIT license:
+# http://www.opensource.org/licenses/mit-license
+# Copyright (c) 2011 globo.com timehome@corp.globo.com
+
from thumbor.detectors.queued_detector import QueuedDetector
+
class Detector(QueuedDetector):
detection_type = 'all'
View
11 thumbor/detectors/queued_detector/queued_face_detector.py
@@ -1,5 +1,16 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# thumbor imaging service
+# https://github.com/globocom/thumbor/wiki
+
+# Licensed under the MIT license:
+# http://www.opensource.org/licenses/mit-license
+# Copyright (c) 2011 globo.com timehome@corp.globo.com
+
from thumbor.detectors.queued_detector import QueuedDetector
+
class Detector(QueuedDetector):
detection_type = 'face'
View
11 thumbor/detectors/queued_detector/queued_feature_detector.py
@@ -1,5 +1,16 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# thumbor imaging service
+# https://github.com/globocom/thumbor/wiki
+
+# Licensed under the MIT license:
+# http://www.opensource.org/licenses/mit-license
+# Copyright (c) 2011 globo.com timehome@corp.globo.com
+
from thumbor.detectors.queued_detector import QueuedDetector
+
class Detector(QueuedDetector):
detection_type = 'feature'
View
19 thumbor/detectors/queued_sqs_detector/__init__.py
@@ -1,6 +1,19 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# thumbor imaging service
+# https://github.com/globocom/thumbor/wiki
+
+# Licensed under the MIT license:
+# http://www.opensource.org/licenses/mit-license
+# Copyright (c) 2011 globo.com timehome@corp.globo.com
+
+
from remotecv.celery_tasks import CeleryTasks
from thumbor.detectors import BaseDetector
+from thumbor.utils import logger
+
class Detector(BaseDetector):
detect_task = None
@@ -9,7 +22,11 @@ def detect(self, callback):
self.context.request.prevent_result_storage = True
try:
if not Detector.detect_task:
- celery_tasks = CeleryTasks(self.context.config.SQS_QUEUE_KEY_ID, self.context.config.SQS_QUEUE_KEY_SECRET, self.context.config.SQS_QUEUE_REGION, None)
+ celery_tasks = CeleryTasks(
+ self.context.config.SQS_QUEUE_KEY_ID,
+ self.context.config.SQS_QUEUE_KEY_SECRET,
+ self.context.config.SQS_QUEUE_REGION, None
+ )
Detector.detect_task = celery_tasks.get_detect_task()
Detector.detect_task.delay('all', self.context.request.image_url, self.context.request.image_url)
View
6 thumbor/engines/__init__.py
@@ -4,10 +4,11 @@
# thumbor imaging service
# https://github.com/globocom/thumbor/wiki
-# Licensed under the MIT license:
+# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com timehome@corp.globo.com
+
class MultipleEngine:
def __init__(self, source_engine):
@@ -37,6 +38,7 @@ def exec_func(*args, **kwargs):
return result
return exec_func
+
class BaseEngine(object):
def __init__(self, context):
@@ -135,7 +137,7 @@ def focus(self, points):
def flip_horizontally(self):
raise NotImplementedError()
-
+
def flip_vertically(self):
raise NotImplementedError()
View
362 thumbor/engines/extensions/pil.py
@@ -13,11 +13,11 @@
# * Neither the name of the <organization> 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"
+#
+# 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 <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> 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
@@ -28,7 +28,7 @@
""" Module images2gif
Provides functionality for reading and writing animated GIF images.
-Use writeGif to write a series of numpy arrays or PIL images as an
+Use writeGif to write a series of numpy arrays or PIL images as an
animated GIF. Use readGif to read an animated gif as a series of numpy
arrays.
@@ -40,11 +40,11 @@
Many thanks to Ant1 for:
* noting the use of "palette=PIL.Image.ADAPTIVE", which significantly
- improves the results.
+ improves the results.
* the modifications to save each image with its own palette, or optionally
the global palette (if its the same).
-Many thanks to Marius van Voorden for porting the NeuQuant quantization
+Many thanks to Marius van Voorden for porting the NeuQuant quantization
algorithm of Anthony Dekker to Python (See the NeuQuant class for its
license).
@@ -52,7 +52,7 @@
which (depening on image content) can give a very significant reduction in
file size.
-This code is based on gifmaker (in the scripts folder of the source
+This code is based on gifmaker (in the scripts folder of the source
distribution of PIL)
@@ -65,7 +65,7 @@
"""
# todo: This module should be part of imageio (or at least based on)
-import os, time
+import os
try:
import PIL
@@ -77,7 +77,8 @@
try:
import numpy as np
except ImportError:
- np = None
+ np = None
+
def get_cKDTree():
try:
@@ -97,39 +98,39 @@ def checkImages(images):
""" checkImages(images)
Check numpy images and correct intensity range etc.
The same for all movie formats.
- """
+ """
# Init results
images2 = []
-
+
for im in images:
if PIL and isinstance(im, PIL.Image.Image):
# We assume PIL images are allright
images2.append(im)
-
+
elif np and isinstance(im, np.ndarray):
# Check and convert dtype
if im.dtype == np.uint8:
- images2.append(im) # Ok
+ images2.append(im) # Ok
elif im.dtype in [np.float32, np.float64]:
im = im.copy()
- im[im<0] = 0
- im[im>1] = 1
+ im[im < 0] = 0
+ im[im > 1] = 1
im *= 255
- images2.append( im.astype(np.uint8) )
+ images2.append(im.astype(np.uint8))
else:
im = im.astype(np.uint8)
images2.append(im)
# Check size
if im.ndim == 2:
- pass # ok
+ pass # ok
elif im.ndim == 3:
- if im.shape[2] not in [3,4]:
+ if im.shape[2] not in [3, 4]:
raise ValueError('This array can not represent an image.')
else:
raise ValueError('This array can not represent an image.')
else:
raise ValueError('Invalid image type: ' + str(type(im)))
-
+
# Done
return images2
@@ -138,75 +139,73 @@ def intToBin(i):
""" Integer to two bytes """
# devide in two parts (bytes)
i1 = i % 256
- i2 = int( i/256)
+ i2 = int(i / 256)
# make string (little endian)
return chr(i1) + chr(i2)
class GifWriter:
""" GifWriter()
-
+
Class that contains methods for helping write the animated GIF file.
-
+
"""
-
+
def getheaderAnim(self, im):
""" getheaderAnim(im)
-
- Get animation header. To replace PILs getheader()[0]
-
+
+ Get animation header. To replace PILs getheader()[0]
+
"""
bb = "GIF89a"
bb += intToBin(im.size[0])
bb += intToBin(im.size[1])
bb += "\x87\x00\x00"
return bb
-
-
+
def getImageDescriptor(self, im, xy=None):
""" getImageDescriptor(im, xy=None)
-
+
Used for the local color table properties per image.
Otherwise global color table applies to all frames irrespective of
whether additional colors comes in play that require a redefined
palette. Still a maximum of 256 color per frame, obviously.
-
+
Written by Ant1 on 2010-08-22
Modified by Alex Robinson in Janurari 2011 to implement subrectangles.
-
+
"""
-
+
# Defaule use full image and place at upper left
if xy is None:
- xy = (0,0)
-
+ xy = (0, 0)
+
# Image separator,
- bb = '\x2C'
-
+ bb = '\x2C'
+
# Image position and size
- bb += intToBin( xy[0] ) # Left position
- bb += intToBin( xy[1] ) # Top position
- bb += intToBin( im.size[0] ) # image width
- bb += intToBin( im.size[1] ) # image height
-
- # packed field: local color table flag1, interlace0, sorted table0,
+ bb += intToBin(xy[0]) # Left position
+ bb += intToBin(xy[1]) # Top position
+ bb += intToBin(im.size[0]) # image width
+ bb += intToBin(im.size[1]) # image height
+
+ # packed field: local color table flag1, interlace0, sorted table0,
# reserved00, lct size111=7=2^(7+1)=256.
- bb += '\x87'
-
+ bb += '\x87'
+
# LZW minimum size code now comes later, begining of [image data] blocks
return bb
-
-
+
def getAppExt(self, loops=float('inf')):
""" getAppExt(loops=float('inf'))
-
+
Application extention. This part specifies the amount of loops.
If loops is 0 or inf, it goes on infinitely.
-
+
"""
-
- if loops==0 or loops==float('inf'):
- loops = 2**16-1
+
+ if loops == 0 or loops == float('inf'):
+ loops = 2 ** 16 - 1
#bb = "" # application extension should not be used
# (the extension interprets zero loops
# to mean an infinite number of loops)
@@ -218,53 +217,51 @@ def getAppExt(self, loops=float('inf')):
bb += intToBin(loops)
bb += '\x00' # end
return bb
-
-
+
def getGraphicsControlExt(self, duration=0.1, dispose=2):
""" getGraphicsControlExt(duration=0.1, dispose=2)
-
+
Graphics Control Extension. A sort of header at the start of
- each image. Specifies duration and transparancy.
-
+ each image. Specifies duration and transparancy.
+
Dispose
-------
* 0 - No disposal specified.
* 1 - Do not dispose. The graphic is to be left in place.
- * 2 - Restore to background color. The area used by the graphic
+ * 2 - Restore to background color. The area used by the graphic
must be restored to the background color.
* 3 - Restore to previous. The decoder is required to restore the
- area overwritten by the graphic with what was there prior to
+ area overwritten by the graphic with what was there prior to
rendering the graphic.
- * 4-7 -To be defined.
-
+ * 4-7 -To be defined.
+
"""
-
+
bb = '\x21\xF9\x04'
bb += chr((dispose & 3) << 2) # low bit 1 == transparency,
# 2nd bit 1 == user input , next 3 bits, the low two of which are used,
# are dispose.
- bb += intToBin( int(duration*100) ) # in 100th of seconds
+ bb += intToBin(int(duration * 100)) # in 100th of seconds
bb += '\x00' # no transparant color
bb += '\x00' # end
return bb
-
-
+
def handleSubRectangles(self, images, subRectangles):
""" handleSubRectangles(images)
-
- Handle the sub-rectangle stuff. If the rectangles are given by the
+
+ Handle the sub-rectangle stuff. If the rectangles are given by the
user, the values are checked. Otherwise the subrectangles are
calculated automatically.
-
- """
-
- if isinstance(subRectangles, (tuple,list)):
+
+ """
+
+ if isinstance(subRectangles, (tuple, list)):
# xy given directly
-
+
# Check xy
xy = subRectangles
if xy is None:
- xy = (0,0)
+ xy = (0, 0)
if hasattr(xy, '__len__'):
if len(xy) == len(images):
xy = [xxyy for xxyy in xy]
@@ -272,111 +269,109 @@ def handleSubRectangles(self, images, subRectangles):
raise ValueError("len(xy) doesn't match amount of images.")
else:
xy = [xy for im in images]
- xy[0] = (0,0)
-
+ xy[0] = (0, 0)
+
else:
# Calculate xy using some basic image processing
-
+
# Check Numpy
if np is None:
raise RuntimeError("Need Numpy to use auto-subRectangles.")
-
+
# First make numpy arrays if required
for i in range(len(images)):
im = images[i]
if isinstance(im, Image.Image):
- tmp = im.convert() # Make without palette
+ tmp = im.convert() # Make without palette
a = np.asarray(tmp)
- if len(a.shape)==0:
+ if len(a.shape) == 0:
raise MemoryError("Too little memory to convert PIL image to array")
images[i] = a
-
+
# Determine the sub rectangles
images, xy = self.getSubRectangles(images)
-
+
# Done
return images, xy
-
-
+
def getSubRectangles(self, ims):
""" getSubRectangles(ims)
-
+
Calculate the minimal rectangles that need updating each frame.
Returns a two-element tuple containing the cropped images and a
list of x-y positions.
-
+
Calculating the subrectangles takes extra time, obviously. However,
if the image sizes were reduced, the actual writing of the GIF
goes faster. In some cases applying this method produces a GIF faster.
-
+
"""
-
+
# Check image count
if len(ims) < 2:
- return ims, [(0,0) for i in ims]
-
+ return ims, [(0, 0) for i in ims]
+
# We need numpy
if np is None:
raise RuntimeError("Need Numpy to calculate sub-rectangles. ")
-
+
# Prepare
ims2 = [ims[0]]
- xy = [(0,0)]
- t0 = time.time()
-
+ xy = [(0, 0)]
+ #t0 = time.time()
+
# Iterate over images
prev = ims[0]
for im in ims[1:]:
-
+
# Get difference, sum over colors
- diff = np.abs(im-prev)
- if diff.ndim==3:
- diff = diff.sum(2)
+ diff = np.abs(im - prev)
+ if diff.ndim == 3:
+ diff = diff.sum(2)
# Get begin and end for both dimensions
X = np.argwhere(diff.sum(0))
Y = np.argwhere(diff.sum(1))
# Get rect coordinates
if X.size and Y.size:
- x0, x1 = X[0], X[-1]+1
- y0, y1 = Y[0], Y[-1]+1
- else: # No change ... make it minimal
+ x0, x1 = X[0], X[-1] + 1
+ y0, y1 = Y[0], Y[-1] + 1
+ else: # No change ... make it minimal
x0, x1 = 0, 2
y0, y1 = 0, 2
-
+
# Cut out and store
- im2 = im[y0:y1,x0:x1]
+ im2 = im[y0:y1, x0:x1]
prev = im
ims2.append(im2)
- xy.append((x0,y0))
-
+ xy.append((x0, y0))
+
# Done
- #print('%1.2f seconds to determine subrectangles of %i images' %
+ #print('%1.2f seconds to determine subrectangles of %i images' %
# (time.time()-t0, len(ims2)) )
return ims2, xy
-
-
+
def convertImagesToPIL(self, images, dither, nq=0):
""" convertImagesToPIL(images, nq=0)
-
- Convert images to Paletted PIL images, which can then be
+
+ Convert images to Paletted PIL images, which can then be
written to a single animaged GIF.
-
+
"""
-
+
# Convert to PIL images
images2 = []
for im in images:
if isinstance(im, Image.Image):
images2.append(im)
elif np and isinstance(im, np.ndarray):
- if im.ndim==3 and im.shape[2]==3:
- im = Image.fromarray(im,'RGB')
- elif im.ndim==3 and im.shape[2]==4:
- im = Image.fromarray(im[:,:,:3],'RGB')
- elif im.ndim==2:
- im = Image.fromarray(im,'L')
+ if im.ndim == 3 and im.shape[2] == 3:
+ im = Image.fromarray(im, 'RGB')
+ elif im.ndim == 3 and im.shape[2] == 4:
+ im = Image.fromarray(im[:, :, :3], 'RGB')
+ elif im.ndim == 2:
+ im = Image.fromarray(im, 'L')
images2.append(im)
-
+
# Convert to paletted PIL images
images, images2 = images2, []
@@ -385,94 +380,89 @@ def convertImagesToPIL(self, images, dither, nq=0):
for im in images:
im = im.convert('P', palette=AD, dither=dither)
images2.append(im)
-
+
# Done
return images2
-
-
+
def writeGifToFile(self, fp, images, durations, loops, xys, disposes):
""" writeGifToFile(fp, images, durations, loops, xys, disposes)
-
+
Given a set of images writes the bytes to the specified stream.
-
+
"""
# Obtain palette for all images and count each occurance
palettes, occur = [], []
for im in images:
- palettes.append( getheader(im)[1] )
+ palettes.append(getheader(im)[1])
for palette in palettes:
- occur.append( palettes.count( palette ) )
-
+ occur.append(palettes.count(palette))
+
# Select most-used palette as the global one (or first in case no max)
- globalPalette = palettes[ occur.index(max(occur)) ]
-
+ globalPalette = palettes[occur.index(max(occur))]
+
# Init
frames = 0
firstFrame = True
-
-
+
for im, palette in zip(images, palettes):
-
+
if firstFrame:
# Write header
-
+
# Gather info
header = self.getheaderAnim(im)
appext = self.getAppExt(loops)
-
+
# Write
fp.write(header)
fp.write(globalPalette)
fp.write(appext)
-
+
# Next frame is not the first
firstFrame = False
-
+
if True:
# Write palette and image data
-
+
# Gather info
data = getdata(im)
imdes, data = data[0], data[1:]
- graphext = self.getGraphicsControlExt(durations[frames],
- disposes[frames])
+ graphext = self.getGraphicsControlExt(durations[frames], disposes[frames])
# Make image descriptor suitable for using 256 local color palette
lid = self.getImageDescriptor(im, xys[frames])
-
+
# Write local header
if (palette != globalPalette) or (disposes[frames] != 2):
# Use local color palette
fp.write(graphext)
- fp.write(lid) # write suitable image descriptor
- fp.write(palette) # write local color table
- fp.write('\x08') # LZW minimum size code
+ fp.write(lid) # write suitable image descriptor
+ fp.write(palette) # write local color table
+ fp.write('\x08') # LZW minimum size code
else:
# Use global color palette
fp.write(graphext)
- fp.write(imdes) # write suitable image descriptor
-
+ fp.write(imdes) # write suitable image descriptor
+
for d in data:
fp.write(d)
-
+
# Prepare for next round
frames = frames + 1
-
+
fp.write(";") # end gif
return frames
-
-
## Exposed functions
-
-def writeGif(filename, images, duration=0.1, repeat=True, dither=False,
- nq=0, subRectangles=True, dispose=None):
+def writeGif(
+ filename, images, duration=0.1, repeat=True, dither=False,
+ nq=0, subRectangles=True, dispose=None):
""" writeGif(filename, images, duration=0.1, repeat=True, dither=False,
nq=0, subRectangles=True, dispose=None)
-
+
Write an animated gif from the specified images.
-
+
Parameters
----------
filename : string
@@ -492,13 +482,13 @@ def writeGif(filename, images, duration=0.1, repeat=True, dither=False,
the color palette. This algorithm is superior, but slower than
the standard PIL algorithm. The value of nq is the quality
parameter. 1 represents the best quality. 10 is in general a
- good tradeoff between quality and speed. When using this option,
+ good tradeoff between quality and speed. When using this option,
better results are usually obtained when subRectangles is False.
subRectangles : False, True, or a list of 2-element tuples
Whether to use sub-rectangles. If True, the minimal rectangle that
is required to update each frame is automatically detected. This
can give significant reductions in file size, particularly if only
- a part of the image changes. One can also give a list of x-y
+ a part of the image changes. One can also give a list of x-y
coordinates if you want to do the cropping yourself. The default
is True.
dispose : int
@@ -506,27 +496,27 @@ def writeGif(filename, images, duration=0.1, repeat=True, dither=False,
in place. 2 means the background color should be restored after
each frame. 3 means the decoder should restore the previous frame.
If subRectangles==False, the default is 2, otherwise it is 1.
-
+
"""
-
+
# Check PIL
if PIL is None:
raise RuntimeError("Need PIL to write animated gif files.")
-
+
# Check images
images = checkImages(images)
-
+
# Instantiate writer object
gifWriter = GifWriter()
-
+
# Check loops
if repeat is False:
loops = 1
elif repeat is True:
- loops = 0 # zero means infinite
+ loops = 0 # zero means infinite
else:
loops = int(repeat)
-
+
# Check duration
if hasattr(duration, '__len__'):
if len(duration) == len(images):
@@ -535,16 +525,16 @@ def writeGif(filename, images, duration=0.1, repeat=True, dither=False,
raise ValueError("len(duration) doesn't match amount of images.")
else:
duration = [duration for im in images]
-
+
# Check subrectangles
if subRectangles:
images, xy = gifWriter.handleSubRectangles(images, subRectangles)
- defaultDispose = 1 # Leave image in place
+ defaultDispose = 1 # Leave image in place
else:
# Normal mode
- xy = [(0,0) for im in images]
- defaultDispose = 2 # Restore to background color.
-
+ xy = [(0, 0) for im in images]
+ defaultDispose = 2 # Restore to background color.
+
# Check dispose
if dispose is None:
dispose = defaultDispose
@@ -553,11 +543,10 @@ def writeGif(filename, images, duration=0.1, repeat=True, dither=False,
raise ValueError("len(xy) doesn't match amount of images.")
else:
dispose = [dispose for im in images]
-
-
+
# Make images in a format that we can write easy
images = gifWriter.convertImagesToPIL(images, dither, nq)
-
+
# Write
fp = open(filename, 'wb')
try:
@@ -566,52 +555,51 @@ def writeGif(filename, images, duration=0.1, repeat=True, dither=False,
fp.close()
-
def readGif(filename, asNumpy=True):
""" readGif(filename, asNumpy=True)
-
- Read images from an animated GIF file. Returns a list of numpy
+
+ Read images from an animated GIF file. Returns a list of numpy
arrays, or, if asNumpy is false, a list if PIL images.
-
+
"""
-
+
# Check PIL
if PIL is None:
raise RuntimeError("Need PIL to read animated gif files.")
-
+
# Check Numpy
if np is None:
raise RuntimeError("Need Numpy to read animated gif files.")
-
+
# Check whether it exists
if not os.path.isfile(filename):
- raise IOError('File not found: '+str(filename))
-
+ raise IOError('File not found: ' + str(filename))
+
# Load file using PIL
- pilIm = PIL.Image.open(filename)
+ pilIm = PIL.Image.open(filename)
pilIm.seek(0)
-
+
# Read all images inside
images = []
try:
while True:
# Get image as numpy array
- tmp = pilIm.convert() # Make without palette
+ tmp = pilIm.convert() # Make without palette
a = np.asarray(tmp)
- if len(a.shape)==0:
+ if len(a.shape) == 0:
raise MemoryError("Too little memory to convert PIL image to array")
# Store, and next
images.append(a)
- pilIm.seek(pilIm.tell()+1)
+ pilIm.seek(pilIm.tell() + 1)
except EOFError:
pass
-
+
# Convert to normal PIL images if needed
if not asNumpy:
images2 = images
images = []
- for im in images2:
- images.append( PIL.Image.fromarray(im) )
-
+ for im in images2:
+ images.append(PIL.Image.fromarray(im))
+
# Done
return images
View
5 thumbor/engines/graphicsmagick.py
@@ -67,7 +67,8 @@ def flip_horizontally(self):
self.image.flop()
def read(self, extension=None, quality=None):
- if quality is None: quality = self.context.request.quality
+ if quality is None:
+ quality = self.context.request.quality
#returns image buffer in byte format.
img_buffer = Blob()
@@ -130,7 +131,7 @@ def paste(self, other_engine, pos, merge=True):
other_engine.enable_alpha()
operator = co.OverCompositeOp if merge else co.CopyCompositeOp
- self.image.composite(other_engine.image, pos[0],pos[1], operator)
+ self.image.composite(other_engine.image, pos[0], pos[1], operator)
def enable_alpha(self):
self.image.type(ImageType.TrueColorMatteType)
View
10 thumbor/engines/json_engine.py
@@ -12,6 +12,7 @@
from thumbor.engines import BaseEngine
+
class JSONEngine(BaseEngine):
def __init__(self, engine, path, callback_name=None):
@@ -56,10 +57,10 @@ def focus(self, points):
self.focal_points.append(point.to_dict())
def flip_vertically(self):
- self.operations.append({ "type": "flip_vertically" })
+ self.operations.append({"type": "flip_vertically"})
def flip_horizontally(self):
- self.operations.append({ "type": "flip_horizontally" })
+ self.operations.append({"type": "flip_horizontally"})
def get_target_dimensions(self):
width = self.width
@@ -68,7 +69,7 @@ def get_target_dimensions(self):
for operation in self.operations:
if operation['type'] == 'crop':
width = operation['right'] - operation['left']
- height= operation['bottom'] - operation['top']
+ height = operation['bottom'] - operation['top']
if operation['type'] == 'resize':
width = operation['width']
@@ -116,7 +117,7 @@ def set_image_data(self, data):
return self.engine.set_image_data(data)
def read(self, extension, quality):
- target_width, target_height= self.get_target_dimensions()
+ target_width, target_height = self.get_target_dimensions()
thumbor_json = {
"thumbor": {
"source": {
@@ -141,4 +142,3 @@ def read(self, extension, quality):
return "%s(%s);" % (self.callback_name, thumbor_json)
return thumbor_json
-
View
6 thumbor/engines/opencv.py
@@ -19,6 +19,7 @@
'.png': 'PNG'
}
+
class Engine(BaseEngine):
def create_image(self, buffer):
@@ -53,7 +54,7 @@ def crop(self, left, top, right, bottom):
new_width = right - left
new_height = bottom - top
cropped = cv.CreateImage((new_width, new_height), 8, 3)
- src_region = cv.GetSubRect(self.image, (left, top, new_width, new_height) )
+ src_region = cv.GetSubRect(self.image, (left, top, new_width, new_height))
cv.Copy(src_region, cropped)
self.image = cropped
@@ -65,7 +66,8 @@ def flip_horizontally(self):
raise NotImplementedError()
def read(self, extension=None, quality=None):
- if quality is None: quality = self.context.request.quality
+ if quality is None:
+ quality = self.context.request.quality
options = None
extension = extension or self.extension
try:
View
2  thumbor/engines/pil.py
@@ -34,7 +34,7 @@
'.png': 'PNG'
}
-ImageFile.MAXBLOCK = 2**25
+ImageFile.MAXBLOCK = 2 ** 25
class Engine(BaseEngine):
View
1  thumbor/filters/brightness.py
@@ -11,6 +11,7 @@
from thumbor.filters import BaseFilter, filter_method
from thumbor.ext.filters import _brightness
+
class Filter(BaseFilter):
@filter_method(BaseFilter.Number)
View
1  thumbor/filters/contrast.py
@@ -11,6 +11,7 @@
from thumbor.filters import BaseFilter, filter_method
from thumbor.ext.filters import _contrast
+
class Filter(BaseFilter):
@filter_method(BaseFilter.Number)
View
2  thumbor/filters/equalize.py
@@ -11,10 +11,10 @@
from thumbor.filters import BaseFilter, filter_method
from thumbor.ext.filters import _equalize
+
class Filter(BaseFilter):
@filter_method()
def equalize(self):
imgdata = _equalize.apply(self.engine.get_image_mode(), self.engine.get_image_data())
self.engine.set_image_data(imgdata)
-
View
7 thumbor/filters/frame.py
@@ -12,6 +12,7 @@
from thumbor.filters import BaseFilter, filter_method
from os.path import splitext
+
class Filter(BaseFilter):
regex = r'(?:frame\((?P<url>.*?))'
@@ -28,7 +29,9 @@ def on_image_ready(self, buffer):
self.handle_padding(padding)
if self.engine.get_image_mode() != self.nine_patch_engine.get_image_mode():
- raise RuntimeError('Image mode mismatch: %s != %s' % (self.engine.get_image_mode(), self.nine_patch_engine.get_image_mode()))
+ raise RuntimeError('Image mode mismatch: %s != %s' % (
+ self.engine.get_image_mode(), self.nine_patch_engine.get_image_mode())
+ )
imgdata = _nine_patch.apply(self.engine.get_image_mode(),
self.engine.get_image_data(),
@@ -74,7 +77,7 @@ def on_fetch_done(self, buffer):
self.storage.put_crypto(self.url)
self.on_image_ready(buffer)
- @filter_method(BaseFilter.String, async = True)
+ @filter_method(BaseFilter.String, async=True)
def frame(self, callback, url):
self.url = url
self.callback = callback
View
1  thumbor/filters/noise.py
@@ -11,6 +11,7 @@
from thumbor.filters import BaseFilter, filter_method
from thumbor.ext.filters import _noise
+
class Filter(BaseFilter):
@filter_method(BaseFilter.PositiveNumber)
View
1  thumbor/filters/quality.py
@@ -10,6 +10,7 @@
from thumbor.filters import BaseFilter, filter_method
+
class Filter(BaseFilter):
@filter_method(BaseFilter.PositiveNumber)
View
8 thumbor/filters/redeye.py
@@ -23,6 +23,7 @@
HAAR_FLAGS = 0
RED_THRESHOLD = 2.0
+
class Filter(BaseFilter):
def get_pixels(self, image, w, h, mode):
@@ -50,7 +51,8 @@ def filter_eyes(self, eyes):
(x, y, w, h), other = eye
for eye2 in eyes:
(x2, y2, w2, h2), other2 = eye2
- if x == x2 and w == w2 and y == y2 and h == h2: continue
+ if x == x2 and w == w2 and y == y2 and h == h2:
+ continue
#if eye2 in intersected_eyes: continue
if (y2 >= y and y2 + h2 <= y + h) or (y2 + h2 >= y and y2 <= y + h):
@@ -97,7 +99,8 @@ def red_eye(self):
cv.SetImageROI(image, (face_x + x, face_y + y, w, h))
if self.context.request.debug:
- cv.Rectangle(image,
+ cv.Rectangle(
+ image,
(0, 0),
(w, h),
cv.RGB(255, 255, 255),
@@ -134,4 +137,3 @@ def red_eye(self):
def load_cascade_file(self):
if not hasattr(self.__class__, 'cascade'):
setattr(self.__class__, 'cascade', cv.Load(CASCADE_FILE_PATH))
-
View
1  thumbor/filters/rgb.py
@@ -11,6 +11,7 @@
from thumbor.filters import BaseFilter, filter_method
from thumbor.ext.filters import _rgb
+
class Filter(BaseFilter):
@filter_method(BaseFilter.Number, BaseFilter.Number, BaseFilter.Number)
View
6 thumbor/filters/round_corner.py
@@ -11,6 +11,7 @@
from thumbor.filters import BaseFilter, filter_method
from thumbor.ext.filters import _round_corner
+
class Filter(BaseFilter):
@filter_method(r'[\d]+(?:\|[\d]+)?', BaseFilter.PositiveNumber, BaseFilter.PositiveNumber, BaseFilter.PositiveNumber)
@@ -20,5 +21,8 @@ def round_corner(self, radius, r, g, b):
a_radius = int(radius_parts[0])
b_radius = int(radius_parts[1]) if len(radius_parts) > 1 else a_radius
- imgdata = _round_corner.apply(1, self.engine.get_image_mode(), a_radius, b_radius, r, g, b, width, height, self.engine.get_image_data())
+ imgdata = _round_corner.apply(
+ 1, self.engine.get_image_mode(), a_radius, b_radius, r, g, b,
+ width, height, self.engine.get_image_data()
+ )
self.engine.set_image_data(imgdata)
View
6 thumbor/filters/sharpen.py
@@ -11,10 +11,14 @@
from thumbor.filters import BaseFilter, filter_method
from thumbor.ext.filters import _sharpen
+
class Filter(BaseFilter):
@filter_method(BaseFilter.DecimalNumber, BaseFilter.DecimalNumber, BaseFilter.Boolean)
def sharpen(self, amount, radius, luminance_only):
width, height = self.engine.size
- imgdata = _sharpen.apply(self.engine.get_image_mode(), width, height, amount, radius, luminance_only, self.engine.get_image_data())
+ imgdata = _sharpen.apply(
+ self.engine.get_image_mode(), width, height, amount, radius,
+ luminance_only, self.engine.get_image_data()
+ )
self.engine.set_image_data(imgdata)
View
1  thumbor/filters/strip_icc.py
@@ -10,6 +10,7 @@
from thumbor.filters import BaseFilter, filter_method
+
class Filter(BaseFilter):
@filter_method()
View
3  thumbor/filters/watermark.py
@@ -12,6 +12,7 @@
from thumbor.filters import BaseFilter, filter_method
from os.path import splitext
+
class Filter(BaseFilter):
regex = r'(?:watermark\((?P<url>.*?),(?P<x>-?[\d]*?),(?P<y>-?[\d]*?),(?P<alpha>[\d]*?)\))'
@@ -46,7 +47,7 @@ def on_fetch_done(self, buffer):
self.storage.put_crypto(self.url)
self.on_image_ready(buffer)
- @filter_method(BaseFilter.String, r'-?[\d]+', r'-?[\d]+', BaseFilter.PositiveNumber, async = True)
+ @filter_method(BaseFilter.String, r'-?[\d]+', r'-?[\d]+', BaseFilter.PositiveNumber, async=True)
def watermark(self, callback, url, x, y, alpha):
self.url = url
self.x = x
View
2  thumbor/handlers/healthcheck.py
@@ -10,7 +10,7 @@
from thumbor.handlers import BaseHandler
+
class HealthcheckHandler(BaseHandler):
def get(self):
self.write('WORKING')
-
View
2  thumbor/handlers/image.py
@@ -53,7 +53,7 @@ def get(self, id):
mime = self.get_mimetype(body)
if mime:
- self.set_header('Content-Type', mime)
+ self.set_header('Content-Type', mime)
max_age = self.context.config.MAX_AGE
if max_age:
View
8 thumbor/handlers/images.py
@@ -10,7 +10,7 @@
import uuid
import mimetypes
-from thumbor.handlers import ImageApiHandler
+from thumbor.handlers import ImageApiHandler
##
@@ -19,7 +19,6 @@
# - through multipart/form-data (designed for forms)
# - or with the image content in the request body (rest style)
##
-
class ImagesHandler(ImageApiHandler):
def post(self):
@@ -43,7 +42,8 @@ def post(self):
if not filename:
content_type = self.request.headers.get('Content-Type', self.get_mimetype(body))
extension = mimetypes.guess_extension(content_type, False)
- if extension == '.jpe': extension = '.jpg' # Hack because mimetypes return .jpe by default
+ if extension == '.jpe':
+ extension = '.jpg' # Hack because mimetypes return .jpe by default
filename = self.context.config.UPLOAD_DEFAULT_FILENAME + extension
# Build image id based on a random uuid (32 characters)
@@ -61,5 +61,3 @@ def multipart_form_data(self):
def location(self, id, filename):
base_uri = self.request.uri
return base_uri + '/' + id + '/' + filename
-
-
View
6 thumbor/handlers/imaging.py