Kodowanie pliku obrazu przy użyciu base64

161

Chcę zakodować obraz w ciągu za pomocą modułu base64. Jednak napotkałem problem. Jak określić obraz, który chcę zakodować? Próbowałem użyć katalogu do obrazu, ale to po prostu prowadzi do zakodowania katalogu. Chcę, aby rzeczywisty plik obrazu był zakodowany.

EDYTOWAĆ

Wypróbowałem ten fragment:

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

ale pojawia się następujący błąd:

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

Co ja robię źle?

prostokąt
źródło

Odpowiedzi:

299

Nie jestem pewien, czy rozumiem twoje pytanie. Zakładam, że robisz coś w stylu:

import base64

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

Musisz oczywiście najpierw otworzyć plik i przeczytać jego zawartość - nie możesz po prostu podać ścieżki do funkcji kodowania.

Edycja: OK, oto aktualizacja po edycji oryginalnego pytania.

Przede wszystkim pamiętaj, aby używać nieprzetworzonych ciągów (poprzedzić je znakiem „r”) podczas używania ograniczników ścieżek w systemie Windows, aby zapobiec przypadkowemu naciśnięciu znaku ucieczki. Po drugie, Image.open w PIL'u akceptuje nazwę pliku lub plik podobny (to znaczy obiekt musi zapewniać metody odczytu, wyszukiwania i informowania).

Mając to na uwadze, możesz użyć cStringIO do stworzenia takiego obiektu z bufora pamięci:

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
źródło
1
Dzięki, jeszcze jeden problem, kiedy drukuję zdekodowany obraz, pojawia się ciąg „ÿØÿà”. Jednak gdy uruchamiam to samodzielnie jako substytut danych, pojawia się błąd. Zakodowany ciąg jest znacznie dłuższy dla porównania. Więc myślę, że prawdopodobnie przechowuje dane obrazu. czy zdekodowany ciąg po prostu odnosi się do zakodowanego ciągu czy coś? Wydaje się, że jest zbyt krótki do przechowywania danych.
prostokąt
Wydruk niekoniecznie odpowiada rzeczywistej zawartości - zależy to od tego, jak i gdzie je drukujesz.
Jim Brissom,
8
W moim przypadku muszę zdekodować: base64.b64encode(fh.read()).decode()aby uzyskać ciąg znaków do wykorzystania w plikach html.
okazania
1
base64.b64encode (fh.read ()). decode () jest subtelny, ale potrzebowałem tego również @qed, dzięki. Różnica polega na tym, że jeden zwraca bajty, a drugi ciąg ... a mój serwer SOAP po prostu nie połknąłby tego bez dekodowania!
zzart
57

W Pythonie 2.x możesz w trywialny sposób kodować używając .encode:

with open("path/to/file.png", "rb") as f:
    data = f.read()
    print data.encode("base64")
Ivo van der Wijk
źródło
12

Pierwsza odpowiedź wypisze łańcuch z prefiksem b '. Oznacza to, że Twój ciąg będzie wyglądał następująco: b'your_string 'Aby rozwiązać ten problem, dodaj następujący wiersz kodu.

encoded_string= base64.b64encode(img_file.read())
print(encoded_string.decode('utf-8'))
CodeSpeedy
źródło
2
Wygląda na to, że to dobra odpowiedź, ale nie publikuj, jeśli celem jest promocja Twojej witryny. Możesz jednak dodawać linki do swojego profilu.
halfer
1
(Nawiasem mówiąc, nie można tu polegać na kolejności odpowiedzi, dlatego warto unikać komentarzy typu „pierwsza odpowiedź”. Ta, która pojawi się jako pierwsza, może się zmienić z czasem. :-))
halfer
W oryginalnej wersji tej odpowiedzi wygląda na to, że utworzyłeś link do własnej witryny lub witryny, z którą jesteś powiązany. Jeśli umieścisz link do takiej witryny, musisz ujawnić, że jest to Twoja witryna . Jeśli nie ujawnisz przynależności, zostanie to uznane za spam. Zobacz: Co oznacza „dobrą” autopromocję? oraz centrum pomocy dotyczące autopromocji . Ujawnienie musi być jawne, ale nie musi być formalne. Jeśli jest to Twoja osobista treść, może to być po prostu coś w rodzaju „na mojej stronie…”, „na moim blogu…” itp.
Makyen
Dzięki za sugestię @Makyen ujawnię, że to moja witryna. Czy będzie teraz legalna edycja odpowiedzi w celu ujawnienia, że ​​to moja witryna? Albo nie powinienem go edytować.
CodeSpeedy
9

Jak powiedziałem w poprzednim pytaniu, nie ma potrzeby kodowania łańcucha przy użyciu base64, spowoduje to jedynie spowolnienie programu. Po prostu użyj 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))
...

Teraz obraz jest przechowywany jako zmienna wywołana image_dataw pliku o nazwie image.py Start a fresh interpreter i zaimportuj image_data

>>> from image import image_data
>>>
John La Rooy
źródło
Naprawdę nie rozumiem, jak repr () może się tutaj przydać.
Ivo van der Wijk
6
@Ivo, Anteater chce mieć możliwość przechowywania obrazów w plikach Pythona. Zwracam uwagę, że używanie base64 jest nieproduktywne, ponieważ dane muszą być dekodowane za każdym razem, gdy moduł jest ładowany. Użycie repr zamiast tego oznacza, że ​​dosłowny ciąg jest przechowywany jako gotowy do natychmiastowego użycia w pliku .pyc bez przetwarzania furthur
John La Rooy
7

Pożyczanie od tego, co van der Wijk Ivo i gnibbler opracowali wcześniej, jest to rozwiązanie dynamiczne

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

Następnie możesz zdecydować o skompilowaniu wyjściowego pliku obrazu za pomocą Cythona, aby był fajny. Dzięki tej metodzie możesz połączyć całą swoją grafikę w jeden moduł.

iChux
źródło
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')

Rozwiązania programowe DooDle
źródło
1
ta odpowiedź naprawdę powinna być na szczycie ... najlepsza - dzięki!
Luther