mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +01:00 
			
		
		
		
	GenAI: add ability to save JPGs sent to provider (#15643)
* GenAI: add ability to save JPGs sent to provider
* Remove mention from GenAI docs
* Change config name to debug_save_thumbnails
* Change  folder structure to clips/genai-requests/{event_id}/{1.jpg}
			
			
This commit is contained in:
		
							parent
							
								
									87e7b62c85
								
							
						
					
					
						commit
						00371546a3
					
				@ -760,6 +760,8 @@ cameras:
 | 
			
		||||
        - cat
 | 
			
		||||
      # Optional: Restrict generation to objects that entered any of the listed zones (default: none, all zones qualify)
 | 
			
		||||
      required_zones: []
 | 
			
		||||
      # Optional: Save thumbnails sent to generative AI for review/debugging purposes (default: shown below)
 | 
			
		||||
      debug_save_thumbnails: False
 | 
			
		||||
 | 
			
		||||
# Optional
 | 
			
		||||
ui:
 | 
			
		||||
 | 
			
		||||
@ -38,6 +38,10 @@ class GenAICameraConfig(BaseModel):
 | 
			
		||||
        default_factory=list,
 | 
			
		||||
        title="List of required zones to be entered in order to run generative AI.",
 | 
			
		||||
    )
 | 
			
		||||
    debug_save_thumbnails: bool = Field(
 | 
			
		||||
        default=False,
 | 
			
		||||
        title="Save thumbnails sent to generative AI for debugging purposes.",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @field_validator("required_zones", mode="before")
 | 
			
		||||
    @classmethod
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ import logging
 | 
			
		||||
import os
 | 
			
		||||
import threading
 | 
			
		||||
from multiprocessing.synchronize import Event as MpEvent
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from typing import Optional
 | 
			
		||||
 | 
			
		||||
import cv2
 | 
			
		||||
@ -217,6 +218,8 @@ class EmbeddingMaintainer(threading.Thread):
 | 
			
		||||
                            _, buffer = cv2.imencode(".jpg", cropped_image)
 | 
			
		||||
                            snapshot_image = buffer.tobytes()
 | 
			
		||||
 | 
			
		||||
                    num_thumbnails = len(self.tracked_events.get(event_id, []))
 | 
			
		||||
 | 
			
		||||
                    embed_image = (
 | 
			
		||||
                        [snapshot_image]
 | 
			
		||||
                        if event.has_snapshot and camera_config.genai.use_snapshot
 | 
			
		||||
@ -225,11 +228,37 @@ class EmbeddingMaintainer(threading.Thread):
 | 
			
		||||
                                data["thumbnail"]
 | 
			
		||||
                                for data in self.tracked_events[event_id]
 | 
			
		||||
                            ]
 | 
			
		||||
                            if len(self.tracked_events.get(event_id, [])) > 0
 | 
			
		||||
                            if num_thumbnails > 0
 | 
			
		||||
                            else [thumbnail]
 | 
			
		||||
                        )
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
                    if camera_config.genai.debug_save_thumbnails and num_thumbnails > 0:
 | 
			
		||||
                        logger.debug(
 | 
			
		||||
                            f"Saving {num_thumbnails} thumbnails for event {event.id}"
 | 
			
		||||
                        )
 | 
			
		||||
 | 
			
		||||
                        Path(
 | 
			
		||||
                            os.path.join(CLIPS_DIR, f"genai-requests/{event.id}")
 | 
			
		||||
                        ).mkdir(parents=True, exist_ok=True)
 | 
			
		||||
 | 
			
		||||
                        for idx, data in enumerate(self.tracked_events[event_id], 1):
 | 
			
		||||
                            jpg_bytes: bytes = data["thumbnail"]
 | 
			
		||||
 | 
			
		||||
                            if jpg_bytes is None:
 | 
			
		||||
                                logger.warning(
 | 
			
		||||
                                    f"Unable to save thumbnail {idx} for {event.id}."
 | 
			
		||||
                                )
 | 
			
		||||
                            else:
 | 
			
		||||
                                with open(
 | 
			
		||||
                                    os.path.join(
 | 
			
		||||
                                        CLIPS_DIR,
 | 
			
		||||
                                        f"genai-requests/{event.id}/{idx}.jpg",
 | 
			
		||||
                                    ),
 | 
			
		||||
                                    "wb",
 | 
			
		||||
                                ) as j:
 | 
			
		||||
                                    j.write(jpg_bytes)
 | 
			
		||||
 | 
			
		||||
                    # Generate the description. Call happens in a thread since it is network bound.
 | 
			
		||||
                    threading.Thread(
 | 
			
		||||
                        target=self._embed_description,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user