<jupyter_start><jupyter_text>Face Recognition Model - Serving FunctionThe function uses sklearn classifier on top of an opencv deep learning model to encode and recognize faces in given image<jupyter_code>#nuclio: ignore
import nuclio<jupyter_output><empty_output><jupyter_text>Install dependencies and set config<jupyter_code>%%nuclio cmd -c
pip install cmake
pip install dlib
pip install face_recognition
pip install opencv-contrib-python
pip install imutils
pip install sklearn
pip install pandas
pip install joblib
pip install v3io_frames
%nuclio config spec.build.baseImage = "python:3.6-jessie"<jupyter_output><empty_output><jupyter_text>Perform necessary imports<jupyter_code>import cv2
import face_recognition
import imutils
import joblib
import json
import numpy as np
import pandas as pd
import random
import string
import v3io_frames as v3f
import os
import datetime<jupyter_output><empty_output><jupyter_text>Set function environment variables<jupyter_code>%%nuclio env
MODEL_PATH = /User/demos/demos/faces/artifacts/model.bst
CLASSES_MAP = /User/demos/demos/faces/artifacts/idx2name.csv
%nuclio env V3IO_ACCESS_KEY=${V3IO_ACCESS_KEY}<jupyter_output><empty_output><jupyter_text>Model serving class<jupyter_code>class SKModel(object):
    def __init__(self):
        self.name = 'model.bst'
        self.model_filepath = os.environ['MODEL_PATH']
        self.model = None
        self.ready = None
        self.classes = os.environ['CLASSES_MAP']
    
    def load(self):
        self.model = joblib.load(self.model_filepath)
        self.ready = True
    
    def predict(self, context, data):
        
        # acquires all metadata 
        time = data['time']
        cam_name = data['camera']
        img_url = data['file_path']
        
        # prepares image for use
        with open(img_url, 'rb') as f:
            content = f.read()
        img_bytes = np.frombuffer(content, dtype=np.uint8)
        image = cv2.imdecode(img_bytes, flags=1)
        
        # converts image format to RGB for comptability with face_recognition library and resize for faster processing
        rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        rgb = imutils.resize(image, width=750)
        ratio = image.shape[1] / float(rgb.shape[1])
        
        #gets mapping from label to name and known encodings
        idx2name_df = pd.read_csv(self.classes).set_index('value')
        
        if not self.model:
            self.load()
        
        #locates faces in image and extracts embbeding vector for each face
        context.logger.info_with('[INFO]', msg="recognizing faces...")
        boxes = face_recognition.face_locations(rgb, model='hog')
        encodings = face_recognition.face_encodings(rgb, boxes)
        
        #determines if face is a clear match/ambiguous.
        names = []
        labels = []
        for encoding in encodings:
            name = 'unknown'
            label = 'unknown'
            probs = self.model.predict_proba(encoding.reshape(1, -1))
            if np.max(probs) > 0.5:
                label = np.argmax(probs)
                name = idx2name_df.loc[label]['name'].replace('_', ' ')
            names.append(name)
            labels.append(label)
            context.logger.info(f'{name} with {probs}')

        #frames client to save necessary data
        client = v3f.Client("framesd:8081", container="users")

        #draw boxes with name on the image and performs logic according to match/ambiguous 
        ret_list = []
        for ((top, right, bottom, left), name, encoding, label) in zip(boxes, names, encodings, labels):  

            #rescale the face coordinates
            top = int(top * ratio)
            right = int(right * ratio)
            bottom = int(bottom * ratio)
            left = int(left * ratio)

            #random string for unique name in our saved data
            rnd_tag = ''.join(random.choices(string.ascii_uppercase + string.digits, k=5))

            new_row = {}
            #saves all extracted data to kv.  
            new_row = {'c' + str(i).zfill(3): encoding[i] for i in range(128)}
            if (name != 'unknown'): #and (len(enc_df.loc[enc_df['label'] == label]) < 50): TODO add different logic for limit of images of same person in dataset
                new_row['label'] = label
            else:
                new_row['label'] = -1
            new_row['imgUrl'] = img_url
            new_row['fileName'] = name.replace(' ', '_') + '_' + rnd_tag 
            new_row['camera'] = cam_name
            new_row['time'] = datetime.datetime.utcnow()
            new_row_df = pd.DataFrame(new_row, index=[0])
            
            client.write(backend='kv', table='iguazio/demos/demos/faces/artifacts/encodings', dfs=new_row_df, index_cols=['fileName'])

            #appends box and name to the returned list            
            ret_list.append(((top, right, bottom, left), name, np.max(probs)))

        return ret_list<jupyter_output><empty_output><jupyter_text>Main functionsimply initializes the model class and invokes the predict method<jupyter_code>def handler(context, event):
    model = SKModel()
    return model.predict(context=context, data=event.body)
#nuclio: end-code<jupyter_output><empty_output><jupyter_text>Set configuration for deployment<jupyter_code># converts the notebook code to deployable function with configurations
from mlrun import code_to_function
fn = code_to_function('recognize-faces', runtime='nuclio')

# set the API/trigger, attach the home dir to the function
fn.with_http(workers=2).add_volume('User','~/')

# set environment variables
fn.set_env('MODEL_PATH', '/User/demos/demos/faces/artifacts/model.bst')
fn.set_env('CLASSES_MAP', '/User/demos/demos/faces/artifacts/idx2name.csv')
fn.set_env('V3IO_ACCESS_KEY', os.environ['V3IO_ACCESS_KEY'])<jupyter_output><empty_output><jupyter_text>Deploy the function to the clusterMay take a few minutes due to building of image<jupyter_code>addr = fn.deploy(project='default')<jupyter_output>[mlrun] 2019-12-04 15:48:41,079 deploy started
[nuclio.deploy] 2019-12-04 15:48:42,147 (info) Building processor image
[nuclio.deploy] 2019-12-04 15:48:47,186 (info) Build complete
[nuclio.deploy] 2019-12-04 15:48:53,250 (info) Function deploy complete
[nuclio.deploy] 2019-12-04 15:48:53,255 done updating recognize-faces, function address: 192.168.3.11:32542
{'fraction_non_alphanumeric': 0.10611539066184562, 'fraction_numerical': 0.022480742021694703, 'mean_word_length': 3.1311688311688313, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 1, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '139734', 'license': 'Apache-2.0', 'n_tokens_mistral': 2095, 'n_tokens_neox': 1966, 'n_words': 492, 'path': 'faces/notebooks/nuclio-face-prediction.ipynb', 'repo_name': 'marcelonyc/demos', 'size': '6361'}