mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-08-31 07:27:57 +00:00
Merge d93f296691bba288e4e671c6c4cf13244d94d8d0 into d37dff1b491e752d7bf6b0ca2c7efbaedca623cd
This commit is contained in:
commit
32035f8a2b
@ -86,8 +86,9 @@ class ImprovedMotionDetector(MotionDetector):
|
||||
# Improve contrast
|
||||
if self.config.improve_contrast:
|
||||
# TODO tracking moving average of min/max to avoid sudden contrast changes
|
||||
min_value = np.percentile(resized_frame, 4).astype(np.uint8)
|
||||
max_value = np.percentile(resized_frame, 96).astype(np.uint8)
|
||||
min_value, max_value = np.percentile(resized_frame, [4, 96]).astype(
|
||||
np.uint8
|
||||
)
|
||||
# skip contrast calcs if the image is a single color
|
||||
if min_value < max_value:
|
||||
# keep track of the last 50 contrast values
|
||||
@ -101,10 +102,17 @@ class ImprovedMotionDetector(MotionDetector):
|
||||
|
||||
avg_min, avg_max = np.mean(self.contrast_values, axis=0)
|
||||
|
||||
resized_frame = np.clip(resized_frame, avg_min, avg_max)
|
||||
resized_frame = (
|
||||
((resized_frame - avg_min) / (avg_max - avg_min)) * 255
|
||||
# Apply the clip-and-rescale as a 256-entry uint8 lookup table
|
||||
# instead of promoting the whole frame to float64 (which
|
||||
# allocated several full-frame temporaries per frame). The
|
||||
# per-pixel result is identical since the input is uint8.
|
||||
contrast_lut = np.clip(
|
||||
np.arange(256, dtype=np.float64), avg_min, avg_max
|
||||
)
|
||||
contrast_lut = (
|
||||
((contrast_lut - avg_min) / (avg_max - avg_min)) * 255
|
||||
).astype(np.uint8)
|
||||
resized_frame = cv2.LUT(resized_frame, contrast_lut)
|
||||
|
||||
if self.save_images:
|
||||
contrasted_saved = resized_frame.copy()
|
||||
|
||||
@ -25,7 +25,7 @@ from frigate.track.stationary_classifier import (
|
||||
)
|
||||
from frigate.util.image import (
|
||||
SharedMemoryFrameManager,
|
||||
get_histogram,
|
||||
get_histogram_from_bgr,
|
||||
intersection_over_union,
|
||||
)
|
||||
from frigate.util.object import average_boxes, median_of_boxes
|
||||
@ -523,6 +523,19 @@ class NorfairTracker(ObjectTracker):
|
||||
yuv_frame = self.frame_manager.get(
|
||||
frame_name, self.camera_config.frame_shape_yuv
|
||||
)
|
||||
|
||||
# Convert the frame to BGR once per call for histogram embeddings rather
|
||||
# than once per detection; every detection histograms a region of the
|
||||
# same frame. Skipped when there are no detections, so a frame without
|
||||
# objects costs no conversion at all.
|
||||
bgr_frame: np.ndarray | None = None
|
||||
if (
|
||||
detections
|
||||
and yuv_frame is not None
|
||||
and self.ptz_metrics.autotracker_enabled.value
|
||||
):
|
||||
bgr_frame = cv2.cvtColor(yuv_frame, cv2.COLOR_YUV2BGR_I420)
|
||||
|
||||
for obj in detections:
|
||||
label = obj[0]
|
||||
if label not in detections_by_type:
|
||||
@ -536,9 +549,9 @@ class NorfairTracker(ObjectTracker):
|
||||
points = np.array([[obj[2][0], obj[2][1]], [obj[2][2], obj[2][3]]])
|
||||
|
||||
embedding = None
|
||||
if self.ptz_metrics.autotracker_enabled.value:
|
||||
embedding = get_histogram(
|
||||
yuv_frame, obj[2][0], obj[2][1], obj[2][2], obj[2][3]
|
||||
if bgr_frame is not None:
|
||||
embedding = get_histogram_from_bgr(
|
||||
bgr_frame, obj[2][0], obj[2][1], obj[2][2], obj[2][3]
|
||||
)
|
||||
|
||||
detection = Detection(
|
||||
|
||||
@ -1243,6 +1243,14 @@ def get_image_from_recording(
|
||||
|
||||
def get_histogram(image, x_min, y_min, x_max, y_max):
|
||||
image_bgr = cv2.cvtColor(image, cv2.COLOR_YUV2BGR_I420)
|
||||
return get_histogram_from_bgr(image_bgr, x_min, y_min, x_max, y_max)
|
||||
|
||||
|
||||
def get_histogram_from_bgr(image_bgr, x_min, y_min, x_max, y_max):
|
||||
"""Compute a normalized BGR histogram for a region of an already-converted
|
||||
BGR frame. Callers that need histograms for several regions of the same
|
||||
frame should convert the frame to BGR once and reuse it here rather than
|
||||
paying a full-frame YUV to BGR conversion per region."""
|
||||
image_bgr = image_bgr[y_min:y_max, x_min:x_max]
|
||||
|
||||
hist = cv2.calcHist(
|
||||
|
||||
@ -106,7 +106,15 @@ def get_cpu_stats() -> dict[str, dict]:
|
||||
"""Get cpu usages for each process id"""
|
||||
usages = {}
|
||||
docker_memlimit = get_docker_memlimit_bytes() / 1024
|
||||
total_mem = os.sysconf("SC_PAGE_SIZE") * os.sysconf("SC_PHYS_PAGES") / 1024
|
||||
page_size = os.sysconf("SC_PAGE_SIZE")
|
||||
total_mem = page_size * os.sysconf("SC_PHYS_PAGES") / 1024
|
||||
|
||||
# These values are invariant across the process loop below, so read them
|
||||
# once per stats cycle instead of once per process. On hosts with many
|
||||
# camera ffmpeg instances the per-process reads were a visible spike.
|
||||
clk_tck = os.sysconf(os.sysconf_names["SC_CLK_TCK"])
|
||||
with open("/proc/uptime") as f:
|
||||
system_uptime_sec = int(float(f.read().split()[0]))
|
||||
|
||||
system_cpu = psutil.cpu_percent(
|
||||
interval=None
|
||||
@ -133,11 +141,6 @@ def get_cpu_stats() -> dict[str, dict]:
|
||||
stime = int(stats[14])
|
||||
start_time = int(stats[21])
|
||||
|
||||
with open("/proc/uptime") as f:
|
||||
system_uptime_sec = int(float(f.read().split()[0]))
|
||||
|
||||
clk_tck = os.sysconf(os.sysconf_names["SC_CLK_TCK"])
|
||||
|
||||
process_utime_sec = utime // clk_tck
|
||||
process_stime_sec = stime // clk_tck
|
||||
process_start_time_sec = start_time // clk_tck
|
||||
@ -148,7 +151,7 @@ def get_cpu_stats() -> dict[str, dict]:
|
||||
|
||||
with open(f"/proc/{pid}/statm") as f:
|
||||
mem_stats = f.readline().split()
|
||||
mem_res = int(mem_stats[1]) * os.sysconf("SC_PAGE_SIZE") / 1024
|
||||
mem_res = int(mem_stats[1]) * page_size / 1024
|
||||
|
||||
if docker_memlimit > 0:
|
||||
mem_pct = round((mem_res / docker_memlimit) * 100, 1)
|
||||
|
||||
@ -75,7 +75,15 @@ def capture_frames(
|
||||
frame_name = f"{config.name}_frame{frame_index}"
|
||||
frame_buffer = frame_manager.write(frame_name)
|
||||
try:
|
||||
frame_buffer[:] = ffmpeg_process.stdout.read(frame_size)
|
||||
# read directly into shared memory to avoid allocating and then
|
||||
# copying a full frame out of the ffmpeg pipe every frame. stdout
|
||||
# is buffered, so readinto returns a full frame unless the pipe
|
||||
# closed mid-frame.
|
||||
if (
|
||||
ffmpeg_process.stdout.readinto(frame_buffer[:frame_size])
|
||||
!= frame_size
|
||||
):
|
||||
raise EOFError("did not read a full frame from ffmpeg")
|
||||
except Exception:
|
||||
# shutdown has been initiated
|
||||
if stop_event.is_set():
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user