2

I have managed to install (after a lot of effort) django-imagekit and I am now able to use django-imagekit to compress the file size of uploaded images.

I can upload an image of 6MB and django-imagekit will compress the image to 230KB when I use a quality of 10 (see below).

Is there a way to use a different file compression (django-imagekit refers to this as quality) when the uploaded image is a size of 300Kb, 1MB , 2MB, 3MB or larger (I am thinking an if/elseif/else statement that would confirm the size of the image and apply a lower quality the larger the size (KB) of the image? The file compression of 10 works well for larger sized images but radically degrades the quality of the image for smaller sized images for example 25Kb.

I am not even sure how I would write the code and where I would place the code that would achieve this. So any help would be appreciated.

Here is my relevant models.py file code:

from imagekit.processors import Adjust, ResizeToFill
from imagekit.models import ProcessedImageField

class NameDetails(models.Model, FillableModelWithLanguageVersion):
    user = models.ForeignKey(User)
    ....
    #name_details_photograph = models.ImageField(upload_to=_get_name_details_photograph_upload_location, null=True, blank=True)
    name_details_photograph = ProcessedImageField(upload_to=_get_name_details_photograph_upload_location, null=True, blank=True, options={'quality': 25}, processors=[Adjust(sharpness=1.1),])
    ....

    def __unicode__(self):
        return unicode(self.user)

EDIT:

I have tried to implement the form field version of the ProcessedImageField class, but this does not upload the image.

Here is the forms code I have tried while changing the models.py code back to a image field (that is commented out above):

from imagekit.forms import ProcessedImageField
from imagekit.processors import Adjust, ResizeToFill

class NameDetailsForm(forms.ModelForm):

    def __init__(self, available_languages, language_preference, file_required, *args, **kwargs):
        """
        available_languages should be a valid choices list
        """
        super(NameDetailsForm, self).__init__(*args, **kwargs)
        self.fields['language_code'] = forms.ChoiceField(choices=available_languages, initial=language_preference, label=_('Language'),)
        #self.fields['name_details_photograph'] = forms.FileField(label=_('Photograph'), required=file_required)
        self.fields['name_details_photograph'] = ProcessedImageField(label=_('Photograph'), required=file_required, spec_id='myapp:profile:name_details_photograph', options={'quality': 25}, processors=[Adjust(sharpness=1.1),])

    class Meta:
        model = NameDetails
user1261774
  • 3,525
  • 10
  • 55
  • 103

2 Answers2

4

The solution I'm gonna offer is totally untested. It's based on the source code of the imagekit library.

The options kwarg is used by the ImageSpec class to passe it to PIL's Image.save().

So for a dynamic options you can create your own Spec class defining options as a property and use the getter to return an on-the-fly options. Something like:

from imagekit import ImageSpec
from imagekit.processors import Adjust, ResizeToFill

class ThumbnailSpec(ImageSpec):
    format = 'JPEG'
    options={'quality': 50}
    processors=[Adjust(sharpness=1.1),]

    @property
    def options(self):
        options = self._options
        #You can create some checks here and choose to change the options
        #you can access the file with self.source
        if self.source.size > 2*100*100:
            options['quality'] -= 25
        return options
    @options.setter
    def options(self, value):
        self._options = value

Finally use your ThumbnailSpec by passing it to the ProcessedImageField

name_details_photograph = ProcessedImageField(
    upload_to=_get_name_details_photograph_upload_location, 
    null=True, 
    blank=True, 
    spec=ThumbnailSpec
)
Todor
  • 15,307
  • 5
  • 55
  • 62
3

You can create a custom processor with django imagekit and then use it in your model. The processor will check the size of the image and then return the edited image. Something like this -

class ConditionalResizer(object):
    min_size = None  # minimum size to reduce in KB
    def __init__(self, *args, min_size=1000, **kwargs):
        super().__init__(self, *args, **kwargs) # code is for python > 3.0, modify for python < 3.0 as necessary 
        self.min_size = min_size

    def process(self, image):
        size = # code to get size of image
        if size > self.min_size:
            # process the image
            image = # processed image
        return image

Then in your Form, add the processor -

from imagekit.forms import ProcessedImageField
from imagekit.processors import Adjust, ResizeToFill

class NameDetailsForm(forms.ModelForm):

def __init__(self, available_languages, language_preference, file_required, *args, **kwargs):
    """
    available_languages should be a valid choices list
    """
    super(NameDetailsForm, self).__init__(*args, **kwargs)
    self.fields['language_code'] = forms.ChoiceField(choices=available_languages, initial=language_preference, label=_('Language'),)
    #self.fields['name_details_photograph'] = forms.FileField(label=_('Photograph'), required=file_required)
    self.fields['name_details_photograph'] = ProcessedImageField(label=_('Photograph'), required=file_required, spec_id='myapp:profile:name_details_photograph', options={'quality': 25}, processors=[Adjust(sharpness=1.1), ConditionalResize(min_size=1000)])

class Meta:
    model = NameDetails

I haven't tested this code yet, but should be able to solve your problem. Let me know if it does not.

You can find more about processors here - https://django-imagekit.readthedocs.org/en/latest/#processors

brainless coder
  • 6,310
  • 1
  • 20
  • 36