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

Python Discussion :

Rogner "automatiquement" une image


Sujet :

Python

  1. #1
    Membre éprouvé

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Par défaut Rogner "automatiquement" une image
    Bonjour,
    j'ai une image avec un fond blanc mais il y a du déchet. Je voudrais trouver le rectangle minimal pour avoir l'image.

    Mon idée est la suivante :
    1. On balaye l'image colonne par colonne à partitir de la gauche pour rechercher un pixel non blanc. Une fois trouvé, on a la côté gauche du rectangle minimal.
    2. On fait de même mais en partant de la droite pour avoir le côté droit du rectangle minimal.
    3. Pour finir, on travaille de façon analogue verticalement.


    Question : comment faire cela via Python ?

  2. #2
    Membre Expert
    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
    Par défaut
    Vite fait, en utilisant la libraire PIL:
    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
    import Image
     
    im = Image.open("test.png")
    pix = im.load()
    width, height = im.size
     
    def product(A,B):
        # existe déjà dans itertools en Python 3.x
        return ((x,y) for x in A for y in B)
     
    for (left,y) in product(xrange(width), xrange(height)):
        if pix[left,y] != (255,255,255): break
     
    for (right,y) in product(xrange(width-1,0,-1), xrange(height)):
        if pix[right,y] != (255,255,255): break
     
    for (top,x) in product(xrange(height), xrange(width)):
        if pix[x,top] != (255,255,255): break
     
    for (bottom,x) in product(xrange(height-1,0,1), xrange(width)):
        if pix[x,bottom] != (255,255,255): break
     
    im = im.crop((left,top,right+1,bottom+1))
    im.save("test-cropped.png")

  3. #3
    Membre éprouvé

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Par défaut
    Merci. J'ai fait ceci, très rapidement et maladroitement, à partir de ton code (qui ne marchait pas chez moi) :
    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
    #! /usr/bin/env python
    #coding=utf-8
     
    # SOURCE : http://www.developpez.net/forums/d857722/autres-langages/python-zope/general-python/rogner-automatiquement-image/
    import Image
     
    im = Image.open("test.png")
    pix = im.load()
    width, height = im.size
     
    left,top,right, bottom =-1, -1, -1, -1
     
    for x in xrange(width):
        for y in xrange(height):
            if pix[x,y][:3] != (255,255,255):  # Canal alpha ATTENTION!!  (255,255,255,...)
                left= x
                break
        if left >=0:
            break
     
    for x in xrange(width-1,left,-1):
        for y in xrange(height):
            if pix[x,y][:3] != (255,255,255):
                right = x
                break
        if right >=0:
            break
     
    for y in xrange(height):
        for x in xrange(width):
            if pix[x,y][:3] != (255,255,255):
                top = y
                break
        if top >=0:
            break
     
     
    for y in xrange(height-1,top,-1):
        for x in xrange(width):
            if pix[x,y][:3] != (255,255,255):
                bottom = y
                break
        if bottom >=0:
            break
     
    im = im.crop((left,top,right+1,bottom+1))
    im.save("test-cropped.png")
    Dernière petite question :
    une fois l'image "cropée" je voudrais ajouter un cadre blanc de largeur fixée L.

    Mon idée serait de créer une image blanche de taille celle de l'image rôgnée plus 2L et ensuite d'y mettre l'image rôgnée à la position (L, L). Comment faire ?

    J'ai ouvert un nouveau post sur ce sujet ici.

  4. #4
    Membre Expert
    Homme Profil pro
    Inscrit en
    Avril 2004
    Messages
    1 068
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 1 068
    Par défaut
    avec une autre lib
    crop l'image et applique la bordure
    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
    import pygame
     
    def crop_and_border(img,border_size=0,border_color=(255,255,255)):
    # retourne une surface
    	img=pygame.image.load(img)
    	x=y=0
    	x1,y1=img.get_size()
    	pixarray=pygame.PixelArray(img).extract(0xffffff,0.2) 
    # les arguments de extract indiquent la couleur a virer et la tolerence
    	while not 0 in pixarray[x]:x+=1
    	while not 0 in pixarray[x1-1]:x1-=1
    	pixarray=[[i[j] for i in pixarray]for j in range(y1)]
    	while not 0 in pixarray[y]:y+=1
    	while not 0 in pixarray[y1-1]:y1-=1
    	del(pixarray)
    	s=pygame.Surface((x1-x+border_size*2,y1-y+border_size*2))
    	s.fill(border_color)
    	s.blit(img,(border_size,border_size),(x,y,x1-x,y1-y))
    	return s
     
    new_pic = crop_and_border('mon_image.jpg',30)
    pygame.image.save(new_pic,'nouvelle_image.jpg')

  5. #5
    Membre éprouvé

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Par défaut
    Merci pour cette seconde solution.

  6. #6
    Membre Expert
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Par défaut
    Le code de josmiley est trés bien, y compris pour insérer l’image rognée au milieu d’un cadre, comme l'autre code proposé dans une autre file.

    Je trouve le paramètre tolérance de PixelArray(img).extract(couleur,tolerance) intéressant; existe-t-il la possibilité de faire la même chose avec PIL ?



    Cependant je ne comprends pas la ligne
    pixarray=[[i[j] for i in pixarray]for j in range(y1)]
    Je ne sais pas quelle est la nature exacte de pixarray. Un tableau à deux dimensions ?





    En restant dans le cadre de PIL, ton code est simplifiable,rambc:
    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
    #! /usr/bin/env python
    #coding=utf-8
    # SOURCE : http://www.developpez.net/forums/d857722/autres-langages/python-zope/general-python/rogner-automatiquement-image/
    import Image
     
    adresse = "planeOfAndWithHoles.png"
    im = Image.open(adresse)
    pix = im.load()
    width, height = im.size
    print "Dimensions de l'image  "+adresse+' :'\
          +'\n\theight:\n\t '+str(height)+'\t\twidth: '+str(width)
     
    for left in xrange(width):
        if any( pix[left,y][:3] != (255,255,255) for y in xrange(height)):  break
     
    for right in xrange(width-1,left,-1):
        if any( pix[right,y][:3] != (255,255,255) for y in xrange(height)):  break
     
    for top in xrange(height):
        if any( pix[x,top][:3] != (255,255,255) for x in xrange(width)):  break
     
    for bottom in xrange(height-1,top,-1):
        if any( pix[x,bottom][:3] != (255,255,255) for x in xrange(width)):  break
     
    print "\n\nRectangle au-dela duquel il n'y a que des pixels blancs :"\
          +'\n\ttop\tleft ~~~~~~~~~~~~~~~~~'\
          +'\n\t'+str(top).ljust(6)+'\t'+str(left).ljust(6)+'\tbottom\t right'\
          +'\n\t~~~~~~~~~~~~~~~~'+str(bottom).rjust(6)+'\t'+str(right).rjust(6)
    Ce qui donne
    Dimensions de l'image planeOfAndWithHoles.png :
    .............height:
    ..............324....................width: 432


    Rectangle au-dela duquel il n'y a que des pixels blancs :
    .............top........left....~~~~~~~~~~~~~~~~~
    .............36.........14......................bottom.......right
    .............~~~~~~~~~~~~~~~~...287...........417


    C’est selon un principe similaire à celui du code de josmiley, mais je préfère une boucle for à un while pour ne pas voir à initialiser les compteurs.

  7. #7
    Membre Expert
    Homme Profil pro
    Inscrit en
    Avril 2004
    Messages
    1 068
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 1 068
    Par défaut
    Citation Envoyé par eyquem Voir le message
    Cependant je ne comprends pas la ligne
    pixarray=[[i[j] for i in pixarray]for j in range(y1)]
    Je ne sais pas quelle est la nature exacte de pixarray. Un tableau à deux dimensions ?
    oui, c'est un tableau 2d filtré par extract(); j'ai trouvé plus simple de le "translater" afin d'utiliser la structure "while x in y" plutôt que d'utiliser des "for/in/if".
    d'ailleur, si vous connaissez une fonction qui fait ça plus proprement syntaxiquement parlant ...

    pour les anglophones:
    PixelArray.extract

    Extracts the passed color from the PixelArray.
    PixelArray.extract (color, distance=0, weights=(0.299, 0.587, 0.114)): Return PixelArray

    Extracts the passed color by changing all matching pixels to white, while non-matching pixels are changed to black. This returns a new PixelArray with the black/white color mask.

    It uses a simple weighted euclidian distance formula to calculate the distance between the colors. The distance space ranges from 0.0 to 1.0 and is used as threshold for the color detection. This causes the extraction to take pixels with a similar, but not exactly identical color, into account as well.
    après quelques essais, je me suis aperçu qu'il était difficile, sauf pitêtre pour les .bmp, d'avoir un cadre parfaitement blanc après compression ce qui rend l'argument "distance" vraiment intéressant.

    Par contre je ne connaissais pas any(), voilà qui va sûrement me simplifier la vie

  8. #8
    Membre éprouvé

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Par défaut
    Citation Envoyé par josmiley Voir le message
    Par contre je ne connaissais pas any(), voilà qui va sûrement me simplifier la vie
    +1
    Merci pour ton complément, eyquem.

    Je préfère la solution juste avec PIL car il est d'office avec Python.

  9. #9
    Membre éprouvé

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Par défaut
    Une question vite faite sur any. Peut-on faire quelque chose comme suit où foncTest renvoie un booléen :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if any( foncTest(x) for x in xrange(width)):  break
    Je dirais oui. Ai-je tors ?

  10. #10
    Membre Expert
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Par défaut
    Tu as tort d’écrire ’tors’
    mais tu as raison avec ta fonction foncTest
    Cependant, dans le cas de any() , la fonction peut renvoyer autre chose que des booléens (même s’il faut bien qu’elle renvoie au moins un booléen de temps à autre pour que l’expression ait un sens).



    all() et any() consomment des itérables
    Les itérables peuvent être des listes, des tuples, des dictionnaires, des générateurs, n’importe quoi dans lequel on peut itérer.
    Par exemple all({True:'234'}) est True (je ne sais pas à quoi peut bien servir un tel dictionnaire soit dit en passant).



    Il faut penser à all() comme “True si all True dans l’itérable“ et à any() comme “True si any True dans l’itérable“


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    print 'all([True,True,True,True]) =',\
          all([True,True,True,True])
    print '\nall([True,True,True,None]) =',\
          all([True,True,True,None])
    print '\nall([True,False,True,True]) =',\
          all([True,False,True,True])
     
     
    print '\n\nany([False,False,False,False]) =',\
          any([False,False,False,False])
    print '\nany([False,False,None,False]) =',\
          any([False,False,None,False])
    print '\nany([False,True,None,False]) =',\
          any([False,True,None,False])
    all([True,True,True,True]) = True

    all([True,True,True,None]) = False

    all([True,False,True,True]) = False


    any([False,False,False,False]) = False

    any([False,False,None,False]) = False

    any([False,True,None,False]) = True


    Mais “all True dans l’itérable“ , cela veut dire que l’itérable doit être une collection de True,True,True,True,True.....



    Tandis que pour que any(iterable) soit True, il suffit qu’il y ait UN True dans l’iterable et TOUS LES AUTRES ÉLÉMENTS peuvent être N’IMPORTE QUOI:



    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class Klass:
        i = 89
     
    def func(x):
        return 2*x
     
    print "\nany([34,func,True,None,('a','b'),False,Klass]) =",\
          any([34,func,True,None,('a','b'),False,Klass])

    any([34,func,True,None,('a','b'),False,Klass]) = True
    Voila le rapport avec la question.






    Nota Bene:

    L’itérable peut être un générateur:
    print any(( x in xrange(10,100,10) for x in xrange(100) if x%6==0))

    Dans ce cas, on peut se dipsenser de mettre deux parenthèses à chaque bout, c’est une disposition du langage, pas un truc exotique.

    print any( x in xrange(10,100,10) for x in xrange(100) if x%6==0)
    Cette forme est bien celle d’un générateur soumis à la fonction any()

  11. #11
    Membre éprouvé

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Par défaut
    Citation Envoyé par eyquem Voir le message
    Tu as tort d’écrire ’tors’
    Au temps pour moi...

    Citation Envoyé par eyquem Voir le message
    Les itérables peuvent être des listes, des tuples, des dictionnaires, des générateurs, n’importe quoi dans lequel on peut itérer.
    Par exemple all({True:'234'}) est True (je ne sais pas à quoi peut bien servir un tel dictionnaire soit dit en passant).
    Peut-être à estimer la valeur marchande de la vérité...


    Sinon merci pour tes explications toujours aussi détaillées.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Taille automatique d'une image en background
    Par shevalo dans le forum Mise en page CSS
    Réponses: 5
    Dernier message: 20/11/2010, 11h53
  2. changement automatique d'une image et d'un texte
    Par apfwl.87 dans le forum Word
    Réponses: 1
    Dernier message: 28/05/2009, 11h49
  3. redimensionement automatique d'une image
    Par biozaxx dans le forum AWT/Swing
    Réponses: 7
    Dernier message: 17/10/2007, 17h24

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