Giter Club home page Giter Club logo

pilbox's Introduction

DEPRECATED

This project will no longer be maintained.

Pilbox

PyPI Version

Build Status

Test Coverage

Code Health

Pilbox is an image processing application server built on Python's Tornado web framework using the Python Imaging Library (Pillow). It is not intended to be the primary source of images, but instead acts as a proxy which requests images and resizes them as desired.

Setup

Dependencies

Pilbox highly recommends installing libcurl and pycurl in order to get better HTTP request performance as well as additional features such as proxy requests and requests over TLS. Installed versions of libcurl should be a minimum of 7.21.1 and pycurl should be a minimum of 7.18.2. Furthermore, it is recommended that the libcurl installation be built with asynchronous DNS resolver (threaded or c-ares), otherwise it may encounter various problems with request timeouts (for more information, see CURLOPT_CONNECTTIMEOUT_MS and comments in curl_httpclient.py)

Install

Pilbox can be installed with pip

$ pip install pilbox

Or easy_install

$ easy_install pilbox

Or from source

$ git clone https://github.com/agschwender/pilbox.git

Running

To run the application, issue the following command

$ python -m pilbox.app

By default, this will run the application on port 8888 and can be accessed by visiting:

http://localhost:8888/

To see a list of all available options, run

$ python -m pilbox.app --help
Usage: pilbox/app.py [OPTIONS]

Options:

  --allowed_hosts            list of allowed hosts (default [])
  --allowed_operations       list of allowed operations (default [])
  --background               default hexadecimal bg color (RGB or ARGB)
  --ca_certs                 filename of CA certificates in PEM format
  --client_key               client key
  --client_name              client name
  --config                   path to configuration file
  --content_type_from_image  override content type using image mime type
  --debug                    run in debug mode
  --expand                   default to expand when rotating
  --filter                   default filter to use when resizing
  --help                     show this help information
  --implicit_base_url        prepend protocol/host to url paths
  --max_operations           maximum operations to perform (default 10)
  --max_requests             max concurrent requests (default 40)
  --max_resize_height        maximum resize height (default 15000)
  --max_resize_width         maximum resize width (default 15000)
  --operation                default operation to perform
  --optimize                 default to optimize when saving
  --port                     run on the given port (default 8888)
  --position                 default cropping position
  --preserve_exif            default behavior for Exif information
  --progressive              default to progressive when saving
  --proxy_host               proxy hostname
  --proxy_port               proxy port
  --quality                  default jpeg quality, 1-99 or keep
  --retain                   default adaptive retain percent, 1-99
  --timeout                  timeout of requests in seconds (default 10)
  --user_agent               user agent
  --validate_cert            validate certificates (default True)
  --workers                  number of worker processes (0 = auto) (default 0)

Calling

To use the image processing service, include the application url as you would any other image. E.g. this image url

<img src="http://i.imgur.com/zZ8XmBA.jpg" />

Would be replaced with this image url

<img src="http://localhost:8888/?url=http%3A%2F%2Fi.imgur.com%2FzZ8XmBA.jpg&w=300&h=300&mode=crop" />

This will request the image served at the supplied url and resize it to 300x300 using the crop mode. The below is the list of parameters that can be supplied to the service.

General Parameters

  • url: The url of the image to be resized
  • op: The operation to perform: noop, region, resize (default), rotate
    • noop: No operation is performed, image is returned as it is received
    • region: Select a sub-region from the image
    • resize: Resize the image
    • rotate: Rotate the image
  • fmt: The output format to save as, defaults to the source format
    • gif: Save as GIF
    • jpeg: Save as JPEG
    • png: Save as PNG
    • webp: Save as WebP
    • tiff: Save as TIFF
  • bg: Background color used with images that have transparency; useful when saving to a format that does not support transparency
    • RGB: 3- or 6-digit hexadecimal number
    • ARGB: 4- or 8-digit hexadecimal number, only relevant for PNG images
  • opt: The output should be optimized, only relevant to JPEGs and PNGs
  • exif: Keep original Exif data in the processed image, only relevant for JPEG
  • prog: Enable progressive output, only relevant to JPEGs
  • q: The quality, (1-99) or keep, used to save the image, only relevant to JPEGs

Resize Parameters

  • w: The desired width of the image
  • h: The desired height of the image
  • mode: The resizing method: adapt, clip, crop (default), fill and scale
    • adapt: Resize using crop if the resized image retains a supplied percentage of the original image; otherwise fill
    • clip: Resize to fit within the desired region, keeping aspect ratio
    • crop: Resize so one dimension fits within region, center, cut remaining
    • fill: Fills the clipped space with a background color
    • scale: Resize to fit within the desired region, ignoring aspect ratio
  • bg: Background color used with fill mode (RGB or ARGB)
    • RGB: 3- or 6-digit hexadecimal number
    • ARGB: 4- or 8-digit hexadecimal number, only relevant for PNG images
  • filter: The filtering algorithm used for resizing
    • nearest: Fastest, but often images appear pixelated
    • bilinear: Faster, can produce acceptable results
    • bicubic: Fast, can produce acceptable results
    • antialias: Slower, produces the best results
  • pos: The crop position
    • top-left: Crop from the top left
    • top: Crop from the top center
    • top-right: Crop from the top right
    • left: Crop from the center left
    • center: Crop from the center
    • right: Crop from the center right
    • bottom-left: Crop from the bottom left
    • bottom: Crop from the bottom center
    • bottom-right: Crop from the bottom right
    • face: Identify faces and crop from the midpoint of their position(s)
    • x,y: Custom center point position ratio, e.g. 0.0,0.75
  • retain: The minimum percentage (1-99) of the original image that must still be visible in the resized image in order to use crop mode

Region Parameters

  • rect: The region as x,y,w,h; x,y: top-left position, w,h: width/height of region

Rotate Parameters

  • deg: The desired rotation angle degrees
    • 0-359: The number of degrees to rotate (clockwise)
    • auto: Auto rotation based on Exif orientation, only relevant to JPEGs
  • expand: Expand the size to include the full rotated image
  • client: The client name
  • sig: The signature

The url parameter is always required as it dictates the image that will be manipulated. op is optional and defaults to resize. It also supports a comma separated list of operations, where each operation is applied in the order that it appears in the list. Depending on the operation, additional parameters are required. All image manipulation requests accept exif, fmt, opt, prog and q. exif is optional and default to 0 (not preserved). fmt is optional and defaults to the source image format. opt is optional and defaults to 0 (disabled). prog is optional and default to 0 (disabled). q is optional and defaults to 90. To ensure security, all requests also support, client and sig. client is required only if the client_name is defined within the configuration file. Likewise, sig is required only if the client_key is defined within the configuration file. See the Signing section for details on how to generate the signature.

For resizing, either the w or h parameter is required. If only one dimension is specified, the application will determine the other dimension using the aspect ratio. mode is optional and defaults to crop. filter is optional and defaults to antialias. bg is optional and defaults to 0fff. pos is optional and defaults to center. retain is optional and defaults to 75.

For region sub-selection, rect is required. For rotating, deg is required. expand is optional and defaults to 0 (disabled). It is recommended that this feature not be used as it typically does not produce high quality images.

Note, all built-in defaults can be overridden by setting them in the configuration file. See the Configuration section for more details.

Examples

The following images show the various resizing modes in action for an original image size of 640x428 that is being resized to 500x400.

Adapt

The adaptive resize mode combines both crop and fill resize modes to ensure that the image always matches the requested size and a minimum percentage of the image is always visible. Adaptive resizing will first calculate how much of the image will be retained if crop is used. Then, if that percentage is equal to or above the requested minimum retained percentage, crop mode will be used. If it is not, fill will be used. The first figure uses a retain value of 80 to illustrate the adaptive crop behavior.

Whereas the second figure requires a minimum of 99 to illustrate the adaptive fill behavior

Clip

The image is resized to fit within a 500x400 box, maintaining aspect ratio and producing an image that is 500x334. Clipping is useful when no portion of the image can be lost and it is acceptable that the image not be exactly the supplied dimensions, but merely fit within the dimensions.

Crop

The image is resized so that one dimension fits within the 500x400 box. It is then centered and the excess is cut from the image. Cropping is useful when the position of the subject is known and the image must be exactly the supplied size.

Fill

Similar to clip, fill resizes the image to fit within a 500x400 box. Once clipped, the image is centered within the box and all left over space is filled with the supplied background color. Filling is useful when no portion of the image can be lost and it must be exactly the supplied size.

Scale

The image is clipped to fit within the 500x400 box and then stretched to fill the excess space. Scaling is often not useful in production environments as it generally produces poor quality images. This mode is largely included for completeness.

Signing

In order to secure requests so that unknown third parties cannot easily use the resize service, the application can require that requests provide a signature. To enable this feature, set the client_key option. The signature is a hexadecimal digest generated from the client key and the query string using the HMAC-SHA1 message authentication code (MAC) algorithm. The below python code provides an example implementation.

import hashlib
import hmac

def derive_signature(key, qs):
    m = hmac.new(key, None, hashlib.sha1)
    m.update(qs)
    return m.hexdigest()

The signature is passed to the application by appending the sig parameter to the query string; e.g. x=1&y=2&z=3&sig=c9516346abf62876b6345817dba2f9a0c797ef26. Note, the application does not include the leading question mark when verifying the supplied signature. To verify your signature implementation, see the pilbox.signature command described in the Tools section.

Configuration

All options that can be supplied to the application via the command line, can also be specified in the configuration file. Configuration files are simply python files that define the options as variables. The below is an example configuration.

# General settings
port = 8888

# One worker process per CPU core
workers = 0

# Set client name and key if the application requires signed requests. The
# client must sign the request using the client_key, see README for
# instructions.
client_name = "sample"
client_key = "3NdajqH8mBLokepU4I2Bh6KK84GUf1lzjnuTdskY"

# Set the allowed hosts as an alternative to signed requests. Only those
# images which are served from the following hosts will be requested.
allowed_hosts = ["localhost"]

# Request-related settings
max_requests = 50
timeout = 7.5

# Set default resizing options
background = "ccc"
filter = "bilinear"
mode = "crop"
position = "top"

# Set default rotating options
expand = False

# Set default saving options
format = None
optimize = 1
quality = "90"

Tools

To verify that your client application is generating correct signatures, use the signature command.

$ python -m pilbox.signature --key=abcdef "x=1&y=2&z=3"
Query String: x=1&y=2&z=3
Signature: c9516346abf62876b6345817dba2f9a0c797ef26
Signed Query String: x=1&y=2&z=3&sig=c9516346abf62876b6345817dba2f9a0c797ef26

The application allows the use of the resize functionality via the command line.

$ python -m pilbox.image --width=300 --height=300 http://i.imgur.com/zZ8XmBA.jpg > /tmp/foo.jpg

If a new mode is added or a modification was made to the libraries that would change the current expected output for tests, run the generate test command to regenerate the expected output for the test cases.

$ python -m pilbox.test.genexpected

Deploying

It is strongly encouraged that pilbox not be directly accessible to the internet. Instead, it should only be accessible via a web server, e.g. NGINX or Apache, or some other application that is designed to handle direct traffic from the internet.

The application itself does not include any caching. It is recommended that the application run behind a CDN for larger applications or behind varnish for smaller ones.

Defaults for the application have been optimized for quality rather than performance. If you wish to get higher performance out of the application, it is recommended you use a less computationally expensive filtering algorithm and a lower JPEG quality. For example, add the following to the configuration.

# Set default resizing options
filter = "bicubic"
quality = 75

If you wish to improve performance further and are using an x86 platform, you may want to consider using Pillow-SIMD. Follow the steps in Installation and it should function as a drop-in replacement for Pillow. To avoid any incompatibility issues, use the same version of Pillow-SIMD as is being used for Pillow.

Another setting that's helpful for fine-tuning performance and memory usage is the workers setting to set the number of Tornado worker processes. The default setting of 0 spawns one worker process per CPU core which can lead to high memory usage and reduced performance due to swapping on low-memory configurations. For Heroku deployments limiting the number of worker processes to 2-3 for the lower-end dynos helped smooth out application response time.

Extension

While it is generally recommended to use Pilbox as a standalone server, it can also be used as a library. To extend from it and build a custom image processing server, use the following example.

#!/usr/bin/env python

import tornado.gen

from pilbox.app import PilboxApplication, ImageHandler, \
    start_server, parse_command_line


class CustomApplication(PilboxApplication):
    def get_handlers(self):
        return [(r"/(\d+)x(\d+)/(.+)", CustomImageHandler)]


class CustomImageHandler(ImageHandler):
    def prepare(self):
        self.args = self.request.arguments.copy()

    @tornado.gen.coroutine
    def get(self, w, h, url):
        self.args.update(dict(w=w, h=h, url=url))

        self.validate_request()
        resp = yield self.fetch_image()
        self.render_image(resp)

    def get_argument(self, name, default=None):
        return self.args.get(name, default)


if __name__ == "__main__":
    parse_command_line()
    start_server(CustomApplication())

Contribution

To contribute to the project or to make and test your own changes, fork and then clone the project.

$ git clone https://github.com/YOUR-USERNAME/pilbox.git

Packaged with Pilbox is a Vagrant configuration file which installs all necessary dependencies on a virtual box using Ansible. See the Vagrant documentation and the Ansible documentation for installation instructions. Once installed, the following will start and provision a virtual machine.

$ vagrant up
$ vagrant provision

This will have installed pilbox in /var/www/pilbox on the virtual machine. To access the virtual machine itself, simply...

$ vagrant ssh

When running via Vagrant, the application is automatically started on port 8888 on 192.168.100.100, i.e.

http://192.168.100.100:8888/

To run pilbox manually, execute the following.

$ sudo /etc/init.d/pilbox stop
$ python -m pilbox.app

To run all tests, issue the following command from the installed pilbox directory.

$ python -m pilbox.test.runtests

To run individual tests, simply indicate the test to be run, e.g.

$ python -m pilbox.test.runtests pilbox.test.signature_test

Changelog

  • 0.1: Image resizing fit
  • 0.1.1: Image cropping
  • 0.1.2: Image scaling
  • 0.2: Configuration integration
  • 0.3: Signature generation
  • 0.3.1: Signature command-line tool
  • 0.4: Image resize command-line tool
  • 0.5: Facial recognition cropping
  • 0.6: Fill resizing mode
  • 0.7: Resize using crop position
  • 0.7.1: Resize using a single dimension, maintaining aspect ratio
  • 0.7.2: Added filter and quality options
  • 0.7.3: Support python 3
  • 0.7.4: Fixed cli for image generation
  • 0.7.5: Write output in 16K blocks
  • 0.8: Added support for ARGB (alpha-channel)
  • 0.8.1: Increased max clients and write block sizes
  • 0.8.2: Added configuration for max clients and timeout
  • 0.8.3: Only allow http and https protocols
  • 0.8.4: Added support for WebP
  • 0.8.5: Added format option and configuration overrides for mode and format
  • 0.8.6: Added custom position support
  • 0.9: Added rotate operation
  • 0.9.1: Added sub-region selection operation
  • 0.9.4: Added Pilbox as a PyPI package
  • 0.9.10: Converted README to reStructuredText
  • 0.9.14: Added Sphinx docs
  • 0.9.15: Added implicit base url to configuration
  • 0.9.16: Added validate cert to configuration
  • 0.9.17: Added support for GIF format
  • 0.9.18: Fix for travis builds on python 2.6 and 3.3
  • 0.9.19: Validate cert fix
  • 0.9.20: Added optimize option
  • 0.9.21: Added console script entry point
  • 1.0.0: Modified for easier library usage
  • 1.0.1: Added allowed operations and default operation
  • 1.0.2: Modified to allow override of http content type
  • 1.0.3: Safely catch image save errors
  • 1.0.4: Added progressive option
  • 1.1.0: Proxy server support
  • 1.1.1: Added JPEG auto rotation based on Exif orientation
  • 1.1.2: Added keep JPEG quality option and set JPEG subsampling to keep
  • 1.1.3: Fixed auto rotation on JPEG with missing Exif data
  • 1.1.4: Exception handling around invalid Exif data
  • 1.1.5: Fixed image requests without content types
  • 1.1.6: Support custom applications that need command line arguments
  • 1.1.7: Support adapt resize mode
  • 1.1.8: Added preserve Exif flag
  • 1.1.9: Increased Pillow version to 2.8.1
  • 1.1.10: Added ca_certs option
  • 1.1.11: Added support for TIFF
  • 1.2.0: Support setting background when saving a transparent image
    • Backwards incompatible: default background property changed to 0fff. To restore previous behavior, set background in config to ffff.
  • 1.2.1: Added max operations config property
  • 1.2.2: Added max resize width and height config properties
  • 1.2.3: Added user_agent option
  • 1.3.0: Increased Pillow to 2.9.0 and Tornado to 4.5.1
  • 1.3.1: Fix pilbox.image CLI for python 3.0
  • 1.3.2: Fix GIF P-mode to JPEG conversion
  • 1.3.3: Increase Pillow version to 5.2.0 and Tornado version to 5.1.0
  • 1.3.4: Added worker config property to set number of Tornado processes

pilbox's People

Contributors

agschwender avatar andygeers avatar darius1988 avatar dbader avatar devoto13 avatar dotpot avatar joevandyk avatar johngibb avatar katafalkas avatar maryokhin avatar rcarmo avatar saschwarz avatar syabro avatar sylvinus avatar xtagon avatar ysimonson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pilbox's Issues

how to cache original image

how to set pilbox to cache original image?
because i'm need when new request resize on same image origin don't to download again from origin.

proportional resize

can I resize image proportional to the width or height by not specifying width or height?

Uwsgi support question.

Hi I've been trying to run pilbox on elastic beanstalk I got pretty close to it but I'm stuck with some async issue.

Here is the code I have so far:

#!/usr/bin/env python

import tornado.wsgi
from pilbox.app import PilboxApplication, main, options

options.port = 80
options.implicit_base_url = 'http://someurl'
uwsgi_adapter = tornado.wsgi.WSGIAdapter(PilboxApplication())
application = tornado.wsgi.WSGIContainer(uwsgi_adapter)


if __name__ == "__main__":
    main(application)

This starts the server correctly and it seems to do it's job but I get this error while running sending a request:

[E 151128 13:13:53 http1connection:53] Uncaught exception
    Traceback (most recent call last):
      File "/Users/marco/git/cc-pilbox/env/lib/python2.7/site-packages/tornado/http1connection.py", line 234, in _read_message
        delegate.finish()
      File "/Users/marco/git/cc-pilbox/env/lib/python2.7/site-packages/tornado/httpserver.py", line 280, in finish
        self.server.request_callback(self.request)
      File "/Users/marco/git/cc-pilbox/env/lib/python2.7/site-packages/tornado/wsgi.py", line 280, in __call__
        WSGIContainer.environ(request), start_response)
      File "/Users/marco/git/cc-pilbox/env/lib/python2.7/site-packages/tornado/wsgi.py", line 230, in __call__
        raise Exception("request did not finish synchronously")
    Exception: request did not finish synchronously
[I 151128 13:13:53 web:1811] 200 GET /?url=/thumbnail/28556751/4000&w=100&h=100&mode=crop (::1) 214.98ms

The reason I use the WSGIContainer is for local development but when submitting to elastic beanstalk I use the WSGIAdapter version. Any ideas would be great.

Healthcheck?

I couldn't find anything on the docs but, what is the best way to make a quick healthcheck on pilbox instances? I'm trying to use it with a load balancer so it would be nice to have something. Thanks!

Config error

My config file:

# General settings
port = 8888

# Set default saving options
format = None
optimize = 1
quality = 80

Error:

root@698a4fd0d906:/# pilbox --config=pilbox.conf
Traceback (most recent call last):
  File "/usr/local/bin/pilbox", line 9, in <module>
    load_entry_point('pilbox==1.1.6', 'console_scripts', 'pilbox')()
  File "/usr/local/lib/python3.4/site-packages/pilbox/app.py", line 332, in main
    parse_command_line()
  File "/usr/local/lib/python3.4/site-packages/pilbox/app.py", line 314, in parse_command_line
    tornado.options.parse_command_line()
  File "/usr/local/lib/python3.4/site-packages/tornado/options.py", line 525, in parse_command_line
    return options.parse_command_line(args, final=final)
  File "/usr/local/lib/python3.4/site-packages/tornado/options.py", line 261, in parse_command_line
    option.parse(value)
  File "/usr/local/lib/python3.4/site-packages/tornado/options.py", line 417, in parse
    self.callback(self._value)
  File "/usr/local/lib/python3.4/site-packages/pilbox/app.py", line 48, in <lambda>
    callback=lambda path: parse_config_file(path, final=False))
  File "/usr/local/lib/python3.4/site-packages/tornado/options.py", line 533, in parse_config_file
    return options.parse_config_file(path, final=final)
  File "/usr/local/lib/python3.4/site-packages/tornado/options.py", line 280, in parse_config_file
    self._options[name].set(config[name])
  File "/usr/local/lib/python3.4/site-packages/tornado/options.py", line 432, in set
    (self.name, self.type.__name__, type(value)))
tornado.options.Error: Option 'quality' is required to be a str (<class 'int'> given)

ar

NOT AN ISSUE
I was typing with wrong windows focus, and it created an issue... very sorry about that

Preserving Exif information

Hi!

This issue somehow connected with #24. Visible effect is that image is rotated after processing. Basically the problem comes from Pillow, which removes all Exif information on save. I saw issue was addressed by adding rotate="auto" in version 1.1.1.

For my use case I would prefer to keep Exif information including Orientation tag in resized image instead of rotating image and removing all the information. It's possible according to python-pillow/Pillow#183.

So the idea is to add another option for Pilbox (preserve_exif for example) and keep this information in processed image. Do you have any thoughts on this topic? Will it be merged if I provide a pull-request?

security issue - cannot validate signature

Assume I start the server with:
python -m pilbox.app --client_key=dupa

Then I launch some operation like this:
http://localhost:8888/?url=some.jpg&w=500&h=500&sig=dupa

I get:
{"status_code": 403, "error_code": 101, "error": "Invalid signature"}

I wonder what I might be doing wrong. I tried to investigate the source code and:
self.settings.get("client_key") is 'dupa'

but
verify_signature(key, urlparse(self.request.uri).query) is False

qs == 'url=some.jpg&w=500&h=500&sig=dupa'
unsigned_qs == 'url=some.jpg&w=500&h=500'

this produces some kind of a hash
sig = derive_signature(key, unsigned_qs) # like 'b4805a1b12a0aeb3fb8e2c7e9c435bd3f3a4088a'

and the final comparison:
urlparse.parse_qs(qs).get("sig", [None])[0] == sig
is always False because
urlparse.parse_qs(qs).get("sig", [None])[0] is 'dupa'
while
sig is 'b4805a1b12a0aeb3fb8e2c7e9c435bd3f3a4088a'

HTTP headers are escaped

Here's the headers I'm seeing when I make a call to pilbox. Note that this is running behind cloudfront, which is why there's the X-Cache and X-Amz-Cf-Id headers. It looks like the header values for Cache-Control and Expires are escaped.

HTTP/1.1 200 OK
Cache-Control: %27max-age%3D155520000%2C+public%27
Connection: keep-alive
Content-Length: 18158
Content-Type: image/jpeg
Date: Wed, 27 Nov 2013 18:58:21 GMT
ETag: "57257755fc4431fa22e16ca26e98f12043f68329"
Expires: %27Sat%2C+29+Apr+2017+13%3A31%3A45-0000+GMT%27
Last-Modified: Fri, 22 Nov 2013 22:59:03 GMT
Server: TornadoServer/3.1
Via: 1.1 05133ea38769f43c08a8a54a913b9e04.cloudfront.net (CloudFront)
X-Amz-Cf-Id: -OyL_VAJjWfzrdite0Lb7Hr2rBK0iL33t8e4adBn5IJ30mGFABnDQA==
X-Cache: RefreshHit from cloudfront

Issue latest version (0.9.18) and Tornado

After installing the latest version of pilbox, I get this error by simply hitting the running tornado server with an example request.

Traceback (most recent call last):
  File "/srv/venvs/pilbox/local/lib/python2.7/site-packages/tornado/web.py", line 1273, in _when_complete
    if result.result() is not None:
  File "/srv/venvs/pilbox/local/lib/python2.7/site-packages/tornado/concurrent.py", line 129, in result
    raise_exc_info(self.__exc_info)
  File "/srv/venvs/pilbox/local/lib/python2.7/site-packages/tornado/gen.py", line 227, in wrapper
    runner.run()
  File "/srv/venvs/pilbox/local/lib/python2.7/site-packages/tornado/gen.py", line 531, in run
    yielded = self.gen.send(next)
  File "/srv/venvs/pilbox/lib/python2.7/site-packages/pilbox/app.py", line 117, in get
    validate_cert=self.settings.get("validate_cert"))
  File "/srv/venvs/pilbox/local/lib/python2.7/site-packages/tornado/httpclient.py", line 134, in __new__
    **kwargs)
  File "/srv/venvs/pilbox/local/lib/python2.7/site-packages/tornado/util.py", line 172, in __new__
    instance.initialize(**args)
TypeError: initialize() got an unexpected keyword argument 'validate_cert'

Does this have to do with a new version of Tornado required by Pilbox? I tried to uninstall everything in a clean venv and run pip install pilbox==0.9.10, which is working fine.
Thanks!

Request: Handle error codes and messages in custom application

Hi,

I have an application inheriting PilboxApplication. Is there a way I can write a handler that will run my own custom action when a Pilbox error happens, and give me the Pilbox code and error message? I would like the normal logging to still occur, but I want to additionally post it to our exception handling service.

What would be the best way for me to get each error when they occur? Is there a way to do this without forking Pilbox? Can I get the error code, error message, and the original request info?

Thanks,
Justin

Images with %20 in their name

I'm attempting to use Pilbox on some images that have %20 in their name to substitute a white space. What I'm running into is that the image works when it's loaded directly but when it's ran through the resizer it refuses to work. I have other images that are pumped in through the same API which do work, but those don't have %20 in their name.

Is there some sort of trick or work around to getting this to work? Changing the file name is not an option as I'm working with a large amount of imported data.

The message I get:
{"error":"not found"}

This image works: http://www.adn.com/sites/default/files/styles/ad_slideshow_940/public/Glacier-150831-13.JPG
This image does not: http://www.adn.com/sites/default/files/styles/ad_slideshow_940/public/helio%20sequence%20horiz.jpg

Passing URL-encoded values to `op` doesn't work

This is very minor since we can fix this on the client side of things. But just in case you want to know:

If I generate an URL with op=resize,rotate it works. But URLs generated as op=resize%2Crotate does not. However, the latter should be perfectly valid and was easy to generate by accident by automatically urlencoding all params passed.

POST images to resize, etc.

For my project I'd like to form POST images to pilbox and resize them. Was wondering if you already had an example of that or would be interested in a PR if I added that functionality? I'm thinking all pilbox options would be made available via the POST(?).

My use case is a mobile app where people will import (possibly) large images for which the app only needs much lower quality/smaller images. Once converted they'd be stored locally. It's not really the proxy use case pilbox targets, but thought you might be interested.

"IndexError: index out of range" internal error for some images with auto rotate enabled

Hi,

We've been noticing that certain images are triggering a 500 Internal Server Error when we use the rotate op with deg=auto. Here's the trace:

Traceback (most recent call last):
  File "/usr/lib64/python2.7/site-packages/tornado/web.py", line 1334, in _execute
    result = yield result
  File "/usr/lib64/python2.7/site-packages/tornado/gen.py", line 628, in run
    value = future.result()
  File "/usr/lib64/python2.7/site-packages/tornado/concurrent.py", line 109, in result
    raise_exc_info(self._exc_info)
  File "/usr/lib64/python2.7/site-packages/tornado/gen.py", line 633, in run
    yielded = self.gen.send(value)
  File "/usr/lib64/python2.7/site-packages/pilbox/app.py", line 144, in get
    self.render_image(resp)
  File "/usr/lib64/python2.7/site-packages/pilbox/app.py", line 193, in render_image
    outfile, outfile_format = self._process_response(resp)
  File "/usr/lib64/python2.7/site-packages/pilbox/app.py", line 220, in _process_response
    self._image_rotate(image)
  File "/usr/lib64/python2.7/site-packages/pilbox/app.py", line 235, in _image_rotate
    image.rotate(self.get_argument("deg"), **opts)
  File "/usr/lib64/python2.7/site-packages/pilbox/image.py", line 201, in rotate
    exif = self.img._getexif() or dict
  File "/usr/lib64/python2.7/site-packages/PIL/JpegImagePlugin.py", line 390, in _getexif
    return _getexif(self)
  File "/usr/lib64/python2.7/site-packages/PIL/JpegImagePlugin.py", line 429, in _getexif
    info.load(file)
  File "/usr/lib64/python2.7/site-packages/PIL/TiffImagePlugin.py", line 424, in load
    for i in range(i16(fp.read(2))):
  File "/usr/lib64/python2.7/site-packages/PIL/_binary.py", line 37, in i16le
    return i8(c[o]) | (i8(c[o+1]) << 8)
IndexError: string index out of range

Minimum Pilbox params to reproduce: op=rotate&deg=auto - If I set deg=90 or some arbitrary degree, the problem doesn't happen.

I suspect there is some edge case with the EXIF data in the images that are failing. If you'd like to inspect one of the failing images I can share it by e-mail. We need it to silently fail if the image doesn't contain orientation metadata.

Regards,
Justin

TypeError when requesting "auto" rotation option

I'm adding the new deg=auto field to my projects, but the following exception occurs (500 error)

Uncaught exception GET /?client_key=REDACTED&deg=auto&filter=bilinear&h=780&mode=clip&op=resize%2Crotate&opt=1&prog=1&q=keep&url=REDACTED&sig=d84e48da2e1349d0be97b6b5359fb5f1e590ef82 (::1)
    HTTPServerRequest(protocol='http', host='localhost:8888', method='GET', uri='/?client_key=REDACTED&deg=auto&filter=bilinear&h=780&mode=clip&op=resize%2Crotate&opt=1&prog=1&q=keep&url=REDACTED&sig=d84e48da2e1349d0be97b6b5359fb5f1e590ef82', version='HTTP/1.1', remote_ip='::1', headers={'Accept-Language': 'en-US,en;q=0.8', 'Accept-Encoding': 'gzip,deflate,sdch', 'Host': 'localhost:8888', 'Accept': 'image/webp,*/*;q=0.8', 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36', 'Dnt': '1', 'Connection': 'keep-alive', 'Referer': 'http://localhost:5001/projects/34', 'Pragma': 'no-cache', 'Cache-Control': 'no-cache', 'Cookie': '_ga=GA1.1.2004554824.1407177578; remember_user_token=W1sxXSwiJDJhJDEwJExVelJKZUl3cG9XMU5zSjc4WjlZbS4iXQ%3D%3D--61bab841ed2be7b502660f397a217339ff86985b; _Bijou_session=N3ovbG9NalNwNXNxVlZ2eE5uOExKVlBTanozMEYwTGhNY0tNWENva1RIRWRjUUJwVmZPMFR2RVRMVStnNXBVYVhpZ0NOZm4xZ3FQMDJXM3N6SkltMUUyT1ltZkJzeGUyNUlnQnNSTExGWjBlVlNZKyszUUVlTlFaSkEwbEkvNHh3VGo4TjdPVHZGQzJaYVBuWW96b0I1dFFpbnl2MjltUlFlMWJidk5hOW1oR0ZXY09QS2ZjUWNPT20xM0pYaXNYTkFPbHRFMDIrYStMeG5KL2lyLzNaOS9kVTdMOGpkMkM4MERiL21OWERUcz0tLWI3Mm1sY2pDbW04dzM5RmVORFRvUmc9PQ%3D%3D--c0da5c4afa420c55b7fe44e14b8f3e6642c962f0'})
    Traceback (most recent call last):
      File "/usr/lib64/python2.7/site-packages/tornado/web.py", line 1334, in _execute
        result = yield result
      File "/usr/lib64/python2.7/site-packages/tornado/gen.py", line 628, in run
        value = future.result()
      File "/usr/lib64/python2.7/site-packages/tornado/concurrent.py", line 109, in result
        raise_exc_info(self._exc_info)
      File "/usr/lib64/python2.7/site-packages/tornado/gen.py", line 633, in run
        yielded = self.gen.send(value)
      File "/usr/lib64/python2.7/site-packages/pilbox/app.py", line 144, in get
        self.render_image(resp)
      File "/usr/lib64/python2.7/site-packages/pilbox/app.py", line 193, in render_image
        outfile, outfile_format = self._process_response(resp)
      File "/usr/lib64/python2.7/site-packages/pilbox/app.py", line 220, in _process_response
        self._image_rotate(image)
      File "/usr/lib64/python2.7/site-packages/pilbox/app.py", line 235, in _image_rotate
        image.rotate(self.get_argument("deg"), **opts)
      File "/usr/lib64/python2.7/site-packages/pilbox/image.py", line 202, in rotate
        deg = _orientation_to_rotation.get(exif.get(274, 0), 0)
    TypeError: descriptor 'get' requires a 'dict' object but received a 'int'

Pilbox version is 1.1.2, Python is 3.4.1

Any help is greatly appreciated :)

Requests from CloudFront fail with "KeyError: Content-Type"

When requesting an image from our CloudFront bucket we get the backtrace below - requesting the same file direct from its S3 bucket doesn't show this issue. While we could conceivably go direct to S3 every time it would mean rewriting responses from our API layer to do so, which we'd rather not do if its avoidable:

[E 150220 12:27:45 web:1407] Uncaught exception GET /?url=https%3A%2F%2Fd1r8772cwfbmfd.cloudfront.net%2Fsystem%2Fproduct_images%2F9404%2Fkl275-big.jpg&w=100&h=100&mode=crop (172.17.42.1)
    HTTPServerRequest(protocol='http', host='hubbub-image-sizer.elasticbeanstalk.com', method='GET', uri='/?url=https%3A%2F%2Fd1r8772cwfbmfd.cloudfront.net%2Fsystem%2Fproduct_images%2F9404%2Fkl275-big.jpg&w=100&h=100&mode=crop', version='HTTP/1.1', remote_ip='172.17.42.1', headers={'X-Real-Ip': '82.71.4.106', 'Host': 'hubbub-image-sizer.elasticbeanstalk.com', 'X-Forwarded-For': '82.71.4.106', 'Accept': '*/*', 'User-Agent': 'curl/7.35.0'})
    Traceback (most recent call last):
      File "/usr/local/lib/python2.7/dist-packages/tornado/web.py", line 1334, in _execute
        result = yield result
      File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 628, in run
        value = future.result()
      File "/usr/local/lib/python2.7/dist-packages/tornado/concurrent.py", line 109, in result
        raise_exc_info(self._exc_info)
      File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 633, in run
        yielded = self.gen.send(value)
      File "/usr/local/lib/python2.7/dist-packages/pilbox/app.py", line 144, in get
        self.render_image(resp)
      File "/usr/local/lib/python2.7/dist-packages/pilbox/app.py", line 194, in render_image
        self._set_headers(resp.headers, outfile_format)
      File "/usr/local/lib/python2.7/dist-packages/pilbox/app.py", line 248, in _set_headers
        self.set_header("Content-Type", headers["Content-Type"])
      File "/usr/local/lib/python2.7/dist-packages/tornado/httputil.py", line 209, in __getitem__
        return dict.__getitem__(self, _normalized_headers[name])
    KeyError: 'Content-Type'
[E 150220 12:27:45 web:1811] 500 GET /?url=https%3A%2F%2Fd1r8772cwfbmfd.cloudfront.net%2Fsystem%2Fproduct_images%2F9404%2Fkl275-big.jpg&w=100&h=100&mode=crop (172.17.42.1) 399.65ms

ansible play-book failing

With ansible 1.6.10 when running vagrant provisioning I get the following:

ASK: [install apt packages] **************************************************
failed: [192.168.100.100] => (item=build-essential,python,python-dev,python-setuptools,python-pip,python-numpy,python-opencv,libjpeg-dev,libfreetype6-dev,zlib1g-dev,libwebp-dev,liblcms2-dev) => {"failed": true, "item": "build-essential,python,python-dev,python-setuptools,python-pip,python-numpy,python-opencv,libjpeg-dev,libfreetype6-dev,zlib1g-dev,libwebp-dev,liblcms2-dev"}
msg: unsupported parameter for module: status

FATAL: all hosts have already failed -- aborting

PLAY RECAP ********************************************************************
           to retry, use: --limit @/Users/maryokhin/playbook.retry

192.168.100.100            : ok=8    changed=1    unreachable=0    failed=1

Ansible failed to complete successfully. Any error output should be
visible above. Please fix these errors and try again.

This can be fixed when changing this line from

apt: pkg=$item

to

apt: "name={{ item  }} state=present"

logging level debug

Hey.

Just installed pilbox. for some reason the logging level is debug. I trying to turn it into INFO:
in /etc/init.d/pilbox:

DAEMON_OPTS="python -m pilbox.app --config=$CFG_PATH --log_file_prefix=/var/log/pilbox.log --logging=info"

or

DAEMON_OPTS="python -m pilbox.app --config=$CFG_PATH --log_file_prefix=/var/log/pilbox.log"

does not seem to work. Any ideas ?

JPEG images failling, being identified as MPO

Hi,

Some of our images are failing with the error "Unknown format: MPO". The images are JPEG but Pillow is for some reason thinking they are MPO. I've reported this issue to Pilllow here: python-pillow/Pillow#1138

I realize this is not your issue, it's Pillows, but I thought I'd let you know about the problem. Maybe there's a workaround you can do, where if it detects an MPO it tries to process it as a JPEG anyway? If not, I understand. Thanks for reading.

Spaces in upstream URLs

Hi

It seems like there's a problem with the encoding of upstream URLs when they contain spaces. This is however only a problem with some upstreams - I am seeing it with S3 but not with Nginx. I have found numerous mentions of this problem specifically with S3, and there's even a forum thread from 2006 which mentions this problem.

The problem only seems to relate to spaces due to how the headers are sent over HTTP. I have put up a test file on S3 and did a quick test via curl. curl doesn't do the URL encoding by itself, so it is good for testing this with. Here's a test with a normal space in the URL:

$ curl --head --verbose 'http://pilbox-space-test.s3-eu-west-1.amazonaws.com/test image.png'
*   Trying 54.231.130.139...
* Connected to pilbox-space-test.s3-eu-west-1.amazonaws.com (54.231.130.139) port 80 (#0)
> HEAD /test image.png HTTP/1.1
> Host: pilbox-space-test.s3-eu-west-1.amazonaws.com
> User-Agent: curl/7.42.1
> Accept: */*
>
< HTTP/1.1 505 HTTP Version not supported
HTTP/1.1 505 HTTP Version not supported
< Date: Mon, 01 Jun 15 14:04:54 GMT
Date: Mon, 01 Jun 15 14:04:54 GMT
< Connection: close
Connection: close
< Transfer-Encoding: chunked
Transfer-Encoding: chunked
< x-amz-id-2: gy/LaxRfoO5dF6Kxz7B8VOcWoTVIYNZXoZorJPHuJiAjJ94mQHkGz+OfRvIXlq0IaDBR71E18Z7O9Dp3lg/TwoLyxfeGk81t
x-amz-id-2: gy/LaxRfoO5dF6Kxz7B8VOcWoTVIYNZXoZorJPHuJiAjJ94mQHkGz+OfRvIXlq0IaDBR71E18Z7O9Dp3lg/TwoLyxfeGk81t
< x-amz-request-id: 872E7839A03D35F
x-amz-request-id: 872E7839A03D35F
< Content-Type: application/xml
Content-Type: application/xml

<
* Excess found in a non pipelined read: excess = 315 url = /test image.png (zero-length body)
* Closing connection 0

And here's where I have replaced the space with %20:

$ curl --head --verbose 'http://pilbox-space-test.s3-eu-west-1.amazonaws.com/test%20image.png'
*   Trying 54.231.130.139...
* Connected to pilbox-space-test.s3-eu-west-1.amazonaws.com (54.231.130.139) port 80 (#0)
> HEAD /test%20image.png HTTP/1.1
> Host: pilbox-space-test.s3-eu-west-1.amazonaws.com
> User-Agent: curl/7.42.1
> Accept: */*
>
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< x-amz-id-2: hKoX6GPzBlfa9SP6AEUpPxaA5l+GfwEBY+J5+Zc/Xo71SAG7vr+YseDieuFGohauCEFcG9GDvv8=
x-amz-id-2: hKoX6GPzBlfa9SP6AEUpPxaA5l+GfwEBY+J5+Zc/Xo71SAG7vr+YseDieuFGohauCEFcG9GDvv8=
< x-amz-request-id: 4A03CDDAB70A708B
x-amz-request-id: 4A03CDDAB70A708B
< Date: Mon, 01 Jun 2015 14:05:44 GMT
Date: Mon, 01 Jun 2015 14:05:44 GMT
< Last-Modified: Mon, 01 Jun 2015 13:57:58 GMT
Last-Modified: Mon, 01 Jun 2015 13:57:58 GMT
< ETag: "814a0034f5549e957ee61360d87457e5"
ETag: "814a0034f5549e957ee61360d87457e5"
< Accept-Ranges: bytes
Accept-Ranges: bytes
< Content-Type: image/png
Content-Type: image/png
< Content-Length: 473831
Content-Length: 473831
< Server: AmazonS3
Server: AmazonS3

<
* Connection #0 to host pilbox-space-test.s3-eu-west-1.amazonaws.com left intact

The difference can be see in the HEAD line where the space in the file name presumably give problems with the parsing of the headers on the S3 end, and they assume "image.png" is a HTTP version.

I have done some testing of Pilbox in relation to this, and it seems like it always sends an literal space character to the upstream, even though it is sent %20. Do you want me to make a patch for replacing spaces with %20 before doing the upstream requests?

Cannot run unit tests

I get the following error when running the tests:

$ python setup.py test
running test
Traceback (most recent call last):
  File "pilbox/test/runtests.py", line 63, in <module>
    tornado.testing.main(**kwargs)
  File "/Users/ysimonson/Desktop/dailymuse/pilbox2/lib/python2.7/site-packages/tornado/testing.py", line 606, in main
    unittest.main(defaultTest="all", argv=argv, **kwargs)
  File "/usr/local/Cellar/python/2.7.4/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/main.py", line 94, in __init__
    self.parseArgs(argv)
  File "/usr/local/Cellar/python/2.7.4/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/main.py", line 149, in parseArgs
    self.createTests()
  File "/usr/local/Cellar/python/2.7.4/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/main.py", line 158, in createTests
    self.module)
  File "/usr/local/Cellar/python/2.7.4/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/loader.py", line 128, in loadTestsFromNames
    suites = [self.loadTestsFromName(name, module) for name in names]
  File "/usr/local/Cellar/python/2.7.4/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/loader.py", line 113, in loadTestsFromName
    test = obj()
  File "pilbox/test/runtests.py", line 20, in all
    return unittest.defaultTestLoader.loadTestsFromNames(TEST_MODULES)
  File "/usr/local/Cellar/python/2.7.4/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/loader.py", line 128, in loadTestsFromNames
    suites = [self.loadTestsFromName(name, module) for name in names]
  File "/usr/local/Cellar/python/2.7.4/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/loader.py", line 91, in loadTestsFromName
    module = __import__('.'.join(parts_copy))
ImportError: No module named pilbox

Mac OS / python 2.7.4. Happens both in and outside of virtualenv environments.

Make benefit of PycURL more clear

Thanks for a nice project!

When trying to set it up and test it, I found that Pilbox couldn't fetch images from upstream HTTPS servers which only supports TLS and not SSL - this is for instance the case for sites which use AWS CloudFront with HTTPS, but doesn't the extra premium to get dedicated IP addresses for the site.
Such errors give a stack trace ending with:

SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:581)

It can be solved by installing PycURL, but perhaps that should be made more clear? I have a feeling that SSL slowly will die out and more and more people will experience this problem, so perhaps a note in the README file or even a warning on start up would be a good idea?

Error "encoder error -2 when writing image file" occurs for some JPEG images

Hi,

I'm having a hard time troubleshooting this issue. Some of our images fail on Pilbox with the following error: encoder error -2 when writing image file

I can't reproduce on my dev machine, but our Pilbox server running on Heroku exhibits the error. I realize this might be a Pillow or libjpeg error and not necessarily Pilbox, but if you could help me figure out next steps to debug this it would be greatly appreciated.

We're running Pilbox 1.1.3 and Python 3.4.1. Let me know what other info might be needed. Thanks!

Publish your Dockerfile

There's a pilbox repository on Docker hub, but it doesn't include a Dockerfile, and looks to be massively out of date.

Do you still have a copy of the Dockerfile which you used, and if so could you add it here and set up a trusted build so it stays up to date? We're using pilbox in production, but I've got no way of changing the configuration in a repeatable way.

Feature request: fallback image on processing error

It would be extremely useful to be able to specify a fallback to be returned to the browser when an image fails to be processed as a thumbnail. Errors happen. We're logging all failures anyway, but we don't want the user to see the browser's missing image icon. It would be better for them to see either a noop of the original image, or an alternate image URL we specify.

Something like this: ?url=URL&op=resize&w=50&fallback=noop or ?url=URL&op=resize&w=50&fallback=IMAGE_NOT_FOUND_URL

Thoughts?

Limit max value for resize `h` and `w`

Since there no any limits applied, there possibility for some kind of DDoS attack, if someone decide to set pretty big value for h/w (for example if they set it to sys.maxint which is 9223372036854775807 for Python2).

Instrumentation with New Relic

Hi,

Would it be possible to support New Relic's Python agent in Pilbox for server instrumentation? I'm running Pilbox on Heroku and it would be nice to see response times and availability monitoring with the New Relic add-on. According to this documentation we can wrap WSGI applications. Is this possible with Twisted?

Could Pilbox Set the Content-Type header based on the PIL Output?

My source (an S3 bucket) has all images set as application/octet-stream (annoyingly). Since PIL needs to detect the image type to perform the transformation, it would be helpful if Pilbox could correct the Content-Type header.

Tracing through the logic, it only seems to update the Content-Type header when either (fmt is passed in or a format is specified on the command line/config). In my usage, I want neither (for it to stay in its existing format, just resized).

https://github.com/agschwender/pilbox/blob/master/pilbox/app.py#L165-166

Auto-rotate a JPEG

Hi,

Great product, by the way!

Thumbor has an option for auto-rotating a JPEG based on the EXIF rotation data. Is this possible with Pilbox? Some of our projects could really use this feature.

Regards,
Justin

Dealing with custom SSL certs

We recently switched up our environment to automatically promote all connections to use TLS/SSL. When run on localhost, we use self-signing SSL certs. Currently, when pilbox makes requests, it errors out because of this:

[W 140402 12:23:34 app:121] Fetch error for https://localhost:8000/static/uploaded/attachments/12525.jpg?v=None: HTTP 599: [Errno 1] _ssl.c:504: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

Is there any way to get pilbox to accept this cert?

3 tests failing

Hi,

I have started pillbox on vagrant and run tests. 3 of them fail. They have issues with webp files.

`======================================================================
FAIL: test_valid (pilbox.test.app_test.AppRestrictedTest)

Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/tornado/testing.py", line 136, in call
result = self.orig_method(*args, **kwargs)
File "pilbox/test/app_test.py", line 579, in test_valid
self.assertEqual(resp.buffer.read(), expected.read(), msg)
AssertionError: /?url=http%3A%2F%2Flocalhost%3A46410%2Ftest%2Fdata%2Ftest4.webp&h=300&client=abc&mode=adapt&w=300&sig=3e789c4ae9a341eb8a6dc0805317fe4d809d8df6 does not match pilbox/test/data/expected/test4-300x300-mode=adapt.webp

======================================================================
FAIL: test_valid_resize (pilbox.test.app_test.AppTest)

Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/tornado/testing.py", line 136, in call
result = self.orig_method(*args, **kwargs)
File "pilbox/test/app_test.py", line 365, in test_valid_resize
self._assert_expected_case(case)
File "pilbox/test/app_test.py", line 403, in _assert_expected_case
self.assertEqual(resp.buffer.read(), expected.read(), msg)
AssertionError: /?url=http%3A%2F%2Flocalhost%3A40399%2Ftest%2Fdata%2Ftest4.webp&h=300&mode=adapt&w=300 does not match pilbox/test/data/expected/test4-300x300-mode=adapt.webp

======================================================================
FAIL: test_resize (pilbox.test.image_test.ImageTest)

Traceback (most recent call last):
File "pilbox/test/image_test.py", line 145, in test_resize
self._assert_expected_resize(case)
File "pilbox/test/image_test.py", line 343, in _assert_expected_resize
self.assertEqual(rv.read(), expected.read(), msg)
AssertionError: pilbox/test/data/test4.webp does not match pilbox/test/data/expected/test4-300x300-mode=adapt.webp


Ran 91 tests in 9.341s

FAILED (failures=3)
[E 171212 12:26:12 testing:737] FAIL
`
Do I need to install something more to make these tests pass?

Cheers,
Maciek

Which version of Pillow-SIMD works with Pilbox 1.3.4?

Is there a recommended version of Pillow-SIMD that works with Pilbox 1.3.4? They don't have a tag for 5.2.0 - I tried 5.1.1.post0 (the latest) but it caused errors:

ImportError: The _imaging extension was built for another version of Pillow or PIL: 

JPEG Rotation Detection

I have a photo which is not being auto rotated by Pilbox. In Pilbox's defense, opening it in a browser, windows photo viewer, window's Photos app, Ubuntu's photo viewer, and probably a bunch more don't detect it correctly on this photo. When running identify --verbose with ImageMagick it detects the EXIF rotation correctly: exif:Orientation: 6. I included the full output from identify just in case it helps:

Image:
  Format: JPEG (Joint Photographic Experts Group JFIF format)
  Mime type: image/jpeg
  Class: DirectClass
  Geometry: 640x480+0+0
  Resolution: 72x72
  Print size: 8.88889x6.66667
  Units: PixelsPerInch
  Type: TrueColor
  Endianess: Undefined
  Colorspace: sRGB
  Depth: 8-bit
  Channel depth:
    red: 8-bit
    green: 8-bit
    blue: 8-bit
  Channel statistics:
    Pixels: 307200
    Red:
      min: 0 (0)
      max: 255 (1)
      mean: 159.854 (0.62688)
      standard deviation: 80.9692 (0.317526)
      kurtosis: -0.800555
      skewness: -0.881085
    Green:
      min: 1 (0.00392157)
      max: 255 (1)
      mean: 151.698 (0.594895)
      standard deviation: 78.9907 (0.309768)
      kurtosis: -0.912923
      skewness: -0.792576
    Blue:
      min: 0 (0)
      max: 255 (1)
      mean: 147.883 (0.579932)
      standard deviation: 74.9123 (0.293774)
      kurtosis: -0.977616
      skewness: -0.708286
  Image statistics:
    Overall:
      min: 0 (0)
      max: 255 (1)
      mean: 153.145 (0.600569)
      standard deviation: 78.3314 (0.307182)
      kurtosis: -0.890637
      skewness: -0.79041
  Rendering intent: Perceptual
  Gamma: 0.454545
  Chromaticity:
    red primary: (0.64,0.33)
    green primary: (0.3,0.6)
    blue primary: (0.15,0.06)
    white point: (0.3127,0.329)
  Background color: white
  Border color: srgb(223,223,223)
  Matte color: grey74
  Transparent color: black
  Interlace: None
  Intensity: Undefined
  Compose: Over
  Page geometry: 640x480+0+0
  Dispose: Undefined
  Iterations: 0
  Compression: JPEG
  Quality: 96
  Orientation: RightTop
  Properties:
    [...]
    exif:Orientation: 6
  Profiles:
    Profile-exif: 1928 bytes
    Profile-xmp: 1906 bytes
  Artifacts:
    verbose: true
  Tainted: False
  Filesize: 50.9KB
  Number pixels: 307K
  Pixels per second: 0B
  User time: 0.000u
  Elapsed time: 0:01.000
  Version: ImageMagick 6.8.9-9 Q16 x86_64 2017-03-14 http://www.imagemagick.org

animated gif support

hi there,

i can't find anything related to supporting animated gifs (try this link as an example).

the first frame of the gif is handled fine, maybe that's on purpose, but i would like to resize, crop etc all the frames and get back the original gif animation. as far as i can see pillow supports animated gif handling.

is this something you would like to support in pilbox?

all feedback appreciated!

/cc @seichner

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.