Codificación de un archivo de imagen con base64

161

Quiero codificar una imagen en una cadena usando el módulo base64. Sin embargo, me he encontrado con un problema. ¿Cómo especifico la imagen que quiero codificar? Intenté usar el directorio para la imagen, pero eso simplemente conduce a que el directorio se codifique. Quiero que el archivo de imagen real esté codificado.

EDITAR

Probé este fragmento:

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))

pero me sale el siguiente error:

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é estoy haciendo mal?

rectángulo
fuente

Respuestas:

299

No estoy seguro de entender tu pregunta. Supongo que está haciendo algo en la línea de:

import base64

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

Primero debe abrir el archivo y leer su contenido; no puede simplemente pasar la ruta a la función de codificación.

Editar: Ok, aquí hay una actualización después de que haya editado su pregunta original.

En primer lugar, recuerde usar cadenas sin formato (prefija la cadena con 'r') cuando use delimitadores de ruta en Windows, para evitar golpear accidentalmente un carácter de escape. En segundo lugar, Image.open de PIL acepta un nombre de archivo o un archivo (es decir, el objeto tiene que proporcionar métodos de lectura, búsqueda y lectura).

Dicho esto, puede usar cStringIO para crear dicho objeto desde un búfer de memoria:

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
fuente
1
Gracias, un problema más cuando imprimo la imagen decodificada me sale la cadena 'ÿØÿà'. Sin embargo, cuando ejecuto esto solo como un sustituto de los datos me sale un error. La cadena codificada es mucho más larga para la comparación. Así que creo que probablemente almacena los datos de la imagen. ¿la cadena decodificada simplemente hace referencia a la cadena codificada o algo así? Parece demasiado corto para el almacenamiento de datos.
rectangletangle
El resultado impreso no es necesariamente igual al contenido real; depende de cómo y dónde lo imprima.
Jim Brissom
8
En mi caso, necesito decodificar: base64.b64encode(fh.read()).decode()para obtener una cadena para usar en archivos html.
qed
1
base64.b64encode (fh.read ()). decode () es sutil pero también necesitaba esto @qed, gracias. La diferencia es que uno devuelve bytes y otra cadena ... ¡y mi servidor SOAP simplemente no lo tragaría sin decodificar!
zzart
57

Con python 2.x, puede codificar trivialmente usando .encode:

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

La primera respuesta imprimirá una cadena con el prefijo b '. Eso significa que su cadena será así b'your_string 'Para resolver este problema, agregue la siguiente línea de código.

encoded_string= base64.b64encode(img_file.read())
print(encoded_string.decode('utf-8'))
CodeSpeedy
fuente
2
Esta parece una buena respuesta, pero no publiques si el propósito es promocionar tu sitio web. Sin embargo, puedes agregar enlaces a tu perfil.
halfer
1
(Por cierto, orden de las respuestas no se puede confiar en que aquí, por lo que vale la pena evitar comentarios como "la primera respuesta" El que aparece por primera vez pueden cambiar con el tiempo.. :-))
halfer
En su versión original de esta respuesta, parece que está vinculado a su propio sitio o al sitio al que está afiliado. Si hace un enlace a dicho sitio, debe revelar que es su sitio . Si no divulga la afiliación, se considera spam. Ver: ¿Qué significa "buena" autopromoción? y el centro de ayuda sobre autopromoción . La divulgación debe ser explícita, pero no tiene que ser formal. Cuando es su propio contenido personal , puede ser algo así como "en mi sitio ...", "en mi blog ...", etc.
Makyen
Gracias por la sugerencia @Makyen. Voy a revelar que es mi sitio. ¿Será legal editar la respuesta ahora para revelar que es mi sitio? O no debería editarlo.
CodeSpeedy
9

Como dije en su pregunta anterior, no hay necesidad de codificar en base64 la cadena, solo hará que el programa sea más lento. Solo usa la 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))
...

Ahora la imagen se almacena como una variable llamada image_dataen un archivo llamado image.py Iniciar un nuevo intérprete e importar el image_data

>>> from image import image_data
>>>
John La Rooy
fuente
Realmente no veo cómo repr () puede ser útil aquí.
Ivo van der Wijk
66
@Ivo, Anteater quiere poder almacenar imágenes en archivos python. Estoy señalando que usar base64 es contraproducente porque los datos deben decodificarse cada vez que se carga el módulo. El uso de repr en su lugar significa que la cadena literal se almacena lista para su uso inmediato en el archivo .pyc sin procesamiento adicional
John La Rooy
7

Tomando en cuenta lo que Ivo van der Wijk y gnibbler han desarrollado anteriormente, esta es una solución dinámica

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')

Luego puede decidir compilar el archivo de imagen de salida con Cython para que sea genial. Con este método, puede agrupar todos sus gráficos en un módulo.

iChux
fuente
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')
Soluciones de software DooDle
fuente
1
esta respuesta realmente debería estar en la parte superior ... la mejor, ¡gracias!
Luther