Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions src/deepstream/pipelines/gpu_frames_skipping_pipeline.py
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as here

Could you please remove the jetson from function names, e.g. build_jetson_pipeline -> build_pipeline?

Also I think your branch is not up-to-date with main: runner is not used

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import cv2
import pyds
import ctypes
from src.deepstream.probes.frame_comparison.gpu import frame_skipping_probe
from src.deepstream.probes.frame_comparison.gpu.frame_skipping_probe import frame_skipping_probe, GPUFrameChangeDetector

from typing import Any, Dict, Optional

Expand All @@ -20,10 +20,10 @@

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

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

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

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

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

# Add probe
detector = GPUFrameChangeDetector()

streammux_src_pad: Gst.Pad = streammux.get_static_pad("src")
streammux_src_pad.add_probe(Gst.PadProbeType.BUFFER, jetson_frame_skip_probe, None)
streammux_src_pad.add_probe(Gst.PadProbeType.BUFFER, gpu_frame_skip_probe, detector)

return pipeline

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

if stats["total"] > 0:
skip_ratio: float = stats["skipped"] / stats["total"]
print(f"\n🔥 Jetson Final Stats:")
print(f"\n🔥 Final Stats:")
print(f" Total: {stats['total']}")
print(f" Processed: {stats['processed']}")
print(f" Skipped: {stats['skipped']}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,10 @@
#include <cmath>
#include <vector>

std::unique_ptr<GPUFrameChangeDetector> GPUFrameChangeDetector::instance = nullptr;

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

GPUFrameChangeDetector* GPUFrameChangeDetector::getInstance() {
if (!instance) {
instance = std::unique_ptr<GPUFrameChangeDetector>(new GPUFrameChangeDetector());
}
return instance.get();
}

static cv::Scalar mean_gpu(const cv::cuda::GpuMat& mat) {
cv::Scalar sum_val = cv::cuda::sum(mat);
double total_elements = static_cast<double>(mat.rows * mat.cols);
Expand Down Expand Up @@ -118,7 +110,10 @@ GPUFrameChangeDetector::should_process_gpu_direct(const cv::cuda::GpuMat& gpu_fr

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

prev_frame_gpu = processed_curr;
bool should_proc = !is_static;
if (should_proc) {
prev_frame_gpu = processed_curr;
}

return {!is_static, {{"MSE", mse_val}, {"SSIM", ssim_val}, {"FLOW", flow_val}}};
return {should_proc, {{"MSE", mse_val}, {"SSIM", ssim_val}, {"FLOW", flow_val}}};
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,22 @@

class GPUFrameChangeDetector {
public:
// Singleton access
static GPUFrameChangeDetector* getInstance();


// Main logic: decide if frame should be processed
std::pair<bool, std::map<std::string, double>>
should_process_gpu_direct(const cv::cuda::GpuMat& gpu_frame);

private:
// Constructor (private for singleton)
GPUFrameChangeDetector();

private:

// Internal processing functions
cv::cuda::GpuMat preprocess_gpu(const cv::cuda::GpuMat& frame);
double mse_gpu(const cv::cuda::GpuMat& imgA, const cv::cuda::GpuMat& imgB);
double simple_ssim_gpu(const cv::cuda::GpuMat& imgA, const cv::cuda::GpuMat& imgB);
double optical_flow_gpu(const cv::cuda::GpuMat& imgA, const cv::cuda::GpuMat& imgB);

// Internal state
static std::unique_ptr<GPUFrameChangeDetector> instance;
cv::cuda::GpuMat prev_frame_gpu;

// Thresholds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace py = pybind11;

bool frame_skipping_probe(uintptr_t buffer_ptr, int batch_id)
bool frame_skipping_probe(uintptr_t buffer_ptr, int batch_id, GPUFrameChangeDetector& detector)
{
static int frame_count = 0;
frame_count++;
Expand Down Expand Up @@ -65,8 +65,7 @@ bool frame_skipping_probe(uintptr_t buffer_ptr, int batch_id)
cv::cuda::GpuMat safe_copy;
gpuMat.copyTo(safe_copy);

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

safe_copy.release();
gpuMat.release();
Expand Down Expand Up @@ -98,6 +97,11 @@ bool frame_skipping_probe(uintptr_t buffer_ptr, int batch_id)
PYBIND11_MODULE(frame_skipping_probe, m)
{
m.def("frame_skipping_probe", &frame_skipping_probe,
"Run DeepStream frame probe on GPU",
py::arg("buffer_ptr"), py::arg("batch_id"));
"Run DeepStream frame probe on GPU",
py::arg("buffer_ptr"), py::arg("batch_id"), py::arg("detector"));

py::class_<GPUFrameChangeDetector>(m, "GPUFrameChangeDetector")
.def(py::init<>())
.def("should_process_gpu_direct", &GPUFrameChangeDetector::should_process_gpu_direct);
}

Binary file not shown.
Loading