IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Calcul scientifique Python Discussion :

[PIL] temps perdu sur une double boucle


Sujet :

Calcul scientifique Python

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2007
    Messages
    44
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 44
    Points : 28
    Points
    28
    Par défaut [PIL] temps perdu sur une double boucle
    Bonjour à toute la troupe !

    Je pose là moins un pb PIL qu'un pb de conception.

    Je suis en train de monter une petite appli de redimensionnement par lot de photos. Le principe est : l'utilisateur indique le dossier des photos à traiter, la taille maxi des photos souhaitée (n) , et le programme crée un sous dossier web dans lequel il stocke les photos resizées par PIL et Python.

    Le mécanisme est le suivant :

    - 1 boucle pour trouver le poids maxi des photos du dossier --> p
    - calculer le taux de réduction : t = n/p
    - 1 boucle où pour chaque photo, je récupère sa size (x,y) et je fais un thumbnail(x*t, y*t)

    A la syntaxe près...

    Cela fait 2 boucles (donc deux fois le boulot, donc pas bon pour le feignant qui programme !).

    J'ai chronométré le temps avec deux versions : taux de réduction imposé pour tout le monde (donc une seule boucle), et taux de réduction choisi par utilisateur (2 boucles).

    Le fait est que la seconde solution met beaucoup plus de temps (presque le double !).

    D'où la question qui tue : je pense que cela vient du fait qu'il y a deux boucles, donc comment faire pour n'en faire qu'une ?

    J'ai penser faire 1 boucle pour créer une liste (chemin image, (x*y)), et travailler ensuite sur la liste, mais avant de me lancer, je demande votre avis ! Et en plus, travailler sur la liste => une boucle !

    Merci d'avance !

    Gram'

  2. #2
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Points : 1 384
    Points
    1 384
    Par défaut
    Je ne suis pas sûr de ce que tu entends par "poids d'une image" et "taille maxi des photos", mais ton dernier exemple (x*y) me laisse supposer qu'il s'agit du nombre de pixels. Mais dans ce cas, le calcul du taux de réduction est faux; cela devrait être t = sqrt(n/p). A moins que le "poids d'une image" ne serait que sa largeur, ou qqch comme ça ?

    Ce n'est pas le fait de faire deux boucles qui est lent, c'est plutôt ce que tu fais dans ces boucles. 3 opérations peuvent prendre du temps dans le programme que tu décris: le chargement de la photo en mémoire, la réduction de la taille et la sauvegarde du résultat. Des 3, c'est sans doute le chargement en mémoire qui est le plus lent, car cela implique des lectures disques (et la décompression vu que les images sont certainement compressées) et que les images sont plus grandes que celles que tu sauves.

    Si le programme est près de deux fois plus lent avec tes deux passes, c'est sans doute parce que tu charges les photos en mémoire pendant chaque passe; stocker un liste de chemins et de produit d'entiers n'aidera pas. Garder les photos en mémoire à la première passe nécessiterait sans doute beaucoup trop de mémoire.

    Il faudrait pouvoir déterminer le "poids des photos" (quoi que cela soit) sans être obligé de lire tout le fichier. Si c'est la résolution (nombre de pixels), c'est normalement possible, vu que tous les formats d'images bitmap stockent ces informations dans l'en-tête du fichier. Le problème est que, à ma connaissance, PIL ne permet pas d'accéder à ces infos sans charger et décompresser l'image en mémoire. Mais peut-être existe-t-il d'autres librairies qui le permettent...

  3. #3
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    222
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2007
    Messages : 222
    Points : 290
    Points
    290
    Par défaut
    Pourrais-tu mettre un bout de code.
    Je ne comprends pas bien non plus ce que tu appels "poids maxi des photos du dossier".
    Tu veux que tes thumbnails aient une taille maxi en Ko ou en pixel?
    Dans tous les cas je pense que c'est possible de faire ça dans une seul boucle.

  4. #4
    Membre éprouvé

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 116
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 116
    Points : 1 111
    Points
    1 111
    Par défaut
    Je pense que c'est vrai que le problème de performance vient du fait que tu as deux boucles. Tu ne veux pas simplement définir une taille statique pour les vignettes ? Pourquoi s'embêter à appliquer le même taux de réduction pour chaque photo ? C'est clair qu'une recherche dans un répertoire pour savoir quelle est la photo de plus haute résolution te fait gaspiller du temps. Ce n'est pas la bonne façon d'aborder le problème à mon avis (c'est trop compliqué et tâtillon).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import PIL.Image
    import sys
     
    if len(sys.argv) < 2 :
        print "USAGE : ./create_thumb.py 'image_filename_1.xxx' 'image_filename_2.xxx' ..."
        print """
    This script make thumbs of 128 by 128 from files given on the command line
    """
    else :
        for filename in sys.argv[1:]:
            splitted = filename.split('.')
            a=PIL.Image.open(filename, 'r')
            a.thumbnail( (128,128) )
            fp2 = open( splitted[0] + "_thumb." + splitted[1] , "w")
            a.save(fp2)
    Citation Envoyé par nyko77 Voir le message
    Pourrais-tu mettre un bout de code.
    Je ne comprends pas bien non plus ce que tu appels "poids maxi des photos du dossier".
    Tu veux que tes thumbnails aient une taille maxi en Ko ou en pixel?
    Dans tous les cas je pense que c'est possible de faire ça dans une seul boucle.
    Bien sûr que c'est faisable : tu appliques un certain taux de réduction (x*taux,y*taux) comme tu nous l'as montré tout à l'heure, et tu fais un test sur la résolution finale avant de créer la vignette. Si elle est trop grande, tu mets tout simplement la photo à la taille maximale permise.

  5. #5
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2007
    Messages
    44
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 44
    Points : 28
    Points
    28
    Par défaut
    Oups, beaucoup de réponses très denses : je suis à la bourre pour répondre ! Veuillez m'en excuser : je suis tout le temps en déplacement, en ce moment !

    D'abord, merci à tous trois pour vos réponses très pertinentes. Comme vous ciblez chacun à votre façon la problématique, je vais développer un peu :

    1. Le but de cette appli est donc de permettre à l'utilisateur de réduire un dossier plein de jolies photos, de façon à ce que les photos fassent un poids (en Mo) au plus de n Mo (défini par lui dans l'interface graphique). Première difficulté : os.path permet de récupérer le poids d'un fichier en Mo, mais PIL ne le gère pas (PIL ne gère, pour ce qui m'intéresse, que les pixel).

    2. La première boucle consiste donc en un balayage du contenu du dossier pour déterminer le poids de photo maximal, que je note p, et en déduire le taux de réduction pour la suite . Un code du style :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    p=0
    for fichier in glob.glob("*.jpg") :
       p=max(p,os.pathsize(fichier))
    taux=n/p
    3. La seconde boucle consiste en la réduction de chaque photo, en utilisant la fonction thumbnail de PIL :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    for fichier in glob.glob("*.jpg") :
       photo=Image.open(fichier)
       taille=photo.size
       cible=(taille[0]*taux,taille[1]*taux)
       photo.thumbnail(cible,Image.ANTIALIAS)
       photo.save(dest, "JPEG") #dest=répertoire de sortie
    4. donc pour répondre à la question de dividee, le poids est une notion de Mo, et non de pixel. Je calcule le taux de réduction à partir de la photo la plus lourde, et je l'applique aux dimensions en pixel de chaque photo.

    5. pour répondre à kromartien, la première version de mon appli utilisait un taux fixe de 126*126. J'en suis à la V3, qui permet de décider quelle poids maxi final doivent faire les photos en fin d'opération : la classe !

    6. bilan des courses avec test à l'appui, j'obtiens, pour les deux photos suivantes, et avec un n= 1 Mo :

    photo1 4694 Ko - 2592*3888
    photo2 2694 Ko- 3888*2592

    photo1bis 63 Ko - 809*539
    photo2bis 59 Ko - 539*809

    Bon, ça n'est pas du tout ce que je voulais obtenir. Je voulais en fait deux photos, maxi 1 Mo, peu importe la taille en pixel...

    Me gourerais-je complètement sur la relation poids/taille ? Sûrement !

    Encore une fois, Python est infoutu de me corriger...

    Merci d'avance !

    Gram'

  6. #6
    Membre éprouvé

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 116
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 116
    Points : 1 111
    Points
    1 111
    Par défaut
    Comme PIL ne fait pas la différence entre poids et taille (en pixels) de la photo, ce n'est pas facile.

  7. #7
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2007
    Messages
    44
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 44
    Points : 28
    Points
    28
    Par défaut
    Exact, pas facile, mais je suis teigneux. J'ai repensé l'ensemble, et me suis lancé dans une classe, histoire d'y voir plus clair...
    Avant de cracher le code, je m'explique :

    1. sachant qu'il est impossible, en JPEG, de faire un lien entre pixel et Mo (because le taux de compression, et la complexité de l'image), il faut ruser

    2. j'ai donc pris le parti, pour chaque photo, de faire une chtite boucle du type "Tant que le poids de mon fichier est supérieur à la consigne, je réduit de 1% chaque dimension en pixel, et je recalcule les Mo de la photo obtenue"

    3. Ca marche plutôt bien quand je teste sur une photo dont x < y, par contre ça me donne un poids final de consigne/2 pour les photos dont x > y !

    Je sens que ça approche sérieux ! Je m'accroche ! Un commentaire ?

    A très bientôt !

    Gram'

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
     
    #_*_ coding: iso-8859-1 _*_
    import os.path
    from PIL import Image
     
    ###définition de la classe
     
    class Photo() :
        '''classe des photos et fichiers manipulés par Pymaweb'''
        def __init__(self) :
            '''initialisation'''
            self.poids=0                          #le poids en octets
            self.taille=(0,0)                      #la taille en pixel
            self.fichier,self.nom='',''           #chemin complet et nom du fichier 
            self.photo=None                    #photo au sens PIL
     
        def recupFichier(self) :
            '''récupère le chemin du fichier à transformer'''
            self.fichier=fichier
     
        def recupPoids(self) :
            '''récupère le poids du fichier en octets'''
            self.poids=os.path.getsize(self.fichier)
     
        def recupPhoto(self) :
            '''crée un objet photo au sens PIL'''
            self.photo=Image.open(self.fichier)
     
        def recupTaille(self) :
            '''récupère les pixel de la photo'''
            self.taille=self.photo.size
     
        def recupNom(self) :
            '''récupère le nom du fichier'''
            self.nom=os.path.basename(self.fichier)
     
        def reduitTaille(self) :
            '''calcule un taux de réduction égal à 1% des dimensions pixel x et y'''
            self.taille=(self.taille[0]*0.99,self.taille[1]*0.99)
     
        def reduitPhoto(self) :
            '''réduit la photo suivant un taux défini à partir de la taille initiale'''
            self.photo.resize(self.taille,Image.ANTIALIAS)
     
        def changeFichier(self) :
            '''modifie le chemin du fichier'''
            self.fichier=os.path.join(out,self.nom)
     
        def sauvePhoto(self) :
            '''sauvegarde la photo dans le sous dossier web - ATTENTION : fichier est changeFichier(self) !'''
            self.photo.save(self.fichier,"JPEG")
     
     
    ###programme principal
     
    courant=os.getcwd()
    if not os.path.isdir('web') :
            os.mkdir('web')
    else :
        pass
     
    out='web'
    fichier='photo.jpg' #essai sur une seul photo
     
    essai=Photo()
    essai.recupFichier()
    essai.recupPoids()
    essai.recupPhoto()
    essai.recupTaille()
    essai.recupNom()
     
    while essai.poids > 1000000 : #1 Mo est la consigne pour cet essai
        essai.reduitTaille()
        essai.reduitPhoto()
        essai.changeFichier()
        essai.sauvePhoto()
        essai.recupPoids()
     
    print "OK !"

  8. #8
    Membre éprouvé

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 116
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 116
    Points : 1 111
    Points
    1 111
    Par défaut
    je dirai simplement que ce n'est pas très pythonique ...

  9. #9
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2007
    Messages
    44
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 44
    Points : 28
    Points
    28
    Par défaut
    Ah, ça c'est une remarque qui m'interpelle, parce que justement, je suis aussi en recherche de l'esprit python. J'ai eu l'impression que faire une classe de ce type, justement, c'est très python. Mais bon, je suppose que tu as raison : en quoi mon code n'est-il pas dans l'esprit python, stp ?

    A+

    Gram'

  10. #10
    Membre éprouvé

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 116
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 116
    Points : 1 111
    Points
    1 111
    Par défaut
    Je voulais parler de la concision, mais bon... Tout est subjectif dans ce que j'ai dit.

    En fait, je ne suis juste pas certain que ce soit une fonctionnalité

    1) Qui soit réellement utile
    2) Qui soit réellement codable simplement et efficacement

    D'où le "ce n'est pas très pythonique", où comment limiter un code au moindre effort, ou comment éviter au programmeur de se mettre en quatre.

    Sinon faire une classe pour gérer l'image en fonction de sa taille et de son poids, ce n'est pas mal, mais j'ai juste l'impression que c'est une heuristique compliquée pour pas grand chose.

    Comme tu sembles très motivé, j'espère que tu trouveras une solution simple, mais je suis sûrement mal placé pour donner mon avis de toute façon.

    Je voulais juste t'éviter de souffrir pour quelque chose qui n'en vaut peut être pas la peine .

    Bon courage néanmoins.

  11. #11
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2007
    Messages
    44
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 44
    Points : 28
    Points
    28
    Par défaut
    Tout à fait en phase avec toi, kromartien ! Je suis de plus en plus motivé par Python, et de moins en moins par mon appli, qui, effectivement, ne tombe pas suffisamment sous le sens. Donc n'est pas bien conçue...

    Je pense que PIL n'est pas la meilleure solution, et que je ferais mieux de faire un GUI abrégé de ImageMagick, idée que j'avais eue au départ, et qu'une violente vague de haute estime de moi-même m'avait fait abandonner au profit d'un "je vais tout faire moi-même"... Où est passé mon instinct de feignant primaire ?

    Cela dit, j'ai appris plein de choses à me casser la tête sur ce pb, et je remercie tous les forumeurs de bonne humeur pour leur chaleureuse participation !

    A bientôt, ça c'est sûr !

    Gram'

    PS : comme je suis hargneux, je vais trouver une solution un jour, que je posterai pour clore ce topic...

Discussions similaires

  1. Savoir le temps passé sur une page avant de passer vers une autre page
    Par karimphp dans le forum Général JavaScript
    Réponses: 1
    Dernier message: 04/09/2007, 23h57
  2. Calcul temps passé sur une page PHP pur
    Par mathieugamin dans le forum Langage
    Réponses: 10
    Dernier message: 29/03/2007, 17h22
  3. ouverture d'un formulaire sur une double condition
    Par mat75019 dans le forum Access
    Réponses: 6
    Dernier message: 27/10/2006, 16h13
  4. Temps passé sur une page !!
    Par nizarsm dans le forum ASP
    Réponses: 4
    Dernier message: 30/06/2006, 21h42
  5. Afficher le "temps restant" sur une longue action
    Par illuzmax dans le forum Langage
    Réponses: 2
    Dernier message: 04/07/2005, 10h23

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo