# RedisEdge realtime video analytics video capture script import argparse import cv2 import redis import time from urllib.parse import urlparse class SimpleMovingAverage(object): ''' Simple moving average ''' def __init__(self, value=0.0, count=7): self.count = int(count) self.current = float(value) self.samples = [self.current] * self.count def __str__(self): return str(round(self.current, 3)) def add(self, value): v = float(value) self.samples.insert(0, v) o = self.samples.pop() self.current = self.current + (v-o)/self.count class Video: def __init__(self, infile=0, fps=30.0): self.isFile = not str(infile).isdecimal() self.ts = time.time() self.infile = infile self.cam = cv2.VideoCapture(self.infile) if not self.isFile: self.cam.set(cv2.CAP_PROP_FPS, fps) self.fps = fps # TODO: some cameras don't respect the fps directive self.cam.set(cv2.CAP_PROP_FRAME_WIDTH, 800) self.cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 600) else: self.fps = self.cam.get(cv2.CAP_PROP_FPS) self.sma = SimpleMovingAverage(value=0.1, count=19) def __iter__(self): self.count = -1 return self def __next__(self): self.count += 1 # Respect FPS for files if self.isFile: delta = time.time() - self.ts self.sma.add(delta) time.sleep(max(0,(1.0 - self.sma.current*self.fps)/self.fps)) self.ts = time.time() # Read image ret_val, img0 = self.cam.read() if not ret_val and self.isFile: self.cam.set(cv2.CAP_PROP_POS_FRAMES, 0) ret_val, img0 = self.cam.read() assert ret_val, 'Video Error' # Preprocess img = img0 if not self.isFile: img = cv2.flip(img, 1) return self.count, img def __len__(self): return 0 if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('infile', help='Input file (leave empty to use webcam)', nargs='?', type=str, default=None) parser.add_argument('-o', '--output', help='Output stream key name', type=str, default='camera:0') parser.add_argument('-u', '--url', help='Redis URL', type=str, default='redis://127.0.0.1:6379') parser.add_argument('-w', '--webcam', help='Webcam device number', type=int, default=0) parser.add_argument('-v', '--verbose', help='Verbose output', type=bool, default=False) parser.add_argument('--count', help='Count of frames to capture', type=int, default=None) parser.add_argument('--fmt', help='Frame storage format', type=str, default='.jpg') parser.add_argument('--fps', help='Frames per second (webcam)', type=float, default=15.0) parser.add_argument('--maxlen', help='Maximum length of output stream', type=int, default=10000) args = parser.parse_args() # Set up Redis connection url = urlparse(args.url) conn = redis.Redis(host=url.hostname, port=url.port) if not conn.ping(): raise Exception('Redis unavailable') # Choose video source if args.infile is None: loader = Video(infile=args.webcam, fps=args.fps) # Default to webcam else: loader = Video(infile=args.infile, fps=args.fps) # Unless an input file (image or video) was specified for (count, img) in loader: _, data = cv2.imencode(args.fmt, img) msg = { 'count': count, 'image': data.tobytes() } _id = conn.xadd(args.output, msg, maxlen=args.maxlen) if args.verbose: print('frame: {} id: {}'.format(count, _id)) if args.count is not None and count+1 == args.count: print('Stopping after {} frames.'.format(count)) break