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.
Related
I have this code:
from PIL import Image
import numpy as np
img = Image.open('img.jpg')
Image.fromarray(np.array([[np.mean(i, axis=1).astype(int).tolist()]*len(i) for i in np.array(img).tolist()]).astype('uint8')).show()
And I am trying to modify the pixels of the image in PIL, however when I run it it gives an error as follows:
KeyError: ((1, 1, 1280), '|u1')
Not just that, it also outputs a second error as follows:
TypeError: Cannot handle this data type
Is there a way to overcome this?
P.S. I searched and the most related question to mine was:
Convert numpy.array object to PIL image object
However I don't get it nor know how to implement it.
For reading specific pixel via any image library such as PIL or OpenCV first channel of image is Height second channel is Width and last one is number of channels and here is 3. When you convert image to gray scale, third channel will be 1.
But this error happen when you want to convert a numpy array to PIL image using Image.fromarray but it shows the following error:
KeyError: ((1, 1, 3062), '|u1')
Here you could see another solution:
Convert numpy.array object to PIL image object
the shape of your data.
Pillow's fromarray function can only do a MxNx3 array (RGB image), or an MxN array (grayscale). To make the grayscale image work, you have to turn you MxNx1 array into a MxN array. You can do this by using the np.reshape() function. This will flatten out the data and then put it into a different array shape.
img = img.reshape(M, N) #let M and N be the dimensions of your image
(add this before the img = Image.fromarray(img))
I am not certain what you are trying to do but if you want the mean:
from PIL import Image
import numpy as np
img = Image.open('img.jpg')
# Make Numpy array
imgN = np.array(img)
mean = np.mean(imgN,axis=2)
# Revert back to PIL Image from Numpy array
result = Image.fromarray(mean)
Alternatively, if you want a greyscale which is an alternative to the mean
from PIL import Image
import numpy as np
img = Image.open('img.jpg').convert('L')
now I have starting cord(79,143) and end cord(200,100), width 500 and height 500 of an image and I wan to use them to save a binary mask like pic.
I can use the skimage to save it,but the line width seems fixed,and I do want to use cv2, so is there any other solution to save the mask with custom line width?
and meanwhile ,I have a cv2 program, but it does not work,
I have a program:
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = np.zeros((1080,1080,3),np.uint8)
for i in range(3):
im=np.squeeze(img[:,:,i])
print(im)
imgg=cv2.line(im,(0,0),(511,511),255,5)
masks=Image.fromarray((imgg).astype(np.uint8))
masks.save("masks"+str(i)+".png")
and I want to save 3 same masks,but it gave error:
Layout of the output array img is incompatible with cv::Mat (step[ndims-1] != elemsize or step1 != elemsize*nchannels)
any idea how to solve it?
Many thanks!
Many thanks!
The OpenCV drawing line function does have a thickness parameter. You can specify it like this:
# Setup an empty image
im = np.zeros((500, 500), dtype=np.uint8)
# Draw with thickness
im = cv2.line(im, (79,143), (200, 100), color=(255, 255, 255), thickness=10)
I have code:
import pygame.camera
pygame.camera.init()
cam = pygame.camera.Camera(pygame.camera.list_cameras()[0])
cam.start()
img = cam.get_image()
The img variable is
<Surface(640x480x24 SW)>
I found the get numpy array from pygame but still I do not know how to convert it effectively to numpy array of RGB colors.
For grabbing 3D image data from class pygame.Surface, use .array3d(), as also the doc states -
Copy pixels into a 3d array
array3d(Surface) -> array
Thus, you could do -
imgdata = pygame.surfarray.array3d(img)
Please note that the resulting imgdata might appear with height and width switched. To fix that, swap the first two axes, like so -
imgdata = imgdata.swapaxes(0,1)
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.
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)