I'm trying to capture two rtsp streams with opencv and then present them with a simple flask server. I can show the two streams together when just using opencv, but when I try to display it through flask it just picks either stream and shows it twice.
Here's the original creators blog
Here is my flask code:
#!/usr/bin/env python
from importlib import import_module
import os
from flask import Flask, render_template, Response
# import camera driver
'''
if os.environ.get('CAMERA'):
Camera = import_module('camera_' + os.environ['CAMERA']).Camera
else:
from camera import Camera
'''
#
from camera_opencv import Camera1, Camera2
app = Flask(__name__)
#app.route('/')
def index():
"""Video streaming home page."""
return render_template('index.html')
def gen(camera):
"""Video streaming generator function."""
while True:
frame = camera.get_frame()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
#app.route('/video_feed1')
def video_feed1():
"""Video streaming route. Put this in the src attribute of an img tag."""
return Response(gen(Camera1()),
mimetype='multipart/x-mixed-replace; boundary=frame')
#app.route('/video_feed2')
def video_feed2():
"""Video streaming route. Put this in the src attribute of an img tag."""
return Response(gen(Camera2()),
mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__':
app.run(host='0.0.0.0', threaded=True, port=8888)
Here's the camera_opencv file
import cv2
from base_camera import BaseCamera
class Camera1(BaseCamera):
video_source = 0
#staticmethod
def set_video_source(source):
Camera1.video_source = source
#staticmethod
def frames():
camera = cv2.VideoCapture(Camera1.video_source)
if not camera.isOpened():
raise RuntimeError('Could not start camera.')
while True:
# read current frame
_, img = camera.read()
# encode as a jpeg image and return it
yield cv2.imencode('.jpg', img)[1].tobytes()
class Camera2(BaseCamera):
video_source = 1
#staticmethod
def set_video_source(source):
Camera2.video_source = source
#staticmethod
def frames():
camera = cv2.VideoCapture(Camera2.video_source)
if not camera.isOpened():
raise RuntimeError('Could not start camera.')
while True:
# read current frame
_, img = camera.read()
# encode as a jpeg image and return it
yield cv2.imencode('.jpg', img)[1].tobytes()
Base camera file
import time
import threading
try:
from greenlet import getcurrent as get_ident
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident
class CameraEvent(object):
"""An Event-like class that signals all active clients when a new frame is
available.
"""
def __init__(self):
self.events = {}
def wait(self):
"""Invoked from each client's thread to wait for the next frame."""
ident = get_ident()
if ident not in self.events:
# this is a new client
# add an entry for it in the self.events dict
# each entry has two elements, a threading.Event() and a timestamp
self.events[ident] = [threading.Event(), time.time()]
return self.events[ident][0].wait()
def set(self):
"""Invoked by the camera thread when a new frame is available."""
now = time.time()
remove = None
for ident, event in self.events.items():
if not event[0].isSet():
# if this client's event is not set, then set it
# also update the last set timestamp to now
event[0].set()
event[1] = now
else:
# if the client's event is already set, it means the client
# did not process a previous frame
# if the event stays set for more than 5 seconds, then assume
# the client is gone and remove it
if now - event[1] > 5:
remove = ident
if remove:
del self.events[remove]
def clear(self):
"""Invoked from each client's thread after a frame was processed."""
self.events[get_ident()][0].clear()
class BaseCamera(object):
thread = None # background thread that reads frames from camera
frame = None # current frame is stored here by background thread
last_access = 0 # time of last client access to the camera
event = CameraEvent()
def __init__(self):
"""Start the background camera thread if it isn't running yet."""
if BaseCamera.thread is None:
BaseCamera.last_access = time.time()
# start background frame thread
BaseCamera.thread = threading.Thread(target=self._thread)
BaseCamera.thread.start()
# wait until frames are available
while self.get_frame() is None:
time.sleep(0)
def get_frame(self):
"""Return the current camera frame."""
BaseCamera.last_access = time.time()
# wait for a signal from the camera thread
BaseCamera.event.wait()
BaseCamera.event.clear()
return BaseCamera.frame
#staticmethod
def frames():
""""Generator that returns frames from the camera."""
raise RuntimeError('Must be implemented by subclasses.')
#classmethod
def _thread(cls):
"""Camera background thread."""
print('Starting camera thread.')
frames_iterator = cls.frames()
for frame in frames_iterator:
BaseCamera.frame = frame
BaseCamera.event.set() # send signal to clients
time.sleep(0)
# if there hasn't been any clients asking for frames in
# the last 10 seconds then stop the thread
if time.time() - BaseCamera.last_access > 10:
frames_iterator.close()
print('Stopping camera thread due to inactivity.')
break
BaseCamera.thread = None
Index.html
<html>
<head>
<title>Video Streaming Demonstration</title>
</head>
<body>
<h1>Video Streaming Demonstration</h1>
<img src="{{ url_for('video_feed1') }}">
<img src="{{ url_for('video_feed2') }}">
</body>
</html>
So I kind of managed to make a hackey workaround. For whatever reason I could not resolve, the app just couldn't handle multiple streams individually.
So I changed the camera class and added multiple sources to it and used numpy.hstack(()) to merge both the frames together thus returning one unique stream.
Will be very grateful if someone could help out here as my method is not at all scalable.
import cv2
from base_camera import BaseCamera
import numpy as np
class Camera(BaseCamera):
video_source1 = 0
video_source2 = 1
#staticmethod
def set_video_source(sources):
Camera.video_source1 = sources[0]
Camera.video_source2 = sources[1]
#staticmethod
def frames():
camera1 = cv2.VideoCapture(Camera.video_source1)
camera2 = cv2.VideoCapture(Camera.video_source2)
if not (camera1.isOpened() or camera2.isOpened()):
raise RuntimeError('Could not start camera.')
while True:
# read current frame
_, img1 = camera1.read()
_, img2 = camera2.read()
img1 = cv2.resize(img1, (704, 396))
img2 = cv2.resize(img2, (704, 396))
img = np.hstack((img1, img2))
# encode as a jpeg image and return it
yield cv2.imencode('.jpg', img)[1].tobytes()
According to the blog, I use two generator and send the image to the index.html. And I can see tow streaming.
def generate2():
# it is a generator
global outputFrame2, lock
while True:
with lock:
if outputFrame is None:
continue
(flag, encodedImage2) = cv2.imencode(".jpg", outputFrame2)
if not flag:
continue
yield(b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' +
bytearray(encodedImage2) + b'\r\n')
And here is videofeed2
#app.route("/video_feed2")
def video_feed2():
return Response(generate2(),
mimetype = "multipart/x-mixed-replace; boundary=frame")
Related
I am making a Flask application to stream 4 camera webcam feeds at once using multithreading based on some code i copied. How can i return the captured camera frame from gen_frames() to display on my website through return Response() below
#camera.route('/video_feed0')
def video_feed0():
#Video streaming route. Put this in the src attribute of an img tag
thread0.start()
return Response(thread0.run(), mimetype='multipart/x-mixed-replace; boundary=frame')
Full Code here:
from flask import Blueprint, render_template, redirect, url_for, request, flash, Response
import cv2
import threading
camera = Blueprint('camera', __name__)
class camThread(threading.Thread):
def __init__(self, previewName, camID):
threading.Thread.__init__(self)
self.previewName = previewName
self.camID = camID
def run(self):
print("Starting " + self.previewName)
gen_frames(self.previewName, self.camID)
yield gen_frames()
def gen_frames(previewName, camID): # generate frame by frame from camera
camerafeed = cv2.VideoCapture(camID)
camerafeed.set(3, 640)
camerafeed.set(4, 480)
print('opening camera 0')
while True:
# Capture frame-by-frame
success, frame = camerafeed.read() # read the camera frame
if not success or camerafeed is None:
break
else:
ret, buffer = cv2.imencode('.jpg', frame)
frame = buffer.tobytes()
yield (b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n') # concat frame one by one and show result
thread0 = camThread("Camera 0", 0)
thread1 = camThread("Camera 1", 1)
thread2 = camThread("Camera 2", 2)
thread3 = camThread("Camera 3", 3)
#camera.route('/camerafeed')
def cameraindex():
return render_template('camera.html')
#camera.route('/video_feed0')
def video_feed0():
#Video streaming route. Put this in the src attribute of an img tag
thread0.start()
return Response(thread0.run(), mimetype='multipart/x-mixed-replace; boundary=frame')
#camera.route('/video_feed1')
def video_feed1():
#Video streaming route. Put this in the src attribute of an img tag
thread1.start()
return Response(thread1.run(), mimetype='multipart/x-mixed-replace; boundary=frame')
#camera.route('/video_feed2')
def video_feed2():
#Video streaming route. Put this in the src attribute of an img tag
thread2.start()
return Response(thread2.run(), mimetype='multipart/x-mixed-replace; boundary=frame')
#camera.route('/video_feed3')
def video_feed3():
#Video streaming route. Put this in the src attribute of an img tag
thread3.start()
return Response(thread3.run(), mimetype='multipart/x-mixed-replace; boundary=frame')
If you want to display the individual feed to the webpage, you can do it inside an img tag in your html page like below:
<img src="{{ url_for('video_feed0') }}"/>
<img src="{{ url_for('video_feed1') }}"/>
<img src="{{ url_for('video_feed2') }}"/>
<img src="{{ url_for('video_feed3') }}"/>
I want to get the signal from a webcam, or csi cam on my jetson nano, display it on a web browser and push some buttons to make a snapshot and depending of the button place the picture in different folders.
I made following code, which it seems to work, but after few button actions, the browser starts to load indefinitely. After a while, the web browser does not load indefinitely. It loads until I press one of the button. And in this case, I'm not more able to see the live signal from the camera, I just see the snapshot taken when I pressed the button.
from flask import Flask, render_template, Response, request
import cv2
import datetime, time
import os, sys
import numpy as np
from threading import Thread
global rec_frame, Polluted, Clear, Treated, OutOfService
Polluted=0
Clear=0
Treated=0
OutOfService=0
#instatiate flask app
app = Flask(__name__, template_folder='./templates')
#select webcam or CSI
#camera = cv2.VideoCapture("nvarguscamerasrc ! video/x-raw(memory:NVMM), width=(int)1280, height=(int)960,format=(string)NV12, framerate=(fraction)20/1 ! nvvidconv flip-method=0 ! video/x-raw,format=(string)BGRx ! videoconvert ! video/x-raw,width=(int)1280, height=(int)960, format=(string)BGR ! appsink"
#, cv2.CAP_GSTREAMER)
camera = cv2.VideoCapture(1)
def snapshot(frame,folder):
cropped = frame[100:400,200:500]
resized = cv2.resize(cropped,(100,100))
cv2.imwrite("/home/ava/Documents/AVA/Get pictures/" + folder + "/frame-" + time.strftime("%d-%m-%Y-%H-%M-%S") + ".jpg",cv2.cvtColor(resized,cv2.COLOR_RGB2BGR))
def gen_frames(): # generate frame by frame from camera
global rec_frame,Polluted,Clear,Treated,OutOfService
while True:
success,frame = camera.read()
if success:
#get snapshot if button pressed
if(Polluted):
snapshot(frame,"Polluted Water")
Polluted = 0
if(Clear):
snapshot(frame,"Cleared Water")
Clear = 0
if(Treated):
snapshot(frame,"Treated Water")
Treated = 0
if(OutOfService):
snapshot(frame,"Out of Service")
OutOfService = 0
try:
ret, buffer = cv2.imencode('.jpg', cv2.flip(frame,1))
frame = buffer.tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
except Exception as e:
print('pass')
pass
else:
pass
#app.route('/')
def index():
return render_template('index.html')
#app.route('/video_feed')
def video_feed():
return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
#app.route('/requests',methods=['POST','GET'])
def tasks():
global switch,camera
if request.method == 'POST':
if request.form.get('Polluted') == 'Polluted':
global Polluted,rec_frame
Polluted=1
print('in polluted')
elif request.form.get('Clear') == 'Clear':
global Clear
Clear = 1
elif request.form.get('Treated') == 'Treated':
global Treated
Treated = 1
elif request.form.get('OutOfService') == 'OutOfService':
global OutOfService
OutOfService = 1
elif request.method=='GET':
return render_template('index.html')
return render_template('index.html')
if __name__ == '__main__':
app.run()
camera.release()
cv2.destroyAllWindows()
I am struggle with generate a mini video clips(Each are 10s video clips) when recording my webcam using open cv and python.
Basically I want to do cut the webcam video into 10S duration clips and store in a folder.
When I doing this video clips cut but when I checked first video clip has 100% of full video.
Second has about 75% of full video and third has about 50% and etc.
So how I can solve this.
I will put my hole code in below. Hope your help to fix that one
camera = cv2.VideoCapture(0)
global rec, img, out
rec = 0
def gen_frames():
global img
while True:
success, img = camera.read()
def record(out):
global rec, img
while(rec != False):
time.sleep(0.05)
out.write(img)
#app.route('/requests',methods=['POST','GET'])
def tasks():
if request.form.get('rec') == 'Start/Stop Recording':
global rec, img, out
rec= not rec
############### This Part work when manualy recode on and off ###############
# if rec:
# print("start")
# global out
# now=datetime.datetime.now()
# fourcc = cv2.VideoWriter_fourcc(*'XVID')
# p = os.path.sep.join(['clips', "vid_{}.avi".format(str(now).replace(":",''))])
# out = cv2.VideoWriter(p, fourcc, 25.0, size)
# thread = Thread(target = record, args=[out,])
# thread.start()
# if(rec==False):
# print("stop")
# out.release()
class TimerClass(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.event = threading.Event()
def run(self):
while (rec != False) and not self.event.is_set():
now=datetime.datetime.now()
fourcc = cv2.VideoWriter_fourcc(*'XVID')
p = os.path.sep.join(['clips', "vid_{}.avi".format(str(now).replace(":",''))])
out = cv2.VideoWriter(p, fourcc, 25.0, size)
thread = Thread(target = record, args=[out,])
thread.start()
self.event.wait(10)
def stop(self):
self.event.set()
tmr = TimerClass()
if(rec):
print("start")
tmr.start()
if(rec==False):
print("stop")
tmr.stop()
elif request.method=='GET':
return render_template('index.html')
return render_template('index.html')
if __name__ == '__main__':
app.run(host='0.0.0.0', threaded=True)
As for me problem is that in loop in TimerClass you create new thread every 10 seconds but you never stop previous thread and it still records frames. I would write frames directly in TimerClass instead of using another thread.
OR thread record() should check time and stop after 10 seconds.
I use timedelta to calculate when to stop recording one file and to create next one. And I do it in TimerClass but you could do something similar in record()
VideoWriter(..., 25.0) doesn't write with 25fps but it is only information for video players how fast to display this video. To get 10 seconds video you need sleep 0.04 because 1s / 25fps = 0.04 or you would have to write 250 frames (10s * 25fps = 250fps).
Because code needs also some time to work I would have to use 0.03 instead of 0.04 to get 10 seconds video.
Full working code.
I use render_template_string instead of render_template to have all in one file - and everyone can simply copy and test it.
from flask import Flask, Response, request, render_template, render_template_string
import cv2
import os
import datetime, time
import threading
# global varaibles
capture = False
rec = False
out = None
img = None
app = Flask(__name__)
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 300
camera = cv2.VideoCapture(0)
frame_width = int(camera.get(3))
frame_height = int(camera.get(4))
size = (frame_width, frame_height)
os.makedirs('./shots', exist_ok=True)
os.makedirs('./clips', exist_ok=True)
def gen_frames():
global capture
global img
print('[DEBUG] gen_frames: start')
while True:
success, img = camera.read()
if not success:
break
if capture:
capture = False
now = datetime.datetime.now()
filename = "shot_{}.png".format(str(now).replace(":",''))
path = os.path.sep.join(['shots', filename])
print('[DEBUG] capture:', path)
cv2.imwrite(path, img)
frame = cv2.imencode('.jpg', img)[1].tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
#app.route('/video_feed')
def video_feed():
return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
#app.route('/')
def index():
#return render_template('index.html')
return render_template_string('''
Go to FORM
''')
# define class only once
class TimerClass(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.event = threading.Event()
def run(self):
seconds_10 = datetime.timedelta(seconds=10)
while rec and not self.event.is_set():
now = datetime.datetime.now()
filename = "vid_{}.avi".format(str(now).replace(":", ''))
path = os.path.sep.join(['clips', filename])
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter(path, fourcc, 25.0, size)
end = now + seconds_10
print('[DEBUG] end:', end)
while now < end and rec and not self.event.is_set():
if img is not None: # `img` can be `numpy.array` so it can't check `if img:`
out.write(img)
time.sleep(0.03) # 1s / 25fps = 0.04 # it needs some time for code.
now = datetime.datetime.now()
out.release()
def stop(self):
self.event.set()
#app.route('/requests', methods=['POST', 'GET'])
def tasks():
global capture
global rec
print('[DEBUG] click:', request.form.get('click'))
print('[DEBUG] rec :', request.form.get('rec'))
if request.method == 'POST':
if request.form.get('click') == 'Capture':
capture = True
if request.form.get('rec') == 'Start/Stop Recording':
rec = not rec
tmr = TimerClass()
if rec:
print("start")
tmr.start()
else:
print("stop")
tmr.stop()
#return render_template_string('index.html')
return render_template_string('''
<img src="/video_feed"><br/>
<form method="POST">
<button type="submit" name="click" value="Capture">Capture</button>
<button type="submit" name="rec" value="Start/Stop Recording">Start/Stop Recording</button>
</form>
''')
if __name__ == '__main__':
thread_cam = threading.Thread(target=gen_frames)
thread_cam.start()
app.run(host='0.0.0.0', threaded=True)
EDIT:
The same with thread record()
def record(seconds):
now = datetime.datetime.now()
filename = "vid_{}.avi".format(str(now).replace(":", ''))
path = os.path.sep.join(['clips', filename])
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter(path, fourcc, 25.0, size)
end = now + datetime.timedelta(seconds=seconds)
print('[DEBUG] end:', end)
while now < end and rec:
if img is not None: # `img` can be `numpy.array` so it can't check `if img:`
out.write(img)
time.sleep(0.03) # 1s / 25fps = 0.04 # it needs some time for code.
now = datetime.datetime.now()
out.release()
# define class only once
class TimerClass(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.event = threading.Event()
def run(self):
length = 10
while rec and not self.event.is_set():
t = threading.Thread(target=record, args=(length,))
t.start()
self.event.wait(length)
def stop(self):
self.event.set()
This is my application architecture:
In my code there is a pedestrian.py file which uses a while loop to read frames from rtsp link and after doing pedestrian detection process (available in this link), it caches the frame in Redis.
(please note that in the loop each time the output frame is replaced with the previous output from loop. it means that there exists only one frame in redis in any moment.)
Then in flask application, I read processed frame from redis and send it for the clients.
This is the code for my pedestrian detection:
from redis import Redis
from concurrent.futures import ThreadPoolExecutor
import cv2
import torch
from os import environ
r = Redis('111.222.333.444')
class RealTimeTracking(object):
"""
This class is built to get frame from rtsp link and continuously
save each frame in the output directory. then we use flask to give it
as service to client.
Args:
args: parse_args inputs
cfg: deepsort dict and yolo-model cfg from server_cfg file
"""
def __init__(self, cfg, args):
# Create a VideoCapture object
self.cfg = cfg
self.args = args
use_cuda = self.args.use_cuda and torch.cuda.is_available()
if not use_cuda:
raise UserWarning("Running in cpu mode!")
self.detector = build_detector(cfg, use_cuda=use_cuda)
self.deepsort = build_tracker(cfg, use_cuda=use_cuda)
self.class_names = self.detector.class_names
self.vdo = cv2.VideoCapture(self.args.input)
self.status, self.frame = None, None
self.total_frames = int(cv2.VideoCapture.get(self.vdo, cv2.CAP_PROP_FRAME_COUNT))
self.im_width = int(self.vdo.get(cv2.CAP_PROP_FRAME_WIDTH))
self.im_height = int(self.vdo.get(cv2.CAP_PROP_FRAME_HEIGHT))
self.output_frame = None
self.thread = ThreadPoolExecutor(max_workers=1)
self.thread.submit(self.update)
print('streaming started ...')
def update(self):
while True:
if self.vdo.isOpened():
(self.status, self.frame) = self.vdo.read()
def run(self):
while True:
try:
if self.status:
frame = self.frame.copy()
# frame = cv2.resize(frame, (640, 480))
self.detection(frame=frame)
frame_to_bytes = cv2.imencode('.jpg', frame)[1].tobytes()
r.set('frame', frame_to_bytes)
except AttributeError:
pass
def detection(self, frame):
im = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# do detection
bbox_xywh, cls_conf, cls_ids = self.detector(im)
if bbox_xywh is not None:
# select person class
mask = cls_ids == 0
bbox_xywh = bbox_xywh[mask]
bbox_xywh[:, 3:] *= 1.2 # bbox dilation just in case bbox too small
cls_conf = cls_conf[mask]
# do tracking
outputs = self.deepsort.update(bbox_xywh, cls_conf, im)
# draw boxes for visualization
if len(outputs) > 0:
self.draw_boxes(img=frame, output=outputs)
#staticmethod
def draw_boxes(img, output, offset=(0, 0)):
for i, box in enumerate(output):
x1, y1, x2, y2, identity = [int(ii) for ii in box]
x1 += offset[0]
x2 += offset[0]
y1 += offset[1]
y2 += offset[1]
# box text and bar
color = compute_color_for_labels(identity)
label = '{}{:d}'.format("", identity)
t_size = cv2.getTextSize(label, cv2.FONT_HERSHEY_PLAIN, 2, 2)[0]
cv2.rectangle(img, (x1, y1), (x2, y2), color, 3)
cv2.rectangle(img, (x1, y1), (x1 + t_size[0] + 3, y1 + t_size[1] + 4), color, -1)
cv2.putText(img, label, (x1, y1 + t_size[1] + 4), cv2.FONT_HERSHEY_PLAIN, 2, [255, 255, 255], 2)
return img
if __name__ == "__main__":
args = parse_args() # argument: --rtsp_link = 'rtsp://me#111.222.333.444/Channels/105'
cfg = get_config()
cfg.merge_from_dict(model)
cfg.merge_from_dict(deep_sort_dict)
vdo_trk = RealTimeTracking(cfg, args)
vdo_trk.run()
This is the code for flask server app.py:
from dotenv import load_dotenv
from time import sleep
from os import getenv
from os.path import join
import subprocess
from flask import Response, Flask
from config.config import DevelopmentConfig
from redis import Redis
r = Redis('111.222.333.444')
app = Flask(__name__)
def gen():
while True:
frame = r.get('frame')
if frame is not None:
yield b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n'
#app.route('/')
def video_feed():
"""Video streaming route. Put this in the src attribute of an img tag."""
return Response(gen(),
mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__':
load_dotenv()
app.config.from_object(DevelopmentConfig)
cmd = ['python', join("my_project.dir", "pedestrian.py"), '--rtsp_link=rtsp://me#111.222.333.444/Channels/105']
p = subprocess.Popen(cmd)
sleep(6)
app.run()
This code runs perfectly in my system.
As you can see I use the cmd command to run the pedestrian detection on rtsp link before running flask server.
But what I really need for this task is to be able to switch between different cameras. I mean while the flask server is running, I want to be able to terminate the pedestrian.py process in any moment the request comes and restart the pedestrian.py with new --rtsp_linkargument (switch to another camera).
something like this:
#app.route('/cam1'):
def cam1():
stop('pedestrian.py')
cmd = ['python', join("my_project.dir", "pedestrian.py"), '--rtsp_link=rtsp://me#111.222.333.444/Channels/101']
p = subprocess.Popen(cmd)
#app.route('/cam2'):
def cam2():
stop('pedestrian.py')
cmd = ['python', join("my_project.dir", "pedestrian.py"), '--rtsp_link=rtsp://me#111.222.333.444/Channels/110']
p = subprocess.Popen(cmd)
My flask knowledge might not be good enough. I probably need to use post method and also authentication for that.
Would you tell me how can I implement such thing in this code?
I found a way to automate start/stop the pedestrian detection. more details available in my repo:
from os.path import join
from os import getenv, environ
from dotenv import load_dotenv
import argparse
from threading import Thread
from redis import Redis
from flask import Response, Flask, jsonify, request, abort
from rtsp_threaded_tracker import RealTimeTracking
from server_cfg import model, deep_sort_dict
from config.config import DevelopmentConfig
from utils.parser import get_config
redis_cache = Redis('127.0.0.1')
app = Flask(__name__)
environ['in_progress'] = 'off'
def parse_args():
"""
Parses the arguments
Returns:
argparse Namespace
"""
assert 'project_root' in environ.keys()
project_root = getenv('project_root')
parser = argparse.ArgumentParser()
parser.add_argument("--input",
type=str,
default=getenv('camera_stream'))
parser.add_argument("--model",
type=str,
default=join(project_root,
getenv('model_type')))
parser.add_argument("--cpu",
dest="use_cuda",
action="store_false", default=True)
args = parser.parse_args()
return args
def gen():
"""
Returns: video frames from redis cache
"""
while True:
frame = redis_cache.get('frame')
if frame is not None:
yield b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n'
def pedestrian_tracking(cfg, args):
"""
starts the pedestrian detection on rtsp link
Args:
cfg:
args:
Returns:
"""
tracker = RealTimeTracking(cfg, args)
tracker.run()
def trigger_process(cfg, args):
"""
triggers pedestrian_tracking process on rtsp link using a thread
Args:
cfg:
args:
Returns:
"""
try:
t = Thread(target=pedestrian_tracking, args=(cfg, args))
t.start()
return jsonify({"message": "Pedestrian detection started successfully"})
except Exception:
return jsonify({'message': "Unexpected exception occured in process"})
#app.errorhandler(400)
def bad_argument(error):
return jsonify({'message': error.description['message']})
# Routes
#app.route('/stream', methods=['GET'])
def stream():
"""
Provides video frames on http link
Returns:
"""
return Response(gen(),
mimetype='multipart/x-mixed-replace; boundary=frame')
#app.route("/run", methods=['GET'])
def process_manager():
"""
request parameters:
run (bool): 1 -> start the pedestrian tracking
0 -> stop it
camera_stream: str -> rtsp link to security camera
:return:
"""
# data = request.args
data = request.args
status = data['run']
status = int(status) if status.isnumeric() else abort(400, {'message': f"bad argument for run {data['run']}"})
if status == 1:
# if pedestrian tracking is not running, start it off!
try:
if environ.get('in_progress', 'off') == 'off':
global cfg, args
vdo = data.get('camera_stream')
if vdo is not None:
args.input = int(vdo)
environ['in_progress'] = 'on'
return trigger_process(cfg, args)
elif environ.get('in_progress') == 'on':
# if pedestrian tracking is running, don't start another one (we are short of gpu resources)
return jsonify({"message": " Pedestrian detection is already in progress."})
except Exception:
environ['in_progress'] = 'off'
return abort(503)
elif status == 0:
if environ.get('in_progress', 'off') == 'off':
return jsonify({"message": "pedestrian detection is already terminated!"})
else:
environ['in_progress'] = 'off'
return jsonify({"message": "Pedestrian detection terminated!"})
if __name__ == '__main__':
load_dotenv()
app.config.from_object(DevelopmentConfig)
# BackProcess Initialization
args = parse_args()
cfg = get_config()
cfg.merge_from_dict(model)
cfg.merge_from_dict(deep_sort_dict)
# Start the flask app
app.run()
I need to serve real-time graphs and I would like to deliver a mjpeg stream over http (so that it is easy to include the graphs in a web-page by using a plain tag).
Is it possible to create an mjpeg stream from multiple jpeg images, in realtime ?
My strategy is:
Output the correct http headers:
Cache-Control:no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0
Connection:close
Content-Type:multipart/x-mixed-replace;boundary=boundarydonotcross
Expires:Mon, 3 Jan 2000 12:34:56 GMT
Pragma:no-cache
Server:MJPG-Streamer/0.2
(got it from a curl -I {on a mjpeg-streamer instance}, but this seems strange)
Simply yield the successive jpeg images binaries, taking care to:
prepend the correct headers at the beginning of the stream (as mjpeg-streamer does):
Content-Type: image/jpeg
Content-Length: 5427
X-Timestamp: 3927662.086099
append the boundary string at the end of each jpeg streams.
--boudary--
Questions:
Have you done that,
do you know a python module that does that,
do you think it would work,
have you got any advice ?
I got it working as a proof-of-concept: https://github.com/damiencorpataux/pymjpeg
For memory:
import os, time
from glob import glob
import sys
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
boundary = '--boundarydonotcross'
def request_headers():
return {
'Cache-Control': 'no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0',
'Connection': 'close',
'Content-Type': 'multipart/x-mixed-replace;boundary=%s' % boundary,
'Expires': 'Mon, 3 Jan 2000 12:34:56 GMT',
'Pragma': 'no-cache',
}
def image_headers(filename):
return {
'X-Timestamp': time.time(),
'Content-Length': os.path.getsize(filename),
#FIXME: mime-type must be set according file content
'Content-Type': 'image/jpeg',
}
# FIXME: should take a binary stream
def image(filename):
with open(filename, "rb") as f:
# for byte in f.read(1) while/if byte ?
byte = f.read(1)
while byte:
yield byte
# Next byte
byte = f.read(1)
# Basic HTTP server
class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
# Response headers (multipart)
for k, v in pymjpeg.request_headers().items():
self.send_header(k, v)
# Multipart content
for filename in glob('img/*'):
# Part boundary string
self.end_headers()
self.wfile.write(pymjpeg.boundary)
self.end_headers()
# Part headers
for k, v in pymjpeg.image_headers(filename).items():
self.send_header(k, v)
self.end_headers()
# Part binary
for chunk in pymjpeg.image(filename):
self.wfile.write(chunk)
def log_message(self, format, *args):
return
httpd = HTTPServer(('', 8001), MyHandler)
httpd.serve_forever()
You may use Flask framework to do this.
It is not only for mjpeg.
I adapted some code from here: https://blog.miguelgrinberg.com/post/video-streaming-with-flask
APP.py
#!/usr/bin/env python
from importlib import import_module
import os
from flask import Flask, render_template, Response
# import camera driver
if os.environ.get('CAMERA'):
Camera = import_module('camera_' + os.environ['CAMERA']).Camera
else:
from camera import Camera
# Raspberry Pi camera module (requires picamera package)
# from camera_pi import Camera
app = Flask(__name__)
#app.route('/')
def index():
"""Video streaming home page."""
return render_template('index.html')
def gen(camera):
"""Video streaming generator function."""
while True:
frame = camera.get_frame()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
#app.route('/video_feed')
def video_feed():
"""Video streaming route. Put this in the src attribute of an img tag."""
return Response(gen(Camera()),
mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__':
app.run(host='0.0.0.0', threaded=True)
base_camera.py
import time
import threading
try:
from greenlet import getcurrent as get_ident
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident
class CameraEvent(object):
"""An Event-like class that signals all active clients when a new frame is
available.
"""
def __init__(self):
self.events = {}
def wait(self):
"""Invoked from each client's thread to wait for the next frame."""
ident = get_ident()
if ident not in self.events:
# this is a new client
# add an entry for it in the self.events dict
# each entry has two elements, a threading.Event() and a timestamp
self.events[ident] = [threading.Event(), time.time()]
return self.events[ident][0].wait()
def set(self):
"""Invoked by the camera thread when a new frame is available."""
now = time.time()
remove = None
for ident, event in self.events.items():
if not event[0].isSet():
# if this client's event is not set, then set it
# also update the last set timestamp to now
event[0].set()
event[1] = now
else:
# if the client's event is already set, it means the client
# did not process a previous frame
# if the event stays set for more than 5 seconds, then assume
# the client is gone and remove it
if now - event[1] > 5:
remove = ident
if remove:
del self.events[remove]
def clear(self):
"""Invoked from each client's thread after a frame was processed."""
self.events[get_ident()][0].clear()
class BaseCamera(object):
thread = None # background thread that reads frames from camera
frame = None # current frame is stored here by background thread
last_access = 0 # time of last client access to the camera
event = CameraEvent()
def __init__(self):
"""Start the background camera thread if it isn't running yet."""
if BaseCamera.thread is None:
BaseCamera.last_access = time.time()
# start background frame thread
BaseCamera.thread = threading.Thread(target=self._thread)
BaseCamera.thread.start()
# wait until frames are available
while self.get_frame() is None:
time.sleep(0)
def get_frame(self):
"""Return the current camera frame."""
BaseCamera.last_access = time.time()
# wait for a signal from the camera thread
BaseCamera.event.wait()
BaseCamera.event.clear()
return BaseCamera.frame
#staticmethod
def frames():
""""Generator that returns frames from the camera."""
raise RuntimeError('Must be implemented by subclasses.')
#classmethod
def _thread(cls):
"""Camera background thread."""
print('Starting camera thread.')
frames_iterator = cls.frames()
for frame in frames_iterator:
BaseCamera.frame = frame
BaseCamera.event.set() # send signal to clients
time.sleep(0)
# if there hasn't been any clients asking for frames in
# the last 10 seconds then stop the thread
if time.time() - BaseCamera.last_access > 10:
frames_iterator.close()
print('Stopping camera thread due to inactivity.')
break
BaseCamera.thread = None
camera.py
#D:\gstreamer\1.0\x86\bin>gst-launch-1.0.exe multifilesrc loop=true start-index=0 stop-index=0 location=d:/python/temp.png ! decodebin ! identity sleep-time=1000000 ! videoconvert ! autovideosink
import shutil
import time
import os,sys
from PIL import Image, ImageFont, ImageDraw, ImageFile
from io import BytesIO
from base_camera import BaseCamera
im = Image.new("RGB", (300, 30), (220, 180, 180))
#im.format'JPEG'
dr = ImageDraw.Draw(im)
font = ImageFont.truetype(os.path.join("fonts", "msyh.ttf"), 16)
text =time.strftime("%m/%d %H:%M:%S") +u"这是一段测试文本。"
dr.text((10, 5), text, font=font, fill="#000000")
im.save("d://python/temp.jpg")
dr.rectangle((0,0,300,500),fill="#FFFFFF")
text =time.strftime("%m/%d %H:%M:%S") +u"这是一段测试文本。"
dr.text((10, 5),text, font=font, fill="#000000")
f = BytesIO()
f.name="sdf.jpg"
im.save(f,"JPEG")
f.seek(0)
f.close()
class Camera(BaseCamera):
"""An emulated camera implementation that streams a repeated sequence of
files 1.jpg, 2.jpg and 3.jpg at a rate of one frame penr second."""
imgs = [open(f + '.jpg', 'rb').read() for f in ['1', '2', '3']]
#staticmethod
def frames():
while True:
text =time.strftime("%m/%d %H:%M:%S") +u"这是一段测试文本。"
dr.rectangle((0,0,300,500),fill="#FFFFFF")
dr.text((10, 5), text, font=font, fill="#000000")
f = BytesIO()
im.save(f,'JPEG')
try :
im.save("d:/python/temp.jpg")
except :
print("Unexpected error:", sys.exc_info()[0])
pass
# shutil.copy("d:/python/temp2.png","d:/python/temp.png")
f.seek(0)
time.sleep(1)
yield f.read() #Camera.imgs[int(time.time()) % 3]