I'm just starting to use OpenCV with Python and am trying to do some simple things. To start with, I tried to create a solid Blue image (or possibly Red, if the image turns out to be RGB, instead of BGR).
I tried the following:
import numpy as np
import cv2
img1 = np.zeros((512,512,3), np.uint8) #Create black image
img1[0,:,:] = 200 #Add intenstity to blue (red?) plane
print img1 #Verify image array
cv2.imshow("II",img1)
cv2.waitKey(0)
cv2.destroyAllWindows()
for _ in range (1,5):
cv2.waitKey(1)
But, what I got was a black image. I'm fairly certain the array was correct, because the print statement gave me the following:
[[[200 200 200]
[200 200 200]
[200 200 200]
...,
[200 200 200]
[200 200 200]
[200 200 200]]
[[ 0 0 0]
[ 0 0 0]
[ 0 0 0]
...,
[ 0 0 0]
[ 0 0 0]
[ 0 0 0]]
[[ 0 0 0]
[ 0 0 0]
[ 0 0 0]
...,
[ 0 0 0]
[ 0 0 0]
[ 0 0 0]]
...,
[[ 0 0 0]
[ 0 0 0]
[ 0 0 0]
...,
[ 0 0 0]
[ 0 0 0]
[ 0 0 0]]
[[ 0 0 0]
[ 0 0 0]
[ 0 0 0]
...,
[ 0 0 0]
[ 0 0 0]
[ 0 0 0]]
[[ 0 0 0]
[ 0 0 0]
[ 0 0 0]
...,
[ 0 0 0]
[ 0 0 0]
[ 0 0 0]]]
Does it make sense that I'm seeing a black instead of a blue (or red?) image?
What you're doing is changing color of 0th row. Instead, you need to change the values of first or 0th channel.
img[:, :, 0] = 255
This will change all the values of first or 0th channel to 255 which would give you blue image as it is a BGR Image.
You need to specify the colors as a tuple ! if you want a RGB image because very Index in array is a pixel and you need 3 value for B,G,R ( opencv set the pixels as BGR)
import numpy as np
import cv2
img1 = np.zeros((512,512,3), np.uint8) #Create black image
img1[:,:] = (255,0,0) #Add intenstity to blue (red?) plane
print img1 #Verify image array
cv2.imshow("II",img1,)
cv2.waitKey(0)
cv2.destroyAllWindows()
for _ in range (1,5):
cv2.waitKey(1)
result:
Related
I have created a numpy array shape(11 x 11) with all pixels 0 excluding one column filled with 1.
[[ 0 0 0 0 0 1 0 0 0 0 0 ]
[ 0 0 0 0 0 1 0 0 0 0 0 ]
[ 0 0 0 0 0 1 0 0 0 0 0 ]
[ 0 0 0 0 0 1 0 0 0 0 0 ]
[ 0 0 0 0 0 1 0 0 0 0 0 ]
[ 0 0 0 0 0 1 0 0 0 0 0 ]
[ 0 0 0 0 0 1 0 0 0 0 0 ]
[ 0 0 0 0 0 1 0 0 0 0 0 ]
[ 0 0 0 0 0 1 0 0 0 0 0 ]
[ 0 0 0 0 0 1 0 0 0 0 0 ]
[ 0 0 0 0 0 1 0 0 0 0 0 ]]
The array was saved as a png image using matplotlib.imsave yielding the expected image - black background with a white line in the middle.
When trying to reimport the saved png image
skipy.imread and Pil.Image.Open yield an array of the form
[[[ 68 1 84 255]
[ 68 1 84 255]
[ 68 1 84 255]
[ 68 1 84 255]
[ 68 1 84 255]
[253 231 36 255]
[ 68 1 84 255]
[ 68 1 84 255]
[ 68 1 84 255]
[ 68 1 84 255]
[ 68 1 84 255]]
...
]
What does this file format mean (could not find an explanation in the scikit image documentation) ?
And how do I convert it back to the binary input image?
What you see is explained thusly:
your data was grayscale
then you plotted that with a colormap
-- the line looks yellow and the background looks dark blue/violet?
then you told matplotlib to save that false-color picture
then you read that false-color picture back
now you have RGBA pixel data. you see the first pixel row, and each value of each color pixel
If you wanted to maintain the grayscale appearance of your data, you'd have some choices.
Use plt.imshow(arr, cmap="gray"), which uses a gray color map rather than a colorful one.
When reading the image, and also converting any color to grayscale, you can choose scikit-image or OpenCV. OpenCV has cv.imread(fname, cv.IMREAD_GRAYSCALE). scikit-image offers skimage.io.imread(fname, as_gray=True).
And really you should use scikit-image or OpenCV for writing your picture in the first place. Matplotlib is for plotting, not for storing data authentically. Matplotlib took your data and rescaled it so the maximum and minimum value become 0 and 1, which is black and white for the gray cmap.
On grayscale, a pixel with value 1 doesn't appear white - this simply happens because matplotlib normalizes the image before displaying it.
Choose either:
a) Keep the original binary values, then the saved image won't have a white line in the middle
b) Have a white line in the middle, but then you'll have to modify the array before saving and after loading it.
Ad b)
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
# This is the array you have
arr = np.zeros((11, 11), dtype=np.uint8)
arr[:, 5] = 1
plt.figure()
plt.imshow(arr, cmap='gray')
plt.show()
# This will ensure that the line appears white in the .png
arr_png = arr * 255 # 2**8 - 1
# Write to disk
cv.imwrite('line.png', arr_png)
# Load from disk
arr_from_disk = np.array(cv.imread('line.png', 0), dtype=np.uint8)
# Rescale
arr_from_disk = np.divide(arr_from_disk, 255)
assert np.array_equal(arr, arr_from_disk), 'Oops'
This question already has an answer here:
How to count RGB or HSV channel combination in an image?
(1 answer)
Closed 2 years ago.
I need to output the number of pixels from an image
Hello guys, I need a hand to output the total number of pixels by color. I am new to image processing with Numpy and Numpy array. I know I need to have knowledge of it when I am dealing with individual pixel in image. I try to look for more resources about obtaining pixels value with Numpy. I couldn't find any helpful to my situation.Please help me. I am eager to learn more. Any documentation or blog post or books. Any comments highly appreciated.
I am trying to print like this:
red : ????? pixels
green : ??????? pixels
blue : ??? pixels
-------------------------
the image shape is (1536, 2048, 3)
I have tried this code
from PIL import Image
import numpy as np
img_path = "sample.png"
with Image.open(img_path) as im:
data = np.array(im)
red, green, blue = data[:, :, 0], data[:, :, 1], data[:, :, 2]
print(red, green, blue)
But it given me something like this
[[0 0 0 ... 0 0 0]
[0 0 0 ... 0 0 0]
[0 0 0 ... 0 0 0]
...
[0 0 0 ... 0 0 0]
[0 0 0 ... 0 0 0]
[0 0 0 ... 0 0 0]] [[255 255 255 ... 255 255 255]
[255 255 255 ... 255 255 255]
[255 255 255 ... 255 255 255]
...
[255 255 255 ... 255 255 255]
[255 255 255 ... 255 255 255]
[255 255 255 ... 255 255 255]] [[0 0 0 ... 0 0 0]
[0 0 0 ... 0 0 0]
[0 0 0 ... 0 0 0]
...
[0 0 0 ... 0 0 0]
[0 0 0 ... 0 0 0]
[0 0 0 ... 0 0 0]]
You could add this at the end.
print(np.count_nonzero(red))
print(np.count_nonzero(green))
print(np.count_nonzero(blue))
EDIT:
If you don't use single channel colors you could do something like this:
from PIL import Image
import numpy as np
img_path = "sample.png"
colors = {}
with Image.open(img_path) as im:
data = np.array(im)
for row in data:
for pixel in row:
color = '{}_{}_{}'.format(*pixel)
colors[color] = colors.get(color, 0)
colors[color] += 1
for color, count in colors.items():
print(color, count)
You can use np.count_nonzero, reducing along the two first axes, which will give you a count of pixels across the channels:
np.count_nonzero(im, axis=(0,1))
For instance:
from sklearn.datasets import load_sample_images
im = load_sample_images()['images'][0]
np.count_nonzero(im, axis=(0,1))
# array([272366, 272418, 268717], dtype=int64)
I am trying to write a white mask on a black, two-dimensional NumPy array — an image with one channel — in OpenCV using Python:
mask = np.zeros(shape=(100, 100), dtype=np.int8)
cv2.fillPoly(mask, np.array([[[0,0], [89, 0], [99,50], [33,96], [0,47]]], dtype=np.int32), color=255)
print(mask)
However, the polygon has a grey color when I print the mask:
[[127 127 127 ... 0 0 0]
[127 127 127 ... 0 0 0]
[127 127 127 ... 0 0 0]
...
[ 0 0 0 ... 0 0 0]
[ 0 0 0 ... 0 0 0]
[ 0 0 0 ... 0 0 0]]
I tried a 3D NumPy array with color=(255,255,255), I tried different colours, all to no avail. Why is it ignoring the color argument?
The problem comes from the initialization of your mask:
mask = np.zeros(shape=(100, 100), dtype=np.int8)
The value range of the int8 data type is -128 ... 127, thus any value above 127 will be "truncated" to 127.
Try your code with color=100, you'll get the expected output:
[[100 100 100 ... 0 0 0]
[100 100 100 ... 0 0 0]
[100 100 100 ... 0 0 0]
...
[ 0 0 0 ... 0 0 0]
[ 0 0 0 ... 0 0 0]
[ 0 0 0 ... 0 0 0]]
I guess, you wanted to use uint8 instead of int8, so maybe it's just a simple typo!?
Changing your code accordingly to
mask = np.zeros(shape=(100, 100), dtype=np.uint8)
then gives the expected result, also for color=255:
[[255 255 255 ... 0 0 0]
[255 255 255 ... 0 0 0]
[255 255 255 ... 0 0 0]
...
[ 0 0 0 ... 0 0 0]
[ 0 0 0 ... 0 0 0]
[ 0 0 0 ... 0 0 0]]
The problem lies in the datatype selection when initializing the numpy array. In your example code you are using np.int8 , which has a range from -128 ... 127.. Instead of np.int8 you should consider using np.uint8, which has a range of 0 ... 255, whixh you are looking for.
mask = np.zeros(shape=(100, 100), dtype=np.int8)
should be
mask = np.zeros(shape=(100, 100), dtype=np.uint8)
[[255 255 255 ... 0 0 0]
[255 255 255 ... 0 0 0]
[255 255 255 ... 0 0 0]
...
[ 0 0 0 ... 0 0 0]
[ 0 0 0 ... 0 0 0]
[ 0 0 0 ... 0 0 0]]
For me the problem was not initializing the mask with depth.
mask = np.zeros(shape = (MASK_WIDTH, MASK_HEIGHT), dtype=np.uint8)
Solved with this code
mask = np.zeros(shape = (MASK_WIDTH, MASK_HEIGHT, 3), dtype=np.uint8)
rcolor = list(np.random.random(size=3) * 256)
cv2.fillPoly(mask, [arr], color=rcolor)
cv2.imwrite(os.path.join(mask_folder, itr + ".jpg") , cv2.cvtColor(mask, cv2.COLOR_RGB2BGR))
I want to use the function cv2.connectedComponents to connect components on a binary image, like the following...
.
Everything works, except the outputted labels array. In this array are only zeros and not sequential numbers as indicated, according to the identified components.
import cv2
import numpy as np
img = cv2.imread('eGaIy.jpg', 0)
img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)[1] # ensure binary
ret, labels = cv2.connectedComponents(img)
# Map component labels to hue val
label_hue = np.uint8(179*labels/np.max(labels))
blank_ch = 255*np.ones_like(label_hue)
labeled_img = cv2.merge([label_hue, blank_ch, blank_ch])
# cvt to BGR for display
labeled_img = cv2.cvtColor(labeled_img, cv2.COLOR_HSV2BGR)
# set bg label to black
labeled_img[label_hue==0] = 0
cv2.imshow('labeled.png', labeled_img)
cv2.waitKey()
outputted labels --> labels.shape: (256L, 250L)
[[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]
...,
[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]]
It works for me:
And you should be careful that the function only find the component of nonzero. In the source image, the components are the edges. And the returned are labeled image as the same size of source.
The output of
[[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]
...,
[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]
[0 0 0 ..., 0 0 0]]
only represent the 4 corner regions(3x3) are all zeros, but it doesn't mean all elements are zeros.
If you call this after you call the cv2.connectedComponents:
print(set(labels.reshape(-1).tolist()))
You will get:
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}
It means there exist 14 components(edges), and 1 background(0).
I am triyng to get the 3 columns of a NumPy (RGB) array:
print px
[[[ 0 0 0]
[255 255 255]
[255 0 0]
[ 0 255 0]
[ 0 0 255]]]
print px[:,0]
print px[:,1]
print px[:,2]
[[0 0 0]]
[[255 255 255]]
[[255 0 0]]
but I would like to get the R, G and B like
[[0 255 255 0 0]]
[[0 255 0 255 0]]
[[0 255 0 0 255]]
Could you help me?
Thank you
Hugo
Your array px is three-dimensional: the first dimension has just a single element: the complete arrays containing rows and colums. The second dimension is rows, the third is colums. Therefore, to select a column, and have it embedded in the outermost dimension like you have, use the following:
>>> print px[:,:,0]
[[ 0 255 255 0 0]]
>>> print px[:,:,1]
[[ 0 255 0 255 0]]
>>> print px[:,:,2]
[[ 0 255 0 0 255]]