예제 #1
0
파일: yolo.py 프로젝트: orens77/pyzm
    def load_model(self):
        self.logger.Debug (1, '|--------- Loading Yolo model from disk ---------|')
        t = Timer()
        self.net = cv2.dnn.readNet(self.options.get('object_weights'),
                                self.options.get('object_config'))
        #self.net = cv2.dnn.readNetFromDarknet(config_file_abs_path, weights_file_abs_path)
        diff_time = t.stop_and_get_ms()

        self.logger.Debug(
            1,'perf: processor:{} Yolo initialization (loading {} model from disk) took: {}'
            .format(self.processor, self.options.get('object_weights'), diff_time))
        if self.processor == 'gpu':
            (maj, minor, patch) = cv2.__version__.split('.')
            min_ver = int(maj + minor)
            if min_ver < 42:
                self.logger.Error('Not setting CUDA backend for OpenCV DNN')
                self.logger.Error(
                    'You are using OpenCV version {} which does not support CUDA for DNNs. A minimum of 4.2 is required. See https://www.pyimagesearch.com/2020/02/03/how-to-use-opencvs-dnn-module-with-nvidia-gpus-cuda-and-cudnn/ on how to compile and install openCV 4.2'
                    .format(cv2.__version__))
                self.processor = 'cpu'
        else:
            self.logger.Debug (1, 'Using CPU for detection')

        if self.processor == 'gpu':
            self.logger.Debug( 2,'Setting CUDA backend for OpenCV')
            self.logger.Debug( 3,'If you did not set your CUDA_ARCH_BIN correctly during OpenCV compilation, you will get errors during detection related to invalid device/make_policy')
            self.net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
            self.net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)
        
        self.populate_class_labels()
예제 #2
0
    def detect(self, image=None):
        Height, Width = image.shape[:2]
        img = image.copy()
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = Image.fromarray(img)

        if self.options.get('auto_lock', True):
            self.acquire_lock()

        try:
            if not self.model:
                self.load_model()

            g.logger.Debug(
                1, '|---------- TPU (input image: {}w*{}h) ----------|'.format(
                    Width, Height))
            t = Timer()
            _, scale = common.set_resized_input(
                self.model, img.size,
                lambda size: img.resize(size, Image.ANTIALIAS))
            self.model.invoke()
            objs = detect.get_objects(
                self.model, float(self.options.get('object_min_confidence')),
                scale)

            #outs = self.model.detect_with_image(img, threshold=int(self.options.get('object_min_confidence')),
            #        keep_aspect_ratio=True, relative_coord=False)
            diff_time = t.stop_and_get_ms()

            if self.options.get('auto_lock', True):
                self.release_lock()
        except:
            if self.options.get('auto_lock', True):
                self.release_lock()
            raise

        diff_time = t.stop_and_get_ms()
        g.logger.Debug(
            1, 'perf: processor:{} Coral TPU detection took: {}'.format(
                self.processor, diff_time))

        bbox = []
        labels = []
        conf = []

        for obj in objs:
            # box = obj.bbox.flatten().astype("int")
            bbox.append([
                int(round(obj.bbox.xmin)),
                int(round(obj.bbox.ymin)),
                int(round(obj.bbox.xmax)),
                int(round(obj.bbox.ymax))
            ])

            labels.append(self.classes.get(obj.id))
            conf.append(float(obj.score))

        g.logger.Debug(
            3, 'Coral object returning: {},{},{}'.format(bbox, labels, conf))
        return bbox, labels, conf, ['coral'] * len(labels)
예제 #3
0
    def load_model(self):
        name = self.options.get('name') or 'TPU'
        g.logger.Debug(
            1, '|--------- Loading "{}" model from disk -------------|'.format(
                name))

        t = Timer()
        self.model = make_interpreter(self.options.get('face_weights'))
        self.model.allocate_tensors()
        diff_time = t.stop_and_get_ms()
        g.logger.Debug(
            1,
            'perf: processor:{} TPU initialization (loading {} from disk) took: {}'
            .format(self.processor, self.options.get('face_weights'),
                    diff_time))
예제 #4
0
    def load_model(self):
        self.logger.Debug(
            1, '|--------- Loading TPU model from disk -------------|')

        #self.model = DetectionEngine(self.options.get('object_weights'))
        # Initialize the TF interpreter
        t = Timer()
        self.model = make_interpreter(self.options.get('object_weights'))
        self.model.allocate_tensors()
        diff_time = t.stop_and_get_ms()
        self.logger.Debug(
            1,
            'perf: processor:{} TPU initialization (loading {} from disk) took: {}'
            .format(self.processor, self.options.get('object_weights'),
                    diff_time))
예제 #5
0
    def detect(self, image):
        height, width = image.shape[:2]
        g.logger.Debug(
            2, f'detect extracted image dimensions as: {width}x{height}')

        # Scale to 300x300 (that's what MobileNetSSD expects)
        image_resized = cv2.resize(image,
                                   (self.MODEL_HEIGHT, self.MODEL_WIDTH))
        height_factor = image.shape[0] / self.MODEL_HEIGHT
        width_factor = image.shape[1] / self.MODEL_WIDTH

        if self.options.get('auto_lock', True):
            self.acquire_lock()

        try:
            if not self.net:
                self.load_model()

            t = Timer()
            blob = cv2.dnn.blobFromImage(image_resized, 0.007843,
                                         (self.MODEL_HEIGHT, self.MODEL_WIDTH),
                                         (127.5, 127.5, 127.5), False)

            self.net.setInput(blob)
            detections = self.net.forward()

            if self.options.get('auto_lock', True):
                self.release_lock()
        except:
            if self.options.get('auto_lock', True):
                self.release_lock()
            raise

        diff_time = t.stop_and_get_ms()
        g.logger.Debug(
            1,
            f'perf: processor:{self.processor} MobileNetSSD detection took: {diff_time}'
        )

        conf_threshold = float(self.options.get('object_min_confidence', 0.2))

        bboxes = []
        labels = []
        confs = []

        for i in range(detections.shape[2]):
            confidence = float(detections[0, 0, i, 2])
            label_id = int(detections[0, 0, i, 1])
            bbox_rel = list(detections[0, 0, i, 3:7])
            try:
                label = self.classes[label_id]
            except ValueError as e:
                g.logger.Warning("LabelID={label_id} is outside of the range")
                continue
            if confidence < conf_threshold:
                if confidence > 0:
                    g.logger.Debug(
                        3, f"Ignored Detection: {label}: {confidence:.2f}")
                continue
            g.logger.Debug(1, f"Detection: {label}: {confidence:.2f}")
            bbox = (
                round(width * bbox_rel[0]),
                round(height * bbox_rel[1]),
                round(width * bbox_rel[2]),
                round(height * bbox_rel[3]),
            )
            bboxes.append(bbox)
            labels.append(label)
            confs.append(confidence)

        return bboxes, labels, confs, ['mobnet'] * len(label)
예제 #6
0
파일: face.py 프로젝트: orens77/pyzm
    def detect(self, image):
      
        Height, Width = image.shape[:2]
        self.logger.Debug(
            1,'|---------- Face recognition (input image: {}w*{}h) ----------|'.
            format(Width, Height))

        downscaled =  False
        upsize_xfactor = None
        upsize_yfactor = None
        max_size = self.options.get('max_size', Width)
        old_image = None

        self.logger.Debug(5, 'Face options={}'.format(self.options))
        
        if Width > max_size:
            downscaled = True
            self.logger.Debug (2, 'Scaling image down to max size: {}'.format(max_size))
            old_image = image.copy()
            image = imutils.resize(image,width=max_size)
            newHeight, newWidth = image.shape[:2]
            upsize_xfactor = Width/newWidth
            upsize_yfactor = Height/newHeight
        

        labels = []
        classes = []
        conf = []

        # Convert the image from BGR color (which OpenCV uses) to RGB color (which face_recognition uses)
        rgb_image = image[:, :, ::-1]
        #rgb_image = image

        # Find all the faces and face encodings in the target image
        #prin (self.options)
        if self.options.get('auto_lock',True):
            self.acquire_lock()

        t = Timer()
        face_locations = face_recognition.face_locations(
            rgb_image,
            model=self.face_model,
            number_of_times_to_upsample=self.upsample_times)

        diff_time = t.stop_and_get_ms()
        self.logger.Debug(1,'perf: processor:{} Finding faces took {}'.format(self.processor, diff_time))

        t = Timer()
        face_encodings = face_recognition.face_encodings(
            rgb_image,
            known_face_locations=face_locations,
            num_jitters=self.num_jitters)
        
        if self.options.get('auto_lock',True):
            self.release_lock()

        diff_time = t.stop_and_get_ms()
        self.logger.Debug(
            1,'perf: processor:{} Computing face recognition distances took {}'.format(
                self.processor, diff_time))

        if not len(face_encodings):
            return [], [], []

        # Use the KNN model to find the best matches for the test face
      
        self.logger.Debug(3,'Comparing to known faces...')

        t = Timer()
        if self.knn:
            #self.logger.Debug(5, 'FACE ENCODINGS={}'.format(face_encodings))
            closest_distances = self.knn.kneighbors(face_encodings, n_neighbors=1)
            self.logger.Debug(5, 'Closest knn match indexes (lesser is better): {}'.format(closest_distances))
            are_matches = [
                closest_distances[0][i][0] <= float(self.options.get('face_recog_dist_threshold',0.6))
                for i in range(len(face_locations))
                
            ]
            prediction_labels = self.knn.predict(face_encodings)
            #self.logger.Debug(5, 'KNN predictions: {} are_matches: {}'.format(prediction_labels, are_matches))

        else:
            # There were no faces to compare
            # create a set of non matches for each face found
            are_matches = [False] * len(face_locations)
            prediction_labels = [''] * len(face_locations)
            self.logger.Debug (1,'No faces to match, so creating empty set')

        diff_time = t.stop_and_get_ms()
        self.logger.Debug(
            1,'perf: processor:{} Matching recognized faces to known faces took {}'.
            format(self.processor, diff_time))

        matched_face_names = []
        matched_face_rects = []


        if downscaled:
            self.logger.Debug (2,'Scaling image back up to {}'.format(Width))
            image = old_image
            new_face_locations = []
            for loc in face_locations:
                a,b,c,d=loc
                a = round(a * upsize_yfactor)
                b = round(b * upsize_xfactor)
                c = round(c * upsize_yfactor)
                d = round(d * upsize_xfactor)
                new_face_locations.append((a,b,c,d))
            face_locations = new_face_locations


        for pred, loc, rec in zip(prediction_labels,
                                  face_locations, are_matches):
            label = pred if rec else self.options.get('unknown_face_name', 'unknown')
            if not rec and self.options.get('save_unknown_faces') == 'yes':
                h, w, c = image.shape
                x1 = max(loc[3] - int(self.options.get('save_unknown_faces_leeway_pixels',0)),0)
                y1 = max(loc[0] - int(self.options.get('save_unknown_faces_leeway_pixels',0)),0)
                x2 = min(loc[1] + int(self.options.get('save_unknown_faces_leeway_pixels',0)), w)
                y2 = min(loc[2] + int(self.options.get('save_unknown_faces_leeway_pixels',0)),h)
                #print (image)
                crop_img = image[y1:y2, x1:x2]
                # crop_img = image
                timestr = time.strftime("%b%d-%Hh%Mm%Ss-")
                unf = self.options.get('unknown_images_path') + '/' + timestr + str(
                    uuid.uuid4()) + '.jpg'
                self.logger.Info(
                    'Saving cropped unknown face at [{},{},{},{} - includes leeway of {}px] to {}'
                    .format(x1, y1, x2, y2,
                            self.options.get('save_unknown_faces_leeway_pixels'), unf))
                cv2.imwrite(unf, crop_img)

            
            matched_face_rects.append([loc[3], loc[0], loc[1], loc[2]])
            matched_face_names.append(label)
            #matched_face_names.append('face:{}'.format(label))
            conf.append(1)

        self.logger.Debug(3,f'FACE:Returning: {matched_face_rects}, {matched_face_names}, {conf}')
        return matched_face_rects, matched_face_names, conf
예제 #7
0
파일: face.py 프로젝트: orens77/pyzm
import pickle
from sklearn import neighbors
import imutils
import math
import uuid
import time
import datetime
from pyzm.helpers.Base import Base
# Class to handle face recognition
import portalocker
import re
from pyzm.helpers.Media import MediaStream
import imutils
from pyzm.helpers.utils import Timer

g_start =Timer()
import face_recognition
g_diff_time = g_start.stop_and_get_ms()

class Face(Base):
    def __init__(self, logger=None, options={},upsample_times=1, num_jitters=0, model='hog'):
        super().__init__(logger)
        global g_diff_time
        #self.logger.Debug (4, 'Face init params: {}'.format(options))

        if dlib.DLIB_USE_CUDA and dlib.cuda.get_num_devices() >=1 :
            self.processor = 'gpu'
        else:
            self.processor = 'cpu'

        self.logger.Debug(
예제 #8
0
    def train(self,size=None):
        t = Timer()
        known_images_path = self.options.get('known_images_path')
        train_model = self.options.get('face_train_model')
        knn_algo = self.options.get('face_recog_knn_algo', 'ball_tree') 
    
        upsample_times = int(self.options.get('face_upsample_times',1))
        num_jitters = int(self.options.get('face_num_jitters',0))

        encoding_file_name = known_images_path + '/faces.dat'
        try:
            if (os.path.isfile(known_images_path + '/faces.pickle')):
                # old version, we no longer want it. begone
                g.logger.Debug(
                    2,'removing old faces.pickle, we have moved to clustering')
                os.remove(known_images_path + '/faces.pickle')
        except Exception as e:
            g.logger.Error('Error deleting old pickle file: {}'.format(e))

        directory = known_images_path
        ext = ['.jpg', '.jpeg', '.png', '.gif']
        known_face_encodings = []
        known_face_names = []

        try:
            for entry in os.listdir(directory):
                if os.path.isdir(directory + '/' + entry):
                    # multiple images for this person,
                    # so we need to iterate that subdir
                    g.logger.Debug(
                        1,'{} is a directory. Processing all images inside it'.
                        format(entry))
                    person_dir = os.listdir(directory + '/' + entry)
                    for person in person_dir:
                        if person.endswith(tuple(ext)):
                            g.logger.Debug(1,'loading face from  {}/{}'.format(
                                entry, person))

                            # imread seems to do a better job of color space conversion and orientation
                            known_face = cv2.imread('{}/{}/{}'.format(
                                directory, entry, person))
                            if known_face is None or known_face.size == 0:
                                g.logger.Error('Error reading file, skipping')
                                continue
                            #known_face = face_recognition.load_image_file('{}/{}/{}'.format(directory,entry, person))
                            if not size:
                                size = int(self.options.get('resize',800))
                            g.logger.Debug (1,'resizing to {}'.format(size))
                            known_face = imutils.resize(known_face,width=size)

                            # Convert the image from BGR color (which OpenCV uses) to RGB color (which face_recognition uses)
                            #g.logger.Debug(1,'Converting from BGR to RGB')
                            known_face = known_face[:, :, ::-1]
                            face_locations = face_recognition.face_locations(
                                known_face,
                                model=train_model,
                                number_of_times_to_upsample=upsample_times)
                            if len(face_locations) != 1:
                                g.logger.Error(
                                    'File {} has {} faces, cannot use for training. We need exactly 1 face. If you think you have only 1 face try using "cnn" for training mode. Ignoring...'
                                .format(person, len(face_locations)))
                            else:
                                face_encodings = face_recognition.face_encodings(
                                    known_face,
                                    known_face_locations=face_locations,
                                    num_jitters=num_jitters)
                                known_face_encodings.append(face_encodings[0])
                                known_face_names.append(entry)
                                #g.logger.Debug ('Adding image:{} as known person: {}'.format(person, person_dir))

                elif entry.endswith(tuple(ext)):
                    # this was old style. Lets still support it. The image is a single file with no directory
                    g.logger.Debug(1,'loading face from  {}'.format(entry))
                    #known_face = cv2.imread('{}/{}/{}'.format(directory,entry, person))
                    known_face = cv2.imread('{}/{}'.format(directory, entry))

                    if not size:
                        size = int(self.options.get('resize',800))
                        g.logger.Debug (1,'resizing to {}'.format(size))
                        known_face = imutils.resize(known_face,width=size)
                    # Convert the image from BGR color (which OpenCV uses) to RGB color (which face_recognition uses)
                    known_face = known_face[:, :, ::-1]
                    face_locations = face_recognition.face_locations(
                        known_face,
                        model=train_model,
                        number_of_times_to_upsample=upsample_times)

                    if len(face_locations) != 1:
                        g.logger.Error(
                                    'File {} has {} faces, cannot use for training. We need exactly 1 face. If you think you have only 1 face try using "cnn" for training mode. Ignoring...'
                                    .format(person), len(face_locations))
                    else:
                        face_encodings = face_recognition.face_encodings(
                            known_face,
                            known_face_locations=face_locations,
                            num_jitters=num_jitters)
                        known_face_encodings.append(face_encodings[0])
                        known_face_names.append(os.path.splitext(entry)[0])

        except Exception as e:
            g.logger.Error('Error initializing face recognition: {}'.format(e))
            raise ValueError(
                'Error opening known faces directory. Is the path correct?')

        # Now we've finished iterating all files/dirs
        # lets create the svm
        if not len(known_face_names):
            g.logger.Error(
                'No known faces found to train, encoding file not created')
        else:
            n_neighbors = int(round(math.sqrt(len(known_face_names))))
            g.logger.Debug(2,'Using algo:{} n_neighbors to be: {}'.format(knn_algo, n_neighbors))
            knn = neighbors.KNeighborsClassifier(n_neighbors=n_neighbors,
                                                algorithm=knn_algo,
                                                weights='distance')

            g.logger.Debug(1,'Training model ...')
            knn.fit(known_face_encodings, known_face_names)

            f = open(encoding_file_name, "wb")
            pickle.dump(knn, f)
            f.close()
            g.logger.Debug(1,'wrote encoding file: {}'.format(encoding_file_name))
        diff_time = t.stop_and_get_ms()
        g.logger.Debug(
            1,'perf: Face Recognition training took: {}'.format(diff_time))
예제 #9
0
파일: yolo.py 프로젝트: orens77/pyzm
    def detect(self, image=None):
        Height, Width = image.shape[:2]
        downscaled =  False
        upsize_xfactor = None
        upsize_yfactor = None
        max_size = self.options.get('max_size', Width)
        old_image = None

        if Width > max_size:
            downscaled = True
            self.logger.Debug (2, 'Scaling image down to max size: {}'.format(max_size))
            old_image = image.copy()
            image = imutils.resize(image,width=max_size)
            newHeight, newWidth = image.shape[:2]
            upsize_xfactor = Width/newWidth
            upsize_yfactor = Height/newHeight        


        if self.options.get('auto_lock',True):
            self.acquire_lock()

        try:
            if not self.net:
                self.load_model()

            self.logger.Debug(
                1,'|---------- YOLO (input image: {}w*{}h, model resize dimensions: {}w*{}h) ----------|'
                .format(Width, Height, self.model_width, self.model_height))

            
            scale = 0.00392  # 1/255, really. Normalize inputs.
                
            t = Timer()
            ln = self.net.getLayerNames()
            ln = [ln[i[0] - 1] for i in self.net.getUnconnectedOutLayers()]
            blob = cv2.dnn.blobFromImage(image,
                                        scale, (self.model_width, self.model_height), (0, 0, 0),
                                        True,
                                        crop=False)
            
            self.net.setInput(blob)
            outs = self.net.forward(ln)

            if self.options.get('auto_lock',True):
                self.release_lock()
        except:
            if self.options.get('auto_lock',True):
                self.release_lock()
            raise

        diff_time = t.stop_and_get_ms()
        self.logger.Debug(
            1,'perf: processor:{} Yolo detection took: {}'.format(self.processor, diff_time))

    
        class_ids = []
        confidences = []
        boxes = []

        nms_threshold = 0.4
        conf_threshold = 0.2

        # first nms filter out with a yolo confidence of 0.2 (or less)
        if float(self.options.get('object_min_confidence')) < conf_threshold:
            conf_threshold = float(self.options.get('object_min_confidence'))

        for out in outs:
            for detection in out:
                scores = detection[5:]
                class_id = np.argmax(scores)
                confidence = scores[class_id]
                center_x = int(detection[0] * Width)
                center_y = int(detection[1] * Height)
                w = int(detection[2] * Width)
                h = int(detection[3] * Height)
                x = center_x - w / 2
                y = center_y - h / 2
                class_ids.append(class_id)
                confidences.append(float(confidence))
                boxes.append([x, y, w, h])

        t = Timer()
        indices = cv2.dnn.NMSBoxes(boxes, confidences, conf_threshold,
                                   nms_threshold)
        diff_time = t.stop_and_get_ms()

        self.logger.Debug(
            2,'perf: processor:{} Yolo NMS filtering took: {}'.format(self.processor, diff_time))

        bbox = []
        label = []
        conf = []

        prefix = '(yolo) ' if self.options.get('show_models')=='yes' else ''

        # now filter out with configured yolo confidence, so we can see rejections in log
        for i in indices:
            i = i[0]
            box = boxes[i]
            x = box[0]
            y = box[1]
            w = box[2]
            h = box[3]

            
            bbox.append([
                int(round(x)),
                int(round(y)),
                int(round(x + w)),
                int(round(y + h))
            ])
            label.append(prefix+str(self.classes[class_ids[i]]))
            conf.append(confidences[i])
           
        if downscaled:
            self.logger.Debug (2,'Scaling image back up to {}'.format(Width))
            image = old_image
            for box in bbox:
                box[0] = round (box[0] * upsize_xfactor)
                box[1] = round (box[1] * upsize_yfactor)
                box[2] = round (box[2] * upsize_xfactor)
                box[3] = round (box[3] * upsize_yfactor)
        
        return bbox, label, conf
예제 #10
0
    def detect_stream(self, stream, options={}, ml_overrides={}):
        """Implements detection on a video stream

        Args:
            stream (string): location of media (file, url or event ID)
            ml_overrides(string): Ignore it. You will almost never need it. zm_detect uses it for ugly foo
            options (dict, optional): Various options that control the detection process. Defaults to {}:
                - delay (int): Delay in seconds before starting media stream
                - download (boolean): if True, will download video before analysis. Defaults to False
                - download_dir (string): directory where downloads will be kept (only applies to videos). Default is /tmp
                - start_frame (int): Which frame to start analysis. Default 1.
                - frame_skip: (int): Number of frames to skip in video (example, 3 means process every 3rd frame)
                - max_frames (int): Total number of frames to process before stopping
                - pattern (string): regexp for objects that will be matched. 'frame_strategy' key below will be applied to only objects that match this pattern
                - frame_set (string): comma separated frames to read. Example 'alarm,21,31,41,snapshot'
                  Note that if you are specifying frame IDs and using ZM, remember that ZM has a frame buffer
                  Default is 20, I think. So you may want to start at frame 21.
                - contig_frames_before_error (int): How many contiguous frames should fail before we give up on reading this stream. Default 5
                - max_attempts (int): Only for ZM indirection. How many times to retry a failed frame get. Default 1
                - sleep_between_attempts (int): Only for ZM indirection. Time to wait before re-trying a failed frame
                - disable_ssl_cert_check (bool): If True (default) will allow self-signed certs to work
                - save_frames (boolean): If True, will save frames used in analysis. Default False
                - save_analyzed_frames (boolean): If True, will save analyzed frames (with boxes). Default False
                - save_frames_dir (string): Directory to save analyzed frames. Default /tmp
                - frame_strategy: (string): various conditions to stop matching as below
                    - 'most_models': Match the frame that has matched most models (does not include same model alternatives) (Default)
                    - 'first': Stop at first match 
                    - 'most': Match the frame that has the highest number of detected objects
                    - 'most_unique' Match the frame that has the highest number of unique detected objects
           
                - resize (int): Width to resize image, default 800
                - polygons(object): object # set of polygons that the detected image needs to intersect
                
        Returns:
           - object: representing matched frame, consists of:

            - box (array): list of bounding boxes for matched frame
            - label (array): list of labels for matched frame
            - confidence (array): list of confidences for matched frame
            - id (int): frame id of matched frame
            - img (cv2 image): image grab of matched frame

           - array of objects:

            - list of boxes,labels,confidences of all frames matched

        Note:

        The same frames are not retrieved depending on whether you set
        ``download`` to ``True`` or ``False``. When set to ``True``, we use
        OpenCV's frame reading logic and when ``False`` we use ZoneMinder's image.php function
        which uses time based approximation. Therefore, the retrieve different frame offsets, but I assume
        they should be reasonably close.
            
        """

        self.ml_overrides = ml_overrides
        self.stream_options = options
        frame_strategy = self.stream_options.get('frame_strategy',
                                                 'most_models')
        all_matches = []
        matched_b = []
        matched_e = []
        matched_l = []
        matched_c = []
        matched_models = []
        matched_frame_id = None
        matched_images = []

        matched_frame_img = None
        manual_locking = False

        if len(self.model_sequence) > 1:
            manual_locking = False
            self.logger.Debug(
                3,
                'Using automatic locking as we are switching between models')
        else:
            manual_locking = True
            self.logger.Debug(
                3, 'Using manual locking as we are only using one model')
            for seq in self.model_sequence:
                self.ml_options[seq]['auto_lock'] = False
        t = Timer()
        media = MediaStream(stream,
                            'video',
                            self.stream_options,
                            logger=self.logger)
        self.media = media

        polygons = copy.copy(self.stream_options.get('polygons', []))

        # Loops across all frames
        while self.media.more():
            frame = self.media.read()
            if frame is None:
                self.logger.Debug(1, 'Ran out of frames to read')
                break
            #fname = '/tmp/{}.jpg'.format(self.media.get_last_read_frame())
            #print (f'Writing to {fname}')
            #cv2.imwrite( fname ,frame)
            self.logger.Debug(
                1, 'perf: Starting for frame:{}'.format(
                    self.media.get_last_read_frame()))
            _labels_in_frame = []
            _boxes_in_frame = []
            _error_boxes_in_frame = []
            _confs_in_frame = []
            _models_in_frame = []

            # For each frame, loop across all models
            found = False
            for seq in self.model_sequence:
                if seq not in self.ml_overrides.get('model_sequence', seq):
                    self.logger.Debug(
                        1, 'Skipping {} as it was overridden in ml_overrides'.
                        format(seq))
                    continue
                self.logger.Debug(
                    1,
                    '============ Frame: {} Running {} model in sequence =================='
                    .format(self.media.get_last_read_frame(), seq))
                pre_existing_labels = self.ml_options.get(seq, {}).get(
                    'general', {}).get('pre_existing_labels')
                if pre_existing_labels:
                    self.logger.Debug(
                        2,
                        'Making sure we have matched one of {} in {} before we proceed'
                        .format(pre_existing_labels, _labels_in_frame))
                    if not any(x in _labels_in_frame
                               for x in pre_existing_labels):
                        self.logger.Debug(
                            1,
                            'Did not find pre existing labels, not running model'
                        )
                        continue

                if not self.models.get(seq):
                    try:
                        self._load_models([seq])
                        if manual_locking:
                            for m in self.models[seq]:
                                m.acquire_lock()
                    except Exception as e:
                        self.logger.Error(
                            'Error loading model for {}:{}'.format(seq, e))
                        self.logger.Debug(2, traceback.format_exc())
                        continue

                same_model_sequence_strategy = self.ml_options.get(
                    seq, {}).get('general',
                                 {}).get('same_model_sequence_strategy',
                                         'first')
                self.logger.Debug(
                    3, '{} has a same_model_sequence strategy of {}'.format(
                        seq, same_model_sequence_strategy))

                # start of same model iteration
                _b_best_in_same_model = []
                _l_best_in_same_model = []
                _c_best_in_same_model = []
                _e_best_in_same_model = []

                cnt = 1
                # For each model, loop across different variations
                for m in self.models[seq]:
                    self.logger.Debug(
                        3,
                        '--------- Frame:{} Running variation: #{} -------------'
                        .format(self.media.get_last_read_frame(), cnt))
                    cnt += 1
                    try:
                        _b, _l, _c = m.detect(image=frame)
                        self.logger.Debug(
                            4,
                            'This model iteration inside {} found: labels: {},conf:{}'
                            .format(seq, _l, _c))
                    except Exception as e:
                        self.logger.Error('Error running model: {}'.format(e))
                        self.logger.Debug(2, traceback.format_exc())
                        continue

                    # Now let's make sure the labels match our pattern
                    _b, _l, _c, _e = self._filter_patterns(
                        seq, _b, _l, _c, polygons)
                    if _e:
                        _e_best_in_same_model.extend(_e)
                    if not len(_l):
                        continue
                    if ((same_model_sequence_strategy == 'first')
                            or ((same_model_sequence_strategy == 'most') and
                                (len(_l) > len(_l_best_in_same_model))) or
                        ((same_model_sequence_strategy == 'most_unique') and
                         (len(set(_l)) > len(set(_l_best_in_same_model))))):
                        _b_best_in_same_model = _b
                        _l_best_in_same_model = _l
                        _c_best_in_same_model = _c
                        _e_best_in_same_model = _e
                    if _l_best_in_same_model and self.stream_options.get(
                            'save_analyzed_frames'
                    ) and self.media.get_debug_filename():
                        d = self.stream_options.get('save_frames_dir', '/tmp')
                        f = '{}/{}-analyzed-{}.jpg'.format(
                            d, self.media.get_debug_filename(),
                            media.get_last_read_frame())
                        self.logger.Debug(
                            4, 'Saving analyzed frame: {}'.format(f))
                        a = utils.draw_bbox(
                            frame, _b_best_in_same_model,
                            _l_best_in_same_model, _c_best_in_same_model,
                            self.stream_options.get('polygons'))
                        for _b in _e_best_in_same_model:
                            cv2.rectangle(a, (_b[0], _b[1]), (_b[2], _b[3]),
                                          (0, 0, 255), 1)
                        cv2.imwrite(f, a)
                    if (same_model_sequence_strategy == 'first') and len(_b):
                        self.logger.Debug(
                            3,
                            'breaking out of same model loop, as matches found and strategy is "first"'
                        )
                        break
                # end of same model sequence iteration
                # at this state x_best_in_model contains the best match across
                # same model variations
                if _l_best_in_same_model:
                    found = True
                    _labels_in_frame.extend(_l_best_in_same_model)
                    _boxes_in_frame.extend(_b_best_in_same_model)
                    _confs_in_frame.extend(_c_best_in_same_model)
                    _error_boxes_in_frame.extend(_e_best_in_same_model)
                    _models_in_frame.append(seq)
                    if (frame_strategy == 'first'):
                        self.logger.Debug(
                            2,
                            'Breaking out of main model loop as strategy is first'
                        )
                        break
                else:
                    self.logger.Debug(
                        2,
                        'We did not find any {} matches in frame: {}'.format(
                            seq, self.media.get_last_read_frame()))

            # end of primary model sequence
            if found:
                all_matches.append({
                    'frame_id': self.media.get_last_read_frame(),
                    'boxes': _boxes_in_frame,
                    'error_boxes': _error_boxes_in_frame,
                    'labels': _labels_in_frame,
                    'confidences': _confs_in_frame,
                    'models': _models_in_frame
                })
                matched_images.append(frame.copy())
                if (frame_strategy == 'first'):
                    self.logger.Debug(
                        2,
                        'Frame strategy is first, breaking out of frame loop')
                    break

        # end of while media loop

        #print ('*********** MATCH_STRATEGY {}'.format(model_match_strategy))
        for idx, item in enumerate(all_matches):
            if ((frame_strategy == 'first')
                    or ((frame_strategy == 'most') and
                        (len(item['labels']) > len(matched_l)))
                    or ((frame_strategy == 'most_models') and
                        (len(item['models']) > len(matched_models)))
                    or ((frame_strategy == 'most_unique') and
                        (len(set(item['labels'])) > len(set(matched_l))))):
                matched_b = item['boxes']
                matched_e = item['error_boxes']
                matched_c = item['confidences']
                matched_l = item['labels']
                matched_frame_id = item['frame_id']
                matched_models = item['models']
                matched_frame_img = matched_images[idx]

        if manual_locking:
            for seq in self.model_sequence:
                for m in self.models[seq]:
                    m.release_lock()

        diff_time = t.stop_and_get_ms()

        self.logger.Debug(
            1,
            'perf: TOTAL detection sequence (with image loads) took: {}  to process {}'
            .format(diff_time, stream))
        self.media.stop()

        matched_data = {
            'boxes': matched_b,
            'error_boxes': matched_e,
            'labels': matched_l,
            'confidences': matched_c,
            'frame_id': matched_frame_id,
            'image_dimensions': self.media.image_dimensions(),
            #'type': matched_type,
            'image': matched_frame_img,
            'polygons': polygons
        }
        # if invoked again, we need to resize polys
        self.has_rescaled = False
        return matched_data, all_matches