浏览代码

add options to define jpeg quality

Blake Blackshear 3 年之前
父节点
当前提交
98d8118fb2
共有 5 个文件被更改,包括 36 次插入6 次删除
  1. 4 0
      docs/docs/configuration/cameras.md
  2. 4 1
      docs/docs/usage/api.md
  3. 12 0
      frigate/config.py
  4. 10 3
      frigate/http.py
  5. 6 2
      frigate/object_processing.py

+ 4 - 0
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: []
 

+ 4 - 1
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/<camera_name>/<object_name>/best.jpg[?h=300&crop=1]`
+### `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.
 
@@ -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/<camera_name>/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/<camera>-<id>.mp4`
 

+ 12 - 0
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):

+ 10 - 3
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

+ 6 - 2
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: