mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
Add endpoint to return camera frame with regions grid overlaid (#8413)
* Add endpoint to view grid overload on camera frame * Add api to docs * Formatting
This commit is contained in:
parent
6eff08eb2d
commit
89366d7b12
@ -263,6 +263,10 @@ Returns the snapshot image from the latest event for the given camera and label
|
|||||||
|
|
||||||
Returns the snapshot image from the specific point in that cameras recordings.
|
Returns the snapshot image from the specific point in that cameras recordings.
|
||||||
|
|
||||||
|
### `GET /api/<camera_name>/grid.jpg`
|
||||||
|
|
||||||
|
Returns the latest camera image with the regions grid overlaid.
|
||||||
|
|
||||||
### `GET /clips/<camera>-<id>.jpg`
|
### `GET /clips/<camera>-<id>.jpg`
|
||||||
|
|
||||||
JPG snapshot for the given camera and event id.
|
JPG snapshot for the given camera and event id.
|
||||||
|
108
frigate/http.py
108
frigate/http.py
@ -41,7 +41,7 @@ from frigate.const import (
|
|||||||
RECORD_DIR,
|
RECORD_DIR,
|
||||||
)
|
)
|
||||||
from frigate.events.external import ExternalEventProcessor
|
from frigate.events.external import ExternalEventProcessor
|
||||||
from frigate.models import Event, Recordings, Timeline
|
from frigate.models import Event, Recordings, Regions, Timeline
|
||||||
from frigate.object_processing import TrackedObject
|
from frigate.object_processing import TrackedObject
|
||||||
from frigate.plus import PlusApi
|
from frigate.plus import PlusApi
|
||||||
from frigate.ptz.onvif import OnvifController
|
from frigate.ptz.onvif import OnvifController
|
||||||
@ -726,6 +726,112 @@ def label_snapshot(camera_name, label):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/<camera_name>/grid.jpg")
|
||||||
|
def grid_snapshot(camera_name):
|
||||||
|
request.args.get("type", default="region")
|
||||||
|
|
||||||
|
if camera_name in current_app.frigate_config.cameras:
|
||||||
|
detect = current_app.frigate_config.cameras[camera_name].detect
|
||||||
|
frame = current_app.detected_frames_processor.get_current_frame(camera_name, {})
|
||||||
|
retry_interval = float(
|
||||||
|
current_app.frigate_config.cameras.get(camera_name).ffmpeg.retry_interval
|
||||||
|
or 10
|
||||||
|
)
|
||||||
|
|
||||||
|
if frame is None or datetime.now().timestamp() > (
|
||||||
|
current_app.detected_frames_processor.get_current_frame_time(camera_name)
|
||||||
|
+ retry_interval
|
||||||
|
):
|
||||||
|
return make_response(
|
||||||
|
jsonify({"success": False, "message": "Unable to get valid frame"}),
|
||||||
|
500,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
grid = (
|
||||||
|
Regions.select(Regions.grid)
|
||||||
|
.where(Regions.camera == camera_name)
|
||||||
|
.get()
|
||||||
|
.grid
|
||||||
|
)
|
||||||
|
except DoesNotExist:
|
||||||
|
return make_response(
|
||||||
|
jsonify({"success": False, "message": "Unable to get region grid"}),
|
||||||
|
500,
|
||||||
|
)
|
||||||
|
|
||||||
|
grid_size = len(grid)
|
||||||
|
grid_coef = 1.0 / grid_size
|
||||||
|
width = detect.width
|
||||||
|
height = detect.height
|
||||||
|
for x in range(grid_size):
|
||||||
|
for y in range(grid_size):
|
||||||
|
cell = grid[x][y]
|
||||||
|
|
||||||
|
if len(cell["sizes"]) == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
std_dev = round(cell["std_dev"] * width, 2)
|
||||||
|
mean = round(cell["mean"] * width, 2)
|
||||||
|
cv2.rectangle(
|
||||||
|
frame,
|
||||||
|
(int(x * grid_coef * width), int(y * grid_coef * height)),
|
||||||
|
(
|
||||||
|
int((x + 1) * grid_coef * width),
|
||||||
|
int((y + 1) * grid_coef * height),
|
||||||
|
),
|
||||||
|
(0, 255, 0),
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
cv2.putText(
|
||||||
|
frame,
|
||||||
|
f"#: {len(cell['sizes'])}",
|
||||||
|
(
|
||||||
|
int(x * grid_coef * width + 10),
|
||||||
|
int((y * grid_coef + 0.02) * height),
|
||||||
|
),
|
||||||
|
cv2.FONT_HERSHEY_SIMPLEX,
|
||||||
|
fontScale=0.5,
|
||||||
|
color=(0, 255, 0),
|
||||||
|
thickness=2,
|
||||||
|
)
|
||||||
|
cv2.putText(
|
||||||
|
frame,
|
||||||
|
f"std: {std_dev}",
|
||||||
|
(
|
||||||
|
int(x * grid_coef * width + 10),
|
||||||
|
int((y * grid_coef + 0.05) * height),
|
||||||
|
),
|
||||||
|
cv2.FONT_HERSHEY_SIMPLEX,
|
||||||
|
fontScale=0.5,
|
||||||
|
color=(0, 255, 0),
|
||||||
|
thickness=2,
|
||||||
|
)
|
||||||
|
cv2.putText(
|
||||||
|
frame,
|
||||||
|
f"avg: {mean}",
|
||||||
|
(
|
||||||
|
int(x * grid_coef * width + 10),
|
||||||
|
int((y * grid_coef + 0.08) * height),
|
||||||
|
),
|
||||||
|
cv2.FONT_HERSHEY_SIMPLEX,
|
||||||
|
fontScale=0.5,
|
||||||
|
color=(0, 255, 0),
|
||||||
|
thickness=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
ret, jpg = cv2.imencode(".jpg", frame, [int(cv2.IMWRITE_JPEG_QUALITY), 70])
|
||||||
|
response = make_response(jpg.tobytes())
|
||||||
|
response.headers["Content-Type"] = "image/jpeg"
|
||||||
|
response.headers["Cache-Control"] = "no-store"
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
return make_response(
|
||||||
|
jsonify({"success": False, "message": "Camera not found"}),
|
||||||
|
404,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/events/<id>/clip.mp4")
|
@bp.route("/events/<id>/clip.mp4")
|
||||||
def event_clip(id):
|
def event_clip(id):
|
||||||
download = request.args.get("download", type=bool)
|
download = request.args.get("download", type=bool)
|
||||||
|
Loading…
Reference in New Issue
Block a user