How to capture image in ellipse form in OpenCV?

Can someone please guide me? I am trying to detect face and then capture image in the ellipse form using OpenCV. The following is an example of how it looks like when I run my python script.

At the moment, I am just detecting face and have created this fixed frame. I also managed to find the solution for of how to do it in rectangle form but I need to capture image in ellipse form. Can someone please help me with? Also, this is my code:

import cv2
import sys
import numpy as np
import matplotlib.pyplot as plt

faceCascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

video_capture = cv2.VideoCapture(0)

while True:
    # Capture frame-by-frame
    ret, frame = video_capture.read()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    faces = faceCascade.detectMultiScale(
        gray,
        scaleFactor=1.1,
        minNeighbors=25,
        minSize=(30, 30),
        flags=cv2.CASCADE_SCALE_IMAGE
    )

    # Draw a rectangle around the faces
    for (x, y, w, h) in faces:
    
        #320 horizontal position, 250 vertical position, 80 horizontal size, 120 vertical size
        #0 angle, 0 startangle, 360 endangle
        #(0, 255, 0) color, 2 thickness
    
        startAngle = 0
        endAngle = 10
        for z in range(20):
            cv2.ellipse(frame, (320, 250), (80, 120), 0, startAngle, endAngle, (255, 255, 255), 2)
            startAngle = startAngle+20
            endAngle = endAngle+20
    
        #Centered Vertical Dashed Line
        xCord = 320
        yCord = 400
        for z in range(12):
            cv2.line(frame, (xCord, yCord), (xCord, yCord-20), (255, 255, 255), 2)
            yCord = yCord-25

        #Upper Horizontal Dashed Line
        xCord = 160
        yCord = 130
        for z in range(13):
            cv2.line(frame, (xCord, yCord), (xCord+20, yCord), (255, 255, 255), 2)
            xCord = xCord+25
    
        #Lower Horizontal Dashed Line
        xCord = 160
        yCord = 370
        for z in range(13):
            cv2.line(frame, (xCord, yCord), (xCord+20, yCord), (255, 255, 255), 2)
            xCord = xCord+25
    
        cv2.putText(frame, 'Head', (330, 120), cv2.FONT_HERSHEY_SIMPLEX , 0.5, (255, 255, 255), 2) 
        cv2.putText(frame, 'Chin', (330, 390), cv2.FONT_HERSHEY_SIMPLEX , 0.5, (255, 255, 255), 2) 

        # Display the resulting frame
        cv2.imshow('Video', frame)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
    if cv2.waitKey(1) & 0xFF == ord('c'):
        crop_img = frame[y-50: y+h+10, x: x+w] # Crop from x, y, w, h -> 100, 200, 300, 400, y-50 is to include head in the picture too y+h+10 is to include chin.
            cv2.imwrite("media/faces/face.jpg", crop_img)

# When everything is done, release the capture
video_capture.release()
cv2.destroyAllWindows()

you cannot “capture” an elliptical region.

but you can overlay an elliptical mask after the (rectangular) detection, using cv2.ellipse

Can you please show me how? I am new to it so don’t know a lot of things. I also tried one solution from here but the end result I get is this:
face

@berak

thickness = -1
will make a filled ellipse, so you can use all of the inside to mask out your face

1 Like

Is there any way that I can eliminate the rest of the background that is black or make it transparent or something? @berak

sure, try to understand the code, and get creative :wink:

Okay. Thanks for the help. @berak

    mask = np.zeros_like(frame)
    rows, cols,_ = mask.shape
    mask=cv2.ellipse(mask, (320, 250), (80, 120), 0, 0, 360, (255,255,255), -1)
    result = np.bitwise_and(frame,mask)
    bgr = result[:,:,:3] # Channels 0..2
    gray = cv2.cvtColor(bgr, cv2.COLOR_BGR2GRAY)
    # Some sort of processing...
    bgr = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)
    alpha = result[:,:,3] # Channel 3
    result = np.dstack([bgr, alpha])
    cv2.imwrite("media/faces/face.png", result)

Can you please help me again @berak ? I manage to change my background to white but the the image gets gray. I think it is due to the this line:

alpha = result[:,:,3] # Channel 3

which throws an error. But, if I change 3 to 2, it works but it doesn’t produce the right result.

face

i’m definitely NOT a python person, but if all you wanted was alpha, it’S much simpler:

  • make a single channel black image, size of your frame
  • draw a white, filled ellipse into it
  • splice the alpha channel into the bgr image

Oh, sorry I didn’t know you are not a python person. I think I have done all the steps, just having trouble with the alpha channel now.