Repository: blakeblackshear/frigate

Issue:
0.8.2 worse snapshot jpeg quality, possible to configure better?
In version 8.2, the size of the snapshot jpg files has been reduced. For some users this is important and for others it is less important. I have no problem with the size of the files, I have enough space, but the quality of the file is very important to me.
You've done a lot of work with this software, a lot. It would be pointless to spoil such an important matter.
I use your software to pass the image on to recognize the car's license plate. The quality of the file is of great importance to me. I notice a slight change in the quality of the file for the worse
Listen, I am replacing IP cameras from 4MP to 5MP or 8MP to have the best possible photo quality and here meanwhile such changes change worse.

**Please make it possible to decide in the configuration file what quality someone wants.** Not everyone needs a tiny file, there are those who need the best quality, for OCR in LPR, every pixel matters.

**If it is possible to configure the JPG files to be large and accurate again, please indicate it to me.**


Discussion:
I didn't see any meaningful difference in quality. Please post an example of before and after where quality is visibly worse. 
The new snapshoots have more compression in jpg and are therefore more noisy. For OCR, it makes a big difference.


![Screenshoot](https://i.imgur.com/BBQW4C2.png)
![Screenshoot](https://i.imgur.com/zt2Zqvs.png)


full original image:


![Screenshoot](https://i.imgur.com/JzKl930.jpeg)
![Screenshoot](https://i.imgur.com/cS2YSq9.jpg)

What endpoint are you using for running OCR?
I am downloading image via http API using ID EVENTS after MQTT receives information from topic "Events" about the car.

Maybe I'm wrong but a few months ago saving pictures from mqtt directly was perfect. Virtually the same quality as rtsp from the camera. Then, after the possibility of downloading the image via http api appeared, I assumed it was the same. And suddenly in version 8.2 it got noticeably worse. It was easy to see because the effectiveness of OCR dropped significantly. I started looking and with my own eyes I noticed more Jpeg compression which looks like the pictures above.

You are using the snapshot.jpg endpoint? 
We did reduce the quality of JPEG _thumbnails_ that are produced. Are you using thumbnails or snapshots here? The snapshots did not receive a change in JPEG quality
I went back and added it in other locations too. 
Sample URL to get snapshoot:
http://192.168.32.61:5000/clips/{{{idcamera}}}-{{{idevent}}}.jpg
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Hey guys.. This is quite an interesting topic!
Would there be any chance of actually implementing the "license plate" recognition in Frigate itself?
Would love to have two things: 
1: Statistics and history of "license plates" entering my driveway.
2: Have a MQTT event triggered with the actual license plate as a value in order to do external events (home automation).

This together with face recognition are the only things Im missing from Frigate right now.. And again, thanks for an awesome project!



Resolving patch:
diff --git a/frigate/config.py b/frigate/config.py
--- a/frigate/config.py
+++ b/frigate/config.py
@@ -231,7 +231,12 @@ class BirdseyeConfig(BaseModel):
     enabled: bool = Field(default=True, title="Enable birdseye view.")
     width: int = Field(default=1280, title="Birdseye width.")
     height: int = Field(default=720, title="Birdseye height.")
-    quality: int = Field(default=8, title="Encoding quality.")
+    quality: int = Field(
+        default=8,
+        title="Encoding quality.",
+        ge=1,
+        le=31,
+    )
     mode: BirdseyeModeEnum = Field(
         default=BirdseyeModeEnum.objects, title="Tracking mode."
     )
@@ -382,6 +387,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 +420,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
--- 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
--- a/frigate/object_processing.py
+++ b/frigate/object_processing.py
@@ -225,7 +225,7 @@ def get_clean_png(self):
             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 @@ def get_jpg_bytes(
                 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 @@ def end(camera, obj: TrackedObject, current_frame_time):
                         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 @@ def snapshot(camera, obj: TrackedObject, current_frame_time):
                     bounding_box=mqtt_config.bounding_box,
                     crop=mqtt_config.crop,
                     height=mqtt_config.height,
+                    quality=mqtt_config.quality,
                 )
 
                 if jpg_bytes is None:


Test patch: