Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeError: src is not a numpy array, neither a scalar

    gray_image = cv2.cvtColor(contrast, cv2.COLOR_BGR2GRAY)

TypeError: src is not a numpy array, neither a scalar

I am currently working to solve this, any help would be appreciated. As mentioned in the comments, the PIL image needs to be converted to CV2 accepted format, can anyone provide an explanation using the example given below?

import cv2
import numpy as np
from matplotlib import pyplot as plt
from cycler import cycler
from PIL import Image, ImageEnhance

# Loads the image then enhances it
image = Image.open('lineCapture.png')
contrast = ImageEnhance.Contrast(image)

# Reads the enhanced image and converts it to grayscale, creates new file
gray_image = cv2.cvtColor(contrast, cv2.COLOR_BGR2GRAY) //there is a problem here
cv2.imwrite('enhancedGrayscaleLineCapture.png', gray_image)

# Adaptive Gaussian Thresholding
th1 = cv2.adaptiveThreshold(gray_image,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
        cv2.THRESH_BINARY,11,2)
# Otsu's thresholding
ret2,th2 = cv2.threshold(gray_image,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
# Otsu's thresholding after Gaussian filtering
blur = cv2.GaussianBlur(gray_image,(5,5),0)
ret3,th3 = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

# writes enhanced and thresholded img
cv2.imwrite('enhancedGrayscaleThresholdLineCapture.png', th2)
like image 229
lizardwizard Avatar asked Aug 24 '17 14:08

lizardwizard


2 Answers

PIL is almost completely object oriented, so most functions return objects.

For example:

>>> image = Image.open('img6.png')
>>> type(image)
<class 'PIL.PngImagePlugin.PngImageFile'>

The PIL Image is a class (hence the capital) so it returns an object. So if the image is an object, it probably has properties, like the image data, the height/width of the image, and so on, along with built-in methods, like .show() which will display the image. You can read the docs for the PIL Image class for more.

So you're passing classes along to functions which expect arrays. Going to have a problem there. One way to solve this is to read the data into a numpy array using image.getdata(), which is the usual way to access pixel values in PIL.

However, numpy will automagically convert the Image into an array for you with a simple command: np.asarray().

>>> image = Image.open('img6.png')
>>> type(image)
<class 'PIL.PngImagePlugin.PngImageFile'>
>>> image_data = np.asarray(image)
>>> type(image_data)
<class 'numpy.ndarray'>

Yay! Now we have an array from the image. Importantly though, PIL reads images as RGB images like most other libraries, but OpenCV actually uses BGR channel ordering. So you'll need to make sure you swap the first and last channels around if you're going to use OpenCV to write, display, or modify images in a way that depends on their color.

Only one problem left...dealing with the contrast adjustment. Contrast from the ImageEnhance module returns an object as well:

>>> contrast = ImageEnhance.Contrast(image)
>>> type(contrast)
<class 'PIL.ImageEnhance.Contrast'>

But this returns a Contrast object, and not an Image object. In fact your code hasn't even modified the image; all you have done is create the enhancer object. You have to call a method to actually perform the contrast adjustment (and a factor of how strong you want it to be). Check the docs for ImageEnhance:

All enhancement classes implement a common interface, containing a single method:

enhance(factor)

Returns an enhanced image.

Parameters: factor – A floating point value controlling the enhancement. Factor 1.0 always returns a copy of the original image, lower factors mean less color (brightness, contrast, etc), and higher values more. There are no restrictions on this value.

Return type: Image

Now, this method returns an Image, so we can run np.asarray() on the result. So the final pipeline would be something like:

  1. Load image into Image class
  2. Create contrast enhancement object
  3. Invoke enhance(factor) on the contrast enhancement object and the return val will be another Image class
  4. Get a numpy array of the contrast adjusted image data
  5. Convert from RGB to BGR order
  6. Use normally

Input Image:

Input image

>>> pil_image = Image.open('img6.png')
>>> contrast_enhancer = ImageEnhance.Contrast(pil_image)
>>> pil_enhanced_image = contrast_enhancer.enhance(2)
>>> enhanced_image = np.asarray(pil_enhanced_image)
>>> r, g, b = cv2.split(enhanced_image)
>>> enhanced_image = cv2.merge([b, g, r])
>>> cv2.imshow('Enhanced Image', enhanced_image)
>>> cv2.waitKey()

Output Image:

Contrast Enhanced Image

like image 161
alkasm Avatar answered Nov 17 '22 13:11

alkasm


Thanks to Alexander Reynolds for the great explanation. lizardwizard, since you are not able to figure out the mistake in your code, check this out

import cv2
import numpy as np
from matplotlib import pyplot as plt
from cycler import cycler
from PIL import Image, ImageEnhance

# Loads the image then enhances it
image = Image.open('lineCapture.png')
contrast = ImageEnhance.Contrast(image)
img=contrast.enhance(2)
img = np.asarray(img)
r, g, b,a = cv2.split(img)
contrast=cv2.merge([b, g, r])
# Reads the enhanced image and converts it to grayscale, creates new file
gray_image = cv2.cvtColor(contrast, cv2.COLOR_BGR2GRAY) #there is a problem here


# Adaptive Gaussian Thresholding
th1 = cv2.adaptiveThreshold(gray_image,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
        cv2.THRESH_BINARY,11,2)
# Otsu's thresholding
ret2,th2 = cv2.threshold(th1,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
# Otsu's thresholding after Gaussian filtering
blur = cv2.GaussianBlur(th2,(5,5),0)
ret3,th3 = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

# writes enhanced and thresholded img
cv2.imwrite('enhancedGrayscaleThresholdLineCapture.png', th3)
like image 23
I.Newton Avatar answered Nov 17 '22 14:11

I.Newton