Skimage rgb2gray reduces one dimension - python

I am trying to convert multiple RGB images to grayscale. However, I am loosing one dimension
# img is an array of 10 images of 32x32 dimensions in RGB
from skimage.color import rgb2gray
print(img.shape) # (10, 32, 32, 3)
img1 = rgb2gray(img)
print(img.shape) # (10, 32, 3)
As you can see, though the shape of img is expected to be (10, 32, 32, 1), it is coming out as (10, 32, 3).
What point am I missing?

This function assumes the input to be one single image of dims 3 or 4 (with alpha).
(As your input has 4 dimensions, it's interpreted as single image of RGB + Alpha; not as N images of 3 dimensions)
If you got multiple images you will need to loop somehow, like this (untested):
import numpy as np
from skimage.color import rgb2gray
print(img.shape) # (10, 32, 32, 3)
img_resized = np.stack([rgb2gray(img[i]) for i in range(img.shape[0])])

Related

cv2 image size is transposed from PIL Image

I have an image of size 72x96. Windows says its size is 72x96. PIL Image also says it is 72x96:
from PIL import Image, ImageOps
with Image.open(<path>) as img:
print(img.size) # (72, 96)
print(ImageOps.exif_transpose(img).size) # (72, 96)
But when I read the image with cv2.imread or skimage.io.imread it says, that the shape of the image is (96, 72, 3):
from skimage.io import imread
im0 = imread(<path>)
print(im0.shape) # (96, 72, 3)
What is wrong here? Even if I do something like that:
import matplotlib.pyplot as plt
plt.imshow(im0)
It shows the image with the correct size, but the written size looks to be transposed.
This is expected behavior.
PIL returns the size of an image as (width, height) (PIL documentation), whereas numpy returns the shape of an array as the lengths of the first and then second dimension (in the case of a 2d array), so (height, width) (Numpy documentation).

After changing the image to numpy array, I want to import only 1 channel

I took some images and replaced them with numpy array.
The image is a RGB image.
The converted numpy array is of size (256, 256, 3).
I wanted to import only the Y channel after I switched this RGB image to YCbCr.
What I want is an array of size (256,256, 1).
So I used [:,:, 0] in the array.
However, I have now become a two-dimensional image as shown in the code below.
I created an array of (256, 256, 1) size with 15 lines of code.
But I failed to see it again as an image.
Below is my code.
from PIL import Image
import numpy as np
img = Image.open('test.bmp') # input image 256 x 256
img = img.convert('YCbCr')
img.show()
print(np.shape(img)) # (256, 256, 3)
arr_img = np.asarray(img)
print(np.shape(arr_img)) # (256, 256, 3)
arr_img = arr_img[:, :, 0]
print(np.shape(arr_img)) # (256, 256)
arr_img = arr_img.reshape( * arr_img.shape, 1 )
print(np.shape(arr_img)) # (256, 256, 1)
pi = Image.fromarray(arr_img)
pi.show # error : TypeError: Cannot handle this data type
When I forcibly changed a two-dimensional image into a three-dimensional image,
The image can not be output.
I want to have a purely (256, 256, 1) sized array.
Y image of the channel!
I tried to use arr_img = arr_img [:,:, 0: 1] but I got an error.
How can I output an image with only Y (256,256,1) size and save it?
A single-channel image should actually be in 2D, with shape of just (256, 256). Extracting out the Y channel is effectively the same as having a greyscale image, which is just 2D. Adding the third dimension is causing the error because it is expecting just the two dimensions.
If you remove the reshape to (256, 256, 1), you will be able to save the image.
Edit:
from PIL import Image
import numpy as np
img = Image.open('test.bmp') # input image 256 x 256
img = img.convert('YCbCr')
arr_img = np.asarray(img) # (256, 256, 3)
arr_img = arr_img[:, :, 0] # (256, 256)
pi = Image.fromarray(arr_img)
pi.show()
# Save image
pi.save('out.bmp')
Try this:
arr_img_1d = np.expand_dims(arr_img, axis=1)
Here is the numpy documentation for the expand_dims function.

Image processing - How to stack one channel images to create a 2 channel array

i have two datasets of images, one with 900 samples and another with other 900 samples. If i take just one image of each dataset and convert them to array they are of shape (1, 128, 118), after the images are read in grayscale mode.
How could i stack these two arrays to be (2, 128, 118)?
import numpy as np
import cv2
img1 = cv2.imread(...)
img2 = cv2.imread(...)
np.stack([img1,img2],axis=0)

Loss of image color when I use PIL convert on numpy array (image)

I am trying to use PIL with numpy array and then find the dominant color. First, my image is a numpy array (28,28) and is not read from a file. I use the following code to get the dominant color from that image.
from PIL import Image
import numpy as np
myimage=np.random.randint(255,size=(28,28))
image = Image.fromarray(myimage,mode="P") # I use mode=P because if I didn't I will get an error in the next line.
result = image.convert('P', palette=Image.ADAPTIVE, colors=1)
result.putalpha(0)
colors = result.getcolors()
Now colors is always black [(784,(14,14,14,0)], however, if I saved myimage to a file and use image=Image.open(myimage.png) I get the correct colors=[(5807904, (222, 158, 23, 0))]. Any ideas why is that happening and how to fix it. The reason I am not saving the image to a files first is because of efficiency measures because I have thousands of images that I loop within them.
Thanks,
The image generated with your code does not contain color data, so no wonder you don't get them. image should be created with:
# note the final '4' indicating the number of channels
# 4 for RGBA, 3 for RGB, 1 for grayscale
myimage = np.random.randint(255, size=(28, 28, 4))
image = Image.fromarray(myimage, mode='RGBA')
This now simulates a random color image with alpha channel.
Now the rest of the code is a mystery to me, in the sense that I don't know exactly what you expect, but from my tests on an image loaded from disk and from the simulated data I get similar results:
from PIL import Image
import numpy as np
import scipy.misc
# for reproducible results
np.random.seed(0)
def _calc(pil_image):
result = pil_image.convert(mode='P', palette=Image.ADAPTIVE, colors=1)
result.putalpha(0)
colors = result.getcolors()
print(colors)
myimage = np.random.randint(255, size=(28, 28, 3))
image = Image.fromarray(myimage, mode='RGBA')
_calc(image)
myimage = np.random.randint(255, size=(28, 28, 4))
image = Image.fromarray(myimage, mode='RGBA')
_calc(image)
myimage = scipy.misc.face(False)
image = Image.fromarray(myimage, mode='RGB')
_calc(image)
import urllib.request
urllib.request.urlretrieve(
'https://www.python.org/static/img/python-logo.png',
'python-logo.png')
image = Image.open('python-logo.png')
_calc(image)
And I obtain, I believe correctly:
[(784, (6, 0, 0, 0))]
[(784, (7, 0, 0, 0))]
[(786432, (111, 118, 102, 0))]
[(23780, (254, 254, 254, 0))]

Image.fromarray changes size

I have data that I want to store into an image. I created an image with width 100 and height 28, my matrix has the same shape. When I use Image.fromarray(matrix) the shape changes:
from PIL import Image
img = Image.new('L', (100, 28))
tmp = Image.fromarray(matrix)
print(matrix.shape) # (100, 28)
print(tmp.size) # (28, 100)
img.paste(tmp, (0, 0, 100, 28) # ValueError: images do not match
When I use img.paste(tmp, (0, 0)) the object is pasted into the image, but the part starting with the x value 28 is missing.
Why does the dimension change?
PIL and numpy have different indexing systems. matrix[a, b] gives you the point at x position b, and y position a, but img.getpixel((a, b)) gives you the point at x position a, and y position b. As a result of this, when you are converting between numpy and PIL matrices, they switch their dimensions. To fix this, you could take the transpose (matrix.transpose()) of the matrix.
Here's what's happening:
import numpy as np
from PIL import Image
img = Image.new('L', (100, 28))
img.putpixel((5, 3), 17)
matrix = np.array(img)
print matrix[5, 3] #This returns 0
print matrix[3, 5] #This returns 17
matrix = matrix.transpose()
print matrix[5, 3] #This returns 17
print matrix[3, 5] #This returns 0
NumPy and PIL have different indexing systems. So a (100, 28) numpy array will be interpreted as an image with width 28 and height 100.
If you want a 28x100 image, then you should swap the dimensions for your image instantiation.
img = Image.new('L', (28, 100))
If you want a 100x28 image, then you should transpose the numpy array.
tmp = Image.fromarray(matrix.transpose())
More generally, if you're working with RGB, you can use transpose() to only swap the first two axes.
>>> arr = np.zeros((100, 28, 3))
>>> arr.shape
(100, 28, 3)
>>> arr.transpose(1, 0, 2).shape
(28, 100, 3)

Categories