Using Azure Face Api in Python, How to Return a single faceId or a group of FaceIds if the same person is detected in Video Stream? - azure

I am using Azure Face APi to detect faces in video stream, but for each detected face Azure returns a unique faceId( which is exactly what the documentation says).
The problem is, Let's say Mr.ABC appears in 20 video frames, 20 unique faceIds gets generated. I want something that Azure Face should return me a single faceId or a group of FaceIds generated particularly for Mr.ABC so that I can know that its the same person that stays in front of camera for x amount of time.
I have read the documentation of Azure Facegrouping and Azure FindSimilar, but didn't understand how can I make it work in case of live video stream.
The code I am using for detecting faces using Azure face is given below:
from azure.cognitiveservices.vision.face import FaceClient
from msrest.authentication import CognitiveServicesCredentials
from azure.cognitiveservices.vision.face.models import TrainingStatusType, Person, SnapshotObjectType, OperationStatusType
import cv2
import os
import requests
import sys,glob, uuid,re
from PIL import Image, ImageDraw
from urllib.parse import urlparse
from io import BytesIO
from azure.storage.blob import BlobServiceClient, BlobClient, ContainerClient,__version__
face_key = 'XABC' #API key
face_endpoint = 'https://XENDPOINT.cognitiveservices.azure.com' #endpoint, e.g. 'https://westus.api.cognitive.microsoft.com'
credentials = CognitiveServicesCredentials(face_key)
face_client = FaceClient(face_endpoint, credentials)
camera = cv2.VideoCapture(0)
samplenum =1
im = ""
work_dir = os.getcwd()
person_group_id = 'test02-group'
target_person_group_id = str(uuid.uuid4())
face_ids = []
#cv2 font
font = cv2.FONT_HERSHEY_SIMPLEX
#empty tuple
width = ()
height = ()
left=0
bottom=0
def getRectangle(faceDictionary):
rect = faceDictionary.face_rectangle
left = rect.left
top = rect.top
right = left + rect.width
bottom = top + rect.height
return ((left, top), (right, bottom))
while True:
check,campic = camera.read()
samplenum=samplenum+1
cv2.imwrite("live_pics/"+str(samplenum)+".jpg",campic)
path = work_dir+"/live_pics/"+str(samplenum)+".jpg"
#im = cv2.imread("pics/"+str(samplenum)+".jpg")
stream = open(path, "r+b")
detected_faces = face_client.face.detect_with_stream(
stream,
return_face_id=True,
return_face_attributes=['age','gender','emotion'],recognitionModel="recognition_03")
for face in detected_faces:
width,height = getRectangle(face)
cv2.rectangle(campic,width,height,(0,0,170),2)
face_ids.append(face.face_id)
#cv2.waitKey(100);
if(samplenum>10):
break
cv2.imshow("campic", campic)
if cv2.waitKey(1) == ord("q"):
break
camera.release()
cv2.destroyAllWindows()

There is no magic on Face API: you have to process it with 2 steps for each face found.
What I suggest is to use "Find similar":
at the beginning, create a "FaceList"
then process your video:
Face detect on each frame
For each face found, use find similar operation on the face list created. If there is no match (with a sufficient confidence), add the face to the facelist.
At the end, your facelist will contain all the different people found on the video.
For your realtime use-case, don't use "Identify" operation with PersonGroup / LargePersonGroup (the choice between those 2 depends on the size of the group), because you will be stuck by the need of training on the group. Example, you would be doing the following:
Step 1, 1 time: generate the PersonGroup / LargePersonGroup for this execution
Step 2, N times (for each image where you want to identify the face):
Step 2a: face detect
Step 2b: face "identify" on each detected face based on the PersonGroup / LargePersonGroup
Step 2c: for each unidentified face, add it to the PersonGroup / LargePersonGroup.
Here the issue is the fact that after 2c, you have to train your group again. Even if it is not so long, it cannot be used in real time as it will be too long.

Per my understanding, you want to show a person's name/identity instead of the face ID detected from Face API.
If so, after you get face ids via Face Detect API, you should use the Face Identify API to do this. You can get a person ID if faces could be recognized by Azure Face service, With this ID, you can just use PersonGroup Person API to get this person's information.
I also wrote a simple demo for you, in this demo, there is only 1 image, we can just image it as a video frame. I created a person group with one superman person and added some faces to him.
This is the code below :
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from PIL import Image
import numpy as np
import asyncio
import io
import glob
import os
import sys
import time
import uuid
import requests
from urllib.parse import urlparse
from io import BytesIO
from azure.cognitiveservices.vision.face import FaceClient
from msrest.authentication import CognitiveServicesCredentials
imPath = "<image path>";
ENDPOINT = '<endpoint>'
KEY = '<key>'
PERSON_GROUP_ID = '<person group name>'
face_client = FaceClient(ENDPOINT, CognitiveServicesCredentials(KEY))
im = np.array(Image.open(imPath), dtype=np.uint8)
faces = face_client.face.detect_with_stream(open(imPath, 'r+b'),recognition_model='recognition_03');
# Create figure and axes
fig,ax = plt.subplots()
# Display the image
ax.imshow(im)
for i in range(len(faces)):
face = faces[i]
rect =patches.Rectangle((face.face_rectangle.left,face.face_rectangle.top),face.face_rectangle.height,face.face_rectangle.width,linewidth=1,edgecolor='r',facecolor='none')
detected_person = face_client.face.identify([face.face_id],PERSON_GROUP_ID)[0]
if(len(detected_person.candidates) > 0):
person_id = detected_person.candidates[0].person_id
person = face_client.person_group_person.get(PERSON_GROUP_ID,person_id)
plt.text(face.face_rectangle.left,face.face_rectangle.top,person.name,color='r')
else:
plt.text(face.face_rectangle.left,face.face_rectangle.top,'unknown',color='r')
ax.add_patch(rect)
plt.show()
Result:

Related

How to split the image into 4 equal parts and do some processing on each part and then finally merge all the processed image parts?

import cv2;
import math;
import numpy as np;
import image_slicer;
import PIL;
from PIL import Image
import time
import concurrent.futures
import image_slicer
from image_slicer import join
def DCP(im,i):
pil_image = im
opencvImage = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR)
src= opencvImage;
I = src.astype('float64')/255;
dark = DarkChannel(I,15);
A = AtmLight(I,dark);
te = TransmissionEstimate(I,A,15);
t = TransmissionRefine(src,te);
J = Recover(I,t,A,0.1);
cv2.imwrite("J"+str(i)+".jpg",J*255);
tiles = image_slicer.slice('o1.jpg', 4, save=False)
with concurrent.futures.ProcessPoolExecutor() as executor:
for tile in tiles:
results = [executor.submit(DCP,tile.image,i)]
i=i+1
i=1
for tile in tiles:
tile.image = Image.open("J"+str(i)+".jpg")
i=i+1
img = join(tiles)
img =img.convert("RGB")
img.save('Dehaze_Parallel.jpg')
finish = time.perf_counter()
print(f'Finished in{round(finish-start,2)} seconds')
The ultimate goal is to split the image into 4 parts, process them parallelly using the multiprocessing concept, and then merge the processed all the processed image parts into a single image.
Please refer my comments for more details.

Different sessions show different streaming data with a single bokeh server, how to solve it?

I'm working on a simulated osilloscope where the server PC collects data and ultimately will publish the streaming plot online. Below is a working script that can do the job. However, when I open multiple browsers, the streaming plots exhibit different data. (Although they are using the same data source). The example 'ohlc' seems to have the same problem. So, what is the right way to do this? I'm considering to write data to a file, but that will bring some issues like file i/o delay and disk storage limitation etc. Thank you for any help.
from bokeh.server.server import Server
from bokeh.models import ColumnDataSource, Label
from bokeh.plotting import figure
from bokeh.layouts import column
import numpy as np
import datetime as dt
from functools import partial
import time
# this will be replaced with the real data collector in the end
def f_emitter(p=0.1):
v = np.random.rand()
return (dt.datetime.now(), 0. if v>p else v)
def make_document(doc, functions, labels):
def update():
for index, func in enumerate(functions):
data = func()
sources[index].stream(new_data=dict(time=[data[0]], data=[data[1]]), rollover=1000)
annotations[index].text = f'{data[1]: .3f}'
sources = [ColumnDataSource(dict(time=[], data=[])) for _ in range(len(functions))]
figs = []
annotations = []
for i in range(len(functions)):
figs.append(figure(x_axis_type='datetime',
y_axis_label=labels[i], toolbar_location=None,
active_drag=None, active_scroll=None))
figs[i].line(x='time', y='data', source=sources[i])
annotations.append(Label(x=10, y=10, text='', text_font_size='40px', text_color='black',
x_units='screen', y_units='screen', background_fill_color='white'))
figs[i].add_layout(annotations[i])
# print(figs[i].plot_height)
doc.add_root(column([fig for fig in figs], sizing_mode='stretch_both'))
doc.add_periodic_callback(callback=update, period_milliseconds=100)
if __name__ == '__main__':
# list of functions and labels to feed into the scope
functions = [f_emitter]
labels = ['emitter']
server = Server({'/': partial(make_document, functions=functions, labels=labels)})
server.start()
server.io_loop.add_callback(server.show, "/")
try:
server.io_loop.start()
except KeyboardInterrupt:
print('keyboard interruption')
When you connect with a new client, by default Bokeh creates a new session. Each session has its own document, so the data source end up not being the same.

How can I compare images on the web to see if they are the same in python3?

I am trying to write a script that compares images, and tells me whether or not the images are the same. Here is some minimal code:
import requests
url1 = 'https://scontent-lga3-1.cdninstagram.com/vp/b4577921aa35369af8980a3d563e4373/5DAE3C31/t51.2885-15/fr/e15/s1080x1080/66126877_342437073345261_1373504971257332049_n.jpg?_nc_ht=scontent-lga3-1.cdninstagram.com'
url2 = 'https://scontent-lga3-1.cdninstagram.com/vp/fab3372181d5ad596280d2c095a3496e/5DE99775/t51.2885-15/e35/67547020_369706770411768_8601267197685673619_n.jpg?_nc_ht=scontent-lga3-1.cdninstagram.com'
print(requests.Session().get(url1).content == requests.Session().get(url2).content)
However, if you manually navigate to each url you will see that the photos are the same. My question; Can I compare these images WITHOUT having to save them to a directory? I was thinking about perhaps reading these images both as binary, then doing the comparison, however I have no idea how to do that on the fly. Thanks for all of those who reply in advance.
If you want to see if two images are exactly the same, you can use BytesIO and PIL
import requests
from io import BytesIO
from PIL import Image
def get_image_data(img_url):
img = Image.open(requests.get(img_url, stream=True).raw).convert('RGB')
byteio = BytesIO()
img.save(byteio, format='PNG')
return byteio.getvalue()
url1 = 'https://scontent-lga3-1.cdninstagram.com/vp/b4577921aa35369af8980a3d563e4373/5DAE3C31/t51.2885-15/fr/e15/s1080x1080/66126877_342437073345261_1373504971257332049_n.jpg?_nc_ht=scontent-lga3-1.cdninstagram.com'
url2 = 'https://scontent-lga3-1.cdninstagram.com/vp/fab3372181d5ad596280d2c095a3496e/5DE99775/t51.2885-15/e35/67547020_369706770411768_8601267197685673619_n.jpg?_nc_ht=scontent-lga3-1.cdninstagram.com'
print(get_image_data(url1)==get_image_data(url2))
Though it seems these images have a slight difference between them, and this code returns false.
It would be better to take an approach which does not depend on the size of the image. Your two images could be a thumbnail and a full-sized image.
I would combine the approaches from:
#Tanner Clark Image comparison - fast algorithm
and the approach above:
import requests
from io import BytesIO
from PIL import Image, ImageFilter
import imagehash
def get_image(img_url):
img = Image.open(requests.get(img_url, stream=True).raw).convert('RGB')
byteio = BytesIO()
img.save(byteio, format='PNG')
return img
url1 = 'http://scontent-lga3-1.cdninstagram.com/vp/b4577921aa35369af8980a3d563e4373/5DAE3C31/t51.2885-15/fr/e15/s1080x1080/66126877_342437073345261_1373504971257332049_n.jpg?_nc_ht=scontent-lga3-1.cdninstagram.com'
url2 = 'http://scontent-lga3-1.cdninstagram.com/vp/fab3372181d5ad596280d2c095a3496e/5DE99775/t51.2885-15/e35/67547020_369706770411768_8601267197685673619_n.jpg?_nc_ht=scontent-lga3-1.cdninstagram.com'
def compare_images(url1, url2):
img1 = get_image(url1)
img2 = get_image(url2)
if img1.width<img2.width:
img2=img2.resize((img1.width,img1.height))
else:
img1=img1.resize((img2.width,img2.height))
img1=img1.filter(ImageFilter.BoxBlur(radius=3))
img2=img2.filter(ImageFilter.BoxBlur(radius=3))
phashvalue=imagehash.phash(img1)-imagehash.phash(img2)
ahashvalue=imagehash.average_hash(img1)-imagehash.average_hash(img2)
threshold = 1 # some experimentally valid value
totalaccuracy=phashvalue+ahashvalue
print(totalaccuracy)
return totalaccuracy <= threshold
print(compare_images(url1, url2))

Use ipython widgets to manipulate plots, that are constantly updated from within an infinite loop

Within an ipython notebook, using Jupyter I try to do a calculation, thats running for an extended period of time in a while loop. I use pyplot to show the current status of my calculation and I would like to be able to communicate with the calculation and the plot via ipywidgets. For example this could be a stop button for the calculation, or a slider that adjusts the update rate of the plot.
Below I show a minimal example of a sin function, that is constantly plotted with different phase shifts. The start button starts the calculation, the stop button stops it and the slider should adjust the speed at which the whole thing is updated. The textbox shows the current phase shift.
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import *
import IPython.display as display
import _thread
import time
%matplotlib nbagg
# set up widgets
buttons = widgets.ToggleButtons(
options=['Stop', 'Start'],
description='',
disabled=False,
)
text = widgets.Text(
value='0',
placeholder='',
description='Shift [rad]',
)
speed_slider = widgets.IntSlider(
description = "Speed",
value = 100,
min = 10,
max = 500,
step = 10,
)
container = widgets.HBox(children = [buttons, speed_slider, text])
display.display(container)
# functions
def mySin(x, x0):
return np.sin(x-x0)
def run_app(x, dx0):
x0 = np.remainder(np.float(text.value), 2*np.pi)
while buttons.value == buttons.options[1]:
x0 = np.remainder(x0 + dx0, 2*np.pi)
line.set_ydata(mySin(x, x0))
text.value = str(x0)
time.sleep(speed_slider.value/1000)
fig.canvas.draw()
# setup plot
N = 1000
x = np.linspace(0, 10*np.pi, N)
dx0 = 0.05*2*np.pi
fig, ax = plt.subplots(ncols = 1, nrows = 1, figsize = (8,3))
line = ax.plot(x, mySin(x, np.float(text.value)))[0]
# act on button change
def buttons_on_changed(val):
if buttons.value == buttons.options[1]:
_thread.start_new_thread(run_app, (x, dx0))
buttons.observe(buttons_on_changed)
I try to run the function "run_app" in a new thread in order to make the interaction possible. I'm aware that _thread is deprecated, but I would like to know first, wether this is the right way to go. The example above works more or less, but the execution stops after a couple of seconds or when I do something else in the notebook (scrolling, clicking, ...). So my questions are the following:
Is the Thread closing or just losing priority and why is it doing that?
Is that a good approach at all, or can that be achieved in an easier way. Unfortunately, I haven't found anything browsing the web.
Thanks a lot,
Frank

Not able to click 3d objects and move them

I want to click on the loaded models and move them around. I used the code from chess sample examples and panda 3d tutorial without any success. Can someone figure out whats wrong with the code.
Thanks
from math import pi, sin, cos
import sys
from direct.showbase.ShowBase import ShowBase
import direct.directbase.DirectStart
from direct.task import Task
from panda3d.core import TextNode
from direct.gui.OnscreenText import OnscreenText
from panda3d.core import CollisionTraverser, CollisionNode
from panda3d.core import CollisionHandlerQueue, CollisionRay
from panda3d.core import Point3, Vec3, Vec4, BitMask32
from direct.showbase.DirectObject import DirectObject
from panda3d.core import AmbientLight, DirectionalLight, LightAttrib
class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# quit when esc is pressed
self.accept('escape', sys.exit)
#base.disableMouse()
# load the box model
self.box = self.loader.loadModel("models/xbox")
self.box.reparentTo(camera)
self.box.setScale(2.0, 2.0, 2.0)
self.box.setPos(8, 50, 0)
self.keyMap = {
"w" :False ,
"s" :False,
"a": False,
"d": False,
"mouse1": False,
"mouse3": False,
}
# CollisionTraverser and a Collision Handler is set up
self.picker = CollisionTraverser()
self.pq = CollisionHandlerQueue()
self.pickerNode = CollisionNode('mouseRay')
self.pickerNP = camera.attachNewNode(self.pickerNode)
self.pickerNode.setFromCollideMask(BitMask32.bit(1))
self.box.setCollideMask(BitMask32.bit(1))
self.pickerRay = CollisionRay()
self.pickerNode.addSolid(self.pickerRay)
self.picker.addCollider(self.pickerNP, self.pq)
self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask')
self.accept("mouse1", self.setKey, ["mouse1", True])
def mouseTask(self,task):
# check if we have access to the mouse
if base.mouseWatcherNode.hasMouse():
# get the mouse position
mpos = base.mouseWatcherNode.getMouse()
# set the position of the ray based on the mouse position
self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
self.picker.traverse(render)
# if we have hit something sort the hits so that the closest is first and highlight the node
if self.pq.getNumEntries() > 0:
self.pq.sortEntries()
pickedObj = self.picker.getEntry(0).getIntoNodePath()
def setKey(self,key,value):
self.keyMap[key] = value
app = MyApp()
app.run()
I was just trying to do the same thing, when I found your question.
Thanks for your code, it help me to start!
I've manage to get it working :)
Just a remark: you use a task, with no return, this make the task run once.
You should have used: return task.cont
Anyway, here my working code for panda3d devel (1.8.0+):
import sys
from direct.showbase.ShowBase import ShowBase
from pandac.PandaModules import *
class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# quit when esc is pressed
self.accept('escape',sys.exit)
#base.disableMouse()
# load the box model
box = self.loader.loadModel("models/box")
box.reparentTo(render)
box.setScale(2.0, 2.0, 2.0)
box.setPos(8, 50, 0)
panda = base.loader.loadModel("models/panda")
panda.reparentTo(render)
panda.setPos(0, 10, 0)
panda.setScale(0.1, 0.1, 0.1)
cNodePanda = panda.attachNewNode(CollisionNode('cnode_panda'))
cNodePanda.node().addSolid(CollisionSphere(0,0,5,5))
cNodePanda.show()
# CollisionTraverser and a Collision Handler is set up
self.picker = CollisionTraverser()
self.picker.showCollisions(render)
self.pq = CollisionHandlerQueue()
self.pickerNode = CollisionNode('mouseRay')
self.pickerNP = camera.attachNewNode(self.pickerNode)
self.pickerNode.setFromCollideMask(BitMask32.bit(1))
box.setCollideMask(BitMask32.bit(1))
panda.setCollideMask(BitMask32.bit(1))
self.pickerRay = CollisionRay()
self.pickerNode.addSolid(self.pickerRay)
self.picker.addCollider(self.pickerNP,self.pq)
self.accept("mouse1",self.mouseClick)
def mouseClick(self):
print('mouse click')
# check if we have access to the mouse
if base.mouseWatcherNode.hasMouse():
# get the mouse position
mpos = base.mouseWatcherNode.getMouse()
# set the position of the ray based on the mouse position
self.pickerRay.setFromLens(base.camNode,mpos.getX(),mpos.getY())
self.picker.traverse(render)
# if we have hit something sort the hits so that the closest is first and highlight the node
if self.pq.getNumEntries() > 0:
self.pq.sortEntries()
pickedObj = self.pq.getEntry(0).getIntoNodePath()
print('click on ' + pickedObj.getName())
app = MyApp()
app.run()

Resources