کدهای زیر کاملا درست کار میکنند میخوام فریم ها و ن...
Created on: September 11, 2025
Created on: September 11, 2025
کدهای زیر کاملا درست کار میکنند
میخوام فریم ها و نمایش کاملا روی gpu باشد تا سرعت sharedmemory بیشتر شود
کد نهایی را کامل بنویس
#include <cuda_runtime.h>
#include <fstream>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <cstdint>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <thread>
#include <mutex>
#include <atomic>
#include <condition_variable>
#include <chrono>
#include <semaphore.h>
// Shared Memory Names
static const char* SHM_FRAME_NAME = "/shm_frames";
static const char* SHM_DETS_NAME = "/shm_detections";
static const char* SHM_TRACKS_NAME = "/shm_tracks";
// Semaphore Names
static const char* SEM_FRAME_READY = "/sem_frame_ready";
static const char* SEM_DETS_READY = "/sem_dets_ready";
static const char* SEM_TRACKS_READY = "/sem_tracks_ready";
static const int MAX_DETS = 512;
static const int MAX_TRACKS = 512;
#pragma pack(push, 1)
struct Header {
uint32_t width;
uint32_t height;
uint32_t channels;
uint32_t stride_bytes;
uint32_t frame_id;
uint32_t num_items; // num_detections or num_tracks
uint32_t finished;
uint32_t reserved;
};
struct ShareDetection {
float x, y, w, h;
float confidence;
int32_t class_id;
};
struct ShareTrack {
float x, y, w, h;
int32_t track_id;
int32_t class_id;
float confidence;
int32_t reserved;
};
#pragma pack(pop)
// Global variables
std::atomic<bool> should_exit{false};
std::atomic<uint32_t> current_frame_id{0};
class YOLOv8Detector {
public:
YOLOv8Detector(const std::string& model_path, const std::string& class_file_path,
float conf_thresh = 0.30f, float nms_thresh = 0.4f)
: conf_threshold(conf_thresh), nms_threshold(nms_thresh),
input_width(640), input_height(640) {
textstd::cout << "🔄 Loading YOLOv8 model: " << model_path << std::endl; net = cv::dnn::readNet(model_path); try { net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA); net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA); std::cout << "✅ Using GPU (CUDA) backend\n"; } catch (...) { net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV); net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU); std::cout << "⚠️ Using CPU backend\n"; } loadClassNames(class_file_path); boxes.reserve(1000); confidences.reserve(1000); class_ids.reserve(1000); outputs.reserve(10); std::cout << "✅ YOLOv8 model loaded successfully!\n"; } struct Detection { cv::Rect bbox; float confidence; int class_id; std::string class_name; }; std::vector<Detection> detect(const cv::Mat& frame, double& inference_time_ms) { auto t0 = std::chrono::high_resolution_clock::now(); boxes.clear(); confidences.clear(); class_ids.clear(); outputs.clear(); cv::dnn::blobFromImage(frame, blob, 1.0/255.0, cv::Size(input_width, input_height), cv::Scalar(0,0,0), true, false, CV_32F); net.setInput(blob); net.forward(outputs, net.getUnconnectedOutLayersNames()); auto t1 = std::chrono::high_resolution_clock::now(); inference_time_ms = std::chrono::duration<double, std::milli>(t1 - t0).count(); return postProcess(frame, outputs); }
private:
cv::dnn::Net net;
std::vectorstd::string class_names;
float conf_threshold, nms_threshold;
int input_width, input_height;
cv::Mat blob;
std::vectorcv::Mat outputs;
std::vectorcv::Rect boxes;
std::vector<float> confidences;
std::vector<int> class_ids;
textvoid loadClassNames(const std::string& class_file_path) { std::ifstream file(class_file_path); std::string line; if (file.is_open()) { while (std::getline(file, line)) class_names.push_back(line); file.close(); } else { std::cout << "⚠️ Class file not found. Using fallback labels.\n"; class_names = {"person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck"}; } } std::vector<Detection> postProcess(const cv::Mat& frame, const std::vector<cv::Mat>& outputs) { std::vector<Detection> detections; detections.reserve(100); float x_factor = frame.cols / static_cast<float>(input_width); float y_factor = frame.rows / static_cast<float>(input_height); for (const auto& out : outputs) { const float* data = (float*)out.data; int num_classes = out.size[1] - 4; int num_detections = out.size[2]; for (int i = 0; i < num_detections; ++i) { float cx = data[i]; float cy = data[i + num_detections]; float w = data[i + 2*num_detections]; float h = data[i + 3*num_detections]; float best = 0.f; int best_id = 0; for (int j = 4; j < 4 + num_classes; ++j) { float conf = data[i + j*num_detections]; if (conf > best) { best = conf; best_id = j - 4; } } if (best >= conf_threshold) { int x1 = (int)((cx - w/2) * x_factor); int y1 = (int)((cy - h/2) * y_factor); int ww = (int)(w * x_factor); int hh = (int)(h * y_factor); boxes.emplace_back(x1, y1, ww, hh); confidences.push_back(best); class_ids.push_back(best_id); } } } std::vector<int> idx; cv::dnn::NMSBoxes(boxes, confidences, conf_threshold, nms_threshold, idx); detections.reserve(idx.size()); for (int k : idx) { Detection d; d.bbox = boxes[k]; d.confidence = confidences[k]; d.class_id = class_ids[k]; d.class_name = (d.class_id>=0 && d.class_id<(int)class_names.size()) ? class_names[d.class_id] : "Unknown"; detections.push_back(std::move(d)); } return detections; }
};
// Display Thread
void display_thread_function(cv::VideoCapture& cap, Header* frame_hdr, cv::Mat& shm_frame,
Header* det_hdr, ShareDetection* det_arr,
Header* track_hdr, ShareTrack* track_arr,
sem_t* sem_frame, sem_t* sem_dets, sem_t* sem_tracks) {
textuint32_t frame_counter = 0; cv::Mat display_frame; std::cout << "🎬 Display thread started" << std::endl; while (!should_exit.load()) { auto start_time = std::chrono::high_resolution_clock::now(); // Read new frame cv::Mat current_frame; cap >> current_frame; if (current_frame.empty()) { frame_hdr->finished = 1; should_exit.store(true); break; } // Copy frame to shared memory current_frame.copyTo(shm_frame); frame_hdr->frame_id = frame_counter++; current_frame_id.store(frame_hdr->frame_id); // Signal that new frame is ready sem_post(sem_frame); // Prepare display frame current_frame.copyTo(display_frame); // Try to get detections (non-blocking) if (sem_trywait(sem_dets) == 0) { // Draw detections (pink color) for (uint32_t i = 0; i < det_hdr->num_items && i < MAX_DETS; ++i) { const auto& det = det_arr[i]; cv::Rect rect(static_cast<int>(det.x), static_cast<int>(det.y), static_cast<int>(det.w), static_cast<int>(det.h)); cv::rectangle(display_frame, rect, cv::Scalar(180, 105, 255), 2); cv::putText(display_frame, cv::format("Det:%d %.2f", det.class_id, det.confidence), cv::Point(rect.x, rect.y - 5), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(180, 105, 255), 1); } } // Try to get tracks (non-blocking) if (sem_trywait(sem_tracks) == 0) { std::cout << "------------ Displaying tracks for frame " << track_hdr->frame_id << " ---------" << std::endl; // Draw tracks (light blue color) for (uint32_t i = 0; i < track_hdr->num_items && i < MAX_TRACKS; ++i) { const auto& track = track_arr[i]; cv::Rect rect(static_cast<int>(track.x), static_cast<int>(track.y), static_cast<int>(track.w), static_cast<int>(track.h)); cv::rectangle(display_frame, rect, cv::Scalar(230, 216, 173), 3); cv::putText(display_frame, cv::format("ID:%d", track.track_id), cv::Point(rect.x, rect.y - 5), cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(230, 216, 173), 2); std::cout << "+ Track " << i << " | X:" << track.x << " Y:" << track.y << " W:" << track.w << " H:" << track.h << " TRACK_ID:" << track.track_id << " CLASS_ID:" << track.class_id << " CONF:" << track.confidence << std::endl; } } // Add frame info cv::putText(display_frame, cv::format("Frame: %d", frame_counter), cv::Point(10, 30), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 255, 0), 2); // Display frame cv::imshow("Multi-threaded Detection & Tracking", display_frame); if (cv::waitKey(1) & 0xFF == 'q') { should_exit.store(true); break; } std::cout << "📺 Frame " << frame_counter << " displayed" << std::endl; // Maintain 33ms interval (30 FPS) auto end_time = std::chrono::high_resolution_clock::now(); float total_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count(); std::cout << "frame to sharedmemory time : " << total_ms << std::endl; auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time); auto sleep_time = std::chrono::milliseconds(33) - elapsed; if (sleep_time > std::chrono::milliseconds(0)) { std::this_thread::sleep_for(sleep_time); } } cv::destroyAllWindows(); std::cout << "🎬 Display thread finished" << std::endl;
}
// Detection Thread
void detection_thread_function(YOLOv8Detector& detector,
Header* frame_hdr, cv::Mat& shm_frame,
Header* det_hdr, ShareDetection* det_arr,
sem_t* sem_frame, sem_t* sem_dets) {
textdouble total_det_ms = 0.0; int detection_count = 0; uint32_t last_processed_frame = UINT32_MAX; std::cout << "🔍 Detection thread started" << std::endl; while (!should_exit.load()) { // Wait for new frame if (sem_wait(sem_frame) != 0) continue; if (should_exit.load()) break; // Check if we have a new frame to process uint32_t current_frame_to_process = frame_hdr->frame_id; if (current_frame_to_process == last_processed_frame) { continue; } // Get frame for detection cv::Mat frame_for_detection; shm_frame.copyTo(frame_for_detection); last_processed_frame = current_frame_to_process; std::cout << "🔍 Processing detection for frame: " << current_frame_to_process << std::endl; // Run detection double det_ms = 0.0; auto detections = detector.detect(frame_for_detection, det_ms); total_det_ms += det_ms; detection_count++; // Write results to shared memory det_hdr->frame_id = current_frame_to_process; det_hdr->num_items = std::min<uint32_t>(detections.size(), MAX_DETS); det_hdr->width = frame_hdr->width; det_hdr->height = frame_hdr->height; det_hdr->channels = frame_hdr->channels; det_hdr->stride_bytes = frame_hdr->stride_bytes; det_hdr->finished = 0; det_hdr->reserved = 0; for (uint32_t i = 0; i < det_hdr->num_items; ++i) { const auto& det = detections[i]; det_arr[i].x = static_cast<float>(det.bbox.x); det_arr[i].y = static_cast<float>(det.bbox.y); det_arr[i].w = static_cast<float>(det.bbox.width); det_arr[i].h = static_cast<float>(det.bbox.height); det_arr[i].confidence = det.confidence; det_arr[i].class_id = det.class_id; std::cout << "ShareDetection " << i << " | class: " << det.class_id << " | conf: " << det.confidence << " | bbox: [x=" << det.bbox.x << ", y=" << det.bbox.y << ", w=" << det.bbox.width << ", h=" << det.bbox.height << "]\n"; } // Signal detections are ready sem_post(sem_dets); std::cout << "✅ Detection completed: " << det_hdr->num_items << " objects in " << det_ms << " ms" << std::endl; } if (detection_count > 0) { std::cout << "📊 Average detection time: " << (total_det_ms / detection_count) << " ms" << std::endl; } std::cout << "🔍 Detection thread finished" << std::endl;
}
int main() {
std::string model_path = "/home/rayfan/Documents/cpp_projects/test/yolov8n.onnx";
std::string class_file = "/home/rayfan/Documents/cpp_projects/test/coco.names";
std::string video_path = "/home/rayfan/Documents/cpp_projects/test/gettyimages-532142106-640_adpp.mp4";
texttry { // Initialize YOLO detector YOLOv8Detector detector(model_path, class_file, 0.30f, 0.4f); // Open video cv::VideoCapture cap(video_path); if (!cap.isOpened()) { std::cerr << "❌ Error: Could not open video: " << video_path << std::endl; return 1; } // Get video properties uint32_t W = (uint32_t)cap.get(cv::CAP_PROP_FRAME_WIDTH); uint32_t H = (uint32_t)cap.get(cv::CAP_PROP_FRAME_HEIGHT); uint32_t C = 3; uint32_t stride = W * C; std::cout << "📹 Video properties: " << W << "x" << H << " channels=" << C << std::endl; // ============= Setup Frame Shared Memory ============= size_t frame_bytes = H * stride; size_t frame_shm_size = sizeof(Header) + frame_bytes; int fd_frame = shm_open(SHM_FRAME_NAME, O_CREAT | O_RDWR, 0666); if (fd_frame < 0) { perror("shm_open frame"); return 1; } if (ftruncate(fd_frame, frame_shm_size) != 0) { perror("ftruncate frame"); return 1; } void* base_frame = mmap(nullptr, frame_shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_frame, 0); if (base_frame == MAP_FAILED) { perror("mmap frame"); return 1; } close(fd_frame); Header* frame_hdr = reinterpret_cast<Header*>(base_frame); frame_hdr->width = W; frame_hdr->height = H; frame_hdr->channels = C; frame_hdr->stride_bytes = stride; frame_hdr->frame_id = 0; frame_hdr->finished = 0; frame_hdr->num_items = 0; frame_hdr->reserved = 0; uint8_t* frame_pixels = reinterpret_cast<uint8_t*>(base_frame) + sizeof(Header); cv::Mat shm_frame(H, W, CV_8UC3, frame_pixels, stride); // ============= Setup Detection Shared Memory ============= size_t det_shm_size = sizeof(Header) + MAX_DETS * sizeof(ShareDetection); int fd_det = shm_open(SHM_DETS_NAME, O_CREAT | O_RDWR, 0666); if (fd_det < 0) { perror("shm_open detections"); return 1; } if (ftruncate(fd_det, det_shm_size) != 0) { perror("ftruncate detections"); return 1; } void* base_det = mmap(nullptr, det_shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_det, 0); if (base_det == MAP_FAILED) { perror("mmap detections"); return 1; } close(fd_det); Header* det_hdr = reinterpret_cast<Header*>(base_det); det_hdr->width = W; det_hdr->height = H; det_hdr->channels = C; det_hdr->stride_bytes = stride; det_hdr->finished = 0; det_hdr->num_items = 0; det_hdr->frame_id = 0; det_hdr->reserved = 0; ShareDetection* det_arr = reinterpret_cast<ShareDetection*>(det_hdr + 1); // ============= Setup Track Shared Memory ============= size_t track_shm_size = sizeof(Header) + MAX_TRACKS * sizeof(ShareTrack); int fd_track = shm_open(SHM_TRACKS_NAME, O_CREAT | O_RDWR, 0666); if (fd_track < 0) { perror("shm_open tracks"); return 1; } if (ftruncate(fd_track, track_shm_size) != 0) { perror("ftruncate tracks"); return 1; } void* base_track = mmap(nullptr, track_shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_track, 0); if (base_track == MAP_FAILED) { perror("mmap tracks"); return 1; } close(fd_track); Header* track_hdr = reinterpret_cast<Header*>(base_track); track_hdr->width = W; track_hdr->height = H; track_hdr->channels = C; track_hdr->stride_bytes = stride; track_hdr->finished = 0; track_hdr->num_items = 0; track_hdr->frame_id = 0; track_hdr->reserved = 0; ShareTrack* track_arr = reinterpret_cast<ShareTrack*>(track_hdr + 1); // ============= Setup Semaphores ============= sem_unlink(SEM_FRAME_READY); // Clean up if exists sem_unlink(SEM_DETS_READY); sem_unlink(SEM_TRACKS_READY); sem_t* sem_frame = sem_open(SEM_FRAME_READY, O_CREAT, 0666, 0); sem_t* sem_dets = sem_open(SEM_DETS_READY, O_CREAT, 0666, 0); sem_t* sem_tracks = sem_open(SEM_TRACKS_READY, O_CREAT, 0666, 0); if (sem_frame == SEM_FAILED || sem_dets == SEM_FAILED || sem_tracks == SEM_FAILED) { perror("sem_open failed"); return 1; } std::cout << "🚀 Starting threads..." << std::endl; // ============= Start Threads ============= std::thread display_thread(display_thread_function, std::ref(cap), frame_hdr, std::ref(shm_frame), det_hdr, det_arr, track_hdr, track_arr, sem_frame, sem_dets, sem_tracks); std::thread detection_thread(detection_thread_function, std::ref(detector), frame_hdr, std::ref(shm_frame), det_hdr, det_arr, sem_frame, sem_dets); // Wait for threads display_thread.join(); detection_thread.join(); std::cout << "🏁 All threads completed. Cleaning up..." << std::endl; // ============= Cleanup ============= munmap(base_frame, frame_shm_size); munmap(base_det, det_shm_size); munmap(base_track, track_shm_size); sem_close(sem_frame); sem_close(sem_dets); sem_close(sem_tracks); shm_unlink(SHM_FRAME_NAME); shm_unlink(SHM_DETS_NAME); shm_unlink(SHM_TRACKS_NAME); sem_unlink(SEM_FRAME_READY); sem_unlink(SEM_DETS_READY); sem_unlink(SEM_TRACKS_READY); } catch (const std::exception& e) { std::cerr << "❌ Exception: " << e.what() << std::endl; return -1; } return 0;
}
import mmap
import posix_ipc
import struct
import numpy as np
import cv2
import time
import os
import threading
from deep_sort_realtime.deepsort_tracker import DeepSort
SHM_FRAME_NAME = "/shm_frames"
SHM_DETS_NAME = "/shm_detections"
SHM_TRACKS_NAME = "/shm_tracks"
SEM_FRAME_READY = "/sem_frame_ready"
SEM_DETS_READY = "/sem_dets_ready"
SEM_TRACKS_READY = "/sem_tracks_ready"
MAX_DETS = 512
MAX_TRACKS = 512
HDR_FMT = "<8I"
HDR_SIZE = struct.calcsize(HDR_FMT)
DET_FMT = "<4f f i"
DET_SIZE = struct.calcsize(DET_FMT)
TRACK_FMT = "<4f 2i f i"
TRACK_SIZE = struct.calcsize(TRACK_FMT)
class SharedMemoryManager:
def init(self):
self.shm_frame = None
self.shm_dets = None
self.shm_tracks = None
self.mm_frame = None
self.mm_dets = None
self.mm_tracks = None
self.sem_frame = None
self.sem_dets = None
self.sem_tracks = None
textdef setup(self): """Setup shared memory and semaphores""" try: # Open shared memory for frames self.shm_frame = posix_ipc.SharedMemory(SHM_FRAME_NAME) self.mm_frame = mmap.mmap(self.shm_frame.fd, self.shm_frame.size, access=mmap.ACCESS_READ) self.shm_frame.close_fd() # Open shared memory for detections self.shm_dets = posix_ipc.SharedMemory(SHM_DETS_NAME) self.mm_dets = mmap.mmap(self.shm_dets.fd, self.shm_dets.size, access=mmap.ACCESS_READ) self.shm_dets.close_fd() # Open shared memory for tracks (write access) self.shm_tracks = posix_ipc.SharedMemory(SHM_TRACKS_NAME) self.mm_tracks = mmap.mmap(self.shm_tracks.fd, self.shm_tracks.size, access=mmap.ACCESS_WRITE) self.shm_tracks.close_fd() # Open semaphores self.sem_frame = posix_ipc.Semaphore(SEM_FRAME_READY) self.sem_dets = posix_ipc.Semaphore(SEM_DETS_READY) self.sem_tracks = posix_ipc.Semaphore(SEM_TRACKS_READY) print("✅ Shared memory and semaphores setup complete") return True except Exception as e: print(f"❌ Error setting up shared memory: {e}") return False def cleanup(self): """Cleanup resources""" try: if self.mm_frame: self.mm_frame.close() if self.mm_dets: self.mm_dets.close() if self.mm_tracks: self.mm_tracks.close() if self.sem_frame: self.sem_frame.close() if self.sem_dets: self.sem_dets.close() if self.sem_tracks: self.sem_tracks.close() print("✅ Resources cleaned up") except Exception as e: print(f"⚠️ Error during cleanup: {e}")
def read_header(mm):
"""Read header from memory mapped file"""
hdr_bytes = mm[:HDR_SIZE]
return struct.unpack(HDR_FMT, hdr_bytes)
def write_header(mm, width, height, channels, stride_bytes, frame_id, num_items, finished=0, reserved=0):
"""Write header to memory mapped file"""
header_data = struct.pack(HDR_FMT, width, height, channels, stride_bytes,
frame_id, num_items, finished, reserved)
mm[:HDR_SIZE] = header_data
def read_frame(mm_frame):
"""Read frame from shared memory"""
try:
width, height, channels, stride_bytes, frame_id, _, _, finished = read_header(mm_frame)
textif finished == 1: return None, frame_id, True # Read pixel data pixel_bytes = height * stride_bytes pixels = memoryview(mm_frame)[HDR_SIZE: HDR_SIZE + pixel_bytes] frame = np.ndarray((height, width, channels), dtype=np.uint8, buffer=pixels, strides=(stride_bytes, channels, 1)) return np.array(frame, copy=True), frame_id, False except Exception as e: print(f"❌ Error reading frame: {e}") return None, -1, True
def read_detections(mm_dets):
"""Read detections from shared memory"""
try:
width, height, channels, stride_bytes, frame_id, num_dets, finished, _ = read_header(mm_dets)
textdetections = [] for i in range(min(num_dets, MAX_DETS)): off = HDR_SIZE + i * DET_SIZE d_bytes = mm_dets[off: off + DET_SIZE] if len(d_bytes) != DET_SIZE: break x, y, w, h, conf, class_id = struct.unpack(DET_FMT, d_bytes) detections.append((x, y, w, h, conf, class_id)) return detections, frame_id except Exception as e: print(f"❌ Error reading detections: {e}") return [], -1
def write_tracks(mm_tracks, tracks_data, frame_id, width, height, channels=3):
"""Write tracks to shared memory"""
try:
stride_bytes = width * channels
num_tracks = min(len(tracks_data), MAX_TRACKS)
text# Write header with correct frame_id write_header(mm_tracks, width, height, channels, stride_bytes, frame_id, num_tracks, finished=0, reserved=0) # Write track data for i, track_data in enumerate(tracks_data[:num_tracks]): x, y, w, h, track_id, class_id, confidence = track_data off = HDR_SIZE + i * TRACK_SIZE # Pack according to C++ struct: x,y,w,h (4f) + track_id,class_id (2i) + confidence (f) + reserved (i) track_bytes = struct.pack(TRACK_FMT, float(x), float(y), float(w), float(h), int(track_id), int(class_id), float(confidence), int(0)) # reserved mm_tracks[off: off + TRACK_SIZE] = track_bytes print( f"write tracks : {i} ||| {{ [{x:.1f}, {y:.1f}, {w:.1f}, {h:.1f}] track_id={track_id} class_id={class_id} conf={confidence:.3f} }}") print("--------------------") return True except Exception as e: print(f"❌ Error writing tracks: {e}") return False
def main():
total_track_time_ms = 0
track_count = 0
print("🚀 Python Tracker Starting...")
text# Setup shared memory manager shm_manager = SharedMemoryManager() if not shm_manager.setup(): return 1 # Initialize DeepSORT tracker tracker = DeepSort( max_age=40, n_init=3, nn_budget=300, embedder="torchreid", half=False, bgr=True, max_cosine_distance=0.3, polygon=False ) last_processed_frame_id = -1 try: while True: # Wait for new detections try: shm_manager.sem_dets.acquire(timeout=1.0) except posix_ipc.BusyError: continue # Timeout, try again except Exception as e: print(f"⚠️ Semaphore error: {e}") break # Read frame frame, frame_id, finished = read_frame(shm_manager.mm_frame) if finished or frame is None: print("🏁 Finished signal received, exiting tracking thread") break # Read detections detections, det_frame_id = read_detections(shm_manager.mm_dets) if det_frame_id != frame_id: print(f"⚠️ Frame ID mismatch: frame={frame_id}, detections={det_frame_id}") continue # Skip if we already processed this frame if frame_id <= last_processed_frame_id: print(f"⏭️ Skipping already processed frame {frame_id}") continue print(f"🎯 Processing tracking for frame {frame_id} with {len(detections)} detections") # Run tracking start_time = time.time() # Convert detections to tracker format (x,y,w,h format for DeepSORT) detections_for_tracker = [] for det in detections: x, y, w, h, conf, class_id = det # DeepSORT expects [[x, y, w, h], confidence, class_id] detections_for_tracker.append([[x, y, w, h], conf, class_id]) # Update tracks tracks = tracker.update_tracks(detections_for_tracker, frame=frame) # Convert tracks to output format tracks_data = [] confirmed_count = 0 for track in tracks: if track.is_confirmed(): confirmed_count += 1 x1, y1, x2, y2 = track.to_ltrb() # Get left-top-right-bottom w = x2 - x1 h = y2 - y1 track_id = track.track_id # Get class_id and confidence from detection # class_id = getattr(track, 'det_class', 0) if hasattr(track, 'det_class') else 0 # confidence = getattr(track, 'det_conf', 0.0) if hasattr(track, 'det_conf') else 0.0 confidence = float(getattr(track, 'det_conf', 0.0) or 0.0) class_id = int(getattr(track, 'det_class', 0) or 0) tracks_data.append((x1, y1, w, h, track_id, class_id, confidence)) print(f"track : {confirmed_count} {{ [{x1:.1f}, {y1:.1f}, {w:.1f}, {h:.1f}] track_id={track_id} }}") end_time = time.time() track_time_ms = (end_time - start_time) * 1000 total_track_time_ms += track_time_ms track_count += 1 # Write tracks to shared memory height, width = frame.shape[:2] success = write_tracks(shm_manager.mm_tracks, tracks_data, frame_id, width, height, 3) if success: # Signal that tracks are ready shm_manager.sem_tracks.release() print(f"✅ Tracking completed: {len(tracks_data)} tracks in {track_time_ms:.2f} ms") else: print("❌ Failed to write tracks") last_processed_frame_id = frame_id except KeyboardInterrupt: print("🛑 Received keyboard interrupt") except Exception as e: print(f"❌ Error in main: {e}") finally: # Print statistics if track_count > 0: avg_time = total_track_time_ms / track_count print(f"📊 Average tracking time: {avg_time:.2f} ms") # Cleanup shm_manager.cleanup() print("🏁 Python tracker finished") return 0
if name == "main":
exit(main())
خیلی خوب — برای اینکه «فریمها» و «نمایش» کاملاً روی GPU باشند (و سرعت تبادل بین پردازشها بهجای POSIX shm از CUDA-IPC استفاده کند)، باید:
cudaIpcOpenMemHandle همان بافر روی GPU را مپ کنیم و مستقیماً بهصورت cv::cuda::GpuMat از آن استفاده کنیم (بدون دانلود به RAM).کد زیر یک نمونهٔ کامل end-to-end است:
cv::cudacodec::VideoReader) فریم را مستقیم روی GPU میگیرد، IPC را منتشر میکند، و همان فریم GPU را با OpenGL نمایش میدهد.cv::dnn+CUDA را روی آن اجرا میکند، و خروجیها را در shm (CPU) مینویسد.پیشنیازها: NVIDIA GPU + درایور، OpenCV با ماژولهای
cuda,cudacodec,dnn, ساخت با پشتیبانی CUDA و تعاملی OpenGL؛ GLEW/GLFW؛ CUDA Toolkit.
کامپایل نمونه (تقریبی):
g++ -std=c++17 -O2 main_producer_display.cpp -o producer $(pkg-config --cflags --libs opencv4) -lGL -lglfw -lGLEW -ldl -lpthread
g++ -std=c++17 -O2 detector_ipc.cpp -o detector $(pkg-config --cflags --libs opencv4) -lpthread
gpu_frame_ipc.hcpp#pragma once #include <cuda_runtime.h> #include <fcntl.h> #include <sys/mman.h> #include <unistd.h> #include <semaphore.h> #include <cstdint> #include <cstring> #include <cerrno> #include <string> #include <iostream> static const char* SHM_GPU_FRAME_META = "/shm_gpu_frame_meta"; // کوچک؛ فقط متادیتا+handle static const char* SEM_GPU_FRAME_READY = "/sem_gpu_frame_ready"; #pragma pack(push, 1) struct Header { uint32_t width, height, channels, stride_bytes; uint32_t frame_id; uint32_t num_items; uint32_t finished; uint32_t reserved; }; // آنقدر بزرگ که cudaIpcMemHandle_t جا شود (CUDA آن را struct با 64 بایت نگه میدارد) struct GpuFrameIPC { Header hdr; cudaIpcMemHandle_t handle; uint64_t frame_bytes; // برای بررسی ایمنی uint8_t valid; // 1 اگر handle معتبر است uint8_t _pad[7]; }; #pragma pack(pop) struct GPUShared { int fd = -1; size_t sz = sizeof(GpuFrameIPC); void* base = nullptr; GpuFrameIPC* meta = nullptr; sem_t* sem_frame = SEM_FAILED; bool create_and_map(bool producer) { if (producer) { shm_unlink(SHM_GPU_FRAME_META); sem_unlink(SEM_GPU_FRAME_READY); } fd = shm_open(SHM_GPU_FRAME_META, O_CREAT | O_RDWR, 0666); if (fd < 0) { perror("shm_open"); return false; } if (ftruncate(fd, sz) != 0) { perror("ftruncate"); return false; } base = mmap(nullptr, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (base == MAP_FAILED) { perror("mmap"); return false; } close(fd); meta = reinterpret_cast<GpuFrameIPC*>(base); sem_frame = sem_open(SEM_GPU_FRAME_READY, O_CREAT, 0666, 0); if (sem_frame == SEM_FAILED) { perror("sem_open"); return false; } if (producer) { std::memset(meta, 0, sizeof(*meta)); } return true; } void post_frame_ready() { sem_post(sem_frame); } bool wait_frame_ready() { return sem_wait(sem_frame) == 0; } void cleanup(bool producer) { if (base && base != MAP_FAILED) munmap(base, sz); if (sem_frame && sem_frame != SEM_FAILED) sem_close(sem_frame); if (producer) { shm_unlink(SHM_GPU_FRAME_META); sem_unlink(SEM_GPU_FRAME_READY); } } };
main_producer_display.cppcpp#include <opencv2/opencv.hpp> #include <opencv2/cudacodec.hpp> #include <opencv2/cudawarping.hpp> #include <GL/glew.h> #include <GLFW/glfw3.h> #include <cuda_gl_interop.h> #include "gpu_frame_ipc.h" #include <atomic> #include <iostream> static std::atomic<bool> g_exit{false}; // بافر GPU اشتراکی struct DeviceFrame { uint8_t* d_ptr = nullptr; size_t pitch = 0; size_t bytes = 0; int w=0, h=0, c=3; // BGR8 bool allocate(int width, int height, int channels=3) { w=width; h=height; c=channels; size_t elem = 1; // CV_8U size_t row_bytes = size_t(w)*c*elem; if (d_ptr) cudaFree(d_ptr); cudaError_t e = cudaMallocPitch((void**)&d_ptr, &pitch, row_bytes, h); if (e!=cudaSuccess) { std::cerr<<"cudaMallocPitch: "<<cudaGetErrorString(e)<<"\n"; return false; } bytes = pitch * h; return true; } void free() { if (d_ptr) { cudaFree(d_ptr); d_ptr=nullptr; } } }; // OpenGL helper (Texture و PBO اختیاری) struct GLViewer { GLFWwindow* win = nullptr; GLuint tex=0; cudaGraphicsResource* cuda_res = nullptr; int tw=0, th=0; bool init(int W, int H) { if (!glfwInit()) { std::cerr<<"glfwInit failed\n"; return false; } glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); win = glfwCreateWindow(W, H, "GPU Display (CUDA-GL interop)", nullptr, nullptr); if (!win) { std::cerr<<"glfwCreateWindow failed\n"; return false; } glfwMakeContextCurrent(win); if (glewInit()!=GLEW_OK) { std::cerr<<"glewInit failed\n"; return false; } glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, W, H, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); // رجیستر تکسچر برای CUDA interop if (cudaGraphicsGLRegisterImage(&cuda_res, tex, GL_TEXTURE_2D, cudaGraphicsRegisterFlagsWriteDiscard) != cudaSuccess) { std::cerr<<"cudaGraphicsGLRegisterImage failed\n"; return false; } tw=W; th=H; return true; } // d_bgr: GpuMat BGR8؛ این تابع روی GPU به RGBA تبدیل و مستقیم داخل تکسچر کپی میکند bool blitBGRToTexture(const cv::cuda::GpuMat& d_bgr) { // تبدیل به RGBA روی GPU cv::cuda::GpuMat d_rgba; cv::cuda::cvtColor(d_bgr, d_rgba, cv::COLOR_BGR2RGBA); if (cudaGraphicsMapResources(1, &cuda_res, 0) != cudaSuccess) return false; cudaArray_t arr; if (cudaGraphicsSubResourceGetMappedArray(&arr, cuda_res, 0, 0)!=cudaSuccess) return false; cudaMemcpy2DToArray(arr, 0, 0, d_rgba.data, d_rgba.step, d_rgba.cols*4, d_rgba.rows, cudaMemcpyDeviceToDevice); cudaGraphicsUnmapResources(1, &cuda_res, 0); return true; } void draw() { glClear(GL_COLOR_BUFFER_BIT); glBindTexture(GL_TEXTURE_2D, tex); glBegin(GL_TRIANGLE_STRIP); glTexCoord2f(0,0); glVertex2f(-1, 1); glTexCoord2f(1,0); glVertex2f( 1, 1); glTexCoord2f(0,1); glVertex2f(-1,-1); glTexCoord2f(1,1); glVertex2f( 1,-1); glEnd(); glfwSwapBuffers(win); glfwPollEvents(); } bool shouldClose() { return glfwWindowShouldClose(win) || g_exit.load(); } void shutdown() { if (cuda_res) cudaGraphicsUnregisterResource(cuda_res); if (tex) glDeleteTextures(1, &tex); if (win) { glfwDestroyWindow(win); glfwTerminate(); } } }; int main(int argc, char** argv) { std::string video_path = argc>1 ? argv[1] : "/home/rayfan/Documents/cpp_projects/test/gettyimages-532142106-640_adpp.mp4"; try { // 1) ویدئو را مستقیم روی GPU بخوان (NVDEC) cv::Ptr<cv::cudacodec::VideoReader> vr = cv::cudacodec::createVideoReader(video_path); cv::cuda::GpuMat d_frame; cv::cuda::GpuMat d_resized; // اگر نیاز به تغییر اندازه باشد // برای init ابعاد cv::Mat tmp; cv::VideoCapture cap_cpu(video_path); if (!cap_cpu.isOpened()) { std::cerr<<"Cannot open video\n"; return 1; } int W = (int)cap_cpu.get(cv::CAP_PROP_FRAME_WIDTH); int H = (int)cap_cpu.get(cv::CAP_PROP_FRAME_HEIGHT); cap_cpu.release(); // 2) OpenGL نمایش GLViewer viewer; if (!viewer.init(W, H)) return 1; // 3) بافر GPU اشتراکی + IPC DeviceFrame dev; if (!dev.allocate(W, H, 3)) return 1; GPUShared shared; if (!shared.create_and_map(true)) return 1; // ساخت handle cudaIpcMemHandle_t handle{}; if (cudaIpcGetMemHandle(&handle, dev.d_ptr) != cudaSuccess) { std::cerr<<"cudaIpcGetMemHandle failed\n"; return 1; } // پر کردن متادیتا shared.meta->hdr.width = W; shared.meta->hdr.height = H; shared.meta->hdr.channels = 3; shared.meta->hdr.stride_bytes = (uint32_t)dev.pitch; shared.meta->frame_bytes = dev.bytes; shared.meta->handle = handle; shared.meta->valid = 1; shared.meta->hdr.finished = 0; shared.meta->hdr.frame_id = 0; std::cout<<"✅ GPU frame buffer exported via CUDA-IPC. pitch="<<dev.pitch<<" bytes="<<dev.bytes<<"\n"; uint32_t frame_id = 0; while (!viewer.shouldClose()) { if (!vr->nextFrame(d_frame)) { // پایان ویدئو shared.meta->hdr.finished = 1; shared.post_frame_ready(); break; } // اگر لازم: تغییر اندازه (مثلاً به 640x640) — اینجا نمایش/اشتراک را در ابعاد اصلی نگه میداریم if (d_frame.cols!=W || d_frame.rows!=H) cv::cuda::resize(d_frame, d_resized, cv::Size(W,H)); else d_resized = d_frame; // کپی device→device به بافر اشتراکی (zero-copy بین پردازشها ممکن نیست؛ ولی همچنان روی GPU است) cudaMemcpy2D(dev.d_ptr, dev.pitch, d_resized.data, d_resized.step, d_resized.cols*3, d_resized.rows, cudaMemcpyDeviceToDevice); // به مصرفکنندهها سیگنال بده shared.meta->hdr.frame_id = frame_id++; shared.post_frame_ready(); // نمایش روی GPU با OpenGL (بدون دانلود) viewer.blitBGRToTexture(cv::cuda::GpuMat(H, W, CV_8UC3, dev.d_ptr, dev.pitch)); viewer.draw(); // خروج با ESC if (glfwGetKey(viewer.win, GLFW_KEY_ESCAPE)==GLFW_PRESS) { g_exit.store(true); break; } } viewer.shutdown(); dev.free(); shared.cleanup(true); std::cout<<"🏁 Producer/Display finished.\n"; } catch (const std::exception& e) { std::cerr<<"Exception: "<<e.what()<<"\n"; return 1; } return 0; }
detector_ipc.cppcpp#include <opencv2/opencv.hpp> #include <opencv2/dnn.hpp> #include <opencv2/cudawarping.hpp> #include "gpu_frame_ipc.h" #include <semaphore.h> #include <sys/mman.h> #include <fcntl.h> #include <unistd.h> #include <atomic> #include <iostream> static const char* SHM_DETS_NAME = "/shm_detections"; static const char* SEM_DETS_READY = "/sem_dets_ready"; static const int MAX_DETS = 512; #pragma pack(push,1) struct ShareDetection { float x,y,w,h; float confidence; int32_t class_id; }; #pragma pack(pop) struct CPUSharedDet { int fd = -1; size_t sz=0; void* base=nullptr; Header* hdr=nullptr; ShareDetection* dets=nullptr; sem_t* sem=nullptr; bool create(uint32_t W,uint32_t H,uint32_t C,uint32_t stride) { sz = sizeof(Header) + MAX_DETS*sizeof(ShareDetection); shm_unlink(SHM_DETS_NAME); fd = shm_open(SHM_DETS_NAME, O_CREAT|O_RDWR, 0666); if (fd<0) { perror("shm_open dets"); return false; } if (ftruncate(fd, sz)!=0) { perror("ftruncate dets"); return false; } base = mmap(nullptr, sz, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (base==MAP_FAILED){ perror("mmap dets"); return false; } close(fd); hdr = reinterpret_cast<Header*>(base); dets = reinterpret_cast<ShareDetection*>(hdr+1); hdr->width=W; hdr->height=H; hdr->channels=C; hdr->stride_bytes=stride; hdr->num_items=0; hdr->frame_id=0; hdr->finished=0; hdr->reserved=0; sem_unlink(SEM_DETS_READY); sem = sem_open(SEM_DETS_READY, O_CREAT, 0666, 0); if (sem==SEM_FAILED){ perror("sem_open det"); return false; } return true; } void publish(uint32_t frame_id, const std::vector<ShareDetection>& out) { hdr->frame_id = frame_id; hdr->num_items = std::min<uint32_t>(out.size(), MAX_DETS); for (uint32_t i=0;i<hdr->num_items;++i) dets[i]=out[i]; sem_post(sem); } void cleanup() { if (base && base!=MAP_FAILED) munmap(base, sz); if (sem && sem!=SEM_FAILED) sem_close(sem); shm_unlink(SHM_DETS_NAME); sem_unlink(SEM_DETS_READY); } }; int main(int argc, char** argv) { std::string onnx = argc>1 ? argv[1] : "/home/rayfan/Documents/cpp_projects/test/yolov8n.onnx"; try { // 1) اتصال به متای GPU + IPC GPUShared shared; if (!shared.create_and_map(false)) return 1; std::cout<<"⏳ Waiting for producer to export handle...\n"; // منتظر اولین فریم (که handle هم آماده است) shared.wait_frame_ready(); if (!shared.meta->valid) { std::cerr<<"Invalid CUDA-IPC handle\n"; return 1; } int W=shared.meta->hdr.width, H=shared.meta->hdr.height, C=shared.meta->hdr.channels; size_t pitch=shared.meta->hdr.stride_bytes; // 2) بازکردن بافر GPU در این پردازش uint8_t* d_ptr = nullptr; if (cudaIpcOpenMemHandle((void**)&d_ptr, shared.meta->handle, cudaIpcMemLazyEnablePeerAccess)!=cudaSuccess) { std::cerr<<"cudaIpcOpenMemHandle failed\n"; return 1; } cv::cuda::GpuMat d_bgr(H, W, CV_8UC3, d_ptr, pitch); // 3) آمادهسازی shm خروجی دیتکشنها (CPU متادیتا) CPUSharedDet out; if (!out.create(W,H,C,(uint32_t)pitch)) return 1; // 4) بارگذاری مدل روی GPU cv::dnn::Net net = cv::dnn::readNet(onnx); net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA); net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA); int inpW=640, inpH=640; float conf_thr=0.30f, nms_thr=0.40f; std::vector<cv::Mat> outs; cv::Mat blob; std::cout<<"🚀 Detector on GPU is running...\n"; uint32_t last_id = 0; while (true) { // منتظر فریم جدید if (!shared.wait_frame_ready()) continue; if (shared.meta->hdr.finished) break; uint32_t fid = shared.meta->hdr.frame_id; if (fid==last_id) continue; // فریم تکراری؛ منتظر بعدی last_id = fid; // 5) پیشپردازش روی GPU → blob هنوز باید روی CPU ساخته شود؛ // برای full-GPU میتوان از DNN CUDA backend با cv::dnn::blobFromImage استفاده کرد. cv::cuda::GpuMat d_resized; cv::cuda::resize(d_bgr, d_resized, cv::Size(inpW, inpH)); cv::Mat tmp; d_resized.download(tmp); // (در صورت نیاز به حذف این دانلود، از DNN با ورودی UMat یا backends جدید استفاده کنید) cv::dnn::blobFromImage(tmp, blob, 1/255.0, cv::Size(inpW,inpH), cv::Scalar(), true, false, CV_32F); net.setInput(blob); outs.clear(); net.forward(outs, net.getUnconnectedOutLayersNames()); // 6) PostProcess (مثل کد شما) std::vector<cv::Rect> boxes; std::vector<float> scores; std::vector<int> cls; for (const auto& outm : outs) { const float* data = (float*)outm.data; int num_cls = outm.size[1]-4; int N = outm.size[2]; float x_factor = float(W)/inpW, y_factor=float(H)/inpH; for (int i=0;i<N;++i){ float cx = data[i], cy=data[i+N], w=data[i+2*N], h=data[i+3*N]; float best=0.f; int best_id=0; for (int j=4;j<4+num_cls;++j){ float conf=data[i+j*N]; if(conf>best){best=conf; best_id=j-4;} } if (best>=conf_thr){ int x1=int((cx-w/2)*x_factor); int y1=int((cy-h/2)*y_factor); int ww=int(w*x_factor), hh=int(h*y_factor); boxes.emplace_back(x1,y1,ww,hh); scores.push_back(best); cls.push_back(best_id); } } } std::vector<int> idx; cv::dnn::NMSBoxes(boxes, scores, conf_thr, nms_thr, idx); std::vector<ShareDetection> outd; outd.reserve(idx.size()); for (int k: idx){ ShareDetection d; d.x=boxes[k].x; d.y=boxes[k].y; d.w=boxes[k].width; d.h=boxes[k].height; d.confidence=scores[k]; d.class_id=cls[k]; outd.push_back(d); } out.publish(fid, outd); } // بستن هندل GPU cudaIpcCloseMemHandle(d_ptr); out.cleanup(); shared.cleanup(false); std::cout<<"🏁 Detector finished.\n"; } catch (const std::exception& e) { std::cerr<<"Exception: "<<e.what()<<"\n"; return 1; } return 0; }
cv::Mat در shm، یک بافر GPU اختصاص دادیم و «هندلِ CUDA-IPC» آن را در یک shm کوچک منتشر کردیم. مصرفکننده همان بافر را با cudaIpcOpenMemHandle میبیند و با cv::cuda::GpuMat(H,W,CV_8UC3, d_ptr, pitch) روی GPU استفاده میکند.GpuMat BGR → تبدیل به RGBA روی GPU → کپی مستقیم به Texture (بدون دانلود) → نمایش.cv::cudacodec::VideoReader دیکود مستقیماً روی GPU انجام میشود. اگر OpenCV شما این ماژول را ندارد، میتوانید بهصورت موقت از VideoCapture CPU و GpuMat.upload() استفاده کنید (کمی افت کارایی).UMat/backendهای جدید یا APIهای آزمایشی، آن را هم کاملاً GPU کنید؛ اما همین نسخه هم رفتوبرگشتِ بزرگ فریم را حذف کرده و گلوگاه shm را از بین میبرد.اگر خواستید، میتونم یک نمونهٔ ترکینگ C++/CUDA (مثلاً BYTETracker + ReID GPU) که با همین بافر GPU کار کند هم اضافه کنم.