Esempio n. 1
0
if not api_options.get('apiurl') or not api_options.get('portalurl'):
    g.log.Fatal('Missing API and/or Portal URLs. Your secrets file probably doesn\'t have these values')

zmapi = zmapi.ZMApi(options=api_options, logger=g.log)

     

ml_options = {}
stream_options = {}

if g.config['ml_sequence'] and g.config['use_sequence'] == 'yes':
        g.log.Debug(2,'using ml_sequence')
        ml_options = g.config['ml_sequence']
        secrets = pyzmutils.read_config(g.config['secrets'])
        ml_options = pyzmutils.template_fill(input_str=ml_options, config=None, secrets=secrets._sections.get('secrets'))
        #print (ml_options)
        ml_options = ast.literal_eval(ml_options)
        #print (ml_options)
else:
    g.logger.Debug(2,'mapping legacy ml data from config')
    ml_options = utils.convert_config_to_ml_sequence()
    g.config['ml_options'] = ml_options


# stream options will come from zm_detect

#print(ml_options)

m = DetectSequence(options=ml_options, logger=g.log)
Esempio n. 2
0
def main_handler():
    # set up logging to syslog
    # construct the argument parse and parse the arguments
  
    ap = argparse.ArgumentParser()
    ap.add_argument('-c', '--config', help='config file with path')
    ap.add_argument('-e', '--eventid', help='event ID to retrieve')
    ap.add_argument('-p',
                    '--eventpath',
                    help='path to store object image file',
                    default='')
    ap.add_argument('-m', '--monitorid', help='monitor id - needed for mask')
    ap.add_argument('-v',
                    '--version',
                    help='print version and quit',
                    action='store_true')

    ap.add_argument('-o', '--output-path',
                    help='internal testing use only - path for debug images to be written')

    ap.add_argument('-f',
                    '--file',
                    help='internal testing use only - skips event download')


    ap.add_argument('-r', '--reason', help='reason for event (notes field in ZM)')

    ap.add_argument('-n', '--notes', help='updates notes field in ZM with detections', action='store_true')
    ap.add_argument('-d', '--debug', help='enables debug on console', action='store_true')

    args, u = ap.parse_known_args()
    args = vars(args)

    if args.get('version'):
        print('hooks:{} pyzm:{}'.format(hooks_version, pyzm_version))
        exit(0)

    if not args.get('config'):
        print ('--config required')
        exit(1)

    if not args.get('file')and not args.get('eventid'):
        print ('--eventid required')
        exit(1)

    utils.get_pyzm_config(args)

    if args.get('debug'):
        g.config['pyzm_overrides']['dump_console'] = True
        g.config['pyzm_overrides']['log_debug'] = True
        g.config['pyzm_overrides']['log_level_debug'] = 5
        g.config['pyzm_overrides']['log_debug_target'] = None

    if args.get('monitorid'):
        log.init(name='zmesdetect_' + 'm' + args.get('monitorid'), override=g.config['pyzm_overrides'])
    else:
        log.init(name='zmesdetect',override=g.config['pyzm_overrides'])
    g.logger = log
    
    es_version='(?)'
    try:
        es_version=subprocess.check_output(['/usr/bin/zmeventnotification.pl', '--version']).decode('ascii')
    except:
        pass


    try:
        import cv2
    except ImportError as e:
        g.logger.Fatal (f'{e}: You might not have installed OpenCV as per install instructions. Remember, it is NOT automatically installed')

    g.logger.Info('---------| pyzm version:{}, hook version:{},  ES version:{} , OpenCV version:{}|------------'.format(pyzm_version, hooks_version, es_version, cv2.__version__))
   

    
    # load modules that depend on cv2
    try:
        import zmes_hook_helpers.image_manip as img
    except Exception as e:
        g.logger.Error (f'{e}')
        exit(1)
    g.polygons = []

    # process config file
    g.ctx = ssl.create_default_context()
    utils.process_config(args, g.ctx)


    # misc came later, so lets be safe
    if not os.path.exists(g.config['base_data_path'] + '/misc/'):
        try:
            os.makedirs(g.config['base_data_path'] + '/misc/')
        except FileExistsError:
            pass  # if two detects run together with a race here

    if not g.config['ml_gateway']:
        g.logger.Info('Importing local classes for Object/Face')
        import pyzm.ml.object as object_detection
       
    else:
        g.logger.Info('Importing remote shim classes for Object/Face')
        from zmes_hook_helpers.apigw import ObjectRemote, FaceRemote, AlprRemote
    # now download image(s)


    start = datetime.datetime.now()

    obj_json = []

    import pyzm.api as zmapi
    api_options  = {
    'apiurl': g.config['api_portal'],
    'portalurl': g.config['portal'],
    'user': g.config['user'],
    'password': g.config['password'] ,
    'logger': g.logger, # use none if you don't want to log to ZM,
    #'disable_ssl_cert_check': True
    }

    g.logger.Info('Connecting with ZM APIs')
    zmapi = zmapi.ZMApi(options=api_options)
    stream = args.get('eventid') or args.get('file')
    ml_options = {}
    stream_options={}
    secrets = None 
    
    if g.config['ml_sequence'] and g.config['use_sequence'] == 'yes':
        g.logger.Debug(2,'using ml_sequence')
        ml_options = g.config['ml_sequence']
        secrets = pyzmutils.read_config(g.config['secrets'])
        ml_options = pyzmutils.template_fill(input_str=ml_options, config=None, secrets=secrets._sections.get('secrets'))
        ml_options = ast.literal_eval(ml_options)
        g.config['ml_sequence'] = ml_options
    else:
        g.logger.Debug(2,'mapping legacy ml data from config')
        ml_options = utils.convert_config_to_ml_sequence()
    

    if g.config['stream_sequence'] and g.config['use_sequence'] == 'yes': # new sequence
        g.logger.Debug(2,'using stream_sequence')
        stream_options = g.config['stream_sequence']
        stream_options = ast.literal_eval(stream_options)
        g.config['stream_sequence'] = stream_options
    else: # legacy
        g.logger.Debug(2,'mapping legacy stream data from config')
        if g.config['detection_mode'] == 'all':
            g.config['detection_mode'] = 'most_models'
        frame_set = g.config['frame_id']
        if g.config['frame_id'] == 'bestmatch':
            if g.config['bestmatch_order'] == 's,a':
                frame_set = 'snapshot,alarm'
            else:
                frame_set = 'alarm,snapshot'
        stream_options['resize'] =int(g.config['resize']) if g.config['resize'] != 'no' else None

       
        stream_options['strategy'] = g.config['detection_mode'] 
        stream_options['frame_set'] = frame_set       

    # These are stream options that need to be set outside of supplied configs         
    stream_options['api'] = zmapi
    
    stream_options['polygons'] = g.polygons

    '''
    stream_options = {
            'api': zmapi,
            'download': False,
            'frame_set': frame_set,
            'strategy': g.config['detection_mode'],
            'polygons': g.polygons,
            'resize': int(g.config['resize']) if g.config['resize'] != 'no' else None

    }
    '''

   
    m = None
    matched_data = None
    all_data = None

    if not args['file'] and int(g.config['wait']) > 0:
        g.logger.Info('Sleeping for {} seconds before inferencing'.format(
            g.config['wait']))
        time.sleep(g.config['wait'])

    if g.config['ml_gateway']:
        stream_options['api'] = None
        stream_options['monitorid'] = args.get('monitorid')
        start = datetime.datetime.now()
        try:
            matched_data,all_data = remote_detect(stream=stream, options=stream_options, api=zmapi)
            diff_time = (datetime.datetime.now() - start)
            g.logger.Debug(1,'Total remote detection detection took: {}'.format(diff_time))
        except Exception as e:
            g.logger.Error ("Error with remote mlapi:{}".format(e))
            g.logger.Debug(2,traceback.format_exc())

            if g.config['ml_fallback_local'] == 'yes':
                g.logger.Debug (1, "Falling back to local detection")
                stream_options['api'] = zmapi
                from pyzm.ml.detect_sequence import DetectSequence
                m = DetectSequence(options=ml_options, logger=g.logger)
                matched_data,all_data = m.detect_stream(stream=stream, options=stream_options)
    

    else:
        from pyzm.ml.detect_sequence import DetectSequence
        m = DetectSequence(options=ml_options, logger=g.logger)
        matched_data,all_data = m.detect_stream(stream=stream, options=stream_options)
    


    #print(f'ALL FRAMES: {all_data}\n\n')
    #print (f"SELECTED FRAME {matched_data['frame_id']}, size {matched_data['image_dimensions']} with LABELS {matched_data['labels']} {matched_data['boxes']} {matched_data['confidences']}")
    #print (matched_data)
    '''
     matched_data = {
            'boxes': matched_b,
            'labels': matched_l,
            'confidences': matched_c,
            'frame_id': matched_frame_id,
            'image_dimensions': self.media.image_dimensions(),
            'image': matched_frame_img
        }
    '''

    # let's remove past detections first, if enabled 
    if g.config['match_past_detections'] == 'yes' and args.get('monitorid'):
        # point detections to post processed data set
        g.logger.Info('Removing matches to past detections')
        bbox_t, label_t, conf_t = img.processPastDetection(
            matched_data['boxes'], matched_data['labels'], matched_data['confidences'], args.get('monitorid'))
        # save current objects for future comparisons
        g.logger.Debug(1,
            'Saving detections for monitor {} for future match'.format(
                args.get('monitorid')))
        try:
            mon_file = g.config['image_path'] + '/monitor-' + args.get(
            'monitorid') + '-data.pkl'
            f = open(mon_file, "wb")
            pickle.dump(matched_data['boxes'], f)
            pickle.dump(matched_data['labels'], f)
            pickle.dump(matched_data['confidences'], f)
            f.close()
        except Exception as e:
            g.logger.Error(f'Error writing to {mon_file}, past detections not recorded:{e}')

        matched_data['boxes'] = bbox_t
        matched_data['labels'] = label_t
        matched_data['confidences'] = conf_t

    obj_json = {
        'labels': matched_data['labels'],
        'boxes': matched_data['boxes'],
        'frame_id': matched_data['frame_id'],
        'confidences': matched_data['confidences'],
        'image_dimensions': matched_data['image_dimensions']
    }

    # 'confidences': ["{:.2f}%".format(item * 100) for item in matched_data['confidences']],
    
    detections = []
    seen = {}
    pred=''
    prefix = ''

    if matched_data['frame_id'] == 'snapshot':
        prefix = '[s] '
    elif matched_data['frame_id'] == 'alarm':
        prefix = '[a] '
    else:
        prefix = '[x] '
        #g.logger.Debug (1,'CONFIDENCE ARRAY:{}'.format(conf))
    for idx, l in enumerate(matched_data['labels']):
        if l not in seen:
            if g.config['show_percent'] == 'no':
                pred = pred + l + ','
            else:
                pred = pred + l + ':{:.0%}'.format(matched_data['confidences'][idx]) + ' '
            seen[l] = 1

    if pred != '':
        pred = pred.rstrip(',')
        pred = prefix + 'detected:' + pred
        g.logger.Info('Prediction string:{}'.format(pred))
        jos = json.dumps(obj_json)
        g.logger.Debug(1,'Prediction string JSON:{}'.format(jos))
        print(pred + '--SPLIT--' + jos)

        if (matched_data['image'] is not None) and (g.config['write_image_to_zm'] == 'yes' or g.config['write_debug_image'] == 'yes'):
            debug_image = pyzmutils.draw_bbox(image=matched_data['image'],boxes=matched_data['boxes'], 
                                              labels=matched_data['labels'], confidences=matched_data['confidences'],
                                              polygons=g.polygons, poly_thickness = g.config['poly_thickness'])

            if g.config['write_debug_image'] == 'yes':
                for _b in matched_data['error_boxes']:
                    cv2.rectangle(debug_image, (_b[0], _b[1]), (_b[2], _b[3]),
                        (0,0,255), 1)
                filename_debug = g.config['image_path']+'/'+os.path.basename(append_suffix(stream, '-{}-debug'.format(matched_data['frame_id'])))
                g.logger.Debug (1,'Writing bound boxes to debug image: {}'.format(filename_debug))
                cv2.imwrite(filename_debug,debug_image)

            if g.config['write_image_to_zm'] == 'yes' and args.get('eventpath'):
                g.logger.Debug(1,'Writing detected image to {}/objdetect.jpg'.format(
                    args.get('eventpath')))
                cv2.imwrite(args.get('eventpath') + '/objdetect.jpg', debug_image)
                jf = args.get('eventpath')+ '/objects.json'
                g.logger.Debug(1,'Writing JSON output to {}'.format(jf))
                try:
                    with open(jf, 'w') as jo:
                        json.dump(obj_json, jo)
                        jo.close()
                except Exception as e:
                    g.logger.Error(f'Error creating {jf}:{e}')
                    
        if args.get('notes'):
            url = '{}/events/{}.json'.format(g.config['api_portal'], args['eventid'])
            try:
                ev = zmapi._make_request(url=url,  type='get')
            except Exception as e:
                g.logger.Error ('Error during event notes retrieval: {}'.format(str(e)))
                g.logger.Debug(2,traceback.format_exc())
                exit(0) # Let's continue with zmdetect

            new_notes = pred
            if ev.get('event',{}).get('Event',{}).get('Notes'): 
                old_notes = ev['event']['Event']['Notes']
                old_notes_split = old_notes.split('Motion:')
                old_d = old_notes_split[0] # old detection
                try:
                    old_m = old_notes_split[1] 
                except IndexError:
                    old_m = ''
                new_notes = pred + 'Motion:'+ old_m
                g.logger.Debug (1,'Replacing old note:{} with new note:{}'.format(old_notes, new_notes))
                

            payload = {}
            payload['Event[Notes]'] = new_notes
            try:
                ev = zmapi._make_request(url=url, payload=payload, type='put')
            except Exception as e:
                g.logger.Error ('Error during notes update: {}'.format(str(e)))
                g.logger.Debug(2,traceback.format_exc())

        if g.config['create_animation'] == 'yes':
            if not args.get('eventid'):
                g.logger.Error ('Cannot create animation as you did not pass an event ID')
            else:
                g.logger.Debug(1,'animation: Creating burst...')
                try:
                    img.createAnimation(matched_data['frame_id'], args.get('eventid'), args.get('eventpath')+'/objdetect', g.config['animation_types'])
                except Exception as e:
                    g.logger.Error('Error creating animation:{}'.format(e))
                    g.logger.Error('animation: Traceback:{}'.format(traceback.format_exc()))
Esempio n. 3
0
    def post(self):
        args = parse_args()
        req = request.get_json()

        fi = None
        stream_options = {}
        stream = None
        ml_overrides = {}
        config_copy = None
        poly_copy = None
        ml_options = None
        mid = None

        if not req:
            req = {}
        if req.get('mid') and str(req.get('mid')) in g.monitor_config:
            mid = str(req.get('mid'))
            g.logger.Debug(
                1,
                'Monitor ID {} provided & matching config found in mlapi, ignoring objectconfig.ini'
                .format(mid))
            config_copy = copy.copy(g.config)
            poly_copy = copy.copy(g.polygons)
            g.polygons = copy.copy(g.monitor_polygons[mid])

            for key in g.monitor_config[mid]:
                # This will also take care of copying over mid specific stream_options
                g.logger.Debug(
                    2, 'Overriding global {} with {}...'.format(
                        key, g.monitor_config[mid][key][:30]))
                g.config[key] = g.monitor_config[mid][key]

            # stupid mlapi and zm_detect config incompatibility
            if not g.config.get('image_path') and g.config.get('images_path'):
                g.config['image_path'] = g.config['images_path']

            # At this stage, polygons has a copy of that monitor polygon set
            # g.config has overriden values of config from the mid

            r = req.get('reason')
            if r and g.config['only_triggered_zm_zones'] == 'yes' and g.config[
                    'import_zm_zones'] == 'yes':
                g.logger.Debug(
                    2, 'Only filtering polygon names that have {}'.format(r))
                g.logger.Debug(
                    2, 'Original polygons being used: {}'.format(g.polygons))

                g.polygons[:] = [
                    item for item in g.polygons
                    if utils.findWholeWord(item['name'])(r)
                ]
                g.logger.Debug(
                    2, 'Final polygons being used: {}'.format(g.polygons))

            if g.config['ml_sequence'] and g.config['use_sequence'] == 'yes':
                g.log.Debug(2, 'using ml_sequence')
                ml_options = g.config['ml_sequence']
                secrets = pyzmutils.read_config(g.config['secrets'])
                ml_options = pyzmutils.template_fill(
                    input_str=ml_options,
                    config=None,
                    secrets=secrets._sections.get('secrets'))
                ml_options = ast.literal_eval(ml_options)
                #print (ml_options)
            else:
                g.logger.Debug(2, 'mapping legacy ml data from config')
                ml_options = utils.convert_config_to_ml_sequence()

            g.logger.Debug(2, 'Overwriting ml_sequence of pre loaded model')
            m.set_ml_options(ml_options)
        else:
            g.logger.Debug(
                1,
                'Monitor ID not specified, or not found in mlapi config, using zm_detect overrides'
            )
            ml_overrides = req.get('ml_overrides', {})
            if g.config['ml_sequence'] and g.config['use_sequence'] == 'yes':
                g.log.Debug(2, 'using ml_sequence')
                ml_options = g.config['ml_sequence']
                secrets = pyzmutils.read_config(g.config['secrets'])
                ml_options = pyzmutils.template_fill(
                    input_str=ml_options,
                    config=None,
                    secrets=secrets._sections.get('secrets'))
                ml_options = ast.literal_eval(ml_options)
                #print (ml_options)
            else:
                g.logger.Debug(2, 'mapping legacy ml data from config')
                ml_options = utils.convert_config_to_ml_sequence()

            #print (ml_options)

        if g.config.get('stream_sequence'):
            g.logger.Debug(
                2,
                'Found stream_sequence in mlapi config, ignoring objectconfig.ini'
            )
            stream_options = ast.literal_eval(g.config.get('stream_sequence'))
        else:
            stream_options = req.get('stream_options')
        if not stream_options:
            if config_copy:
                g.log.Debug(2, 'Restoring global config & ml_options')
                g.config = config_copy
                g.polygons = poly_copy
            abort(400, msg='No stream options found')
        stream_options['api'] = zmapi
        stream_options['polygons'] = g.polygons

        stream = req.get('stream')

        if args['type'] == 'face':
            g.log.Debug(1, 'Face Recognition requested')

        elif args['type'] == 'alpr':
            g.log.Debug(1, 'ALPR requested')

        elif args['type'] in [None, 'object']:
            g.log.Debug(1, 'Object Recognition requested')
            #m = ObjectDetect.Object()
        else:
            if config_copy:
                g.log.Debug(2, 'Restoring global config & ml_options')
                g.config = config_copy
                g.polygons = poly_copy
            abort(400, msg='Invalid Model:{}'.format(args['type']))

        if not stream:
            g.log.Debug(1, 'Stream info not found, looking at args...')
            fip, ext = get_file(args)
            fi = fip + ext
            stream = fi

            #image = cv2.imread(fi)
        #bbox,label,conf = m.detect(image)

        stream_options['mid'] = mid
        if not stream_options.get('delay') and g.config.get('wait'):
            stream_options['delay'] = g.config.get('wait')
        g.log.Debug(1, 'Calling detect streams')
        matched_data, all_matches = m.detect_stream(stream=stream,
                                                    options=stream_options,
                                                    ml_overrides=ml_overrides)

        if matched_data['image_dimensions']:
            oldh = matched_data['image_dimensions']['original'][0]
            oldw = matched_data['image_dimensions']['original'][1]

        if config_copy:
            g.log.Debug(2, 'Restoring global config & ml_options')
            g.config = config_copy
            g.polygons = poly_copy

        matched_data['image'] = None
        if args.get('response_format') == 'zm_detect':
            resp_obj = {
                'matched_data': matched_data,
                'all_matches': all_matches,
            }
            g.log.Debug(1, 'Returning {}'.format(resp_obj))
            return resp_obj

        # legacy format
        bbox = matched_data['boxes']
        label = matched_data['labels']
        conf = matched_data['confidences']

        detections = []
        for l, c, b in zip(label, conf, bbox):
            c = "{:.2f}%".format(c * 100)
            obj = {'type': 'object', 'label': l, 'confidence': c, 'box': b}
            detections.append(obj)

        if args['delete'] and fi:
            #pass
            os.remove(fi)
        return detections