openCV not accept numpy array? - python

I want to do a matchTemplate from a screenshot (with mss)
from mss import mss
import cv2
import numpy
with mss() as sct:
screenshot_numpy = numpy.array(sct.shot())
template = cv2.imread('./templates/player.png')
result = cv2.matchTemplate(screenshot_numpy,template,cv2.TM_CCOEFF_NORMED)
Error message:
Traceback (most recent call last):
File "main.py", line 14, in <module>
result = cv2.matchTemplate(screenshot_numpy,template,cv2.TM_CCOEFF_NORMED)
TypeError: image data type = 18 is not supported

From the mss examples page:
img = numpy.array(sct.grab(monitor))
So here we can see the .grab() method to get the raw pixel data from the image. In this case sct.grab() returns a PIL Image, and numpy.array(Image) will thus convert the PIL Image object into a numpy ndarray.
Check the numpy ndarray dtype after you convert; for e.g. if your code is ndarray_img = numpy.array(sct.grab()), then check ndarray_img.dtype. If it's np.uint8 then you're done. If it's np.uint16, then you'll have to divide by 256 and convert to np.uint8 with ndarray_img = (ndarray_img/255).astype(np.uint8).
Further down you'll see another example which flips the R and B channels of the image:
cv2.imshow(title, cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
except this is actually backwards. It really doesn't matter because either way it's just swapping the first and third channel, so BGR2RGB and RGB2BGR do exactly the same thing, but PIL (and other libraries) give you RGB order while you need BGR order to display with OpenCV, so technically it should be
cv2.imshow(title, cv2.cvtColor(img, cv2.COLOR_RGB2BGR))

Related

Error in cv2.cvtColor converting an image to HSV (using cv2.COLOR_BGR2HSV)

When I open multiple images from a folder and try to convert each into BGR2HSV, I receive this error:
Traceback (most recent call last):
File "create_gestures_manual.py", line 123, in
get_img(lab_path)
File "create_gestures_manual.py", line 114, in get_img
store_images(g_id, dirr)
File "create_gestures_manual.py", line 61, in store_images
imgHSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
cv2.error: OpenCV(4.5.2) /tmp/pip-req-build-wu1ri_rx/opencv/modules/imgproc/src/color.simd_helpers.hpp:92: error: (-2:Unspecified error) in function 'cv::impl::{anonymous}::CvtHelper<VScn, VDcn, VDepth, sizePolicy>::CvtHelper(cv::InputArray, cv::OutputArray, int) [with VScn = cv::impl::{anonymous}::Set<3, 4>; VDcn = cv::impl::{anonymous}::Set<3>; VDepth = cv::impl::{anonymous}::Set<0, 5>; cv::impl::{anonymous}::SizePolicy sizePolicy = cv::impl::::NONE; cv::InputArray = const cv::_InputArray&; cv::OutputArray = const cv::_OutputArray&]'
Invalid number of channels in input image:
'VScn::contains(scn)'
where
'scn' is 1
Below are snippet of my code:
while True:
framee = cv2.imread(dirr,0)
img = cv2.flip(framee, 1)
cv2.imshow('IMG',img)
imgHSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
What does this mean?. Please help me out. And how to solve this
The source of the error is that framee is a single channel grayscale image (and so is img which is created based on it). It is not a 3 channel color image, and therefore cannot be converted directly to hsv.
As you can see in this link: imread, the 2nd parameter flags determines the format of the read image.
In your code you use 0, which internally maps to IMREAD_GRAYSCALE. So even if your image file contains an rgb image, it will be converted to 1 channel grayscale.
Since the default for flags is IMREAD_COLOR, you can simply use:
framee = cv2.imread(dirr)
On a side note: even if you don't want this default, it's recommended to use the opencv enum values (e.g. cv2.IMREAD_GRAYSCALE) instead of an integer. Doing so you would never encounter this problem.
you read your image as grayscale image.
change this part:
framee = cv2.imread(dirr)
to have bgr image and then convert bgr to hsv.

Not able to convert RGB image to gray scale using scikit image

I have tried to convert an RGB image to Otsu binary image (gray scale) but that doesn't seem to work as I get the error as mentioned below.
from cv2 import cv2
import numpy as np
from skimage.color import rgb2gray
from skimage.filters import threshold_otsu
from skimage.io import imread
from skimage.morphology import skeletonize
from skimage.util import invert
import matplotlib.pyplot as plt
img = rgb2gray(imread('Ared.png'))
binary = img > threshold_otsu(img)
np.unique(binary)
skeleton = skeletonize(invert(binary))
cv2.imshow('original', img)
cv2.imshow('skeleton', skeleton)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result in terminal
img = rgb2gray(imread('Ared.png'))
Traceback (most recent call last):
File "preprocessing.py", line 16, in <module>
cv2.imshow('skeleton', skeleton)
TypeError: Expected Ptr<cv::UMat> for argument '%s'
Your format is wrong. You need to change it to float32. That's a common error with opencv. You can change this line to convert it to float32 and it should work fine.
cv2.imshow('skeleton', np.float32(skeleton))
Bad exception messages are bad...
cv2.imshow does not handle binary arrays. The acceptable types according to this answer are uint8, uint16, int, float, and double.
you should be able to convert the array to uint8 with:
skeleton.astype('u1')
This will leave you with values between 0 and 1 though, which is all very dark. If you the multiply the array by 255, the colors should be black and white as expected:
skeleton.astype('u1') * 255
full example with data image from skimage:
from cv2 import cv2
from skimage.filters import threshold_otsu
from skimage.morphology import skeletonize
from skimage.util import invert
from skimage.data import camera
img = camera()
binary = img > threshold_otsu(img)
skeleton = skeletonize(invert(binary))
cv2.imshow('original', img)
cv2.imshow('skeleton', skeleton.astype('u1')*255)
cv2.waitKey(0)
cv2.destroyAllWindows()

Want to append colored images to a list and convert that list to grayscale using OpenCV

So basically I'm trying to convert a set of RGB images to grayscale using cv2.cvtColor and python is throwing the following error:
Traceback (most recent call last):
File "MCG.py", line 53, in
gray = cv2.cvtColor(data, cv2.COLOR_BGR2GRAY)
TypeError: src is not a numpy array, neither a scalar.
This here is the code:
import numpy as np
import cv2
import dlib
import sys
import skimage
from PIL import Image
import os
import glob
folderpath = sys.argv[1]
cascPath = sys.argv[2]
imageformat = ".tif"
path = folderpath
imfilelist = [os.path.join(path,f) for f in os.listdir(path) if f.endswith(imageformat)]
data = []
for IMG in imfilelist:
print IMG
image = cv2.imread(IMG)
data.append(image)
cv2.imshow('Image', image)
cv2.waitKey(0)
faceCascade = cv2.CascadeClassifier(cascPath)
predictor = dlib.shape_predictor(PREDICTOR_PATH)
gray = cv2.cvtColor(data, cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.05,
minNeighbors=5,
minSize=(100,100)
)
As you can see, I'm trying to append all these images to a list, which will then be converted using the cv2.cvtColor function. However, that error is thrown. What am I doing wrong? Thank you.
P.S if anyone is wondering why I imported modules that don't seem to be used in this code, this code is just a segment of the whole thing and all of those modules have are being utilized in one way or the other.
If you read the cv2.cvtColor documentation, you can see that the first parameter is the Src 8-bit single channel image. However, in your case you are giving an entire list of images.
So change the code as
gray = []
for j in range(0,len(data)):
gray.append(cv2.cvtColor(np.array(data[j]), cv2.COLOR_BGR2GRAY))
I guess this should work.
You are collecting the images into a list with
data = []
for IMG in imfilelist:
...
data.append(image)
....
and then trying to convert the list with
gray = cv2.cvtColor(data, cv2.COLOR_BGR2GRAY)
This is why you are getting the error - the error is telling you that data is not an image (numpy array) but is a list. You need to convert one image at a time with cv2.cvtColor().
You could try
gray = []
for img in data:
gray.append(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY))
This would give you a list of greyscaled images, which is what I think you want to do.

how to get the number of channels from an image, in OpenCV 2?

The answers at Can I determine the number of channels in cv::Mat Opencv answer this question for OpenCV 1: you use the Mat.channels() method of the image.
But in cv2 (I'm using 2.4.6), the image data structure I have doesn't have a channels() method. I'm using Python 2.7.
Code snippet:
cam = cv2.VideoCapture(source)
ret, img = cam.read()
# Here's where I would like to find the number of channels in img.
Interactive attempt:
>>> img.channels()
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
AttributeError: 'numpy.ndarray' object has no attribute 'channels'
>>> type(img)
<type 'numpy.ndarray'>
>>> img.dtype
dtype('uint8')
>>> dir(img)
['T',
'__abs__',
'__add__',
...
'transpose',
'var',
'view']
# Nothing obvious that would expose the number of channels.
Thanks for any help.
Use img.shape
It provides you the shape of img in all directions. ie number of rows, number of columns for a 2D array (grayscale image). For 3D array, it gives you number of channels also.
So if len(img.shape) gives you two, it has a single channel.
If len(img.shape) gives you three, third element gives you number of channels.
For more details, visit here
I'm kind of late but there is another simple way out there:
Use image.ndim Source, will give your right number of channels as below:
if image.ndim == 2:
channels = 1 #single (grayscale)
if image.ndim == 3:
channels = image.shape[-1]
Edit: In one-liners:
channels = image.shape[-1] if image.ndim == 3 else 1
Since a image is a nothing but a numpy array. Checkout OpenCV docs here: docs
As i know, u should use image.shape[2] to determine number of channels, not len(img.shape), the latter gives the dimensions of the array.
I would like to add here a self-contained script using the PIL library and another one using the cv2 library
CV2 Library script
import cv2
import numpy as np
img = cv2.imread("full_path_to_image")
img_np = np.asarray(img)
print("img_np.shape: ", img_np.shape)
The last column of the last print will show the number of channels, for example
img_np.shape: (1200, 1920, 4)
PIL Library script
from PIL import Image
import numpy as np
img = Image.imread("full_path_to_image")
img_np = np.asarray(img)
print("img_np.shape: ", img_np.shape)
The last column of the last print will show the number of channels, for example
img_np.shape: (1200, 1920, 4)
Note: from the scripts above you would be tempted (I was) to use img_np.shape[2] to retrieve the number of channels. However, if your image contains 1 channel (e.g., grayscale), that line would give you a problem (IndexError: tuple index out of range). Instead with just a simple print of shape (as I did in my script) you will get something like this
img_np.shape: (1200, 1920)

Converting Numpy Array to OpenCV Array

I'm trying to convert a 2D Numpy array, representing a black-and-white image, into a 3-channel OpenCV array (i.e. an RGB image).
Based on code samples and the docs I'm attempting to do this via Python like:
import numpy as np, cv
vis = np.zeros((384, 836), np.uint32)
h,w = vis.shape
vis2 = cv.CreateMat(h, w, cv.CV_32FC3)
cv.CvtColor(vis, vis2, cv.CV_GRAY2BGR)
However, the call to CvtColor() is throwing the following cpp-level Exception:
OpenCV Error: Image step is wrong () in cvSetData, file /build/buildd/opencv-2.1.0/src/cxcore/cxarray.cpp, line 902
terminate called after throwing an instance of 'cv::Exception'
what(): /build/buildd/opencv-2.1.0/src/cxcore/cxarray.cpp:902: error: (-13) in function cvSetData
Aborted
What am I doing wrong?
Your code can be fixed as follows:
import numpy as np, cv
vis = np.zeros((384, 836), np.float32)
h,w = vis.shape
vis2 = cv.CreateMat(h, w, cv.CV_32FC3)
vis0 = cv.fromarray(vis)
cv.CvtColor(vis0, vis2, cv.CV_GRAY2BGR)
Short explanation:
np.uint32 data type is not supported by OpenCV (it supports uint8, int8, uint16, int16, int32, float32, float64)
cv.CvtColor can't handle numpy arrays so both arguments has to be converted to OpenCV type. cv.fromarray do this conversion.
Both arguments of cv.CvtColor must have the same depth. So I've changed source type to 32bit float to match the ddestination.
Also I recommend you use newer version of OpenCV python API because it uses numpy arrays as primary data type:
import numpy as np, cv2
vis = np.zeros((384, 836), np.float32)
vis2 = cv2.cvtColor(vis, cv2.COLOR_GRAY2BGR)
This is what worked for me...
import cv2
import numpy as np
#Created an image (really an ndarray) with three channels
new_image = np.ndarray((3, num_rows, num_cols), dtype=int)
#Did manipulations for my project where my array values went way over 255
#Eventually returned numbers to between 0 and 255
#Converted the datatype to np.uint8
new_image = new_image.astype(np.uint8)
#Separated the channels in my new image
new_image_red, new_image_green, new_image_blue = new_image
#Stacked the channels
new_rgb = np.dstack([new_image_red, new_image_green, new_image_blue])
#Displayed the image
cv2.imshow("WindowNameHere", new_rgbrgb)
cv2.waitKey(0)
The simplest solution would be to use Pillow lib:
from PIL import Image
image = Image.fromarray(<your_numpy_array>.astype(np.uint8))
And you can use it as an image.

Categories