python - RGB matrix of an image - python

Taking an image as input, how can I get the rgb matrix corresponding to it?
I checked out the numpy.asarray function. Does that give me the rgb matrix or some other matrix?

Note that this answer is outdated as of 2018; scipy has deprecated imread, and you should switch to imageio.imread. See this transition doc about differences between the two. The code below should work with no changes if you just import the new library in place of the old, but I haven’t tested it.
The simplest answer is to use the NumPy and SciPy wrappers around PIL. There's a great tutorial, but the basic idea is:
from scipy import misc
arr = misc.imread('lena.png') # 640x480x3 array
arr[20, 30] # 3-vector for a pixel
arr[20, 30, 1] # green value for a pixel
For a 640x480 RGB image, this will give you a 640x480x3 array of uint8.
Or you can just open the file with PIL (or, rather, Pillow; if you're still using PIL, this may not work, or may be very slow) and pass it straight to NumPy:
import numpy as np
from PIL import Image
img = Image.open('lena.png')
arr = np.array(img) # 640x480x4 array
arr[20, 30] # 4-vector, just like above
This will give you a 640x480x4 array of type uint8 (the 4th is alpha; PIL always loads PNG files as RGBA, even if they have no transparency; see img.getbands() if you're every unsure).
If you don't want to use NumPy at all, PIL's own PixelArray type is a more limited array:
arr = img.load()
arr[20, 30] # tuple of 4 ints
This gives you a 640x480 PixelAccess array of RGBA 4-tuples.
Or you can just call getpixel on the image:
img.getpixel(20, 30) # tuple of 4 ints

I have a feeling I'm not doing exactly what you wanted here, so please specify if this is totally off. You could open the image like this and get an array of pixels:
import Image
im = Image.open('Lenna.png')
pixels = list(im.getdata())
This will get you a flat list of RGB data that looks like
[(226, 137, 125), (226, 137, 125), (223, 137, 133), (223, 136, 128),
(226, 138, 120), (226, 129, 116), (228, 138, 123), (227, 134, 124),
(227, 140, 127), (225, 136, 119), (228, 135, 126), (225, 134, 121),...
Now this will be all pixels in a flat array, if you want a two dimensional array then some additional code would be needed for that. Not sure if there is a direct function for it in PIL.

I tried imageio.imread and it worked great, but a minute later stumbled upon a function in matplotlib which worked exactly the same, getting a numpy n by m by 3 array:
from matplotlib import pyplot as plt
image = plt.imread(path)

You can do that with Pillow, the getdata method gives you a flat array of the pixels, you can then build a matrix from that using the size of the image.
from PIL import Image
def getPixels(filename):
img = Image.open(filename, 'r')
w, h = img.size
pix = list(img.getdata())
return [pix[n:n+w] for n in range(0, w*h, w)]

Also to add, if you or anyone else is using opencv.
imgc=cv2.imread(file)
or to read in as grayscale
imgc=cv2.imread(file,0)
If you will be doing some comparison between the images you may want to think about turning the array of pixels into histograms to normalise the data.
hist = np.histogram(img.flatten(),256,[0,256])[0]
The above line firstly flattens your img array so you do lose the dimensionality of your image. It then produces bins from 0 to 256 (for the grayscale image) and adds the counts from the img to these bins and returns them as hist which can then be plotted. For example, if the 100 bin has a value of 20 it means that 20 pixels in your image had a value of 100.
Hope this adds another possiblity to think about or to anyone looking to get started in opencv.

Related

Grayscale image as np.array has shape (100, 80, 3), how?

I am currently working on a neural network working with grayscale images in form of numpy arrays. For some reason however I occasionally get images with the shape (.., .., 3) even though I should only be getting arrays with the shape (.., ..). This means that for some reason some images represent their grayscale color like this: [100, 100, 100] instead of just 100. Is there an effective way to fix this or simply to replace the [x, x, x] with an x?
Here is the code I use to import images and convert them to be black and white:
from PIL import Image
img = Image.open(Filepath)
img.convert("1")
print(np.array(img).shape) # -> (.., .., 3)??
The .convert() method returns a copy of the image, so you will need to assign it to a variable.
from PIL import Image
img = Image.open(filepath)
img = img.convert("1")
# Or img = Image.open(filepath).convert("1")

How to correctly convert RGB images into nifty format?

I have RGB images which I'm loading as a numpy array. I would like to convert these images into the nifty format, which I can open using ITK-SNAP (http://www.itksnap.org/pmwiki/pmwiki.php).
Here is what I have tried to do:
import nibabel as nib
import numpy as np
x = load_jpg_image(filename='input.jpg') # --> x is a numpy array containing the RGB image with shape (128, 128, 3)
img = nib.Nifti1Image(x, eye(4))
nib.save(img, filename='output.nii')
However, ITK-SNAP interprets output.nii as a 3D grayscale volume rather than an RGB image. To me, it seems that ITK-SNAP should be able to handle RGB data (see this); however, I don't understand how I should save img to make this possible. I'm using ITK-SNAP 3.6.0.
unfortunately NIfTI was never really overly developed for RGB images. You can see in the latest NIfTI2 spec, the RGB and RGBA voxel types are defined (RGB having 3 bytes per pixel, RGBA 4 bytes) but I'm not aware of any tools that process these images.
The difference with your case is that the dimensions of the images are the number of pixels and the colour channels are within the pixel. It looks like ITK-snap displays colour NIfTI images correctly from version 2 -- I guess they follow this format.
It seems you can create RGB images by casting them in a custom dtype:
import nibabel as nib
import numpy as np
RGB_DTYPE = np.dtype([('R', 'u1'), ('G', 'u1'), ('B', 'u1')])
x = load_jpg_image(filename='input.jpg') # --> x is a numpy array containing the RGB image with shape (128, 128, 3)
# cast to custom type:
x = x.copy().view(dtype=RGB_DTYPE) # copy used to force fresh internal structure
img = nib.Nifti1Image(x, eye(4))
nib.save(img, filename='output.nii')
ITK-SNAP can handle this type of image by right-clicking the image name on the left panel and selecting the option: Multi-Component Display -> RGB.

Convert a one dimensional dataframe into a 3 dimensional for RGB Image

I have a data frame of 2304 columns , as it is a 48*48 image pixels, when I convert it into one channel using this code
x = (df.iloc[:,1:].values).astype('float32')
x = x.reshape(-1,48,48,1)
its perfectly output of shape
(48*48*1)
with generating exact image by this code:
plt.imshow(x[0][:,:,0])
I want to make it into a 3Dimentional like in three channels. I try to merged the df 3 times and do this (48*48*3) it successfully change the df shape but I cannot generate the image again,
If you essentially want to convert a single channel image (which should essentially be a greyscale image) into a 3 channel greyscale image, its the same as concatenating the same image array thrice along the last axis. You can use np.concatenate to achieve the desired result.
import numpy as np
a = np.zeros((2304), dtype = np.uint8) #Just a dummy array representing a single pic
single_channel = a.reshape(48, 48, 1)
result = np.concatenate([single_channel,single_channel,single_channel], axis = -1)
print(result.shape) #(48, 48, 3)
At this point you should have an array that can be accepted by any image library. Just throwing a sample code to show how you may proceed to create the image from the array.
import cv2
cv2.imwrite("hi.jpg", result)
As stated earlier, use numpy instead of pandas for image manipulation.
EDIT: If you were unfortunately starting with a dataframe in the first place, you can always convert it to a numpy array with an extra dimension representing each image.
import pandas as pd
import cv2
import numpy as np
a = np.zeros((2304), dtype = np.uint8) #dummy row
dummy_df = pd.DataFrame(np.concatenate([a.reshape(1,-1)]*10)) #dummy df with 10 rows.
print(dummy_df.shape) #(10, 2304)
arr_images = np.array(dummy_df, dtype = np.uint8)
print(arr_images.shape) #(10, 2304)
multiple_single_channel = arr_images.reshape(-1, 48, 48, 1)
print(multiple_single_channel.shape) #(10, 48, 48, 1)
result = np.concatenate([multiple_single_channel] * 3, axis = -1)
print(result.shape) #(10, 48, 48, 3)
for i,img in enumerate(result):
print(i)
cv2.imwrite("{}.jpg".format(i), img)
#do something with image. you PROBABLY don't want to run this for 35k images though.
The bottom line really is that you should not need to use a dataframe, even for multiple images.
1)Dont use pandas
2) you cant transform 1channel image into 3 channels,
3) Dont use float32, images are usually 8bit (np.uint8)
4) use numpy in combination with OpenCV or with Pillow.
5) Dont use matplotlib to generate images. use libraries mentioned in 4.
6) if you have array with shape (x,y,3) there is nothing more simply than generate image with opencv cv2.imshow('image',array)

Error in Counting Non-Zero Pixels

I would like to count the non-zero pixel of an image without converting the image into a Numpy array. Here is the piece of code I have
from PIL import Image, ImageDraw
import cv
image = Image.new('1', (100, 100))
draw = ImageDraw.Draw(image)
draw.ellipse((20, 20, 80, 80), fill ='white')
non_zeros = cv.CountNonZero(image)
However, when I run this I get the following error:
TypeError: CvArr argument 'arr' must be IplImage, CvMat or CvMatND. Use fromarray() to convert numpy arrays to CvMat or cvMatND
How can I solve this problem? either by continuing with cv.CountNonZero or any other way which is computationally cheap and efficient.

Thresholding of a grayscale Image in a range

Does OpenCV cv.InRange function work only for RGB images?Can I do thresholding of grayscale image using this function?
I got an error,Following is my code:
import cv2
image=cv2.imread("disparitySGB.jpg")
thresh=cv2.inRange(image,190,255);
It gives the following error:
thresh=cv2.inRange(image,190,255); TypeError: unknown is not a
numpy array
I tried fixing it by:
thresh=cv2.inRange(image,numpy.array(190),numpy.array(255));
Now there is no error but it produces black image.
For a gray-valued image which has shape (M, N) in numpy and size MxN with one single channel in OpenCV, then cv2.inRange takes scalar bounds:
gray = cv2.imread(filename, cv2.CV_LOAD_IMAGE_GRAYSCALE)
gray_filtered = cv2.inRange(gray, 190, 255)
But for RGB-images which have shape (M, N, 3) in numpy and size MxN with three channels in OpenCV you need to have the bounds match the "channel size".
rgb = cv2.imread(filename, cv2.CV_LOAD_IMAGE_COLOR)
rgb_filtered = cv2.inRange(gray, (190, 190, 190), (255, 255, 255))
This is explained in the documentation, although not very clearly.
cv2.inRange(src, lowerb, upperb[, dst]) → dst
Takes src as array and lowerand upper as array or a scalar, this means you can use it to Threshold Grayscale images. You just have to use scalars for upper and lower.
Example:
myResult = cv2.InRange(myGrayscale, 50, 100)
You just need to 'import numpy as np' and your original code should work fine.
Your cv2.imread is reading a RGB image. To read in grayscale it is
image = cv2.imread("disparitySGB.jpg", 0)

Categories

Resources