Coder Social home page Coder Social logo

pilkit's Introduction

PILKit is a collection of utilities for working with PIL (the Python Imaging Library).

One of its main features is a set of processors which expose a simple interface for performing manipulations on PIL images.

Looking for more advanced processors? Check out Instakit!

For the complete documentation on the latest stable version of PILKit, see PILKit on RTD.

Installation

  1. Install PIL or Pillow.
  2. Run pip install pilkit (or clone the source and put the pilkit module on your path)

Note

If you've never seen Pillow before, it considers itself a more-frequently updated "friendly" fork of PIL that's compatible with setuptools. As such, it shares the same namespace as PIL does and is a drop-in replacement.

Usage Overview

Processors

The "pilkit.processors" module contains several classes for processing PIL images, which provide an easy to understand API:

from pilkit.processors import ResizeToFit

img = Image.open('/path/to/my/image.png')
processor = ResizeToFit(100, 100)
new_img = processor.process(img)

A few of the included processors are:

  • ResizeToFit
  • ResizeToFill
  • SmartResize
  • Adjust
  • TrimBorderColor
  • Transpose

There's also a ProcessorPipeline class for executing processors sequentially:

from pilkit.processors import ProcessorPipeline, ResizeToFit, Adjust

img = Image.open('/path/to/my/image.png')
processor = ProcessorPipeline([Adjust(color=0), ResizeToFit(100, 100)])
new_image = processor.process(img)

Utilities

In addition to the processors, PILKit contains a few utilities to ease the pain of working with PIL. Some examples:

prepare_image
Prepares the image for saving to the provided format by doing some common-sense conversions, including preserving transparency and quantizing.
save_image
Wraps PIL's Image.save() method in order to gracefully handle PIL's "Suspension not allowed here" errors, and (optionally) prepares the image using prepare_image

Utilities are also included for converting between formats, extensions, and mimetypes.

Community

Please use the GitHub issue tracker to report bugs. A mailing list also exists to discuss the project and ask questions, as well as the official #imagekit channel on Freenode. (Both of these are shared with the django-imagekit project—from which PILKit spun off.)

pilkit's People

Contributors

3x0dv5 avatar ademaro avatar baffolobill avatar dmarler avatar dulacp avatar edwardbetts avatar fish2000 avatar fladi avatar hassanabidpk avatar kezabelle avatar kravemir avatar lexifdev avatar matthewwithanm avatar nrsimha avatar petry avatar pix666 avatar scyclops avatar vstoykov avatar xrmx avatar ykiu 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

pilkit's Issues

Gif animated handling bug

Hello,

When using Gif animated images, the processors remove the "animated" part of the gif and only resize the first frame of the gif.

Is there a way not to loose animated content ?

Thanks in advance,

Please make a new pypi release

The latest release on pypi was made in 2017: https://pypi.org/project/pilkit/

There have been fixes made in the last 6 years, particularly with regards to webp alpha support.

Could you please make a release which includes these fixes as well as all the other development that has been made in this time?

Thank you =]

Deprecation warning from Pillow

When I run my tests, I get the following warnings:-

../usr/local/lib/python3.8/site-packages/pilkit/processors/base.py:119
  /usr/local/lib/python3.8/site-packages/pilkit/processors/base.py:119: DeprecationWarning: FLIP_LEFT_RIGHT is deprecated and will be removed in Pillow 10 (2023-07-01). Use Transpose.FLIP_LEFT_RIGHT instead.
    FLIP_HORIZONTAL = Image.FLIP_LEFT_RIGHT

../usr/local/lib/python3.8/site-packages/pilkit/processors/base.py:120
  /usr/local/lib/python3.8/site-packages/pilkit/processors/base.py:120: DeprecationWarning: FLIP_TOP_BOTTOM is deprecated and will be removed in Pillow 10 (2023-07-01). Use Transpose.FLIP_TOP_BOTTOM instead.
    FLIP_VERTICAL = Image.FLIP_TOP_BOTTOM

../usr/local/lib/python3.8/site-packages/pilkit/processors/base.py:121
  /usr/local/lib/python3.8/site-packages/pilkit/processors/base.py:121: DeprecationWarning: ROTATE_90 is deprecated and will be removed in Pillow 10 (2023-07-01). Use Transpose.ROTATE_90 instead.
    ROTATE_90 = Image.ROTATE_90

../usr/local/lib/python3.8/site-packages/pilkit/processors/base.py:122
  /usr/local/lib/python3.8/site-packages/pilkit/processors/base.py:122: DeprecationWarning: ROTATE_180 is deprecated and will be removed in Pillow 10 (2023-07-01). Use Transpose.ROTATE_180 instead.
    ROTATE_180 = Image.ROTATE_180

../usr/local/lib/python3.8/site-packages/pilkit/processors/base.py:123
  /usr/local/lib/python3.8/site-packages/pilkit/processors/base.py:123: DeprecationWarning: ROTATE_270 is deprecated and will be removed in Pillow 10 (2023-07-01). Use Transpose.ROTATE_270 instead.
    ROTATE_270 = Image.ROTATE_270

Any ideas how to fix this?

Improve documentation: processors

I came here looking for some documentation on the Pilkit processors. Unfortunately, the documentation is lacking when it comes to what the processors actually do.

Ideally, I would like to see, for each processor, a short description of what that processor does, and an example image that has been run through that processor.

Take advantage of Pillow's "reducing_gap" argument

Hiya! Thanks for providing django-imagekit and pilkit. We've noticed that thumbnailing huge images (~320 megapixels) is extremely heavy and we've experienced timeouts due to this.

Pillow 7.0.0 introduced a new "reducing_gap" argument that makes resizing these large images a lot more efficient: https://pillow.readthedocs.io/en/stable/releasenotes/7.0.0.html#new-argument-reducing-gap-for-image-resize-and-image-thumbnail-methods

I'll probably be forking and applying this change myself, so I can make a pull-request at that point. What's this project's philosophy on supporting older Pillow/PIL versions? I obviously have no need of backwards-compatibility for my own purposes.

IOError cannot identify image file

With pilkit 1.1.7 I get the following error when generating image renditions.
Downgrading to 1.1.5 fixes this problem.

Traceback:
File "/Users/moritz/python/project/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response

  1.                 response = wrapped_callback(request, _callback_args, *_callback_kwargs)
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/django/views/generic/base.py" in view
  2.         return self.dispatch(request, _args, *_kwargs)
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/django/views/decorators/csrf.py" in wrapped_view
  3.     return view_func(_args, *_kwargs)
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/rest_framework/views.py" in dispatch
  4.         response = self.handle_exception(exc)
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/rest_framework/views.py" in dispatch
  5.         response = handler(request, _args, *_kwargs)
    
    File "/Users/moritz/Alp-Phone/Projects/project/project_server/frontendone/views.py" in get
  6.         response['resorts'] = resort_serializer.data
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/rest_framework/serializers.py" in data
  7.             self._data = [self.to_native(item) for item in obj]
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/rest_framework/serializers.py" in to_native
  8.         value = field.field_to_native(obj, field_name)
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/rest_framework/serializers.py" in field_to_native
  9.         return [self.to_native(item) for item in value.all()]
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/rest_framework/serializers.py" in to_native
  10.         value = field.field_to_native(obj, field_name)
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/rest_framework/fields.py" in field_to_native
  11.     value = getattr(self.parent, self.method_name)(obj)
    
    File "/Users/moritz/Alp-Phone/Projects/project/project_server/frontendone/serializers.py" in get_preview_image_url
  12.     return get_slideshow_preview_image_url(obj=obj, request=self.context['request'], absolute_url=True)
    
    File "/Users/moritz/Alp-Phone/Projects/project/project_server/frontendone/url_generators.py" in get_slideshow_preview_image_url
  13.  url = obj.slideshow_preview_retina.url
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/imagekit/cachefiles/init.py" in url
  14.     return self._storage_attr('url')
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/imagekit/cachefiles/init.py" in _storage_attr
  15.         existence_required.send(sender=self, file=self)
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/django/dispatch/dispatcher.py" in send
  16.         response = receiver(signal=self, sender=sender, **named)
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/imagekit/registry.py" in existence_required_receiver
  17.     self._receive(file, 'on_existence_required')
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/imagekit/registry.py" in _receive
  18.         call_strategy_method(file, callback)
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/imagekit/utils.py" in call_strategy_method
  19.     fn(file)
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/imagekit/cachefiles/strategies.py" in on_existence_required
  20.     file.generate()
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/imagekit/cachefiles/init.py" in generate
  21.         self.cachefile_backend.generate(self, force)
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/imagekit/cachefiles/backends.py" in generate
  22.     self.generate_now(file, force=force)
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/imagekit/cachefiles/backends.py" in generate_now
  23.         file._generate()
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/imagekit/cachefiles/init.py" in _generate
  24.     content = generate(self.generator)
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/imagekit/utils.py" in generate
  25. content = generator.generate()
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/imagekit/specs/init.py" in generate
  26.         img = open_image(self.source)
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/pilkit/utils.py" in open_image
  27. return Image.open(buffer)
    
    File "/Users/moritz/python/project/lib/python2.7/site-packages/PIL/Image.py" in open
  28. raise IOError("cannot identify image file")
    

Exception Type: IOError at /api/1.1/resorts/
Exception Value: cannot identify image file

Processor ancestor class and ABCs

Hi, I’m Alexander Böhn, longtime fan of django-imagekit, PILKit, and the maintainer of Instakit, here with a question that is relevant to both your project and mine.

One thing I have consistently enjoyed about programming processors – either for use with django-instakit or for other purposes – is the fact that they are so simple: the requirement is merely a class with a process(self, image) method.

Lately, though, I have been stretching the limit of this otherwise elastic design feature: I am implementing an automatically-generated CLI using the argparser module, to afford an Instakit user a sort of imaging testing gallery.

python

To do this, I am employing a bunch of honestly rather gnarly heuristic assumptive decisions in the code - stuff that could be seriously stabilized by using an ABC, e.g. like:

import abc

class ProcessorBase(abc.ABC):

    @abc.abstractmethod
    def process(self, image): ...

And so while I am loathe to complicate what I consider to be one of the major selling points of PILKit and its ilk (that being, the lack of hand-wavey code theater necessary to start working) it has not escaped my attention that using some sort of common ancestor of this sort could be a good thing. Furthermore, the use of such a thing could even be optional – imagine for a moment, with me, a world in which a processor is still any class adopting the process(self, image) method (which in Objective-C-land we call an “informal protocol”) but one can also opt-in to using an ABC ancestor along the lines of the one above.

What do you think?

Excessive memory usage of Resize* processors

Hi. I've been using pilkit via django-imagekit and they have been helping me a lot!

Recently I noticed my django app consumes weirdly large amount of memory when handling requests with some jpeg photos. The high memory usage turned out to be caused by this line of code, which adds an alpha channel to address this antialiasing issue.

When fed with a 2.8 MB image of sizes 4000x3000, the following Resize.process() call consumed 157 MiB of RAM:

from PIL import Image
from pilkit import processors
img = Image.open('path/to/image')
processors.Resize(100, 100).process(img)  # 157 MiB of RAM usage

When I removed img = img.convert('RGBA') from inside Resize.process(), the above code consumed only 65 MiB.

Since the original antialiasing issue seems to affect only GIF images, why don't we just skip the RGBA conversion for non-GIF images? (i.e. adding an if statement)

Would love to hear your thoughts. Thanks!

ImportError: cannot import name 'GaussianBlur'

ImportError: cannot import name 'GaussianBlur'
Unable to import GaussianBlur

from pilkit.processors import GaussianBlur
from pilkit.processors.filter import GaussianBlur

None works!!

Packaging issue with the 'tests/' directory

Hi,
as your tests directory is included in the distributed tarball as a top-level package, it is installed in python's site-packages (which can cause conflicts with other packages doing the same thing):

~ $ ls /usr/lib/python3.4/site-packages/tests
assets/  __init__.py  __pycache__/  test_processors.py  test_utils.py  utils.py

One option would be to exclude it from the distributed tarball, and the other option is to move it to pilkit.tests.

Image mode reset at the end of process method

Thank you for this great library.

I'm trying to convert a jpg image to black and white and store it in its original format, but I got the following error:
IOError at ... cannot write mode RGBA as JPEG

The cause is that some processors need to change image mode to perform operations, but doesn't reset it to its init value at the end of the process.

I ended up writing my own filter to preserve image mode:

from pilkit.processors import Adjust


class BlackAndWhite(object):

    def __init__(self):
        self.processor = Adjust(color=0.0)

    def process(self, img):
        img_mode = img.mode
        img = self.processor.process(img)
        img = img.convert(img_mode)
        return img

Transparency Loss in Palletized PNG Files - Regression in Version 3.0

Hello!

In version 3.0, there is a regression issue related to the loss of transparency in palette-based PNG files.

input.png

from PIL import Image
from pilkit.processors.utils import resolve_palette

img = Image.open('input.png')
new_img = resolve_palette(img)
new_img.save('output.png')

As a result of executing the provided code, the output image will lose its transparency and appear on a white background.

The root cause of the error is that, despite the image having transparency, the image.palette.mode property is equal to RGB.

Related commit

Remove dependency on nose

Hi! I am packaging this project for Arch Linux.

I believe it would be good to rely on pytest instead of nose and nose-progressive for tests (the latter project has been dead for years and I just removed it from the official Arch Linux repositories as well: erikrose/nose-progressive#90).
Currently it is possible to run pytest on the codebase (without nose-progressbar), but it still requires nose (which hasn't had a release since 2015).

Drop support for python < 3.7

Hi! I package this project for Arch Linux.

I would like to ask, whehter it would be possible to drop support for Python versions that are no longer supported by upstream.
Currently that's anything below 3.7: https://www.python.org/downloads/

Personally, I do not believe it is worth maintaining support for such old versions, as it introduces a lot of workarounds for maintaining compatibility.
As a plus: Issues like #54 will be much easier to solve.

New version

@matthewwithanm probably we need to release new version - 1.2 or 2.0. Because we are dropping Python 2.5, 2.6 and 3.2.

What you think?

Use buffer for input image

Instead of using the file directly, let's read its contents into a buffer.

This was done by #10, but apparently caused issues that require further investigation.

Using save_image with a filename

Hi,
First, thanks a lot for making pilkit, it's really great and useful.
Since version 1.1.2, it's no longer possible to use save_image with a filename. A file pointer is now required with the new FileWrapper. Do you think it is possible to support the previous behavior ? Maybe save_image could open the file if outfile is a string ?

passing spec to ProcessedImageField

I have separate definitions for the image spec:

class BrowseImageSpec(ImageSpec):
    processors = [
        processors.SmartResize(*settings.IMAGE_BROWSE_SIZE, upscale=True)
    ]
    format = 'JPEG'
    options = None
    autoconvert = True

and I want to use it as parameter for the ProcessedImageField:

    image = models.ImageField(_('Image'), upload_to=upload_path)
    thumb_index = ProcessedImageField(spec=BrowseImageSpec(source='image'))

which looks like a reasonable thing to do... but this fails:

TypeError: You can provide either an image spec or arguments for the ImageSpec constructor, but not both.

Problem when specifying a label argument for a ProcessedImageField

I'm getting an interesting exception when using a ProcessedImageField from django-imagekit in processors/base.py line 15 using Python 3.4 and Django 1.7.4 and specifying a label argument on my model:

AttributeError: 'str' object has no attribute 'process'

self     ['P', 'r', 'o', 'f', 'i', 'l', 'e', ' ', 'I', 'm', 'a', 'g', 'e']
proc    'P'
img    <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=80x80 at 0x7F6A6445A550>

It looks like the ProcessedImageField might be making some incorrect assumptions about what args are passed to it. If I define my model as:

class Profile(models.Model):
    image = ProcessedImageField('Profile Image', format='JPEG', options={'quality': 60})

the AttributeError is thrown. If I remove the label argument for the field, it works as expected.

ResizeToCover rounding error

The process function in ResizeToCover calculates wrong.

Example:
original size: 95x95
target size: 28x28
result: 27x27

The Problem is the float 2 int cast in line 48 and 49 in pilkit/processors/resize.py.

new_width, new_height = (int(original_width * ratio),
  int(original_height * ratio))

I recommend a round() before the cast.

new_width, new_height = (int(round(original_width * ratio)),
  int(round(original_height * ratio)))

That bug might be in other processors too.

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.