Converting multi-dimensional array to a tuple in python - python

I am getting some frame data from a web cam in the form of rgb values.
import numpy as np
frame = get_video()
print np.shape(frame)
The output is (480, 640, 3). Now I want to construct image from these values. So, I want to use
im = Image.new("RGB", (480, 640),frame)
But, here the third argument takes a tuple. I get this error
SystemError: new style getargs format but argument is not a tuple
So, my question is what is the best way to convert this frame data to a tuple, so that I can construct my image.

I am assuming here that you are importing the class Image from PIL.
The documentation for Image.new(), accessed with console command Image.new? is:
In [3]: Image.new?
Type: function
Base Class:
String Form:
Namespace: Interactive
File: /usr/lib/python2.7/dist-packages/PIL/Image.py
Definition: Image.new(mode, size, color=0)
Docstring: Create a new image
The third parameter is an RGB color, such as (255,255,255), to fill the hole image. You can't initialize individual pixels with this function.
I am also assuming that frame is a 3D array. As your output suggests, it's 480 lines and 640 rows of RGB tuples.
I don't know if there is a simpler way to do this, but I would set the pixel values by the putpixel() function, something like:
im = Image.new( "RGB", (480, 640), (0, 0, 0) ) #initialize 480:640 black image
for i in range(480):
for j in range(640):
im.putpixel( (i, j), tuple(frame[i][j]) )
Always check the docstring via console, it saves a lot of time. I also recommend using ipython as your console.

I found this implementation to be faster
from PIL import Image
im = Image.new("RGB", (480, 640), (0, 0, 0) ) #initialize 480:640 black image
while True:
frame = get_video()
im = Image.fromarray(frame)
im.save('some-name', 'JPEG')

Related

PIL TypeError: Cannot handle this data type

I have an image stored in a numpy array that I want to convert to PIL.Image in order to perform an interpolation only available with PIL.
When trying to convert it through Image.fromarray() it raises the following error:
TypeError: Cannot handle this data type
I have read the answers here and here but they do not seem to help in my situation.
What I'm trying to run:
from PIL import Image
x # a numpy array representing an image, shape: (256, 256, 3)
Image.fromarray(x)
tl;dr
Does x contain uint values in [0, 255]? If not and especially if x ranges from 0 to 1, that is the reason for the error.
Explanation
Most image libraries (e.g. matplotlib, opencv, scikit-image) have two ways of representing images:
as uint with values ranging from 0 to 255.
as float with values ranging from 0 to 1.
The latter is more convenient when performing operations between images and thus is more popular in the field of Computer Vision.
However PIL seems to not support it for RGB images.
If you take a look here
it seems that when you try to read an image from an array, if the array has a shape of (height, width, 3) it automatically assumes it's an RGB image and expects it to have a dtype of uint8!
In your case, however, you have an RBG image with float values from 0 to 1.
Solution
You can fix it by converting your image to the format expected by PIL:
im = Image.fromarray((x * 255).astype(np.uint8))
I solved it different way.
Problem Situation:
When working with gray image or binary image, if the numpy array shape is (height, width, 1), this error will be raised also.
For example, a 32 by 32 pixel gray image (value 0 to 255)
np_img = np.random.randint(low=0, high=255, size=(32, 32, 1), dtype=np.uint8)
# np_img.shape == (32, 32, 1)
pil_img = Image.fromarray(np_img)
will raise TypeError: Cannot handle this data type: (1, 1, 1), |u1
Solution:
If the image shape is like (32, 32, 1), reduce dimension into (32, 32)
np_img = np.squeeze(np_img, axis=2) # axis=2 is channel dimension
pil_img = Image.fromarray(np_img)
This time it works!!
Additionally, please make sure the dtype is uint8(for gray) or bool(for binary).
In my case it was only because I forgotted to add the "RGB" arg in the "fromarray" func.
pil_img = Image.fromarray(np_img, 'RGB')
I found a different issue for the same error in my case. The image I used was in RGBA format, so before using fromarray() function just convert it to RGB using the convert() function and it will work perfectly fine.
image_file = Image.open(image_file)
image_file = image_file.convert('RGB')
P.S.: Posting this solution as an initial step, before converting the image to np.
In my case file format of the images was changed to png to jpg. It worked well when I corrected the image format of the error images.
According to the scikit-image's documentation, you can convert an image to unsigned byte format, with values in [0, 255] using img_as_ubyte:
from skimage import img_as_ubyte
from PIL import Image
new_image = Image.fromarray(img_as_ubyte(image))

PIL convertion to CMYK does not set K component

Using pythons PIL from Pillow 5.2 to convert images to CMYK I want to do some rule-of-thumb estimationswith the results.
The conversion using Image.convert("CMYK") seems to never use the K component.
Since I need it for printing purposes. I wish to same save color ink and use black ink whenever it is possible.
I could do these convertion manually using this code which produces the results I expect. But I do not only have RGB sources and I want to prevent me from converting my sources to RGB by PIL and then use this code to convert it to CMYK.
Is there a better PIL-native way?
example:
img = Image.open("5procent_gray.png")
im = img.load()
print(im[0,0])
img2 = img.convert("CMYK")
im2 = img2.load()
print(im2[0,0])
returns
(50, 50, 50)
(205, 205, 205, 0)
instead of (0,0,0,205) which I would expect.

How to check and convert image data types in OpenCV using Python

I'm using various methods in OpenCV to preprocess some images. I often get errors relating to the data type when passing objects been methods eg:
import cv2
import numpy as np
#import image and select ROI
image1 = cv2.imread('image.png')
roi_1 = cv2.selectROI(image1) # spacebar to confirm selection
cv2.waitKey(0)
cv2.destroyAllWindows()
# preprocesssing
imCrop_1 = image1[int(roi_1[1]):int(roi_1[1]+roi_1[3]), int(roi_1[0]):int(roi_1[0]+roi_1[2])]
grey1 = cv2.cvtColor(imCrop_1, cv2.COLOR_RGB2GRAY)
thresh, bw_1 = cv2.threshold(grey1, 200, 255, cv2.THRESH_OTSU)
canny_edge1 = cv2.Canny(bw_1, 50, 100)
#test=roi_1 # Doesn't work with error: /home/bprodz/opencv/modules/photo/src/denoising.cpp:182: error: (-5) Type of input image should be CV_8UC3 or CV_8UC4! in function fastNlMeansDenoisingColored
#test = imCrop_1 # works
#test = grey1 # doesn't work with error above
#test = bw_1 # doesn't work with error above
#test = canny_edge1 # doesn't work with error above
dst = cv2.fastNlMeansDenoisingColored(test,None,10,10,7,21)
# Check object types
type(imCrop_1) # returns numpy.ndarray - would like to see ~ CV_8UC3 etc.
type(grey1) # returns numpy.ndarray
Presently I just use trial and error, is a there a more methodical approach that I can use for checking and converting between different object types?
You are probably using wrong method, for this purpose, you may also get a hint from the method name you are using, as per documentation of cv2. fastNlMeansDenoisingColored:
src – Input 8-bit 3-channel image.
dst – Output image with the same size and type as src .
So, if want to use cv2. fastNlMeansDenoisingColored then you need to convert the src mat to a 3-channel matrix which can be done as:
cv2.cvtColor(src, cv2.COLOR_GRAY2BGR)
But if you have gray-scale image, then you may use cv2.fastNlMeansDenoising, which accepts the both single channel and three channel as source mats, and save the step for converting the image.
You can also use img.shape to check the number of channels for a given matrix. it would return (100, 100) for a gray-scale matrix, (100, 100, 3) for 3-channel matrix and (100, 100, 4) for 4-channel matrix. You can also get the type of matrix using img.dtype

src data type 17 not supported error with OpenCV Python

I want to take screenshots of a particular region of my screen with ImageGrab and convert the image to a numpy array to analyze with OpenCV. However I stumbled upon a src data type 17 error which I keep getting only at random when I change the parameters of the grab function. So for example when the parameters are: (10, 10, 50, 40) it prints out a normal numpy array which can then be converted to grayscale with opencv, however with other parameters such as: (100, 100, 100, 100) it keeps giving a src data type = 17 is not supported error. When I try to print out the numpy array it also doesn't show the array but only place in memory with size 0x0, for example: <PIL.Image.Image image mode=RGB size=0x0 at 0x532C570> Below is example of the code. Appreciate it if anybody could help me explain why this happens and perhaps suggest the fix for it.
import cv2
import numpy as np
from PIL import ImageGrab
while True:
img = ImageGrab.grab(bbox=(10, 10, 50, 40))
img_np = np.array(img)
print img_np
#img_grayscaled = cv2.cvtColor(img_np, cv2.COLOR_BGR2GRAY)
PIL's bounding box is a 4-tuple defining the left, upper, right, and lower pixel coordinates, see docs for getbbox. So (100, 100, 100, 100) does not give a proper image (zero height and width).

How to create a white image in Python?

Upon doing my homework, I stumbled across a problem concerning Python and image manipulation. I must say, using the Image lib is not an option. So here it is
from scipy.misc import imread,imsave
from numpy import zeros
imga = zeros([100,100,3])
h = len(imga)
w = len(imga[0])
for y in range(h):
for x in range(w):
imga[y,x] = [255,255,255]
imsave("Result.jpg",imga)
I would assume it makes my picture white, but it turns it black, and I have no idea why
It's not about the code (and I know it looks very ugly). Its just about the fact, that it is a black image.
Every color in an image is represented by one byte. So to create an image array, you should set it's dtype to uint8.
And, you don't need for-loop to set every elements to 255, you can use fill() method or slice index:
import numpy as np
img = np.zeros([100,100,3],dtype=np.uint8)
img.fill(255) # or img[:] = 255
Easy!
Check the below Code:
whiteFrame = 255 * np.ones((1000,1000,3), np.uint8)
255 is the color for filling the bytes.
1000, 1000 is the size of the image.
3 is the color channel for the image.
And unit8 is the type
Goodluck
Here's a simple way to create a white image with a python one liner.
$ python3 -c "from PIL import Image;Image.new('RGB', (1900, 1080), color = (255,255,255)).save('Img.jpg')"
This will create a white image with a width of 1900 and hight of 1080.
When creating imga, you need to set the unit type. Specifically, change the following line of code:
imga = zeros([100,100,3], dtype=np.uint8)
And, add the following to your imports:
import numpy as np
That gives a white image on my machine.
The headline is too broad and shows up at Google first. I needed a white image and used PIL and numpy. PILlow actually works well with numpy
import numpy as np
from PIL import Image
img = np.zeros([100,100,3],dtype=np.uint8)
img.fill(255) # numpy array!
im = Image.fromarray(img) #convert numpy array to image
im.save('whh.jpg')
Just regarding the headline of this question, I did need a white image as well as a pillow input. And the solutions presented here did not work for me.
Therefore here a different way to generate white images for other purposes:
from PIL import Image
img = Image.new('RGB', (200, 50), color = (255,255,255))
Size and color may be changed in the 2nd and 3rd parameter of the Image.new()-function.
And if you want to write something on this image or save it, this would be example code for this.
from PIL import ImageFont, ImageDraw
fnt = ImageFont.truetype("Pillow/Tests/fonts/FreeMono.ttf", 30)
ImageDraw.Draw(img).text((0,0), "hello world", font=fnt, fill=(0,0,0))
img.save('test.jpg')
# Create an array with a required colours
# The colours are given in BGR [B, G, R]
# The array is created with values of ones, the size is (H, W, Channels)
# The format of the array is uint8
# This array needs to be converted to an image of type uint8
selectedColor = [75, 19, 77] * np.ones((640, 480, 3), np.uint8)
imgSelectedColor = np.uint8(np.absolute(selectedColor))

Categories