Script TOP (OpenCV) - can't render image because it is has 2 channels instead of 3

Hi,

I’m using OpenCV in a script TOP for color detection. My problem is that the TOP will not render the mask as it has the wrong dimensions. I think the problem is that the mask is 2 dimensions and has no alpha channel. I’m a little confused with python array slicing and have messed about with stacking an alpha on top but I’m making mistakes and wasting time:

Error message: NumPy array must be 3 dimensions (w, h, numComponents)

my script (with some of my commented out code):

import numpy as np
import cv2 as cv

# define range of blue color in HSV
lower_blue = np.array([110/255,50/255,50/255])
upper_blue = np.array([130/255,255/255,255/255])

# press 'Setup Parameters' in the OP to call this function to re-create the parameters.
def onSetupParameters(scriptOp):
	return

# called whenever custom pulse parameter is pushed
def onPulse(par):
	return

def onCook(scriptOp):
	img = op('videodevin1').numpyArray(delayed=True)
	#rgb_frame = cv.cvtColor(img, cv.COLOR_BGR2RGB)
	hsv_frame = cv.cvtColor(img, cv.COLOR_BGR2HSV)
	#ret, thresh = cv.threshold(hsv_frame, 127, 255, cv.THRESH_BINARY)
	mask  = cv.inRange(hsv_frame, low_red, high_red)
	alpha = np.ones(mask.shape, dtype=int) * 100
	#mask = mask.append(alpha)
	#mask  = np.dstack((mask, alpha, alpha, alpha))
	#mask = mask.append(alpha[-1])
	#mask = cv.merge((mask, alpha))

	op('text1').text =  "alpha:" + str(alpha.shape) + " mask:" + str(mask.shape) + " img:" + str(img.shape) 

	scriptOp.copyNumpyArray(mask)

I’d appreciate your help. Thanks

For now my workaround is to perform a bitwise operation on the video image using the mask and then output this to the TOP:

import numpy as np
import cv2 as cv

# define range of blue color in HSV
lower_blue = np.array([0,1/255,1/255])
upper_blue = np.array([225/255,255/255,255/255])


# press 'Setup Parameters' in the OP to call this function to re-create the parameters.
def onSetupParameters(scriptOp):
	return

# called whenever custom pulse parameter is pushed
def onPulse(par):
	return


def onCook(scriptOp):
	img = op('videodevin1').numpyArray(delayed=True)
	hsv_frame = cv.cvtColor(img, cv.COLOR_BGR2HSV)

	# Threshold the HSV image to get only blue 
	mask  = cv.inRange(hsv_frame, lower_blue, upper_blue)

	# Bitwise-AND  - select the thresholded region
	res = cv.bitwise_and(img, img, mask= mask) 

	scriptOp.copyNumpyArray(res)

copyNumpyArray expects a 3-dimensional array.
The dimensions of that array are (rows, cols, number of channels) where channels is either RGB or RGBA. So for a 1280x720 image which has RGBA, the array dimensions must be (720,1280,4)

You mask is a 2-dimensional array, as it has only one value for each pixel.

So there are many ways to approach this, one way is to create an empty canvas of zeros in the correct shape, and then write your mask over the R channel.

img = scriptOp.inputs[0].numpyArray(delayed=True)
hsv_frame = cv.cvtColor(img, cv.COLOR_BGR2HSV)
mask  = cv.inRange(hsv_frame, lower_blue, upper_blue)
canvas = np.zeros((720, 1280, 4), dtype=np.float32)
canvas[:,:,0] = mask
scriptOp.copyNumpyArray(canvas)

Other approaches are to use one of the many NumPy methods to change your array’s shape and fill the empty values , like numpy.pad, numpy.ndarray.resize, numpy.expand_dims, etc

Let me know if you have further questions, I’ve build a lot in NumPy & TD.

1 Like

Thanks so much for taking the time to explain so well I really appreciate it. Ok I think I understand your method; pre-prepare a matrix (canvas) with depth 4 then replace the red channel with the mask (image in BGR format?

I’ll brush up on my python slicing and keeping things clean and smipler. Thanks again for your help!

Gratefully,

Marc