holoviews doesn't display PIL image format - mnist

I am trying to import the MNIST data set and just display it using Holoviews. When I run the following:
import holoviews as hv
from torchvision import datasets, transforms
hv.extension('bokeh')
mnist_images = datasets.MNIST('data', train=True, download=True)
image_list = []
for k, (image, label) in enumerate(mnist_images):
if k >= 18:
break
image.show()
bounds = (0,0,1,1)
temp = hv.Image(image, bounds=bounds)
image_list.append(temp)
layout = hv.Layout(image_list).cols(2)
layout
I get the following error at the line with 'temp = hv.Image(...)':
holoviews.core.data.interface.DataError: None of the available storage backends were able to support the supplied data format.
The 'image' variable is the following object: <PIL.Image.Image image mode=L size=28x28 at 0x7F7F28567910>
and image.show() renders the image correctly. Also if I use matplotlib's .imshow() I can get a correct render.
What I want is to see the image rendered in Holoviews and I expected the Holoviews.Image() would do that. Is that not a correct assumption? If it is, then what is wrong with the code/approach?

HoloViews works with numerical arrays rather than images, so hv.Image is for constructing an image out of a 2D array, not for showing things that are already images. But you can get numerical arrays out of PIL objects, e.g. hv.RGB(np.array(image), bounds=bounds) to display it as an RGB image or something similar to pull out just the grayscale values to pass to hv.Image.

Related

How to read pdf images as opencv images using PyMuPDF?

I would like to read all images found in a pdf file by PyMuPDF as opencv images, as close as they are from the source (avoiding funky format conversions that would lead to precision loss). Basically, I would like the result to be the exact same as if I was doing a cv2.imread(filename): (in terms of the type it outputs, color space, etc...)
# Libraries
import os
import cv2
import fitz
import numpy as np
# Input file
filename = "myfile.pdf"
# Read all images in file as a list of opencv images
def read_images(filename):
images = []:
_, extension = os.path.splitext(filename)
# If it's a pdf process each image
if (extension == ".pdf"):
pdf = fitz.open(file)
for index in range(len(pdf)):
page = pdf[index]
for im in page.getImageList():
xref = im[0]
pix = fitz.Pixmap(pdf, xref)
images.append(pix_to_opencv_image(pix)) # DO SOMETHING HERE
# Otherwise just do an imread
else:
images.append(cv2.imread(filename))
return images
Basically I would like to know what the function pix_to_opencv_image should be:
# Equivalent of doing a "cv2.imread" on a pdf pixmap:
def pix_to_opencv_image(pix):
# DO SOMETHING HERE
If found example explaining how to convert pdf pixmaps to numpy arrays, but nothing that outputs an opencv image.
How can I achieve this?
I used help() function to find the various data descriptors associated with it --> help(pix)
pix.samples stores the image information as bytes. Using numpy's frombuffer, the image array can be obtained from these bytes after reshaping accordingly.
pix.height and pix.width gives the height and width of the image array respectively. pix.n is the number of channels. These can be used for reshaping the resulting array.
Your complete function would be:
def pix_to_image(pix):
bytes = np.frombuffer(pix.samples, dtype=np.uint8)
img = bytes.reshape(pix.height, pix.width, pix.n)
return img
You can display the result using cv2.imshow().

Convert Python List Object to numpy array [duplicate]

I used to use scipy which would load an image from file straight into an ndarray.
from scipy import misc
img = misc.imread('./myimage.jpg')
type(img)
>>> numpy.ndarray
But now it gives me a DeprecationWarning and the docs say it will be removed in 1.2.0. and I should use imageio.imread instead. But:
import imageio
img = imageio.imread('./myimage.jpg')
type(img)
>>> imageio.core.util.Image
I could convert it by doing
img = numpy.array(img)
But this seems hacky. Is there any way to load an image straight into a numpy array as I was doing before with scipy's misc.imread (other than using OpenCV)?
The result of imageio.imread is already a NumPy array; imageio.core.util.Image is an ndarray subclass that exists primarily so the array can have a meta attribute holding image metadata.
If you want an object of type exactly numpy.ndarray, you can use asarray:
array = numpy.asarray(img)
Unlike numpy.array(img), this will not copy img's data.
If it was a bitmap or even jpeg, you can do:
import matplotlib.pyplot as plt
import numpy as np
# 'pip install pillow' but import PIL
from PIL import Image
png_filepath = 'somepng.png'
png_pil_img = Image.open(png_filepath)
# this will print info about the PIL object
print(png_pil_img.format, png_pil_img.size, png_pil_img.mode)
png_np_img = np.asarray(png_pil_img)
plt.imshow(png_np_img) # this will graphit in a jupyter notebook
# or if its grayscale plt.imshow(png_np_img, cmap='gray')
# FWIW, this will show the np characteritics
print("shape is ", png_np_img.shape)
print("dtype is ", png_np_img.dtype)
print("ndim is ", png_np_img.ndim)
print("itemsize is ", png_np_img.itemsize) # size in bytes of each array element
print("nbytes is ", png_np_img.nbytes) # size in bytes of each array element
If you have a jpg, it works the same. PIL.image will decode the compressed JPG, and convert it to an array for you. Literally it will do all this for you. Perhaps you could load the raw bitmap with file io skipping the header, yadda yadda, but PIL is popular for a reason.
The output for a grayscale png will look like this:
PNG (3024, 4032) L
shape is (4032, 3024)
dtype is uint8
ndim is 2
itemsize is 1
nbytes is 12192768
The output for a color jpeg will look like this:
JPEG (704, 480) RGB
shape is (480, 704, 3)
dtype is uint8
ndim is 3
itemsize is 1
nbytes is 1013760
In either case, the pixel values range 0-255 as ints. They are not floats. The color image has three channels corresponding to red green and blue. The grayscale image is much greater resolution and the jpg.

Color diffusion when merging multiple images in a folder using PIL in python

I have set of 17 images and one of them has a highlighted pixel for my use. But, when I merge these 17 images, I get the color but it diffuses out of the pixel boundaries and I start seeing some colored pixel in black background.
I am using PIL library for the merging. I am attaching my code and the images for the reference. Any help would be appreciated.
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
# Cretaing the Pixel array
from PIL import Image
from PIL import ImageColor
img_path = '/Volumes/MY_PASSPORT/JRF/cancer_genome/gopal_gen/png_files/'
image_list = []
for entry in os.listdir(img_path):
if entry.endswith('.png'):
entry = int(entry.rstrip('.csv.png'))
image_list.append(entry)
image_list.sort()
list_img = []
for j in range(len(image_list)):
stuff = str(image_list[j])+'.csv.png'
list_img.append(stuff)
#print(list_img[0])
images = [Image.open(img_path+x) for x in list_img]
widths, heights = zip(*(i.size for i in images))
total_width = sum(widths)
max_height = max(heights)
#print(total_width, max_height)
new_im = Image.new('RGB', (total_width, max_height))
x_offset = 0
for im in images:
new_im.paste(im, (x_offset,0))
#print(im.size)
x_offset += im.size[0]
#print(x_offset)
new_im.save(img_path+'final_result_image.jpg')
Here is the combined image:
The third column has a pixel highlighted.
Here is the zoomed in part with the problem.
The JPEG format is lossy - it is allowed to change your pixels to make the file smaller. If your image is a conventional photo of a real-life scene, this doesn't normally matter. If your data is a blocky, computer-generated image, or a set of classes from a classification process, it can go horribly wrong if you use JPEG.
So, the answer is to use PNG (or potentially TIFF) format for images that need to be lossless.

trying to convert a grayscale image to binary, hope anyone can help me

I have a text file from a thermal camera, I read it as an image with opencv python and it works, then I convert it to gray scale image and it works as well! so I can see the person in the image, the problem is that when I convert it to binary I get a blank black image and I need to draw contours around the person's face. So I wonder what might be the problem.
Hint: The text file is filled with temperature values, when I read it it views the whole person and then I do thresholding to the text file to make the values between 32.0 and 37.0 so that I would show only the face area and it works fine until I try to convert it to binary form.
Thanks in advance.
drive.google.com/file/d/1KgHmPAwcL_uPzVxwkigcQgCsY3jFjx8T/… #link to the file
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
import os
text=np.loadtxt('1_thermal.txt') #the text file of the image
text[text < 32.0]=0 #threshholding to plot and save only the face
text[text > 37.0]=0
plt.imshow(text,cmap='jet')
plt.savefig('text.png', dpi=300, bbox_inches='tight')
plt.show()
person = cv.imread('text.png')
plt.imshow(cv.cvtColor(person, cv.COLOR_BGR2RGB))
cv.imwrite('person.jpg', person)
# get binary image and apply Gaussian blur
person_gray = cv.cvtColor(person, cv.COLOR_BGR2GRAY)
person_preprocessed = cv.GaussianBlur(person_gray, (5, 5), 0)
cv.imwrite('person-Gray.png', person_preprocessed)
plt.imshow(cv.cvtColor(person_preprocessed, cv.COLOR_GRAY2RGB))
You can load the image into a Numpy array like this:
import numpy as np
# Load image
im = np.loadtxt('thermal.txt')
If we check im.dtype and im.shape, they are:
float64, (288, 382)
Now you want a binary image. I presume you mean it will only have values of True/False, so we will need a threshold. As the face is everything above 32, we will use 32 as the threshold:
# Threshold - greater than 32 becomes True, all else becomes False
binary = im > 32
binary will now be True where the face is and False elsewhere, but I guess you want an image that is black (0) where the background is, and white (255) elsewhere. So you want:
# Convert from False/True, to 0/255 and save
binary = (binary*255).astype(np.uint8)
cv2.imwrite('result.png', binary)
That gives:

How can I get an array of ImageDraw object?

I'm writing a generic algorithm for the pictures so I started with Image class of PIL library and created a numpy array of input image. So now I want to draw some figures and the easiest way is to use ImageDraw, but then again I should use arrays for the next evolution so I need to convert ImageDraw object either to the Image object or to a numpy array.
Any suggestions how can I do that?
I tried to use a numpy conversion which worked on the Image objects. Tried to find included methods of conversion
from PIL import Image, ImageDraw
import numpy
input_image = Image.open("i2.jpg")
width, height = input_image.size
num_weights = width * height
image_draw = ImageDraw.Draw(Image.new('RGB', (width, height), 'WHITE'))
input_image = numpy.array(input_image.getdata())
#Do some staff with image_draw using information from input_image
#And try to convert image_draw to input_image
I want to have as the output a numpy array or Image object
I think you want to process an image both as a PIL Image so you can draw on it, and also as a Numpy array so you can do processing on it.
So, here is an example of how to draw on an image with PIL, then convert it to a Numpy array and do some processing on it, then convert it back to a PIL Image.
#!/usr/bin/env python3
from PIL import Image, ImageDraw
# Create a black 600x200 image
img = Image.new('RGB', (600, 200))
# Get a drawing handle
draw = ImageDraw.Draw(img)
# Draw on image
draw.rectangle(xy=[10,20,300,80], fill='red')
# Save as "result1.png"
img.save('result1.png')
# Convert PIL Image to Numpy array for processing
na = np.array(img)
# Make mask selecting red pixels then make them blue
Rmask =(na[:, :, 0:3] == [255,0,0]).all(2)
na[Rmask] = [0,0,255]
# Convert Numpy array back to PIL Image
img = Image.fromarray(na)
# Save as "result2.png"
img.save('result2.png')
The two images are "result1.png":
and "result2.png":

Resources