Archive

Archive for the ‘Development’ Category

Compresser un Jpeg à une taille donnée… en préservant les métadonnées

23 septembre 2011 Laisser un commentaire

Tout d’abord merci à Zigazou d’avoir partagé le script original et pour le temps qu’il m’a fait gagné !

De mon côté, si l’algorithme est exactement celui qu’il me fallait, je devais toutefois conserver les métadonnées des images. La conversion en PNM les supprimant toutes, j’ai tout d’abord pensé les sauvegarder puis les restaurer à l’aide de l’indispensable exiftool.

Malheureusement, je ne suis pas parvenu à réinjecter l’intégralité des champs (impossible notamment de réécrire la famille ICC_Profile malgré que celle-ci soit théoriquement supportée).

Après des heures de bagarre infructueuse, je décide de chercher une autre stratégies et pense soudain que, plus tôt dans mon workflow, convert (du paquet imagemagick) me convertit parfaitement les TIFF en JPEG en transférant impeccablement les métadonnées !

Je m’attaque donc à la page de man de convert (prévoyez un p’tit café !) et finit par tomber sur l’option espérée: -quality permettant de régler le niveau de compression des fichiers JPEG/MIFF/PNG. Un rapide test de recompression… Joie, tous les champs sont là !

Je vous livre donc ma version adaptée du script Python de Zigazou:

#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
"""Reduce JPEG file to the desired weight

Original script by zigazou:
http://zigazou.wordpress.com/2009/05/07/compresser-un-jpeg-a-une-taille-donnee/

The script uses convert utility from imagemagick package.
On Debian and derivatives, install it with:
        apt-get install imagemagick

Usage:
        jpegoptim   [ ...]

This version preserves metadatas which may lead to higher compression
to reach the target file weight, and so, lower quality.

Input files are overwritten

"""

import sys
from subprocess import Popen, PIPE

# Compress JPEG file to given quality with no color downsampling
def shell_command(quality,filename):
    p = Popen(["convert", "-quality", str(quality),
        "-sampling-factor", "4:4:4", filename, '-'],
        stdout=PIPE)

    return p.communicate()[0]

# "Compute" file weight for the given quality
# File (re)compression is actually done, even if discarded, so this
# can be very resource intensive
# TODO: maybe add process "renicing"
def compute(quality, filename):
        return len(shell_command(quality, filename))

# Finally generate the optimized file
def generate(quality, filename_in, filename_out):
        output = shell_command(quality, filename_in)

        f = open(filename_out, "w")
        f.write(output)
        f.close()

# Generic, dichotomic search function
def find_param(minimum, maximum, search_result, function, func_parms):
        index = (minimum + maximum) / 2
        best_index = index
        best_result = function(best_index, func_parms)
        result = best_result

        while index not in (minimum, maximum) and best_result != search_result:
                result = function(index, func_parms)

                if result  abs(search_result - result):
                        best_result = result
                        best_index = index

                index = (minimum + maximum) / 2

        if abs(search_result - best_result) > abs(search_result - result):
                best_result = result
                best_index = index

        return best_index

for fName in sys.argv[2:]:
    quality = find_param(0, 100, int(sys.argv[1]) * 1024, compute, fName)
    generate(quality, fName)

# vim: ts=4:sts=4:sw=4:et:ft=python:

Dans mon cas, j’ai préféré ne pas jouer sur l’échantillonnage des couleurs. Ceci est toutefois possible grâce au paramètre -sampling-factor en lui donnant les valeurs 4:2:2 pour un sous-échantillonnage standard, ou encore 4:1:1 pour une réduction maximum (pire qualité).

Pour plus d’informations sur cette question, je vous recommande la lecture de la page suivante: Optimization of JPEG (JPG) images: good quality and small size.

Encore merci à Zigazou qui m’a sauvé quelques poils sur le cailloux 😉

Python: constrain objects attribute list

From Python 2.2 on, it’s possible to constrain the list of attributes that can be referenced on an object using the new __slots__ class attribute. Python objects are usually very dynamic; at any time it’s possible to define a new attribute on an instance by just doing obj.new_attr=1. A new-style class can define a class attribute named __slots__ to limit the legal attributes to a particular set of names. An example will make this clear:

>>> class C(object):
...     __slots__ = ('template', 'name')
...
>>> obj = C()
>>> print obj.template
None
>>> obj.template = 'Test'
>>> print obj.template
Test
>>> obj.newattr = None
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
AttributeError: 'C' object has no attribute 'newattr'

Note how you get an AttributeError on the attempt to assign to an attribute not listed in __slots__.

Snipped from Python 2.2 release notes.
See also the __slots__ paragraph in Python langage reference about customizing attributes access.

Catégories :Python Étiquettes : , , , ,

An Introduction to the Zen of Python

A splendid introductory presentation of Python by Doug Hellmann the father of PyMOTW indispensible series and author of The Python Standard Library by Example.

Catégories :Python Étiquettes : , ,
%d blogueurs aiment cette page :