OSError when using PIL to save image as TIFF with compression - python

I am trying to change the dpi of my PNG images and convert them to TIFF using Pillow/PIL like so,
from PIL import Image
import os
for fl in os.listdir(os.getcwd()):
name, ext = fl.split(".")
im = Image.open(fl)
im.save(name + ".tiff", dpi=(500,500), compression="tiff_jpeg")
print("Done '{}'".format(name))
which works fine if the compression kwarg is not set, but I end up with massive 100MB TIFF files from my 1MB PNGs. If I set the compression type to any of the available options, I end up with the following error:
Traceback (most recent call last):
File "<ipython-input-1-3631f05e05f4>", line 7, in <module>
im.save(name + ".tiff", dpi=(500,500), compression="tiff_jpeg")
File "C:\Users\Patrick\Anaconda3\lib\site-packages\PIL\Image.py", line 1687, in save
save_handler(self, fp, filename)
File "C:\Users\Patrick\Anaconda3\lib\site-packages\PIL\TiffImagePlugin.py", line 1457, in _save
raise IOError("encoder error %d when writing image file" % s)
OSError: encoder error -2 when writing image file
In the docs for the Image.save method it mentions that compression is only available if the libtiff library is installed which I do have.
Here is the versions for Python and Pillow that I'm working with:
Python 3.5.1 |Anaconda 4.0.0 (64-bit)| (default, Feb 16 2016, 09:49:46) [MSC v.1900 64 bit (AMD64)] on win32
libtiff: 4.0.6-vc14_2 [vc14]
pillow: 3.2.0-py35_1
What might be the cause of this error and what steps can I take to resolve? This is the first time I've used Pillow/PIL and am unsure of where to begin.

Just got bitten by this myself so I thought I'd share my finds despite the question being a few months old.
The tiff_jpeg compression refers to the "old-style" JPEG-encoded TIFF files and is now obsolete. Using compression='jpeg' instead of compression='tiff_jpeg' worked for me.
The available options for compression can currently be found in the COMPRESSION_INFO dict at https://github.com/python-pillow/Pillow/blob/master/PIL/TiffImagePlugin.py. The tiff_lzw compression also isn't mentioned in the docs but worked for creating an LZW encoded TIFF for me using Pillow 3.4 on Windows.

I had a very similar issue with 'group4' compression on B&W tiffs. I solved it by changing the image array values from 0, 255 to True, False.
src = <your B&W tiff file>
dst = <output filename>
# Open PIL.Image
img = Image.open(src)
# Change img values
a = np.array(img)
a = np.array([x == 255 for x in a])
img = Image.fromarray(a)
# Save Out Compressed Tiff
img.save(dst, compression='group4')

Not a direct answer to this question, but might help someone who is experiencing similar issues in the future.
I've also been getting OSError: encoder error -2 when writing image file when trying to save a TiffImageFile with compression. This error was happening when trying to to apply tiff_ccitt, group3, or group4 compression options.
I fixed it by simply converting the TIFF to monochrome with image.convert('1') before compressing it. Note that the compression algorithms I mentioned are only used for monochrome images, so we are not losing any colour by adding this step.
How I compressed and saved a multi-page TIFF:
# convert all images to monochrome. Without this step CCITT compression will fail.
images = [image.convert('1') for image in images]
images[0].save(out_path,
compression="tiff_ccitt",
save_all=True,
append_images=images[1:])

Related

too many open files when saving tiff images with PIL.Image and tiff_lzw compression

When saving several tif images using the PIL.Image.save function and tiff_lzw compression I get the
OSError: [Errno 24] Too many open files error.
The error happens after saving 8187 images.
The error does not happen if no compression (or compression = "raw") is used.
The same error happens if using another compression method (I tried "tiff_adobe_deflate" as well)
The error can be reproduced by running this code.
from PIL import Image
import numpy as np
fn = "test.tif"
compression = "tiff_lzw"
for i in range(10000):
a = np.array([[0,0],[0,0]], dtype=np.uint8)
with Image.fromarray(a) as rawtiff:
rawtiff.save(fn, compression = compression)
rawtiff.close()
In this code I used a context manager and I also tried to explicitely close the Image object. The error still persists.
Tested on Windows;
Python version: 3.10.6;
Pillow version: 9.2.0

Python Imaging Library - Cannot open large .tif file

I'm working on large satellite image files in the .tif format. To start, I am just trying to open the files and view them using PIL. Here is the code I have written so far:
from PIL import Image
import os.path
script_dir = os.path.dirname(os.path.abspath(__file__))
im = Image.open(os.path.join(script_dir, 'orthoQB02_11JUL040015472-M1BS-101001000DB70900_u16ns3413.tif'))
im.show()
Unfortunately, I am receiving the error message:
IOError Traceback (most recent call last)
/Applications/Canopy.app/appdata/canopy-1.3.0.1715.macosx-x86_64/Canopy.app/Contents/lib/python2.7/site-packages/IPython/utils/py3compat.pyc in execfile(fname, *where)
202 else:
203 filename = fname
----> 204 __builtin__.execfile(filename, *where)
/Users/zlazow/Desktop/Geo Research Files/documents-export-2014-02-13 (3)/showfiles.py in <module>()
3
4 script_dir = os.path.dirname(os.path.abspath(__file__))
----> 5 im = Image.open(os.path.join(script_dir, 'orthoQB02_11JUL040015472-M1BS-101001000DB70900_u16ns3413.tif'))
6 im.show()
/Users/zlazow/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/PIL/Image.pyc in open(fp, mode)
1978 pass
1979
----> 1980 raise IOError("cannot identify image file")
1981
1982 #
IOError: cannot identify image file
Are the image files simply too large for PIL? I can open one of the smaller (200MB) .tif files in the Preview Application, but when I try to open it using PIL it creates a BMP image that opens in Preview, but the image never loads.
All of the rest of the files (300MB++) will not open with Preview or PIL at all.
Thanks for any assistance.
The Image constructor looks through its internal list of formats (depends on how PIL was compiled) and asks each one if it can parse the file.
As an input to the detector function, the first few bytes of the image file is used. By looking inside the TIFF image reader, it looks for one of the following magic bytes:
["MM\000\052", "II\052\000", "II\xBC\000"]
As indicated by the error message, the detector fails while reading the first few bytes of the file, way before it has gotten to read the dimensions of the image. One of the following reasons appear more likely:
The file is corrupt
The file is not a TIFF image
The file is some exotic/new TIFF sub-format that PIL can't understand
And as for solution, I would suggest:
Use the file command to try to identify the file format, e.g.
file orthoQB02_11JUL040015472-M1BS-101001000DB70900_u16ns3413.tif
which should print something like
Untitled.tiff: TIFF image data, big-endian
Try to open the file in e.g. Photoshop and see if it can understand the file.
Inspect the header manually, see if the file starts with the magic bytes above.
EDIT: Since you identified the format (BigTIFF), you have two options: Convert it or find a Python library to load it. http://bigtiff.org has unofficial libtiff versions with BigTIFF built in. You could try to compile pylibtiff against this libtiff version, or use ImageMagick (compiled with BigTIFF support) to convert the images to regular TIFF files first.

PIL "IOError: image file truncated" with big images

I think this problem is not Zope-related. Nonetheless I'll explain what I'm trying to do:
I'm using a PUT_factory in Zope to upload images to the ZODB per FTP. The uploaded image is saved as a Zope Image inside a newly created container object. This works fine, but I want to resize the image if it exceeds a certain size (width and height). So I'm using the thumbnail function of PIL to resize them i.e. to 200x200. This works fine as long as the uploaded images are relatively small. I didn't check out the exact limit, but 976x1296px is still ok.
With bigger pictures I get:
Module PIL.Image, line 1559, in thumbnail
Module PIL.ImageFile, line 201, in load
IOError: image file is truncated (nn bytes not processed).
I tested a lot of jpegs from my camera. I don't think they are all truncated.
Here is my code:
if img and img.meta_type == 'Image':
pilImg = PIL.Image.open( StringIO(str(img.data)) )
elif imgData:
pilImg = PIL.Image.open( StringIO(imgData) )
pilImg.thumbnail((width, height), PIL.Image.ANTIALIAS)
As I'm using a PUT_factory, I don't have a file object, I'm using either the raw data from the factory or a previously created (Zope) Image object.
I've heard that PIL handles image data differently when a certain size is exceeded, but I don't know how to adjust my code. Or is it related to PIL's lazy loading?
I'm a little late to reply here, but I ran into a similar problem and I wanted to share my solution. First, here's a pretty typical stack trace for this problem:
Traceback (most recent call last):
...
File ..., line 2064, in ...
im.thumbnail(DEFAULT_THUMBNAIL_SIZE, Image.ANTIALIAS)
File "/Library/Python/2.7/site-packages/PIL/Image.py", line 1572, in thumbnail
self.load()
File "/Library/Python/2.7/site-packages/PIL/ImageFile.py", line 220, in load
raise IOError("image file is truncated (%d bytes not processed)" % len(b))
IOError: image file is truncated (57 bytes not processed)
If we look around line 220 (in your case line 201—perhaps you are running a slightly different version), we see that PIL is reading in blocks of the file and that it expects that the blocks are going to be of a certain size. It turns out that you can ask PIL to be tolerant of files that are truncated (missing some file from the block) by changing a setting.
Somewhere before your code block, simply add the following:
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
...and you should be good.
EDIT: It looks like this helps for the version of PIL bundled with Pillow ("pip install pillow"), but may not work for default installations of PIL
Here is what I did:
Edit LOAD_TRUNCATED_IMAGES = False line from /usr/lib/python3/dist-packages/PIL/ImageFile.py:40 to LOAD_TRUNCATED_IMAGES = True.
Editing the file requires root access though.
I encountered this error while running some pytorch which was maybe using the PIL library.
Do this fix only if you encounter this error, without directly using PIL.
Else please do
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
Best thing is that you can:
if img and img.meta_type == 'Image':
pilImg = PIL.Image.open( StringIO(str(img.data)) )
elif imgData:
pilImg = PIL.Image.open( StringIO(imgData) )
try:
pilImg.load()
except IOError:
pass # You can always log it to logger
pilImg.thumbnail((width, height), PIL.Image.ANTIALIAS)
As dumb as it seems - it will work like a miracle. If your image has missing data, it will be filled with gray (check the bottom of your image).
Note: usage of camel case in Python is discouraged and is used only in class names.
This might not be a PIL issue. It might be related to your HTTP Server setting. HTTP servers put a limit on the size of the entity body that will be accepted.
For eg, in Apache FCGI, the option FcgidMaxRequestLen determines the maximum size of file that can be uploaded.
Check that for your server - it might be the one that is limiting the upload size.
I was trapped with the same problem. However, setting ImageFile.LOAD_TRUNCATED_IMAGES = True is not suitable in my case, and I have checked that all my image files were unbroken, but big.
I read the images using cv2, and then converted it to PIL.Image to get round the problem.
img = cv2.imread(imgfile, cv2.IMREAD_GRAYSCALE)
img = Image.fromarray(img)
I had to change the tds version to 7.2 to prevent this from happening. Also works with tds version 8.0, however I had some other issues with 8.0.
When image was partly broken, OSError will be caused.
I use below code for check and save the wrong image list.
try:
img = Image.open(file_path)
img.load()
## do the work with the loaded image
except OSError as error:
print(f"{error} ({file_path})")
with open("./error_file_list.txt", "a") as error_log:
log.write(str(file_path))

Python Image Library (PIL) error with floating point tiff

I am writing a script that changes the resolution of a floating point 2K (2048x2048) tiff image to 1024x1024.
But I get the following error:
File "C:\Python26\lib\site-packages\PIL\Image.py", line 1916, in open
IOError: cannot identify image file
My Code:
import Image
im = Image.open( inPath )
im = im.resize( (1024, 1024) , Image.ANTIALIAS )
im.save( outPath )
Any Ideas?
Download My Image From This Link
Also I'm using pil 1.1.6. The pil install is x64 same as the python install (2.6.6)
Try one of these two:
open the file in binary mode,
give the full path to the file.
HTH!
EDIT after testing the OP's image:
It definitively seems like is the image having some problem. I'm on GNU/Linux and couldn't find a single program being able to handle it. Among the most informative about what the problem is have been GIMP:
and ImageMagik:
display: roadnew_disp27-dm_u0_v0_hr.tif: invalid TIFF directory; tags are not sorted in ascending order. `TIFFReadDirectory' # warning/tiff.c/TIFFWarnings/703.
display: roadnew_disp27-dm_u0_v0_hr.tif: unknown field with tag 18 (0x12) encountered. `TIFFReadDirectory' # warning/tiff.c/TIFFWarnings/703.
I did not try it myself, but googling for "python tiff" returned the pylibtiff library, which - being specifically designed for TIFF files, it might perhaps offer some more power in processing this particular ones...
HTH!

Python and 16 Bit Tiff

How can I convert and save a 16 bit single-channel TIF in Python?
I can load a 16 and 32 bit image without an issue, and see that the 32 bit image is mode F and the 16 bit image is mode I;16S:
import Image
i32 = Image.open('32.tif')
i16 = Image.open('16.tif')
i32
# <TiffImagePlugin.TiffImageFile image mode=F size=2000x1600 at 0x1098E5518>
i16
# <TiffImagePlugin.TiffImageFile image mode=I;16S size=2000x1600 at 0x1098B6DD0>
But I am having trouble working with the 16 bit image. If I want to save either as PNG, I cannot do so directly:
i32.save('foo.png')
# IOError: cannot write mode F as PNG
i16.save('foo.png')
# ValueError: unrecognized mode
If I convert the 32 bit image, I can save it:
i32.convert('L').save('foo.png')
But the same command will not work with the 16 bit image:
i16.convert('L').save('foo.png')
# ValueError: unrecognized mode
For lossless conversion from 16 bit grayscale TIFF to PNG use PythonMagick:
from PythonMagick import Image
Image('pinei_2002300_1525_modis_ch02.tif').write("foo.png")
You appear to have stumbled into a PIL bug, or a corner case that was unimplemented.
Here's a workaround:
i16.mode = 'I'
i16.point(lambda i:i*(1./256)).convert('L').save('foo.png')
Stumbled on this thread trying to save 16 bit TIFF images with PIL / numpy.
Versions: python 2.7.1 - numpy 1.6.1 - PIL 1.1.7
Here's a quick test I wrote. uint16 numpy array -> converted to string -> converted to a PIL image of type 'I;16' -> saved as a 16 bit TIFF.
Opening the image in ImageJ shows the right horizontal gradient pattern and the image type is 'Bits per pixel: 16 (unsigned)'
import Image
import numpy
data = numpy.zeros((1024,1024),numpy.uint16)
h,w = data.shape
for i in range(h):
data[i,:] = numpy.arange(w)
im = Image.fromstring('I;16',(w,h),data.tostring())
im.save('test_16bit.tif')
edit: As of 1.1.7, PIL does not support writing compressed files, but pylibtiff does (lzw compression). The test code thus becomes (tested with pylibtiff 0.3):
import Image
import numpy
from libtiff import TIFFimage
data = numpy.zeros((1024,1024),numpy.uint16)
h,w = data.shape
for i in range(w):
data[:,i] = numpy.arange(h)
tiff = TIFFimage(data, description='')
tiff.write_file('test_16bit.tif', compression='lzw')
#flush the file to disk:
del tiff
Please note: test code changed to generate a vertical gradient otherwise, no compression achieved (refer to warning: pylibtiff currently supports reading and writing images that are stored using TIFF strips).
Convert an ImageJ TIFF to a JPEG with PIL 4.1+
im = numpy.array(Image.open('my.tiff'))
image = Image.fromarray(im / numpy.amax(im) * 255)
image.save('my.jpg')

Categories