RLE8 image support/decompression with Pillow (PIL fork) - python

I'm using Pillow (version 5.2.0) on Python3 to open both PNG and BMP images, and display them with a Tkinter GUI. The PNG images display correctly with no issues, however, I'm encountering an IOError ("Unsupported BMP compression") with some of the BMP images, when Pillow's BmpImagePlugin.py is used.
Using the bitmap plugin's source and some print statements, I found that the exception is thrown at line 193, and that the images causing the exception are compressed using RLE8 (denoted by the dictionary on line 63); all others work because they're a RAW format.
It would seem to me that if a compression type is listed in that dictionary it should be supported, but apparently that isn't the case.
My question: is anyone aware of a workaround in Pillow or of any other python library that can open RLE8 bitmap images?
Here's an image displaying my PATH environment, as well as the command-line error described in a comment below.
Path issues

I note that your first image (test1.bmp) appears to be corrupt and ImageMagick reports it has incorrect length.
Your second image does not appear to be compressed with RLE8 compression and also is a palettised image, but with alpha/transparency.
Your third image is palletised, non-alpha with RLE8 compression.
My version of PIL can read only the second file - the first and third, which are RLE encoded cannot be read.
You asked for a workaround - may I suggest pyvips which can read the files without issues:
import pyvips
from PIL import Image
# Load troublesome file using vips, and write to a memory buffer
image = pyvips.Image.new_from_file('test1.bmp')
mem_img = image.write_to_memory()
# Read from memory buffer into Numpy array
imgnp=np.frombuffer(mem_img, dtype=np.uint8).reshape(image.height, image.width, 3)
# Convert Numpy array to PIL Image and write to disk
Image.fromarray(imgnp).save('result.png')

Related

How to find the original format of images (pages) present in a tiff file using python?

I have a multi-page tiff file (merged.tiff) out of which I need to extract individual images in their original format. PIL allows you to iterate through pages and writing them to disk in a format I need (png/jpg).
Ex:
from PIL import Image
img = Image.open('merged.tiff')
for i in range(img.n_frames):
try:
img.seek(i)
img.save(f'individual_{i}.jpg')
img.save(f'individual_{i}.png')
except EOFError:
break
But is there a way to know the original format of those images?
I have tried with tifffile and tiffany which allow me to convert the pages to a numpy array and then write to disk as an image, but they don't allow me to know the source format of the images contained in the TIFF file.
In the most general case, I believe this is impossible, because it is perfectly feasible to take, say, a JPEG image and include it in the TIFF file as an uncompressed RGB array.
Realistically, though, you should be able to look at some of the tags of the TIFF file, e.g. Compression, to make an educated guess about what the image used to be. Tools like tiffinfo and tiffdump (from the libtiff package) can be used to examine the TIFF file.

Larger than expected file sizes when saving a TIFF with OpenCV

I am creating a python program to load a .tiff image, crop out a selection from the image, and save the selection as a tiff. The dataset images are large, exceeding 1GB. I can successfully crop out what I need and save as a tiff, but the new image file sizes are much larger than what I expect and need.
Opening
I am using tifffile to open the image as a numpy array. OpenCV and PIL were not able to open the files due to size. I tried using OpenSlide as well, but encountered other problems down the road with read_region().
Cropping
The numpy array has the shape (height, width, 3), so I crop using something like large_image[top:bottom, left:right, :]. This works as intended.
Saving
Using cv2.imwrite() has resulted in the smallest file sizes thus far, but they are still much larger than they should be. PIL.Image.save() and TiffWriter from tifffile created even larger images.
Best results: Cropping 13 new images from a 250MB file - using only about 20% of the original image - gives me files totaling over 900MB. I would expect the total to be something like 50MB.
Note: The cropped .tiff files have correct dimensions. If the original is 200,000 x 50,000, then the cropped file will be, say, 8,000 x 3,000. Also, I am unable to open the original 250MB image using Preview on my Mac, but I can quickly open a 500MB cropped image created by my program when I save the image with TiffWriter (I can open files saved with opencv as well).
Summary of the code:
import tifffile
import cv2
import numpy as np
original_image = tifffile.imread('filepath') #original_image is a numpy array
#...calculations for top, bottom, etc...
cropped_image = original_image[top:bottom, left:right, :]
cv2.imwrite('output_filepath', cropped_image)
These 3 lines are all the IO that I use.
tl;dr - trying to load images, crop, and save new images as .tiff, but new file sizes are much larger than expected.
If you are on a Mac, homebrew is great and you can install libtiff and ImageMagick with:
brew install libtiff imagemagick
Then you can really start to understand what compression, number of bits/sample and data sizes/types using:
tiffinfo YOURINPUTFILE.TIF
tiffinfo YOUROUTPUTFILE.TIF
and:
magick identify -verbose YOURINPUTFILE.TIF
magick identify -verbose YOUROUTPUTFILE.TIF
If you want to see the two side-by-side, use:
magick identify -verbose YOURINPUTFILE.TIF > a
magick identify -verbose YOUROUTPUTFILE.TIF > b
opendiff a b

How can I insert EXIF/other metadata into a JPEG stored in a memory buffer?

I have created a JPEG using Python OpenCV, EXIF data being lost in the process and apparently not being able to be re-added when calling imwrite (reference: Can't keep image exif data when editing it with opencv in python).
Two questions:
In general, how can I write the original EXIF data/new custom metadata into a JPEG that exists in memory rather than a file?
Would pillow/PIL be able to maintain the EXIF data and allow supplementary metadata to be added? As of 2013 (reference: how maintain exif data of images resizes using PIL) this did not seem possible except via a tmp file (which is not an option for me).
Thanks as ever
I'm not certain I understand what you are trying to do, but I think you are trying to process an image with OpenCV and then re-insert the EXIF data you lost when OpenCV opened it...
So, hopefully you can do what you are already doing, but also open the image with PIL/Pillow and extract the EXIF data and then write it into the image processed by OpenCV.
from PIL import Image
import io
# Read your image with EXIF data using PIL/Pillow
imWithEXIF = Image.open('image.jpg')
You will now have a dict with the EXIF info in:
imWIthEXIF.info['exif']
You now want to write that EXIF data into your image you processed with OpenCV, so:
# Make memory buffer for JPEG-encoded image
buffer = io.BytesIO()
# Convert OpenCV image onto PIL Image
OpenCVImageAsPIL = Image.fromarray(OpenCVImage)
# Encode newly-created image into memory as JPEG along with EXIF from other image
OpenCVImageAsPIL.save(buffer, format='JPEG', exif=imWIthEXIF.info['exif'])
Beware... I am assuming in the code above, that OpenCVImage is a Numpy array and that you have called cvtColor(cv2.COLOR_BGR2RGB) to go to the conventional RGB channel ordering that PIL uses rather than OpenCV's BGR channel ordering.
Keywords: Python, OpenCV, PIL, Pillow, EXIF, preserve, insert, copy, transfer, image, image processing, image-processing, dict, BytesIO, memory, in-memory, buffer.

How convert image into 16bit zip (deflate) compressed TIF in python?

We got 50TB of 16bit uncompressed TIF images from a industrial sensor in our server, and we want to compress them all with lossless zip compression using python. Using python because it's easier to use Python to communicate our database.
However after hours of search and documentation reading, I found that there's not even a matured python library that can convert 16bit TIF into zip compressed tif. The latest PIL cannot write compressed tif, OpenCV hardcoded output file into LZW tif not zip(deflate). And there is no sufficient documentation in smc.freeimage, PythonImageMagick so I don't know if they can do it. I also found this tifffile.py, there seems something about compression in its source code, but there is no example code that let me understand how to config compression option for output.
Of course I can use an external executable, but I just don't want to use python as scripting language here.
So that I really appreciate if anyone give me an efficient example here, thanks.
Update:
cgohlke's code works, here I provide another light weight solution.
Checkout the patched pythontifflib code from here https://github.com/delmic/pylibtiff.
The original PythonTiffLib from google code doesn't handle RGB information well and it didn't work on my data, this patched version works, however because the code is very old, it implies PythonTiffLib may be not maintained very well.
Use the code like this:
from libtiff import TIFF
tif = TIFF.open('Image.tiff', mode='r')
image = tif.read_image()
tifw = TIFF.open('testpylibtiff.tiff', mode='w')
tifw.write_image(image, compression='deflate', write_rgb=True)
PythonMagick works for me on Windows:
from PythonMagick import Image, CompressionType
im = Image('tiger-rgb-strip-contig-16.tif')
im.compressType(CompressionType.ZipCompression)
im.write("tiger-rgb-strip-contig-16-zip.tif")
Scikit-image includes a wrapper for the FreeImage library:
import skimage.io._plugins.freeimage_plugin as fi
im = fi.read('tiger-rgb-strip-contig-16.tif')
fi.write(im, 'tiger-rgb-strip-contig-16-zip.tif',
fi.IO_FLAGS.TIFF_ADOBE_DEFLATE)
Or via tifffile.py, 2013.11.03 or later:
from tifffile import imread, imsave
im = imread('tiger-rgb-strip-contig-16.tif')
imsave("tiger-rgb-strip-contig-16-zip.tif", im, compress=6)
These might not preserve all other TIFF tags or properties but that wasn't specified in the question.

Compress Images in Python (No Archive)

I'm writing a Python script that deals with images. Is there a module that can compress an image without putting it into an archive, and decompress it back? (e.g. A 1MB image is now 0.8MB after compression, then 1MB after decompression).
Can I see example code of compressing and decompressing an image in Python without the use of archives?
I've already taken a look at some modules, but those compress strings.
You probably want to take a look at the Python Image Library (PIL), and the PNG and JPEG formats.
The PIL Image.save() method will let you save PNG or JPEG images.
PNG - Lossless, good for "cartoony"/logo images with solid colors or small numbers of colors.
JPEG - Lossy, good for photos, images with lots "going on".
Modern image formats such PNG and JPEG are already compressed and my general recommendation is take Brendan Long's advice and use those formats and exploit all the work that's been put into them.
That said, if you want to compress the contents of any arbitrary file in Python, here's a very simple example:
import zlib
with open("MyImage.jpg", "rb") as in_file:
compressed = zlib.compress(in_file.read(), 9)
with open("MyCompressedFile", "wb") as out_file:
out_file.write(compressed)

Categories

Resources