I have a script chop that detects faces in a video feed using opencv haarcascades.
I am in the process of adapting this script to work with opencv DNN-based Face Detection And Recognition
https://docs.opencv.org/4.x/d0/dd4/tutorial_dnn_face.html
I’m a newb to python and touchdesigner, so running into issues.
I am currently getting this error on line 58:
cv2.error: OpenCV(4.7.0) /Users/malcolm/dev/devel/opencv/modules/dnn/src/layers/convolution_layer.cpp:420: error: (-2:Unspecified error) Number of input channels should be multiple of 3 but got 1 in function 'getMemoryShapes'
Line 58 is:
faces1 = detector.detect(gray, faces)
I’ve tried various other things in here based on research I’ve done but I’m shooting in the dark for the most part.
Not sure if line 57 is at all correct…but it doesn’t throw an error:
faces = np.zeros((0,0), dtype=np.float32)
Here’s my .toe if you want to take a look. I removed the artistic effects that I am using in my project just to simplify. In the file you can see the working haarcascade version and how I am handling the outputs. I’d like to do the same or similar with the DNN method.
005.opencv-face-v3-DNN-FORFORUM.toe (7.0 KB)
Here is the full script:
# me - this DAT
# scriptOp - the OP which is cooking
import cv2
import numpy as np
# press 'Setup Parameters' in the OP to call this function to re-create the parameters.
def onSetupParameters(scriptOp):
page = scriptOp.appendCustomPage('Face Detection')
p = page.appendTOP('Top', label='TOP')
p = page.appendFloat('Scale', label='Scale Factor')
p.normMin = 1.001
p.normMax = 2
p.clampMin = True
p.default = 1.1
p = page.appendInt('Neighbors', label='Min Neighbors')
p.normMin = 0
p.normMax = 10
p.default = 1
p = page.appendFile('Facedetectionmodel', label='Face Detection yunet .onnx file')
p = page.appendFile('Facerecognitionmodel', label='Face Recognition sface .onnx file')
p = page.appendFloat('Scorethreshold', label='Filtering out faces of score < score threshold')
p = page.appendFloat('Nmsthreshold', label='Suppress bounding boxes of iou >= nms_threshold')
p = page.appendInt('Topk', label='Keep top_k bounding boxes before NMS')
return
# get the node this DAT is docked to
# this way we can get to the custom file parameter above
# dockedScript = me.dock
# face_cascade = cv2.CascadeClassifier(me.dock.par.Facedetectionmodel.eval())
# called whenever custom pulse parameter is pushed
def onPulse(par):
return
def onCook(scriptOp):
scriptOp.clear()
inputFrame = scriptOp.par.Top.eval().numpyArray(delayed=True)
gray = cv2.cvtColor(inputFrame, cv2.COLOR_RGB2GRAY)
gray = (gray * 255).astype(np.uint8)
# detect faces
detector = cv2.FaceDetectorYN.create(
scriptOp.par.Facedetectionmodel.eval(),
"",
(320, 320),
scriptOp.par.Scorethreshold.eval(),
scriptOp.par.Nmsthreshold.eval(),
scriptOp.par.Topk.eval(),
)
# Set input size before inference
detector.setInputSize((640, 360))
faces = np.zeros((0,0), dtype=np.float32)
faces1 = detector.detect(gray, faces)
# draw rectangles around the faces
# for (x, y, w, h) in faces:
# cv2.rectangle(inputFrame, (x, y), (x+w, y+h), (255, 0, 255), 2)
# scriptOp.copyNumpyArray(inputFrame)
# setup CHOP
facesDetected = len(faces)
scriptOp.numSamples = facesDetected
x = scriptOp.appendChan('x')
y = scriptOp.appendChan('y')
w = scriptOp.appendChan('w')
h = scriptOp.appendChan('h')
a = scriptOp.appendChan('active')
# transpose the array with detected faces
if len(faces):
faces = faces.transpose()
# now we get an array of 4 arrays
# this is easy to assign to channel values
x.vals = faces[0]
y.vals = faces[1]
w.vals = faces[2]
h.vals = faces[3]
a.vals = [facesDetected]
return