mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-01-07 00:06:57 +01:00
FEAT: Replace best jpg endpoint (#2944)
* Added object thumbnail def and made camera tracked objects use it. * Add object snapshot def * Remove documentation for best.jpg * Update docs for label thumbnail and snapshot defs
This commit is contained in:
parent
dccfc3b84f
commit
0abd0627df
@ -24,16 +24,6 @@ Accepts the following query string parameters:
|
||||
|
||||
You can access a higher resolution mjpeg stream by appending `h=height-in-pixels` to the endpoint. For example `http://localhost:5000/api/back?h=1080`. You can also increase the FPS by appending `fps=frame-rate` to the URL such as `http://localhost:5000/api/back?fps=10` or both with `?fps=10&h=1000`.
|
||||
|
||||
### `GET /api/<camera_name>/<object_name>/best.jpg[?h=300&crop=1&quality=70]`
|
||||
|
||||
The best snapshot for any object type. It is a full resolution image by default.
|
||||
|
||||
Example parameters:
|
||||
|
||||
- `h=300`: resizes the image to 300 pixes tall
|
||||
- `crop=1`: crops the image to the region of the detection rather than returning the entire image
|
||||
- `quality=70`: sets the jpeg encoding quality (0-100)
|
||||
|
||||
### `GET /api/<camera_name>/latest.jpg[?h=300]`
|
||||
|
||||
The most recent frame that frigate has finished processing. It is a full resolution image by default.
|
||||
@ -200,6 +190,10 @@ Sets retain to false for the event id (event may be deleted quickly after removi
|
||||
|
||||
Returns a thumbnail for the event id optimized for notifications. Works while the event is in progress and after completion. Passing `?format=android` will convert the thumbnail to 2:1 aspect ratio.
|
||||
|
||||
### `GET /api/<camera_name>/<label>/thumbnail.jpg`
|
||||
|
||||
Returns the thumbnail from the latest event for the given camera and label combo. Using `any` as the label will return the latest thumbnail regardless of type.
|
||||
|
||||
### `GET /api/events/<id>/clip.mp4`
|
||||
|
||||
Returns the clip for the event id. Works after the event has ended.
|
||||
@ -218,6 +212,10 @@ Accepts the following query string parameters, but they are only applied when an
|
||||
| `crop` | int | Crop the snapshot to the (0 or 1) |
|
||||
| `quality` | int | Jpeg encoding quality (0-100). Defaults to 70. |
|
||||
|
||||
### `GET /api/<camera_name>/<label>/snapshot.jpg`
|
||||
|
||||
Returns the snapshot image from the latest event for the given camera and label combo. Using `any` as the label will return the latest thumbnail regardless of type.
|
||||
|
||||
### `GET /clips/<camera>-<id>.jpg`
|
||||
|
||||
JPG snapshot for the given camera and event id.
|
||||
|
106
frigate/http.py
106
frigate/http.py
@ -226,6 +226,39 @@ def event_thumbnail(id):
|
||||
response.headers["Cache-Control"] = "private, max-age=31536000"
|
||||
return response
|
||||
|
||||
@bp.route("/<camera_name>/<label>/best.jpg")
|
||||
@bp.route("/<camera_name>/<label>/thumbnail.jpg")
|
||||
def label_thumbnail(camera_name, label):
|
||||
if label == "any":
|
||||
event_query = (
|
||||
Event.select()
|
||||
.where(Event.camera == camera_name)
|
||||
.where(Event.has_snapshot == True)
|
||||
.order_by(Event.start_time.desc())
|
||||
)
|
||||
else:
|
||||
event_query = (
|
||||
Event.select()
|
||||
.where(Event.camera == camera_name)
|
||||
.where(Event.label == label)
|
||||
.where(Event.has_snapshot == True)
|
||||
.order_by(Event.start_time.desc())
|
||||
)
|
||||
|
||||
try:
|
||||
event = event_query.get()
|
||||
|
||||
return event_thumbnail(event.id)
|
||||
except DoesNotExist:
|
||||
frame = np.zeros((175, 175, 3), np.uint8)
|
||||
ret, jpg = cv2.imencode(
|
||||
".jpg", frame, [int(cv2.IMWRITE_JPEG_QUALITY), 70]
|
||||
)
|
||||
|
||||
response = make_response(jpg.tobytes())
|
||||
response.headers["Content-Type"] = "image/jpeg"
|
||||
return response
|
||||
|
||||
|
||||
@bp.route("/events/<id>/snapshot.jpg")
|
||||
def event_snapshot(id):
|
||||
@ -271,6 +304,37 @@ def event_snapshot(id):
|
||||
] = f"attachment; filename=snapshot-{id}.jpg"
|
||||
return response
|
||||
|
||||
@bp.route("/<camera_name>/<label>/snapshot.jpg")
|
||||
def label_snapshot(camera_name, label):
|
||||
if label == "any":
|
||||
event_query = (
|
||||
Event.select()
|
||||
.where(Event.camera == camera_name)
|
||||
.where(Event.has_snapshot == True)
|
||||
.order_by(Event.start_time.desc())
|
||||
)
|
||||
else:
|
||||
event_query = (
|
||||
Event.select()
|
||||
.where(Event.camera == camera_name)
|
||||
.where(Event.label == label)
|
||||
.where(Event.has_snapshot == True)
|
||||
.order_by(Event.start_time.desc())
|
||||
)
|
||||
|
||||
try:
|
||||
event = event_query.get()
|
||||
return event_snapshot(event.id)
|
||||
except DoesNotExist:
|
||||
frame = np.zeros((720, 1280, 3), np.uint8)
|
||||
ret, jpg = cv2.imencode(
|
||||
".jpg", frame, [int(cv2.IMWRITE_JPEG_QUALITY), 70]
|
||||
)
|
||||
|
||||
response = make_response(jpg.tobytes())
|
||||
response.headers["Content-Type"] = "image/jpeg"
|
||||
return response
|
||||
|
||||
|
||||
@bp.route("/events/<id>/clip.mp4")
|
||||
def event_clip(id):
|
||||
@ -391,48 +455,6 @@ def stats():
|
||||
return jsonify(stats)
|
||||
|
||||
|
||||
@bp.route("/<camera_name>/<label>/best.jpg")
|
||||
def best(camera_name, label):
|
||||
if camera_name in current_app.frigate_config.cameras:
|
||||
best_object = current_app.detected_frames_processor.get_best(camera_name, label)
|
||||
best_frame = best_object.get("frame")
|
||||
if best_frame is None:
|
||||
best_frame = np.zeros((720, 1280, 3), np.uint8)
|
||||
else:
|
||||
best_frame = cv2.cvtColor(best_frame, cv2.COLOR_YUV2BGR_I420)
|
||||
|
||||
crop = bool(request.args.get("crop", 0, type=int))
|
||||
if crop:
|
||||
box_size = 300
|
||||
box = best_object.get("box", (0, 0, box_size, box_size))
|
||||
region = calculate_region(
|
||||
best_frame.shape,
|
||||
box[0],
|
||||
box[1],
|
||||
box[2],
|
||||
box[3],
|
||||
box_size,
|
||||
multiplier=1.1,
|
||||
)
|
||||
best_frame = best_frame[region[1] : region[3], region[0] : region[2]]
|
||||
|
||||
height = int(request.args.get("h", str(best_frame.shape[0])))
|
||||
width = int(height * best_frame.shape[1] / best_frame.shape[0])
|
||||
resize_quality = request.args.get("quality", default=70, type=int)
|
||||
|
||||
best_frame = cv2.resize(
|
||||
best_frame, dsize=(width, height), interpolation=cv2.INTER_AREA
|
||||
)
|
||||
ret, jpg = cv2.imencode(
|
||||
".jpg", best_frame, [int(cv2.IMWRITE_JPEG_QUALITY), resize_quality]
|
||||
)
|
||||
response = make_response(jpg.tobytes())
|
||||
response.headers["Content-Type"] = "image/jpeg"
|
||||
return response
|
||||
else:
|
||||
return "Camera named {} not found".format(camera_name), 404
|
||||
|
||||
|
||||
@bp.route("/<camera_name>")
|
||||
def mjpeg_feed(camera_name):
|
||||
fps = int(request.args.get("fps", "3"))
|
||||
|
@ -134,7 +134,7 @@ export default function Camera({ camera }) {
|
||||
key={objectType}
|
||||
header={objectType}
|
||||
href={`/events?camera=${camera}&label=${objectType}`}
|
||||
media={<img src={`${apiHost}/api/${camera}/${objectType}/best.jpg?crop=1&h=150`} />}
|
||||
media={<img src={`${apiHost}/api/${camera}/${objectType}/thumbnail.jpg`} />}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user