4
votes

I have a peculiar problem at the intersection of the EMF image format, the python PIL (as well as Pillow) image libraries, and the Pyinstaller program for packaging Python into a Windows executable.

I have a script that uses PIL/Pillow to convert an EMF file to JPEG. This works correctly when I run the python script in python. However, when I package it into an EXE using Pyinstaller.exe -F, it does not work.

With the Pillow version, I get a simple error saying

"Cannot convert image1.emf".

With the PIL version, I get a longer message that says:

Traceback (most recent call last): File "", line 38, in File "", line 27, in convertImageFile File "C:\Embibe\Git\content-ingestion\src\build\convertImage\out00-PYZ.pyz\PIL .Image", line 2126, in open IOError: cannot identify image file 'image1.emf'

Has anyone else encountered this and found a working solution?

The gory details follow, if you need them... :-)

OS: Windows 7 64-bit (but all software is 32 bit)

Software:: Python:2.7.5, Pyinstaller:2.1, PIL:built-in with Python, Pillow: 2.4.0

Python script convImg.py:

from __future__ import print_function
import os, sys
from PIL import Image

for infile in sys.argv[1:]:
    f, e = os.path.splitext(infile)
    outfile = f + ".jpg"
    if infile != outfile:
        try:
            Image.open(infile).convert('RGB').save(outfile)
        except IOError:
            print("cannot convert", infile)

run as: convImg.py image1.emf works correctly and generates image1.jpg.

When packaged to exe using \python27\scripts\pyinstaller.exe -F convImg.py and run as convImg.exe image1 gives the errors listed above for the Pillow and PIL versions.

I found a related post here, Pyinstaller troubles with Pillow but the solution for it, namely using py2app instead of pyinstaller is not an option for me since that is for MacOS and I need Windows. I considered using similar alternatives for windows, py2exe and cx_freeze, but they do not create a single self-contained exe like pyinstaller does.

Thanks, Amit

1
Update: I found another question here that may be relevant, will need to understand it in detail and try it out: stackoverflow.com/questions/10453858/… - AmitRao
Update: Another possible clue here in the py2exe documentation: py2exe.org/index.cgi/py2exeAndPIL - AmitRao
Welcome to SO. Way to ask a fantastic question and also do research after asking! +1. The link in your second comment is what I was just about to suggest - it's how I work around using PIL in a compiled app (I use py2exe, not pyinstaller, but the fix is likely the same). Well done, friend. - g.d.d.c
Thanks, g.d.d.c :) Just learning my way around SO... what a fantastic resource! I realized I should probably have edited my post with the two updates rather than add them as comments :P Yes, will try out the second option and update here... - AmitRao

1 Answers

3
votes

Ok, I found the answer to my own question at http://www.py2exe.org/index.cgi/py2exeAndPIL

The issue is that PIL relies on dynamically loading many image plugins, and when packaged using pyinstaller or py2exe, it cannot find these plugins. So the key is to a. explicitly import all the plugins in your code b. mark the Image class status as already initialized c. explicitly specify the target format to the save command

So, I modify convImag.py to be:

from __future__ import print_function
import os, sys
from PIL import Image
from PIL import BmpImagePlugin,GifImagePlugin,Jpeg2KImagePlugin,JpegImagePlugin,PngImagePlugin,TiffImagePlugin,WmfImagePlugin # added this line

Image._initialized=2 # added this line

for infile in sys.argv[1:]:
    f, e = os.path.splitext(infile)
    outfile = f + ".jpg"
    if infile != outfile:
        try:
            Image.open(infile).convert('RGB').save(outfile,"JPEG") # added "JPEG"
        except IOError:
            print("cannot convert", infile)

After this, the pyinstaller tool works like a charm and the packaged exe runs correctly :-) Thanks to g.d.d.c. for confirming I was on the right track with the solution!