Python - Numpy RGB pixel array to image - python

I have an array of pixels
np.shape(pred2)
Out[35]: (3000, 3, 32, 32)
It has 3000 images, 3 values rgb and is 32*32 in size for each image. I want to create an image from this.
Here is what I have so far:
img = Image.new( 'RGB', (32,32), "black") # create a new black image
pixels = img.putdata(pred2[1,:])
Can anyone give me a hand here as to what I am doing wrong?

Images are shape (h, w, 3), not (3, h, w). You need to permute your axes accordingly. Depending on whether you care about width vs height, it appears you can just do:
im = pred2[1].T
scipy.misc.imsave('the_image_file.png', im)

Related

How to iterate over multiple images of different dimensions and stack them into a single picture horizontally?

I have a multiple pictures with different dimensions. I have been trying to concat them horizontally using openCV.
The process is kind of following:
Iterate over all the images to find the max width and total height.
Create a black mask that is with the size of max width and total height got from all the images.
Stack all the images horizontally on that black mask.
I am not sure how to do this thing. Kindly help me!
Images are just 3D matrices, so you can do this very easily by creating a matrix of zeros (= black) of the desired size, then filling in your images.
I've created fake images here but you can use cv2 to read in your real images.
import numpy as np
import matplotlib.pyplot as plt
# create three images of different shapes and different shades of grey
img1 = np.ones((100, 200, 3), dtype=int)*50
img2 = np.ones((200, 400, 3), dtype=int)*100
img3 = np.ones((100, 300, 3), dtype=int)*150
imgs = [img1, img2, img3]
# get max width and total height
max_width = 0
total_height = 0
for img in imgs:
total_height += img.shape[0]
max_width = max(max_width, img.shape[1])
# make black canvas of appropriate shape
canvas = np.zeros((total_height, max_width, 3), dtype=int)
# stack images on canvas
start_height = 0
for img in imgs:
print(img.shape)
canvas[start_height:start_height+img.shape[0], 0:img.shape[1], :] = img
start_height+= img.shape[0]
# show results
plt.imshow(canvas)
This produces the following result:

OpenCV - I need to insert color image into black and white image and

I insert black and white image into color image with this code and it is ok:
face_grey = cv.cvtColor(face, cv.COLOR_RGB2GRAY)
for row in range(0, face_grey.shape[0]):
for column in range(face_grey.shape[1]):
img_color[row + 275][column + 340] = face_grey[row][column]
plt.imshow(img_color)
but, when i try to insert color image into black and white image with this code, i get error:
img_gray = cv.cvtColor(img, cv.COLOR_RGB2GRAY)
for row in range(0, face.shape[0]):
for column in range(face.shape[1]):
img_gray[row + 275][column + 340] = face[row][column]
plt.imshow(img_gray)
TypeError: only size-1 arrays can be converted to Python scalars
ValueError: setting an array element with a sequence.
A colour image needs 3 channels to represent red, green and blue components.
A greyscale image has only a single channel - grey.
You cannot put a 3-channel image in a 1-channel image any more than you can put a 3-pin plug in a single hole socket.
You need to promote your grey image to 3 identical channels first:
background = cv2.cvtColor(background, cv2.COLOR_GRAY2BGR)
Example:
# Make grey background, i.e. 1 channel
background = np.full((200,300), 127, dtype=np.uint8)
# Make small colour overlay, i.e. 3 channels
color = np.random.randint(0, 256, (100,100,3), dtype=np.uint8)
# Upgrade background from grey to BGR, i.e. 1 channel to 3 channels
background = cv2.cvtColor(background, cv2.COLOR_GRAY2BGR)
# Check its new shape
print(background.shape) # prints (200, 300, 3)
# Paste in small coloured image
background[10:110, 180:280] = color
A color image has the shape (n,m,3) for the three color channels rgb.
A gray scale image has the shame (n,m,1) as the color information is thrown away.
The error happens in this line:
img_gray[row + 275][column + 340] = face[row][column]
img_gray[row+275][column + 340] expects only one value because img_gray has the a shape (height, width, 1).
face[row][column] has a shape (3,)
You would see that if you do the following:
print(f"{ img_gray[row+275][column + 340].shape} and {face[row][column].shape}")

Numpy array not displaying color dimension of greyscale image after converting from PIL image

I'm trying to convert an RGB image to a greyscale image, then to a numpy array using the following code snippet:
img = Image.open("image1.png")
img = img.convert('L')
img = np.array(img, dtype='f')
print(img.shape)
The result is a numpy array of shape (128, 128). Is there anyway that I could convert a greyscale image to a numpy array so that it would have the color channel as well, i.e. the shape would be (128, 128, 1)?
Like #Mark mentioned in comments, add a dimension to the end if your array using newaxis:
img=img[...,None]
None will do similar as np.newaxis. It does not create a color, but adds a dimension similar to a single channel image.

Converting multiple numpy images to gray scale

I currently have a numpy array 'images' containing 2000 photos. I am looking for an improved way of converting all the photos in 'images' to gray scale. The shape of the images is (2000, 100, 100, 3). This is what I have so far:
# Function takes index value and convert images to gray scale
def convert_gray(idx):
gray_img = np.uint8(np.mean(images[idx], axis=-1))
return gray_img
#create list
g = []
#loop though images
for i in range(0, 2000):
#call convert to gray function using index of image
gray_img = convert_gray(i)
#add grey image to list
g.append(gray_img)
#transform list of grey images back to array
gray_arr = np.array(g)
I wondered if anyone could suggest a more efficient way of doing this? I need the output in an array format
With your mean over the last axis you do right now:
Gray = 1/3 * Red + 1/3 * Green + 1/3 * Blue
But actually another conversion formula is more common (See this answer):
Gray = 299/1000 * Red + 587/1000 * Green + 114/1000 * Blue
The code provided by #unutbu also works for arrays of images:
import numpy as np
def rgb2gray(rgb):
return np.dot(rgb[...,:3], [0.2989, 0.5870, 0.1140])
rgb = np.random.random((100, 512, 512, 3))
gray = rgb2gray(rgb)
# shape: (100, 512, 512)

Compare multiple images based on pixel values

Hi I am trying to compare a set of images where I want to produce a final image with maximum pixels from each individual image. Say I have 3 images (converted to 10x10 matrix) in which I know the pixel values of each block> Now I want to compare these values block by block and produce a final separate image with maximum block values from each image.
To do so I have changed the image dimensions(250x250) such that each block is a square of 25x25
I even tried comparing two images and taking the maximum pixels from both the images and displaying them
image = cv2.resize(im,(250,250))
hs,ws,c= image.shape
print(hs, ws,c)
hs = round(h/10)
ws = round(w/10)
resized = cv2.resize(image, (ws,hs), interpolation = cv2.INTER_AREA)
cv2.imshow("Resized image", resized)
cv2.waitKey(0)
cv2.destroyAllWindows()
print(list(resized))
#comparing two images
data = np.maximum.reduce([resized,resized1])
from matplotlib import pyplot as plt
plt.imshow(data, interpolation='nearest')
plt.show()
So these are two images and the tiled image is the result of the block #comparing two images. Now using these two images I want to produce a final image similar to the original photos not the tiled version but the final photo should have pixels from both original photos(maximum from each). I hope it clears the question
For better visualization purposes, I skip the whole resizing part you got earlier, and stick to resized (250 x 250) images.
My approach is the following: Store all your resized images in some NumPy array with dimensions (width x height x numberOfChannels x numberOfImages), and then use NumPy's max along axis=3, such that you get a final image (width x height x numberOfChannels) with maximum BGR values (or grayscale, if desired) over all images.
Here's some exemplary code:
import cv2
import numpy as np
# Set up empty images array
width, height, nChannels, nImages = (250, 250, 3, 3)
images = np.zeros((width, height, nChannels, nImages), np.uint8)
# Read and resize exemplary input images
images[:, :, :, 0] = cv2.resize(cv2.imread('WeQow.png'), (width, height))
images[:, :, :, 1] = cv2.resize(cv2.imread('gIHOd.png'), (width, height))
images[:, :, :, 2] = cv2.resize(cv2.imread('lAdfO.jpg'), (width, height))
# Generate maximum image along last axis, i.e. the images.
# For each BGR value you get the maximum over all images.
image = images.max(axis=3)
# Show images
cv2.imshow('image0', images[:, :, :, 0])
cv2.imshow('image1', images[:, :, :, 1])
cv2.imshow('image2', images[:, :, :, 2])
cv2.imshow('image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
These are the three input images:
The final output image then looks like this:
Hope that helps!
You can try using block_reduce function with block size (no_of_images, channels, block_height, block_width).
skimage.measure.block_reduce(np.asarray([image1, image2, image3,...]), (no_of_images, channels, 1, 1), np.max)
If your input images are in RGB scale and you want per-channel max value (output also RGB image) among images just replace channels value with 1 or if you want max value across channels among the images then use 3 as channels value that will generate a gray scale image.

Categories