detect_objects.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import cv2
  2. import time
  3. import queue
  4. import yaml
  5. import multiprocessing as mp
  6. import subprocess as sp
  7. import numpy as np
  8. from flask import Flask, Response, make_response, jsonify
  9. import paho.mqtt.client as mqtt
  10. from frigate.video import track_camera
  11. from frigate.object_processing import TrackedObjectProcessor
  12. from frigate.util import EventsPerSecond
  13. from frigate.edgetpu import EdgeTPUProcess
  14. with open('/config/config.yml') as f:
  15. CONFIG = yaml.safe_load(f)
  16. MQTT_HOST = CONFIG['mqtt']['host']
  17. MQTT_PORT = CONFIG.get('mqtt', {}).get('port', 1883)
  18. MQTT_TOPIC_PREFIX = CONFIG.get('mqtt', {}).get('topic_prefix', 'frigate')
  19. MQTT_USER = CONFIG.get('mqtt', {}).get('user')
  20. MQTT_PASS = CONFIG.get('mqtt', {}).get('password')
  21. MQTT_CLIENT_ID = CONFIG.get('mqtt', {}).get('client_id', 'frigate')
  22. # Set the default FFmpeg config
  23. FFMPEG_CONFIG = CONFIG.get('ffmpeg', {})
  24. FFMPEG_DEFAULT_CONFIG = {
  25. 'global_args': FFMPEG_CONFIG.get('global_args',
  26. ['-hide_banner','-loglevel','panic']),
  27. 'hwaccel_args': FFMPEG_CONFIG.get('hwaccel_args',
  28. []),
  29. 'input_args': FFMPEG_CONFIG.get('input_args',
  30. ['-avoid_negative_ts', 'make_zero',
  31. '-fflags', 'nobuffer',
  32. '-flags', 'low_delay',
  33. '-strict', 'experimental',
  34. '-fflags', '+genpts+discardcorrupt',
  35. '-vsync', 'drop',
  36. '-rtsp_transport', 'tcp',
  37. '-stimeout', '5000000',
  38. '-use_wallclock_as_timestamps', '1']),
  39. 'output_args': FFMPEG_CONFIG.get('output_args',
  40. ['-f', 'rawvideo',
  41. '-pix_fmt', 'rgb24'])
  42. }
  43. GLOBAL_OBJECT_CONFIG = CONFIG.get('objects', {})
  44. WEB_PORT = CONFIG.get('web_port', 5000)
  45. DEBUG = (CONFIG.get('debug', '0') == '1')
  46. # MODEL_PATH = CONFIG.get('tflite_model', '/lab/mobilenet_ssd_v2_coco_quant_postprocess_edgetpu.tflite')
  47. MODEL_PATH = CONFIG.get('tflite_model', '/lab/detect.tflite')
  48. LABEL_MAP = CONFIG.get('label_map', '/lab/labelmap.txt')
  49. def main():
  50. # connect to mqtt and setup last will
  51. def on_connect(client, userdata, flags, rc):
  52. print("On connect called")
  53. if rc != 0:
  54. if rc == 3:
  55. print ("MQTT Server unavailable")
  56. elif rc == 4:
  57. print ("MQTT Bad username or password")
  58. elif rc == 5:
  59. print ("MQTT Not authorized")
  60. else:
  61. print ("Unable to connect to MQTT: Connection refused. Error code: " + str(rc))
  62. # publish a message to signal that the service is running
  63. client.publish(MQTT_TOPIC_PREFIX+'/available', 'online', retain=True)
  64. client = mqtt.Client(client_id=MQTT_CLIENT_ID)
  65. client.on_connect = on_connect
  66. client.will_set(MQTT_TOPIC_PREFIX+'/available', payload='offline', qos=1, retain=True)
  67. if not MQTT_USER is None:
  68. client.username_pw_set(MQTT_USER, password=MQTT_PASS)
  69. client.connect(MQTT_HOST, MQTT_PORT, 60)
  70. client.loop_start()
  71. # start plasma store
  72. plasma_cmd = ['plasma_store', '-m', '400000000', '-s', '/tmp/plasma']
  73. plasma_process = sp.Popen(plasma_cmd, stdout=sp.DEVNULL, stderr=sp.DEVNULL)
  74. time.sleep(1)
  75. rc = plasma_process.poll()
  76. if rc is not None:
  77. raise RuntimeError("plasma_store exited unexpectedly with "
  78. "code %d" % (rc,))
  79. ##
  80. # Setup config defaults for cameras
  81. ##
  82. for name, config in CONFIG['cameras'].items():
  83. config['snapshots'] = {
  84. 'show_timestamp': config.get('snapshots', {}).get('show_timestamp', True)
  85. }
  86. # Queue for cameras to push tracked objects to
  87. tracked_objects_queue = mp.Queue()
  88. # Start the shared tflite process
  89. tflite_process = EdgeTPUProcess(MODEL_PATH)
  90. # start the camera processes
  91. camera_processes = []
  92. camera_stats_values = {}
  93. for name, config in CONFIG['cameras'].items():
  94. camera_stats_values[name] = {
  95. 'fps': mp.Value('d', 10.0),
  96. 'avg_wait': mp.Value('d', 0.0)
  97. }
  98. camera_process = mp.Process(target=track_camera, args=(name, config, FFMPEG_DEFAULT_CONFIG, GLOBAL_OBJECT_CONFIG,
  99. tflite_process.detect_lock, tflite_process.detect_ready, tflite_process.frame_ready, tracked_objects_queue,
  100. camera_stats_values[name]['fps'], camera_stats_values[name]['avg_wait']))
  101. camera_process.daemon = True
  102. camera_processes.append(camera_process)
  103. for camera_process in camera_processes:
  104. camera_process.start()
  105. print(f"Camera_process started {camera_process.pid}")
  106. object_processor = TrackedObjectProcessor(CONFIG['cameras'], client, MQTT_TOPIC_PREFIX, tracked_objects_queue)
  107. object_processor.start()
  108. # create a flask app that encodes frames a mjpeg on demand
  109. app = Flask(__name__)
  110. @app.route('/')
  111. def ishealthy():
  112. # return a healh
  113. return "Frigate is running. Alive and healthy!"
  114. @app.route('/debug/stats')
  115. def stats():
  116. stats = {
  117. 'coral': {
  118. 'fps': tflite_process.fps.value,
  119. 'inference_speed': tflite_process.avg_inference_speed.value
  120. }
  121. }
  122. for name, camera_stats in camera_stats_values.items():
  123. stats[name] = {
  124. 'fps': camera_stats['fps'].value,
  125. 'avg_wait': camera_stats['avg_wait'].value
  126. }
  127. return jsonify(stats)
  128. @app.route('/<camera_name>/<label>/best.jpg')
  129. def best(camera_name, label):
  130. if camera_name in CONFIG['cameras']:
  131. best_frame = object_processor.get_best(camera_name, label)
  132. if best_frame is None:
  133. best_frame = np.zeros((720,1280,3), np.uint8)
  134. best_frame = cv2.cvtColor(best_frame, cv2.COLOR_RGB2BGR)
  135. ret, jpg = cv2.imencode('.jpg', best_frame)
  136. response = make_response(jpg.tobytes())
  137. response.headers['Content-Type'] = 'image/jpg'
  138. return response
  139. else:
  140. return "Camera named {} not found".format(camera_name), 404
  141. @app.route('/<camera_name>')
  142. def mjpeg_feed(camera_name):
  143. if camera_name in CONFIG['cameras']:
  144. # return a multipart response
  145. return Response(imagestream(camera_name),
  146. mimetype='multipart/x-mixed-replace; boundary=frame')
  147. else:
  148. return "Camera named {} not found".format(camera_name), 404
  149. def imagestream(camera_name):
  150. while True:
  151. # max out at 1 FPS
  152. time.sleep(1)
  153. frame = object_processor.get_current_frame(camera_name)
  154. if frame is None:
  155. frame = np.zeros((720,1280,3), np.uint8)
  156. frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
  157. ret, jpg = cv2.imencode('.jpg', frame)
  158. yield (b'--frame\r\n'
  159. b'Content-Type: image/jpeg\r\n\r\n' + jpg.tobytes() + b'\r\n\r\n')
  160. app.run(host='0.0.0.0', port=WEB_PORT, debug=False)
  161. for camera_process in camera_processes:
  162. camera_process.join()
  163. plasma_process.terminate()
  164. if __name__ == '__main__':
  165. main()