Search code examples
pythonpython-3.xopencvkerasimshow

How to create a meter based on model's probability in Python OpenCV


I have this simple Python code that makes predictions on the emotions of the face (refer to here in case you need to run it), whether the person is happy, sad, etc. It uses cv2 and Keras. Now, I would like to visualize and place a meter on the frame based on the probability of each frame (prob value below which is a percentage). How can I do that?

Something like this. Don't worry about the colors for now.

enter image description here

cap = cv2.VideoCapture(1)

canvasImage = cv2.imread("fg2.png")

x0, x1 = 330, 1290
y0, y1 = 155, 700


prediction_history = []
LOOKBACK = 5 # how far you want to look back

counter = 0
while True:
    # Find haar cascade to draw bounding box around face
    ret, frame = cap.read()
    frame=cv2.flip(frame,3)
    if not ret:
        break
    facecasc = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    faces = facecasc.detectMultiScale(gray,scaleFactor=1.3, minNeighbors=5)

    for (x, y, w, h) in faces:
        cv2.rectangle(frame, (x, y-50), (x+w, y+h+10), (255, 0, 0), 2)
        roi_gray = gray[y:y + h, x:x + w]
        cropped_img = np.expand_dims(np.expand_dims(cv2.resize(roi_gray, (48, 48)), -1), 0)
        prediction = model.predict(cropped_img)
        
        maxindex = int(np.argmax(prediction))
        text = emotion_dict[maxindex]
        prob = round(prediction[0][3]*100, 2)
        
        prediction_history.append(maxindex)
        most_common_index = max(set(prediction_history[-LOOKBACK:][::-1]), key = prediction_history.count)
        text = emotion_dict[most_common_index]
        
        #if ("Sad" in text) or ("Angry" in text) or ("Disgusted" in text):
        #    text = "Sad"
        if ("Happy" in text) or ("Sad" in text) :
            cv2.putText(frame, text+": "+str(prob), (x+20, y-60), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
  

    dim = (800,480)
    frame_shrunk = cv2.resize(frame, (x1 - x0, y1 - y0))
    canvasImage[y0:y1, x0:x1] = frame_shrunk
    #cv2.imshow('Video', cv2.resize(frame,dim,interpolation = cv2.INTER_CUBIC))
    cv2.imshow('Demo', canvasImage)
    
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

Solution

  • There is no built in function in OpenCV for drawing meters, here is a helper function that you can use to draw a meter over an image:

    def draw_indicator(img, percentage):
    
        def percentage_to_color(p):
            return 0, 255 * p, 255 - (255 * p)
    
        # config
        levels = 10
        indicator_width = 80
        indicator_height = 220
        level_width = indicator_width - 20
        level_height = int((indicator_height - 20) / levels - 5)
        # draw
        img_levels = int(percentage * levels)
        cv2.rectangle(img, (10, img.shape[0] - (indicator_height + 10)), (10 + indicator_width, img.shape[0] - 10), (0, 0, 0), cv2.FILLED)
    
        for i in range(img_levels):
            level_y_b = int(img.shape[0] - (20 + i * (level_height + 5)))
            cv2.rectangle(img, (20, level_y_b - level_height), (20 + level_width, level_y_b), percentage_to_color(i / levels), cv2.FILLED)
    
    # test code
    img = cv2.imread('a.jpg')
    draw_indicator(img, 0.7)
    cv2.imshow("test", img)
    cv2.waitKey(10000)