0

Problem Background:

I have created an Azure FaceList and I am using my webcam to capture live feed and:

  1. sending the stream to Azure Face Detect
  2. getting Face Rectangle returned by Face Detect
  3. using the returned Face rectangle to add Face Detected from Live Video Stream to my FaceList.

(I need to create Face List in order to solve the problem I explained in my other question which is answered by Nicolas, which is what I am following)

Problem Details:

According to Azure FaceList documentation at https://learn.microsoft.com/en-us/rest/api/cognitiveservices/face/facelist/addfacefromstream ,if there are multiple faces in the image, we need to specify the target Face to add to Azure FaceList.

The Problem is, What if we need to add all the detected faces (multiple faces) in Face List? Suppose there are 2 or more faces in a single frame of video, then how can I add those two Faces to Face List?

I have tried adding the face rectangles returned from Azure Face Detect into a Python List and then iterating Over List indexes, so that each face Rectangle can be passed to Azure FaceList one-by-one. But no use.

Still getting the error:

There are more than one faces in the image

My Code:

face_list_id = "newtest-face-list"
vid = cv2.VideoCapture(0)


count = 0


face_ids_live_Detected = []  #This list will store faceIds from detected faces
list_of_face_rectangles = []
face_rect_counter=0

while True:
        ret, frame = vid.read()
        check,buffer = cv2.imencode('.jpg', frame)
        img = cv2.imencode('.jpg', frame)[1].tobytes()
        base64_encoded = base64.b64encode(buffer).decode()
        print(type(img))
        detected_faces = utils.detect_face_stream(endpoint=ENDPOINT, key=KEY, image=img,face_attributes=attributes,recognition_model='recognition_03')
        print('Image num {} face detected {}'.format(count, detected_faces))
        count += 1
        color = (255, 0, 0)
        thickness = 2

        for face in detected_faces:
        
            detected_face_id = face['faceId']
            face_ids_live_Detected.append(detected_face_id)
            

            detected_face_rectangle = face['faceRectangle']
            list_of_face_rectangles.append(detected_face_rectangle)
            print("detected rectangle =",detected_face_rectangle)

            face_rect_for_facelist = list_of_face_rectangles[face_rect_counter]
            face_rect_counter +=1

       frame = cv2.rectangle(frame, *utils.get_rectangle(face), color, thickness)
       cv2.imshow('frame', frame)

       for face_id_live in face_ids_live_Detected:
                        similar_faces = face_client.face.find_similar(face_id=face_id_live, face_list_id=face_list_id)                
                        if not similar_faces:
                                print('No similar faces found !')
                                print('Adding Unknown Face to FaceList...')
                                facelist_result = utils.facelist_add(endpoint=ENDPOINT, key=KEY, face_list_id=face_list_id,data=img,params=face_rect_for_facelist)
                                persisted_face_id = facelist_result['persistedFaceId']
                        else:
                                print('Similar Face Found!')
                                for similar_face in similar_faces:
                                        face_id_similar = similar_face.face_id
                                        print("Confidence: "+str(similar_face.confidence))

From my utils file, code for function facelist_add is as follows:

def facelist_add(endpoint, key, face_list_id, data=None, json=None, headers=None,params=None, targetFace=None):
    # pylint: disable=too-many-arguments
    """Universal interface for request."""
    method = 'POST'
    url = endpoint + '/face/v1.0/facelists/'+face_list_id+'/persistedfaces'

    # Make it possible to call only with short name (without BaseUrl).
    if not url.startswith('https://'):
        url = BaseUrl.get() + url

    params={}

    # Setup the headers with default Content-Type and Subscription Key.
    headers = headers or {}
    if 'Content-Type' not in headers:
        headers['Content-Type'] = 'application/octet-stream'
    headers['Ocp-Apim-Subscription-Key'] = key


    params['detectionModel']='detection_03'
    

    response = requests.request(
        method,
        url,
        params=params,
        data=data,
        json=json,
        headers=headers)

    if response.text:
         result = response.json()
    else:
         result = {}

    return result
Ateeb
  • 5,413
  • 3
  • 10
  • 17

2 Answers2

0

When you have several faces in a picture, you have to provide a 'targetFace' in your call to AddFace:

A face rectangle to specify the target face to be added into the face list, in the format of "targetFace=left,top,width,height". E.g. "targetFace=10,10,100,100". If there is more than one face in the image, targetFace is required to specify which face to add. No targetFace means there is only one face detected in the entire image.

See API documentation for this method: https://westeurope.dev.cognitive.microsoft.com/docs/services/563879b61984550e40cbbe8d/operations/563879b61984550f30395250

Nicolas R
  • 13,812
  • 2
  • 28
  • 57
  • This is what I am doing here , sending each face rectangle in params, but it's not working: print('No similar faces found !') print('Adding Unknown Face to FaceList...') facelist_result = utils.facelist_add(endpoint=ENDPOINT, key=KEY, face_list_id=face_list_id,data=img,params=face_rect_for_facelist) – Ateeb Dec 23 '20 at 08:00
  • On your « facelist_add » method I don’t see where you are mapping those rectangles in the call to the API – Nicolas R Dec 23 '20 at 09:00
  • Face Rectangles are sent via params in facelist_add function. Look at image 1: https://imgur.com/a/LR4taQd and then image 2: https://imgur.com/kBW3LSB – Ateeb Dec 23 '20 at 09:46
  • Read again my comment: once in your facelist_add operation you are not using this « params » input in the call to the API – Nicolas R Dec 23 '20 at 10:19
  • I am sendin req to API here: response = requests.request( method, url, params=params, data=data, json=json, headers=headers) – Ateeb Dec 23 '20 at 10:52
  • 1
    Yes but a few lines before: ‘params={}’ . Does this clean what you have passed in params in the method’s input? – Nicolas R Dec 23 '20 at 12:47
  • Hello Nicolas, I have figured that the problem is in API call. it's where I am sending params. Acc to Azure documentation I should send a list of faceRectangle in params. But unfortunately, I am unable to do that after a number of tries. my request URL is like this: {Endpoint}/face/v1.0/facelists/sample_face_list/persistedfaces?overload=stream&targetFace=10&targetFace=51targetFace=124targetFace=248&detectionModel=detection_01 params is sending each targetFace value separately and not the whole list. Can you help me with that ? – Ateeb Dec 24 '20 at 15:06
  • You should populate params['targetFace'] with the values of left,top,width,height of the face you want to add. something like params['targetFace'] = 'widthValue,heightValue,LeftValue,TopValue' - coming from your Detect call – Nicolas R Dec 24 '20 at 15:09
  • I have also removed that params={} line – Ateeb Dec 24 '20 at 15:10
  • I think you should have a look to how Query strings work. When you do the following: "targetFace=10&targetFace=51targetFace=124targetFace=248", you are not passing the right thing – Nicolas R Dec 24 '20 at 15:12
  • i am doing this: params['targetFace']=targetFace(This is the list containing left,width,top,height of face) but when I print the URL it automatically extract the values from list and send each value separately with repeating targetFace= like I have written above. – Ateeb Dec 24 '20 at 15:13
  • "targetFace=10&targetFace=51targetFace=124targetFace=248" this is automatically done when I made request using python requests module – Ateeb Dec 24 '20 at 15:14
  • Someone explained the problem I am facin here: https://stackoverflow.com/questions/35851315/how-to-send-a-list-in-python-requests-get but the solution didn't work for me – Ateeb Dec 24 '20 at 15:18
0

Thanks to everyone who helped and especially Nicolas R. I just found a mistake and corrected it. Now the program is working like Charm.

Actually, Azure 'Face Detect' returns face Rectangle in top,left,width,height sequence which I was feeding directly to the faceList's targetFace.

Now I just swapped the first two values of Face Rectangle and it becomes left,top,width,height which is what the documentation says, and it's working fine now.

Solution:

I have added a new function that takes the faceRectangle dictionary and swap first two values.

list_of_faceRect_valuesonly=[]

def face_rect_values(faceRect_dict):
        temp_list=[]
        for key,value in faceRect_dict.items():
                temp_list.append(value)
        temp_list[0], temp_list[1] = temp_list[1], temp_list[0]
        list_of_faceRect_valuesonly.append(temp_list)

In order to extract values from list, I did following:

face_rect_counter=0
face_rect_for_facelist = list_of_faceRect_valuesonly[face_rect_counter]
face_rect_counter +=1

Request to facelist_add function:

facelist_result = utils.facelist_add(endpoint=ENDPOINT, key=KEY, face_list_id=face_list_id,targetFace=face_rect_for_facelist,data=img)

I have also changed my facelist_add function a little bit:

def facelist_add(endpoint, key, face_list_id, targetFace=[],data=None ,jsondata=None, headers=None):
    # pylint: disable=too-many-arguments
    """Universal interface for request."""
    method = 'POST'
    url = endpoint + '/face/v1.0/facelists/'+face_list_id+'/persistedfaces'

    # Make it possible to call only with short name (without BaseUrl).
    if not url.startswith('https://'):
        url = BaseUrl.get() + url

    params={}
    

    # Setup the headers with default Content-Type and Subscription Key.
    headers = headers or {}
    if 'Content-Type' not in headers:
        headers['Content-Type'] = 'application/octet-stream'
    headers['Ocp-Apim-Subscription-Key'] = key

    list_of_targetfaces  =[]
    list_of_targetfaces.append(targetFace)


    params={'targetFace':json.dumps(targetFace)}
    params = {'targetFace': ','.join(map(str,targetFace))}

    print("Printing TargetFaces(facelist_add function) ...",params['targetFace'])
    

    params['detectionModel']='detection_03'


    url=url + "?"


    response = requests.post(url,params=params,data=data,headers=headers)
    


    print("Request URL: ", response.url)

    result = None

    # Prevent `response.json()` complains about empty response.
    if response.text:
        result = response.json()
    else:
        result = {}

    return result
Ateeb
  • 5,413
  • 3
  • 10
  • 17