Skip to content

Commit bf022cb

Browse files
authored
Align C++ frame skipping probe logic with Python implementation (#168)
* Align C++ frame skipping probe logic with Python implementation * Add type annotation for detector in jetson_frame_skip_probe parameters * Apply code review feedback * fix frame update logic
1 parent 978c8eb commit bf022cb

5 files changed

Lines changed: 27 additions & 29 deletions

File tree

src/deepstream/pipelines/gpu_frames_skipping_pipeline.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import cv2
44
import pyds
55
import ctypes
6-
from src.deepstream.probes.frame_comparison.gpu import frame_skipping_probe
6+
from src.deepstream.probes.frame_comparison.gpu.frame_skipping_probe import frame_skipping_probe, GPUFrameChangeDetector
77

88
from typing import Any, Dict, Optional
99

@@ -20,10 +20,10 @@
2020

2121
stats: Dict[str, int] = {"total": 0, "skipped": 0, "processed": 0}
2222

23-
def jetson_frame_skip_probe(pad: Gst.Pad, info: Gst.PadProbeInfo, u_data: Optional[Any]) -> Gst.PadProbeReturn:
23+
def gpu_frame_skip_probe(pad: Gst.Pad, info: Gst.PadProbeInfo, detector: GPUFrameChangeDetector) -> Gst.PadProbeReturn:
2424
buffer_ptr: int = hash(info.get_buffer())
2525
batch_id: int = 0
26-
should_process: bool = frame_skipping_probe.frame_skipping_probe(buffer_ptr, batch_id)
26+
should_process: bool = frame_skipping_probe(buffer_ptr, batch_id, detector)
2727
if should_process:
2828
stats["processed"] += 1
2929
print(f"✅ PROCESSING frame {stats['total']}")
@@ -34,8 +34,8 @@ def jetson_frame_skip_probe(pad: Gst.Pad, info: Gst.PadProbeInfo, u_data: Option
3434
return Gst.PadProbeReturn.DROP
3535

3636
def build_pipeline() -> Gst.Pipeline:
37-
"""Build Jetson-compatible pipeline"""
38-
pipeline: Gst.Pipeline = Gst.Pipeline.new("jetson-frame-skipping-pipeline")
37+
"""Build GPU-compatible pipeline"""
38+
pipeline: Gst.Pipeline = Gst.Pipeline.new("gpu-frame-skipping-pipeline")
3939

4040
# Elements
4141
rtspsrc: Gst.Element = Gst.ElementFactory.make("rtspsrc", "source")
@@ -62,7 +62,7 @@ def build_pipeline() -> Gst.Pipeline:
6262
streammux.set_property("width", 640)
6363
streammux.set_property("height", 480)
6464
nvinfer.set_property("config-file-path", CONFIG_FILE)
65-
sink.set_property("location", os.path.join(OUTPUT_DIR, "jetson_frame_%05d.jpg"))
65+
sink.set_property("location", os.path.join(OUTPUT_DIR, "frame_%05d.jpg"))
6666

6767
caps: Gst.Caps = Gst.Caps.from_string("video/x-raw(memory:NVMM), format=RGBA")
6868
capsfilter.set_property("caps", caps)
@@ -97,8 +97,10 @@ def on_pad_added_decode(src: Gst.Element, pad: Gst.Pad) -> None:
9797
srcpad.link(sinkpad)
9898

9999
# Add probe
100+
detector = GPUFrameChangeDetector()
101+
100102
streammux_src_pad: Gst.Pad = streammux.get_static_pad("src")
101-
streammux_src_pad.add_probe(Gst.PadProbeType.BUFFER, jetson_frame_skip_probe, None)
103+
streammux_src_pad.add_probe(Gst.PadProbeType.BUFFER, gpu_frame_skip_probe, detector)
102104

103105
return pipeline
104106

@@ -109,7 +111,7 @@ def on_pad_added_decode(src: Gst.Element, pad: Gst.Pad) -> None:
109111

110112
if stats["total"] > 0:
111113
skip_ratio: float = stats["skipped"] / stats["total"]
112-
print(f"\n🔥 Jetson Final Stats:")
114+
print(f"\n🔥 Final Stats:")
113115
print(f" Total: {stats['total']}")
114116
print(f" Processed: {stats['processed']}")
115117
print(f" Skipped: {stats['skipped']}")

src/deepstream/probes/frame_comparison/gpu/frame_change_detector.cpp

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,10 @@
55
#include <cmath>
66
#include <vector>
77

8-
std::unique_ptr<GPUFrameChangeDetector> GPUFrameChangeDetector::instance = nullptr;
98

109
GPUFrameChangeDetector::GPUFrameChangeDetector()
1110
: mse_thresh(500.0), ssim_thresh(0.99), flow_thresh(2.0), initialized(true) {}
1211

13-
GPUFrameChangeDetector* GPUFrameChangeDetector::getInstance() {
14-
if (!instance) {
15-
instance = std::unique_ptr<GPUFrameChangeDetector>(new GPUFrameChangeDetector());
16-
}
17-
return instance.get();
18-
}
19-
2012
static cv::Scalar mean_gpu(const cv::cuda::GpuMat& mat) {
2113
cv::Scalar sum_val = cv::cuda::sum(mat);
2214
double total_elements = static_cast<double>(mat.rows * mat.cols);
@@ -118,7 +110,10 @@ GPUFrameChangeDetector::should_process_gpu_direct(const cv::cuda::GpuMat& gpu_fr
118110

119111
bool is_static = (mse_val < 10.0 && ssim_val > 0.995 && flow_val < 0.05);
120112

121-
prev_frame_gpu = processed_curr;
113+
bool should_proc = !is_static;
114+
if (should_proc) {
115+
prev_frame_gpu = processed_curr;
116+
}
122117

123-
return {!is_static, {{"MSE", mse_val}, {"SSIM", ssim_val}, {"FLOW", flow_val}}};
118+
return {should_proc, {{"MSE", mse_val}, {"SSIM", ssim_val}, {"FLOW", flow_val}}};
124119
}

src/deepstream/probes/frame_comparison/gpu/frame_change_detector.hpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,22 @@
66

77
class GPUFrameChangeDetector {
88
public:
9-
// Singleton access
10-
static GPUFrameChangeDetector* getInstance();
11-
9+
1210
// Main logic: decide if frame should be processed
1311
std::pair<bool, std::map<std::string, double>>
1412
should_process_gpu_direct(const cv::cuda::GpuMat& gpu_frame);
1513

16-
private:
17-
// Constructor (private for singleton)
1814
GPUFrameChangeDetector();
1915

16+
private:
17+
2018
// Internal processing functions
2119
cv::cuda::GpuMat preprocess_gpu(const cv::cuda::GpuMat& frame);
2220
double mse_gpu(const cv::cuda::GpuMat& imgA, const cv::cuda::GpuMat& imgB);
2321
double simple_ssim_gpu(const cv::cuda::GpuMat& imgA, const cv::cuda::GpuMat& imgB);
2422
double optical_flow_gpu(const cv::cuda::GpuMat& imgA, const cv::cuda::GpuMat& imgB);
2523

2624
// Internal state
27-
static std::unique_ptr<GPUFrameChangeDetector> instance;
2825
cv::cuda::GpuMat prev_frame_gpu;
2926

3027
// Thresholds

src/deepstream/probes/frame_comparison/gpu/frame_skipping_probe.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
namespace py = pybind11;
1212

13-
bool frame_skipping_probe(uintptr_t buffer_ptr, int batch_id)
13+
bool frame_skipping_probe(uintptr_t buffer_ptr, int batch_id, GPUFrameChangeDetector& detector)
1414
{
1515
static int frame_count = 0;
1616
frame_count++;
@@ -65,8 +65,7 @@ bool frame_skipping_probe(uintptr_t buffer_ptr, int batch_id)
6565
cv::cuda::GpuMat safe_copy;
6666
gpuMat.copyTo(safe_copy);
6767

68-
GPUFrameChangeDetector *detector = GPUFrameChangeDetector::getInstance();
69-
auto [should_process, metrics] = detector->should_process_gpu_direct(safe_copy);
68+
auto [should_process, metrics] = detector.should_process_gpu_direct(safe_copy);
7069

7170
safe_copy.release();
7271
gpuMat.release();
@@ -98,6 +97,11 @@ bool frame_skipping_probe(uintptr_t buffer_ptr, int batch_id)
9897
PYBIND11_MODULE(frame_skipping_probe, m)
9998
{
10099
m.def("frame_skipping_probe", &frame_skipping_probe,
101-
"Run DeepStream frame probe on GPU",
102-
py::arg("buffer_ptr"), py::arg("batch_id"));
100+
"Run DeepStream frame probe on GPU",
101+
py::arg("buffer_ptr"), py::arg("batch_id"), py::arg("detector"));
102+
103+
py::class_<GPUFrameChangeDetector>(m, "GPUFrameChangeDetector")
104+
.def(py::init<>())
105+
.def("should_process_gpu_direct", &GPUFrameChangeDetector::should_process_gpu_direct);
103106
}
107+
Binary file not shown.

0 commit comments

Comments
 (0)