digital image read as an array of integers - python

Using Python's PIL module, we can read an digital image into an array of integers,
from PIL import Image
from numpy import array
img = Image.open('x.jpg')
im = array(img) # im is the array representation of x.jpg
I wonder how does PIL interpret an image as an array? First I tried this
od -tu1 x.jpg
and it indeed gave a sequence of numbers, but how does PIL interpret a color image into a 3D array?
In short, my question is that I want to know how can I get a color image's array representation without using any module like PIL, how could do the job using Python?

Well, it depends on the image format I would say.
For a .jpg, there is a complete description of the format that permits to read the image .
You can read it here
What PIL does is exactly what you did at first. But then it reads the bytes following the specifications, which allow it to transform this into a human readable format (in this case an array).
It may seem complex for JPEG, but if you take png (the version without compression) everything can seem way more simple.
For example this image
If you open it, you will see something like that :
You can see several information on top that corresponds to the header.
Then you see all those zeroes, that are the numerical representation of black pixels of the image (the top left corner).
If you open the image with PIL, you will get an array that is mostly filled with 0.
If you want to know more about the first bytes, look at the png specifications chapter 11.2.2.
You will see that some of the bytes corresponds to the width and height of the image. This is how PIL is able to create the array :).
Hope this helps !

Depends on the color mode. In PIL an image is stored as a list of integers with all channels interleaved on a per-pixel basis.
To illustrate this:
Grayscale image: [pixel1, pixel2, pixel3, ...]
RGB: [pixel1_R, pixel1_G, pixel1_B, pixel2_R, pixel_2_G, ...]
Same goes for RBGA and so on.

Related

How to combine two image arrays into one RGB

I am working on a dataset that has two features, real and imaginary impedances. I applied data-to-image conversion using MTF in order to represent each one as an image (50x50). I was thinking of creating a 3-D image (50x50x2). I tried doing
Image = np.array([tag_gadf_re[0],tag_gadf_im[0]])
where tag_gadf_re[0] and tag_gadf_im[0] are the real and imaginary impedance image arrays. However, I tried saving the image using:
plt.imsave("Directory", Image)
However, I am getting the following error:
ValueError: Third dimension must be 3 or 4
Also note that the shape of Image is (2x50x50), when it should be (50x50x2). The solution seems simple, but I am a bit lost in the process. How can I combine both arrays appropriately and save the image, or do I need a 3rd layer in order to appropriately represent it as an RGB image?
If you want to store data as an image you need to be aware of its type and range so that you can choose an appropriate format. You also need to be aware of whether you can tolerate a "lossy" format which, when read, will not return identical values to those you stored.
If your data is integer and 16-bit or less, you can store it in a PNG. If it's multi-channel and 16-bit, you'll come unstuck with PIL. You can use tifffile though to store a 2-channel TIFF - maybe that can be greyscale + transparency or maybe 2 IFDs.
If your data is floating point, you pretty much have to use TIFF, PFM or EXR format. Again, tifffile can do this for you.
tifffile is here.
wand can also do whatever tifffile can do.
Of course, you might choose to represent your two arrays/images as one above the other in a double-height image. It's your data.

How to compare two image files pixel by pixel in python using selenium?

I want to compare two images (.png format) pixel by pixel using selenium in python. Or how could i do it using pillow library.
I have a base image and i get the compare image by taking screenshot of the webpage. I want to compare those two images and assert that they are equal. how can I do it.
Below is what I have tried:
def assert_images_are_equal(base_image, compare_image):
with open(base_image, 'rb') as f1, open(compare_image, 'rb') as f2:
base_image_contents = f1.read()
compare_image_contents = f2.read()
assert base_image_contents == compare_image_contents
But this doesnt work always. I want to compare pixel by pixel. Could someone help me with this using pillow library or any other library apart from PIL? thanks.
It is rather difficult to say whether 2 images are the same or similar, because it depends on your definitions of "same" and "similar".
You can make a solid red image, save it as a PNG and then save the exact same image again and it could be different because the PNG format contains a timestamp in the image header that may have ticked over to the next second in between saves.
You can make a solid red PNG file that is 8-bits deep, and another that is 16-bits deep and you cannot see the difference but the data will be grossly different.
You can make a TIF file in Motorola byte order and the same file in Intel byte order. Visually, and in calculations, they will be indistinguishable, but the files will be grossly different.
You can make a GIF file that is red and it will look no different from a PNG file but the files will differ.
You can make a palette image and a true-colour image and the pixels will be grossly different but they will look identical.
You could make a simple black image with a white rectangle in the middle and write it using one JPEG library and it will come out different from the same image written with a different JPEG library, or even a different release version of the same library.
There are many more cases...
One a more helpful note, you may want to look at Perceptual Hashing which tells you if images look pretty similar. One library that does Perceptual Hashing is ImageMagick and it has a Python binding here and here.

How can I reduce the file size of a PNG image without changing its dimensions?

I want to reduce the file size of a PNG file using Python. I have gone through a lot of material on the internet, but I could not find anything where I would reduce the file size for an image without changing the dimensions i.e. height/width. What I have found is how to change the dimensions of the image, using PIL or some other library of Python.
How can I reduce image file size while keeping it's dimensions constant?
PNG is a lossless format but it still can be compressed in a lossless fashion. Pillow's Image class comes with a png writer accepting a compress_level parameter.
It can easily be demonstrated that it works:
Step 1: Load generic rgb lena image in .png format:
import os
import requests
from PIL import Image
from io import BytesIO
img_url = 'http://pngnq.sourceforge.net/testimages/lena.png'
response = requests.get(img_url)
img = Image.open(BytesIO(response.content))
Step 2: Write png with compress_level=0:
path_uncompressed = './lena_uncompressed.png'
img.save(path_uncompressed,compress_level=0)
print(os.path.getsize(path_uncompressed))
> 691968
Step 3: Write png with compress_level=9:
path_compressed = './lena_compressed.png'
img.save(path_compressed,compress_level=9)
print(os.path.getsize(path_compressed))
> 406889
which in this case gives us a respectable 30% compression without any obvious image quality degradation (which should be expected for a lossless compression algorithm).
PNG is lossless format and obviously it will consume more space.
If you are only concerned on resolution, then you can convert to any of the lossy form like JPG.
https://whatis.techtarget.com/definition/lossless-and-lossy-compression
The dimension after conversion would be the same, but quality depends on the compression needed
Snippet to convert PNG to JPG
from PIL import Image
im = Image.open("Picture2.png")
rgb_im = im.convert('RGB')
rgb_im.save('Picture2.jpg')
By default, most PNG writers will use the maximum compression setting, so trying to change that will not help much. Uncompressed PNGs exist, but they make no sense, I don't think you'll run into many of those.
Thus, the only way of making PNGs significantly smaller is to reduce the number of pixels it stores, or to reduce the number of colors it stores. You specifically said not to want to reduce the number of pixels, so the other option is to reduce the number of colors.
Most PNG files will be in "true color" mode (typically 24 bits per pixel, with one byte each for the red, green and blue components of each pixel). However, it is also possible to make indexed PNG files. These store a color map (a.k.a. palette), and a single value per pixel, the index into a color map. If you, for example, pick a color map of 64 entries, then each pixel will need 6 bits to encode the index. You'd store 64*3 bytes + 3/4 bytes per pixel (which of course compress as well). I found this web site comparing a few example images, what they'd look like and how big the file ends up being when reducing colors.
This other question shows how to use PIL to convert an RGB image to an indexed image:
img.convert("P", palette=Image.ADAPTIVE)
This seems to generate an 8-bit color map though, PIL has no support for smaller color maps. The PyPNG module would allow you to write PNG files with any number of colors in the color map.

Python 3: How to blur a GeoTIFF image with color table?

I've got a GeoTIFF image that I need to make blurry by applying a smoothing filter. The image itself contains metadata that needs to be preserved. It has a bit-depth of 8 and uses a color table with 256 32-bit RGBA values to look up a color for each pixel, but in order for the resulting image to look smooth it will probably have to use a bit-depth of 24 or 32 and no color table, alternatively use jpeg compression. What may complicate this further is that the image is 23,899x18,330 pixels large, which is almost five times as large as the largest file PIL wants to open by default.
How can create the blurry version of this image in Python 3?
I have also tried using PIL to just open and save it again:
from PIL import Image
Image.MAX_IMAGE_PIXELS = 1000000000
im = Image.open(file_in)
im.save(file_out)
This code doesn't crash, and I get a new .tif file that is approximatelly as large as the original file, but when I try to open it in Windows Photo Viewer to look at it the application says it is corrupt, and it cannot be re-opened by PIL.
I have also tried using GDAL. When I try this code, I get an output image that is 835 MB large, which corresponds to an uncompressed image with a bit-depth of 16 (which is also what the file metadata says when I right-click on it and choose "Properties" – I'm using Windows 10). However, the resulting image is monochrome and very dark, and the colors look like they have been jumbled up, which makes me believe that the code I'm trying interprets the pixel values as intensity values and not as table keys.
So in order to make this method work, I need to figure out how to apply the color table (which is some sort of container for tuples, of type osgeo.gdal.ColorTable) to the raster band (whatever a raster band is), which is a numpy array with the shape (18330, 23899), to get a new numpy array with the shape (18330, 23899, 4) or (4, 18330, 23899) (don't know which is the correct shape), insert this back into the loaded image and remove the color table (or create a new one with the same metadata), and finally save the modified image with compression enabled (so I get closer to the original file size – 11.9 MB – rather than 835 MB which is the size of the file I get now). How can I do that?
pyvips can process huge images quickly using just a small amount of memory, and supports palette TIFF images.
Unfortunately it won't support the extra geotiff tags, since libtiff won't work on unknown tag types. You'd need to copy that metadata over in some other way.
Anyway, if you can do that, pyvips should work on your image. I tried this example:
import sys
import pyvips
# the 'sequential' hint tells libvips that we want to stream the image
# and don't need full random access to pixels ... in this mode,
# libvips can read, process and write in parallel, and without needing
# to hold the whole image in memory
image = pyvips.Image.new_from_file(sys.argv[1], access='sequential')
image = image.gaussblur(2)
image.write_to_file(sys.argv[2])
On an image of the type and size you have, generating a JPEG-compressed TIFF:
$ tiffinfo x2.tif
TIFF Directory at offset 0x1a1c65c6 (438068678)
Image Width: 23899 Image Length: 18330
Resolution: 45118.5, 45118.5 pixels/cm
Bits/Sample: 8
Compression Scheme: None
Photometric Interpretation: palette color (RGB from colormap)
...
$ /usr/bin/time -f %M:%e python3 ~/try/blur.py x2.tif x3.tif[compression=jpeg]
137500:2.42
So 140MB of memory, 2.5 seconds. The output image looks correct and is 24mb, so not too far off yours.
A raster band is just the name given to each "layer" of the image, in your case they will be the red, green, blue, and alpha values. These are what you want to blur. You can open the image and save each band to a separate array by using data.GetRasterBand(i) to get the ith band (with 1-indexing, not 0-indexing) of the image you opened using GDAL.
You can then try and use SciPy's scipy.ndimage.gaussian_filter to achieve the blurring. You'll want to send it an array that is shape (x,y), so you'll have to do this for each raster band individually. You should be able to save your data as another GeoTIFF using GDAL.
If the colour table you are working with means that your data is stored in each raster band in some odd format that isn't just floats between 0 and 1 for each of R, G, B, and A, then consider using scipy.ndimage.generic_filter, although without knowing how your data is stored it's hard to give specifics on how you'd do this.

How do you display a 2D numpy array in glade-3 ?

I'm making live video GUI using Python and Glade-3, but I'm finding it hard to convert the Numpy array that I have into something that can be displayed in Glade. The images are in black and white with just a single value giving the brightness of each pixel. I would like to be able to draw over the images in the GUI so I don't know whether there is a specific format I should use (bitmap/pixmap etc) ?
Any help would be much appreciated!
In the end i decided to create a buffer for the pixels using:
self.pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,0,8,1280,1024)
I then set the image from the pixel buffer:
self.liveImage.set_from_pixbuf(self.pixbuf)
I think these are the steps you need:
use scipy.misc.toimage to convert your array to a PIL image
check out the answer to this question to convert your PIL image to a cairo surface
use gdk_pixbuf_get_from_surface to convert this to a pixbuf (I don't know it's name in the python api)
make a Gtk.Image out of this using Gtk.Image.new_from_pixbuf
I'm sorry it needs so many conversion steps.

Categories

Resources