diff --git a/docs/docs/configuration/cameras.md b/docs/docs/configuration/cameras.md index 225f36677..e22912105 100644 --- a/docs/docs/configuration/cameras.md +++ b/docs/docs/configuration/cameras.md @@ -181,6 +181,8 @@ snapshots: crop: False # Optional: height to resize the snapshot to (default: original size) height: 175 + # Optional: jpeg encode quality (default: shown below) + quality: 70 # Optional: Restrict snapshots to objects that entered any of the listed zones (default: no required zones) required_zones: [] # Optional: Camera override for retention settings (default: global values) @@ -407,6 +409,8 @@ cameras: crop: True # Optional: height to resize the snapshot to (default: shown below) height: 270 + # Optional: jpeg encode quality (default: shown below) + quality: 70 # Optional: Restrict mqtt messages to objects that entered any of the listed zones (default: no required zones) required_zones: [] diff --git a/docs/docs/usage/api.md b/docs/docs/usage/api.md index 1ad60b6af..86824b5a8 100644 --- a/docs/docs/usage/api.md +++ b/docs/docs/usage/api.md @@ -24,7 +24,7 @@ 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///best.jpg[?h=300&crop=1]` +### `GET /api///best.jpg[?h=300&crop=1&quality=70]` The best snapshot for any object type. It is a full resolution image by default. @@ -32,6 +32,7 @@ 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//latest.jpg[?h=300]` @@ -48,6 +49,7 @@ Accepts the following query string parameters: | `mask` | int | Overlay the mask on the image (0 or 1) | | `motion` | int | Draw blue boxes for areas with detected motion (0 or 1) | | `regions` | int | Draw green boxes for areas where object detection was run (0 or 1) | +| `quality` | int | Jpeg encoding quality (0-100). Defaults to 70. | Example parameters: @@ -202,6 +204,7 @@ Accepts the following query string parameters, but they are only applied when an | `bbox` | int | Show bounding boxes for detected objects (0 or 1) | | `timestamp` | int | Print the timestamp in the upper left (0 or 1) | | `crop` | int | Crop the snapshot to the (0 or 1) | +| `quality` | int | Jpeg encoding quality (0-100). Defaults to 70. | ### `/clips/-.mp4` diff --git a/frigate/config.py b/frigate/config.py index 5b524abad..1561a160f 100644 --- a/frigate/config.py +++ b/frigate/config.py @@ -382,6 +382,12 @@ class CameraSnapshotsConfig(BaseModel): retain: RetainConfig = Field( default_factory=RetainConfig, title="Snapshot retention." ) + quality: int = Field( + default=70, + title="Quality of the encoded jpeg (0-100).", + ge=0, + le=100, + ) class ColorConfig(BaseModel): @@ -409,6 +415,12 @@ class CameraMqttConfig(BaseModel): default_factory=list, title="List of required zones to be entered in order to send the image.", ) + quality: int = Field( + default=70, + title="Quality of the encoded jpeg (0-100).", + ge=0, + le=100, + ) class CameraClipsConfig(BaseModel): diff --git a/frigate/http.py b/frigate/http.py index b39596e9d..2f3a15221 100644 --- a/frigate/http.py +++ b/frigate/http.py @@ -208,6 +208,7 @@ def event_snapshot(id): bounding_box=request.args.get("bbox", type=int), crop=request.args.get("crop", type=int), height=request.args.get("h", type=int), + quality=request.args.get("quality", default=70, type=int), ) except: return "Event not found", 404 @@ -317,11 +318,14 @@ def best(camera_name, label): 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), 70]) + ret, jpg = cv2.imencode( + ".jpg", best_frame, [int(cv2.IMWRITE_JPEG_QUALITY), resize_quality] + ) response = make_response(jpg.tobytes()) response.headers["Content-Type"] = "image/jpg" return response @@ -367,8 +371,9 @@ def latest_frame(camera_name): "motion_boxes": request.args.get("motion", type=int), "regions": request.args.get("regions", type=int), } + resize_quality = request.args.get("quality", default=70, type=int) + if camera_name in current_app.frigate_config.cameras: - # max out at specified FPS frame = current_app.detected_frames_processor.get_current_frame( camera_name, draw_options ) @@ -380,7 +385,9 @@ def latest_frame(camera_name): frame = cv2.resize(frame, dsize=(width, height), interpolation=cv2.INTER_AREA) - ret, jpg = cv2.imencode(".jpg", frame, [int(cv2.IMWRITE_JPEG_QUALITY), 70]) + ret, jpg = cv2.imencode( + ".jpg", frame, [int(cv2.IMWRITE_JPEG_QUALITY), resize_quality] + ) response = make_response(jpg.tobytes()) response.headers["Content-Type"] = "image/jpg" return response diff --git a/frigate/object_processing.py b/frigate/object_processing.py index 47b5ab5d3..706d04328 100644 --- a/frigate/object_processing.py +++ b/frigate/object_processing.py @@ -225,7 +225,7 @@ class TrackedObject: return None def get_jpg_bytes( - self, timestamp=False, bounding_box=False, crop=False, height=None + self, timestamp=False, bounding_box=False, crop=False, height=None, quality=70 ): if self.thumbnail_data is None: return None @@ -284,7 +284,9 @@ class TrackedObject: position=self.camera_config.timestamp_style.position, ) - ret, jpg = cv2.imencode(".jpg", best_frame, [int(cv2.IMWRITE_JPEG_QUALITY), 70]) + ret, jpg = cv2.imencode( + ".jpg", best_frame, [int(cv2.IMWRITE_JPEG_QUALITY), quality] + ) if ret: return jpg.tobytes() else: @@ -624,6 +626,7 @@ class TrackedObjectProcessor(threading.Thread): bounding_box=snapshot_config.bounding_box, crop=snapshot_config.crop, height=snapshot_config.height, + quality=snapshot_config.quality, ) if jpg_bytes is None: logger.warning( @@ -665,6 +668,7 @@ class TrackedObjectProcessor(threading.Thread): bounding_box=mqtt_config.bounding_box, crop=mqtt_config.crop, height=mqtt_config.height, + quality=mqtt_config.quality, ) if jpg_bytes is None: