plt.imshow() not showing numpy matrix correctly - python-3.x

I'm having trouble drawing a rectangle on an image with OpenCV, and then displaying the image with matplotlib. This code in a Jupyter Notebook results in a black image being displayed, with no green rectangle:
import numpy as np
import cv2
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
%matplotlib inline
image = np.zeros((720, 1280, 3))
cv2.rectangle(image, (100,100), (200,200), color=(0,255,0), thickness=2) # Green
plt.imshow(image)
I believe I can see the rectangle if I display the image with cv2.imshow(), but I don't know that OpenCV images can be displayed inline in Jupyter Notebooks like matplotlib images can.
EDIT: I think what's really going on is explained in this excerpt from the documentation:
The value for each component of MxNx3 and MxNx4 float arrays should be
in the range 0.0 to 1.0.
It can handle values between 0 and 255 if it's an integer array.

By default when you create a numpy array using np.zeros() or np.ones(), the default data type of the matrix is set to be float, or more precisely np.float64, And there seems to be some problem in matplotlib to render a matrix with float data type (As per convention the RGB values in an image are always integral, so we can't blame matplotlib for this.)
Now you have 2 options:
Create the numpy matrix with np.uint8 datatype
image = np.zeros((720, 1280, 3), dtype = np.uint8)
Convert the matrix in integral type before rendering it on plt.
image = np.astype(image, np.uint8)
Also you need to add plt.show() at the end.

Related

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.

How to save a manipulated numpy array (annotated image) as an image, using python?

I have loaded an image, converted it to an array and annotated it (56.01). Now I want to save it back as an image.
How can I do that?
With this code, the image is annotated. But I want to remove the axes and save it as an image.
from matplotlib import image
import matplotlib.pyplot as plt
ax=plt.gca()
# load image as pixel array
data = image.imread('yellow.jpg')
ax.annotate('56.05',xy=(1000, 500), xycoords='data')
# display the array of pixels as an image
plt.imshow(data)
plt.savefig('imagenet1.png', bbox_inches='tight', dpi = 1000)
plt.show()
ANNOTATED ARRAY
I WANT THIS, BUT THE ANNOTATION IS NOT HERE
You want to annotate after calling imshow, and hide the x and y axes. Alternatively you could plot things in whatever order you want as long as you provided them with a logical zorder parameter.
from matplotlib import image
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
# load image as pixel array
data = image.imread('yellow.jpg')
# display the array of pixels as an image
ax.imshow(data)
ax.annotate('56.05', xy=(1000, 500), xycoords='data')
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
fig.savefig('imagenet1.png', bbox_inches='tight', dpi=1000)
fig.show()

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:

convert RGB arrays to PIL image

In my code, I am creating a RGB array (256 * 256 * 3) and I need to show it.
I am having trouble creating a PIL image from a RGB array.
I wrote this code to explain:
import numpy as np
from PIL import Image
image = Image.open('img_test.png')
image.thumbnail((256, 256))
image = image.convert("RGB")
image = np.asarray(image, dtype=np.float32) / 255
PIL.Image.fromarray(image, "RGB").show()
I am getting this image back:
If I am using
import matplotlib.pyplot as plt
plt.imshow(image)
plt.show()
Then I am getting this image:
What am I doing wrong with this line?
PIL.Image.fromarray(image, "RGB").show()
You are expecting PIL to handle a 32-bit floating point RGB image, which it cannot - see here.
It can handle, amongst others:
RGB as three 8-bit integer values (RGB888), or
greyscale float32.
Just do
Image.fromarray(np.array(img).astype(np.uint8).transpose(1,2,0))
The line you mention seems fine, however, I wonder why you do this:
image = np.asarray(image, dtype=np.float32) / 255
If you replace that line by the following, it works for me, using either PIL or matplotlib to show the image:
image = np.asarray(image)

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