Encodage d'un fichier image avec base64

161

Je veux encoder une image dans une chaîne à l'aide du module base64. J'ai cependant rencontré un problème. Comment spécifier l'image que je souhaite encoder? J'ai essayé d'utiliser le répertoire sur l'image, mais cela conduit simplement à encoder le répertoire. Je veux que le fichier image réel soit encodé.

ÉDITER

J'ai essayé cet extrait:

with open("C:\Python26\seriph1.BMP", "rb") as f:
    data12 = f.read()
    UU = data12.encode("base64")
    UUU = base64.b64decode(UU)

    print UUU

    self.image = ImageTk.PhotoImage(Image.open(UUU))

mais j'obtiens l'erreur suivante:

Traceback (most recent call last):
  File "<string>", line 245, in run_nodebug
  File "C:\Python26\GUI1.2.9.py", line 473, in <module>
    app = simpleapp_tk(None)
  File "C:\Python26\GUI1.2.9.py", line 14, in __init__
    self.initialize()
  File "C:\Python26\GUI1.2.9.py", line 431, in initialize
    self.image = ImageTk.PhotoImage(Image.open(UUU))
  File "C:\Python26\lib\site-packages\PIL\Image.py", line 1952, in open
    fp = __builtin__.open(fp, "rb")
TypeError: file() argument 1 must be encoded string without NULL bytes, not str

Qu'est-ce que je fais mal?

rectangle
la source

Réponses:

299

Je ne suis pas sûr de comprendre votre question. Je suppose que vous faites quelque chose du genre:

import base64

with open("yourfile.ext", "rb") as image_file:
    encoded_string = base64.b64encode(image_file.read())

Vous devez d'abord ouvrir le fichier bien sûr, et lire son contenu - vous ne pouvez pas simplement passer le chemin vers la fonction d'encodage.

Modifier: OK, voici une mise à jour après avoir modifié votre question d'origine.

Tout d'abord, n'oubliez pas d'utiliser des chaînes brutes (préfixez la chaîne avec 'r') lorsque vous utilisez des délimiteurs de chemin sous Windows, pour éviter de frapper accidentellement un caractère d'échappement. Deuxièmement, Image.open de PIL accepte soit un nom de fichier, soit un fichier semblable (c'est-à-dire que l'objet doit fournir des méthodes de lecture, de recherche et de notification).

Cela étant dit, vous pouvez utiliser cStringIO pour créer un tel objet à partir d'une mémoire tampon:

import cStringIO
import PIL.Image

# assume data contains your decoded image
file_like = cStringIO.StringIO(data)

img = PIL.Image.open(file_like)
img.show()
Jim Brissom
la source
1
Merci, encore un problème lorsque j'imprime l'image décodée, j'obtiens la chaîne 'ÿØÿà'. Cependant, lorsque je l'exécute seul en remplacement des données, j'obtiens une erreur. La chaîne codée est beaucoup plus longue pour la comparaison. Je pense donc que cela stocke probablement les données d'image. la chaîne décodée fait-elle simplement référence à la chaîne encodée ou quelque chose? Cela semble bien trop court pour le stockage des données.
rectangletangle
La sortie imprimée n'est pas nécessairement égale au contenu réel - cela dépend de la manière et de l'endroit où vous l'imprimez.
Jim Brissom
8
Dans mon cas, j'ai besoin de décoder: base64.b64encode(fh.read()).decode()pour obtenir une chaîne à utiliser dans les fichiers html.
qed
1
base64.b64encode (fh.read ()). decode () est subtile mais j'en avais aussi besoin @qed, merci. La différence est que l'on renvoie des octets et une autre chaîne ... et mon serveur SOAP ne l'avalerait tout simplement pas sans décodage!
zzart
57

Avec python 2.x, vous pouvez facilement encoder en utilisant .encode:

with open("path/to/file.png", "rb") as f:
    data = f.read()
    print data.encode("base64")
Ivo van der Wijk
la source
12

La première réponse affichera une chaîne avec le préfixe b '. Cela signifie que votre chaîne sera comme ceci b'your_string 'Pour résoudre ce problème, veuillez ajouter la ligne de code suivante.

encoded_string= base64.b64encode(img_file.read())
print(encoded_string.decode('utf-8'))
CodeSpeedy
la source
2
Cela semble être une bonne réponse, mais veuillez vous abstenir de publier si le but est de promouvoir votre site Web. Vous pouvez cependant ajouter des liens dans votre profil.
halfer
1
(Incidemment, on ne peut pas se fier à l'ordre des réponses ici, il vaut donc la peine d'éviter les commentaires comme "la première réponse". Celui qui apparaît en premier peut changer avec le temps. :-))
Halfer
Dans votre version originale de cette réponse, il semble que vous soyez lié à votre propre site ou à un site auquel vous êtes affilié. Si vous créez un lien vers un tel site, vous devez déclarer qu'il s'agit de votre site . Si vous ne divulguez pas votre affiliation, c'est considéré comme du spam. Voir: Que signifie une «bonne» auto-promotion? et le centre d'aide sur l'auto-promotion . La divulgation doit être explicite, mais n'a pas besoin d'être formelle. Quand c'est votre proprecontenu personnel , cela peut être quelque chose comme «sur mon site…», «sur mon blog…», etc.
Makyen
Merci pour la suggestion @Makyen, je vais divulguer que c'est mon site. Sera-t-il légal de modifier la réponse maintenant pour révéler qu'il s'agit de mon site? Ou je ne devrais pas le modifier.
CodeSpeedy
9

Comme je l'ai dit dans votre question précédente, il n'est pas nécessaire d'encoder la chaîne en base64, cela ne fera que ralentir le programme. Utilisez simplement le repr

>>> with open("images/image.gif", "rb") as fin:
...  image_data=fin.read()
...
>>> with open("image.py","wb") as fout:
...  fout.write("image_data="+repr(image_data))
...

Maintenant, l'image est stockée sous forme de variable appelée image_data dans un fichier appelé image.py Démarrer un nouvel interpréteur et importer l'image_data

>>> from image import image_data
>>>
John La Rooy
la source
Je ne vois pas vraiment comment repr () peut être utile ici.
Ivo van der Wijk
6
@Ivo, Anteater veut pouvoir stocker des images dans des fichiers python. Je souligne que l'utilisation de base64 est contre-productive car les données doivent être décodées à chaque fois que le module est chargé. Utiliser repr à la place signifie que la chaîne littérale est stockée prête pour une utilisation immédiate dans le fichier .pyc sans traitement furthur
John La Rooy
7

Empruntant à ce qu'Ivo van der Wijk et gnibbler ont développé précédemment, il s'agit d'une solution dynamique

import cStringIO
import PIL.Image

image_data = None

def imagetopy(image, output_file):
    with open(image, 'rb') as fin:
        image_data = fin.read()

    with open(output_file, 'w') as fout:
        fout.write('image_data = '+ repr(image_data))

def pytoimage(pyfile):
    pymodule = __import__(pyfile)
    img = PIL.Image.open(cStringIO.StringIO(pymodule.image_data))
    img.show()

if __name__ == '__main__':
    imagetopy('spot.png', 'wishes.py')
    pytoimage('wishes')

Vous pouvez ensuite décider de compiler le fichier image de sortie avec Cython pour le rendre cool. Avec cette méthode, vous pouvez regrouper tous vos graphiques dans un seul module.

iChux
la source
7
import base64
from PIL import Image
from io import BytesIO

with open("image.jpg", "rb") as image_file:
    data = base64.b64encode(image_file.read())

im = Image.open(BytesIO(base64.b64decode(data)))
im.save('image1.png', 'PNG')
Solutions logicielles DooDle
la source
1
cette réponse devrait vraiment être au sommet ... la meilleure - merci!
Luther