Initial commit to add all 3 custom projects
This commit is contained in:
810
ANSCustomWeaponDetection/ANSCustomCodeWeaponDetection.cpp
Normal file
810
ANSCustomWeaponDetection/ANSCustomCodeWeaponDetection.cpp
Normal file
@@ -0,0 +1,810 @@
|
||||
#include "ANSCustomCodeWeaponDetection.h"
|
||||
|
||||
//#define FNS_DEBUG
|
||||
ANSCustomWD::ANSCustomWD()
|
||||
{
|
||||
// Initialize the model
|
||||
}
|
||||
bool ANSCustomWD::OptimizeModel(bool fp16)
|
||||
{
|
||||
try {
|
||||
if (this->_detector == nullptr || this->_filter == nullptr)
|
||||
{
|
||||
std::cout << "Model is not initialized." << std::endl;
|
||||
return false;
|
||||
}
|
||||
int vResult = _detector->Optimize(fp16);
|
||||
int tResult = _filter->Optimize(fp16);
|
||||
if (vResult != 1 || tResult != 1)
|
||||
{
|
||||
std::cout << "Model optimization failed." << std::endl;
|
||||
return false;
|
||||
}
|
||||
std::cout << "Model optimization successful." << std::endl;
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
return false;
|
||||
}
|
||||
catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CustomObject> ANSCustomWD::RunInference(const cv::Mat& input)
|
||||
{
|
||||
return RunInference(input, "CustomCam");
|
||||
}
|
||||
bool ANSCustomWD::Destroy()
|
||||
{
|
||||
try {
|
||||
this->_detector.reset();
|
||||
this->_filter.reset();
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
return false;
|
||||
}
|
||||
catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ANSCustomWD::Initialize(const std::string& modelDirectory, float detectionScoreThreshold, std::string& labelMap)
|
||||
{
|
||||
//1. The modelDirectory is supplied by ANSVIS and contains the path to the model files
|
||||
_modelDirectory = modelDirectory;
|
||||
_detectionScoreThreshold = detectionScoreThreshold;
|
||||
|
||||
// Detector model configuration
|
||||
_detectorModelName = "train_last";
|
||||
_detectorClassName = "classes.names";
|
||||
_detectorModelType = 4; // Assuming 4 represents TensorRT YoloV11
|
||||
_detectorDetectionType = 1; // Assuming 1 represents object detection
|
||||
|
||||
// Fileter model configuration
|
||||
_filterModelName = "filter";
|
||||
_filterClassName = "filter.names";
|
||||
_filterModelType = 4; // Assuming 4 represents TensorRT YoloV11
|
||||
_filterDetectionType = 1; // Assuming 1 represents object detection
|
||||
|
||||
// Create model instances
|
||||
_detector = ANSLIBPtr(ANSCENTER::ANSLIB::Create(), &ANSCENTER::ANSLIB::Destroy);
|
||||
_filter = ANSLIBPtr(ANSCENTER::ANSLIB::Create(), &ANSCENTER::ANSLIB::Destroy);
|
||||
|
||||
_detectorModelType = 4; // Assuming 4 represents TensorRT YoloV11
|
||||
_detectorDetectionType = 1; // Assuming 1 represents object detection
|
||||
_filterModelType = 4; // Assuming 4 represents TensorRT YoloV12
|
||||
_filterDetectionType = 1; // Assuming 1 represents object detection
|
||||
|
||||
// Check the hardware type
|
||||
engineType = _detector->GetEngineType();
|
||||
if (engineType == 1) {
|
||||
// detector will be TensorRT (v8, v11) and filter will be TensorRT (v12)
|
||||
_detectorModelType = 4; // Assuming 4 represents TensorRT YoloV11
|
||||
_detectorDetectionType = 1; // Assuming 1 represents object detection
|
||||
_filterModelType = 4; // Assuming 4 represents TensorRT YoloV12
|
||||
_filterDetectionType = 1; // Assuming 1 represents object detection
|
||||
std::cout << "NVIDIA GPU detected. Using TensorRT" << std::endl;
|
||||
}
|
||||
else {
|
||||
// detetector will be OpenVINO (v8, v11) and filter will be Yolo (v12)
|
||||
_detectorModelType = 5; // Assuming 5 represents OpenVINO YoloV11
|
||||
_detectorDetectionType = 1; // Assuming 1 represents object detection
|
||||
_filterModelType = 17; // Assuming 17 represents ONNX YoloV12
|
||||
_filterDetectionType = 1; // Assuming 1 represents object detection
|
||||
std::cout << "CPU detected. Using OpenVINO and ONNX" << std::endl;
|
||||
}
|
||||
|
||||
//Clear parameters
|
||||
this->_params.ROI_Config.clear();
|
||||
this->_params.ROI_Options.clear();
|
||||
this->_params.Parameters.clear();
|
||||
this->_params.ROI_Values.clear();
|
||||
|
||||
|
||||
//2. User can start impelementing the initialization logic here
|
||||
|
||||
double _modelConfThreshold = 0.5;
|
||||
double _modelNMSThreshold = 0.5;
|
||||
std::string licenseKey = "";
|
||||
#ifdef FNS_DEBUG // Corrected preprocessor directive
|
||||
_loadEngineOnCreate = true;
|
||||
#endif
|
||||
int loadEngineOnCreation = 0; // Load engine on creation
|
||||
if (_loadEngineOnCreate)loadEngineOnCreation = 1;
|
||||
int autoEngineDetection = 1; // Auto engine detection
|
||||
int detectorResult = _detector->LoadModelFromFolder(licenseKey.c_str(),
|
||||
_detectorModelName.c_str(),
|
||||
_detectorClassName.c_str(),
|
||||
detectionScoreThreshold,
|
||||
_modelConfThreshold,
|
||||
_modelNMSThreshold, autoEngineDetection,
|
||||
_detectorModelType,
|
||||
_detectorDetectionType,
|
||||
loadEngineOnCreation,
|
||||
_modelDirectory.c_str(),
|
||||
labelMap);
|
||||
|
||||
int filterResult = _filter->LoadModelFromFolder(licenseKey.c_str(),
|
||||
_filterModelName.c_str(),
|
||||
_filterClassName.c_str(),
|
||||
_detectionScoreThreshold,
|
||||
_modelConfThreshold,
|
||||
_modelNMSThreshold, autoEngineDetection,
|
||||
_filterModelType,
|
||||
_filterDetectionType,
|
||||
loadEngineOnCreation,
|
||||
_modelDirectory.c_str(),
|
||||
_filterLabelMap);
|
||||
|
||||
if ((detectorResult == 1) && (filterResult == 1)) return true;
|
||||
return false;
|
||||
}
|
||||
ANSCustomWD::~ANSCustomWD()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
std::vector<CustomObject> ANSCustomWD::RunInference(const cv::Mat& input, const std::string& camera_id) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
|
||||
// Early validation
|
||||
if (input.empty() || input.cols < 10 || input.rows < 10) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!_detector) {
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
#ifdef FNS_DEBUG
|
||||
cv::Mat draw = input.clone();
|
||||
#endif
|
||||
|
||||
std::vector<ANSCENTER::Object> output;
|
||||
|
||||
// A. Check if detected area is retained and valid
|
||||
if (_detectedArea.width > 50 && _detectedArea.height > 50) {
|
||||
output = ProcessExistingDetectedArea(input, camera_id
|
||||
#ifdef FNS_DEBUG
|
||||
, draw
|
||||
#endif
|
||||
);
|
||||
}
|
||||
else {
|
||||
// B. Find new detected area
|
||||
ProcessNewDetectedArea(input, camera_id, output
|
||||
#ifdef FNS_DEBUG
|
||||
, draw
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
#ifdef FNS_DEBUG
|
||||
cv::imshow("Combined Detected Areas", draw);
|
||||
cv::waitKey(1);
|
||||
#endif
|
||||
|
||||
// Convert to CustomObjects
|
||||
std::vector<CustomObject> results;
|
||||
if (!output.empty()) {
|
||||
results.reserve(output.size());
|
||||
const float scoreThreshold = _detectionScoreThreshold;
|
||||
|
||||
for (const auto& obj : output) {
|
||||
if (obj.confidence >= scoreThreshold) {
|
||||
CustomObject customObj;
|
||||
customObj.box = obj.box;
|
||||
customObj.classId = obj.classId;
|
||||
customObj.confidence = obj.confidence;
|
||||
customObj.cameraId = obj.cameraId;
|
||||
customObj.className = obj.className;
|
||||
customObj.extraInfo = "Detection Score Threshold:" + std::to_string(scoreThreshold);
|
||||
results.push_back(std::move(customObj));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (results.empty()) {
|
||||
UpdateNoDetectionCondition();
|
||||
}
|
||||
|
||||
return results;
|
||||
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ANSCENTER::Object> ANSCustomWD::RunFilterGetPersons(const cv::Mat& frame) {
|
||||
std::vector<ANSCENTER::Object> persons;
|
||||
if (!_filter) return persons;
|
||||
|
||||
std::vector<ANSCENTER::Object> filteredObjects;
|
||||
_filter->RunInference(frame, "cam", filteredObjects);
|
||||
|
||||
for (const auto& obj : filteredObjects) {
|
||||
if (obj.classId == 0) { // person
|
||||
persons.push_back(obj);
|
||||
}
|
||||
}
|
||||
return persons;
|
||||
}
|
||||
|
||||
std::vector<ANSCENTER::Object> ANSCustomWD::ProcessExistingDetectedArea(
|
||||
const cv::Mat& frame,
|
||||
const std::string& camera_id
|
||||
#ifdef FNS_DEBUG
|
||||
, cv::Mat& draw
|
||||
#endif
|
||||
) {
|
||||
std::vector<ANSCENTER::Object> output;
|
||||
|
||||
// Validate _detectedArea is within frame bounds
|
||||
cv::Rect frameRect(0, 0, frame.cols, frame.rows);
|
||||
_detectedArea &= frameRect;
|
||||
if (_detectedArea.width <= 0 || _detectedArea.height <= 0) {
|
||||
_detectedArea = cv::Rect();
|
||||
UpdateNoDetectionCondition();
|
||||
return output;
|
||||
}
|
||||
|
||||
#ifdef FNS_DEBUG
|
||||
cv::rectangle(draw, _detectedArea, cv::Scalar(0, 0, 255), 2); // RED
|
||||
#endif
|
||||
|
||||
cv::Mat activeROI = frame(_detectedArea);
|
||||
|
||||
// Run weapon detection on cropped ROI
|
||||
std::vector<ANSCENTER::Object> detectedObjects;
|
||||
int detectorDetectionResult = _detector->RunInference(activeROI, camera_id.c_str(), detectedObjects);
|
||||
|
||||
if (detectedObjects.empty()) {
|
||||
UpdateNoDetectionCondition();
|
||||
return output;
|
||||
}
|
||||
|
||||
// Run filter once for all candidates in this frame
|
||||
std::vector<ANSCENTER::Object> personDetections = RunFilterGetPersons(frame);
|
||||
bool filterHadResults = !personDetections.empty() || !_filter;
|
||||
|
||||
const float scoreThreshold = _detectionScoreThreshold;
|
||||
std::vector<ANSCENTER::Object> movementObjects;
|
||||
int detectedMovementResult = _detector->DetectMovement(activeROI, camera_id.c_str(), movementObjects);
|
||||
|
||||
// Convert movement objects to frame coordinates
|
||||
std::vector<ANSCENTER::Object> weaponRects;
|
||||
weaponRects.reserve(movementObjects.size());
|
||||
for (const auto& rect : movementObjects) {
|
||||
ANSCENTER::Object mObj;
|
||||
mObj.box = rect.box;
|
||||
mObj.box.x += _detectedArea.x;
|
||||
mObj.box.y += _detectedArea.y;
|
||||
weaponRects.push_back(mObj);
|
||||
}
|
||||
|
||||
#ifdef FNS_DEBUG
|
||||
for (const auto& rect : weaponRects) {
|
||||
cv::rectangle(draw, rect.box, cv::Scalar(0, 255, 0), 2); // GREEN
|
||||
}
|
||||
#endif
|
||||
|
||||
for (auto& detectedObj : detectedObjects) {
|
||||
// Adjust to frame coordinates
|
||||
detectedObj.box.x += _detectedArea.x;
|
||||
detectedObj.box.y += _detectedArea.y;
|
||||
detectedObj.cameraId = camera_id;
|
||||
|
||||
if (detectedObj.confidence <= scoreThreshold) {
|
||||
UpdateNoDetectionCondition();
|
||||
continue;
|
||||
}
|
||||
|
||||
const float area_threshold = calculateIoU(_detectedArea, detectedObj.box);
|
||||
if (area_threshold >= 0.5f) {
|
||||
UpdateNoDetectionCondition();
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef FNS_DEBUG
|
||||
cv::rectangle(draw, detectedObj.box, cv::Scalar(0, 255, 255), 2); // Yellow
|
||||
#endif
|
||||
|
||||
if (weaponRects.empty()) {
|
||||
UpdateNoDetectionCondition();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsOverlapping(detectedObj, weaponRects, 0)) {
|
||||
UpdateNoDetectionCondition();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Process valid detection
|
||||
if (ProcessWeaponDetection(frame, detectedObj, output, personDetections, filterHadResults
|
||||
#ifdef FNS_DEBUG
|
||||
, draw
|
||||
#endif
|
||||
)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
bool ANSCustomWD::ProcessWeaponDetection(
|
||||
const cv::Mat& frame,
|
||||
ANSCENTER::Object& detectedObj,
|
||||
std::vector<ANSCENTER::Object>& output,
|
||||
const std::vector<ANSCENTER::Object>& personDetections,
|
||||
bool filterHadResults
|
||||
#ifdef FNS_DEBUG
|
||||
, cv::Mat& draw
|
||||
#endif
|
||||
) {
|
||||
// Already established as real weapon after FILTERFRAMES consecutive confirmations
|
||||
if (_realWeaponCheck > FILTERFRAMES) {
|
||||
AddConfirmedWeaponDetection(frame, detectedObj, output);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Filter not available - confirm directly
|
||||
if (!_filter) {
|
||||
AddConfirmedWeaponDetection(frame, detectedObj, output);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Filter ran but detected nothing at all - treat as inconclusive
|
||||
// Require more consecutive frames before confirming without person validation
|
||||
if (!filterHadResults) {
|
||||
_realWeaponCheck++;
|
||||
if (_realWeaponCheck > FILTERFRAMES / 2) {
|
||||
AddConfirmedWeaponDetection(frame, detectedObj, output);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef FNS_DEBUG
|
||||
for (const auto& person : personDetections) {
|
||||
cv::rectangle(draw, person.box, cv::Scalar(23, 25, 0), 2);
|
||||
cv::putText(draw, "person",
|
||||
cv::Point(person.box.x, person.box.y - 10),
|
||||
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 255, 0), 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
// No persons found - weapon without person context
|
||||
if (personDetections.empty()) {
|
||||
_realWeaponCheck = std::max(0, _realWeaponCheck - 1);
|
||||
if (_realWeaponCheck <= 0) _isRealWeaponFrame = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if weapon overlaps with ANY person (use meaningful IoU threshold)
|
||||
if (IsOverlapping(detectedObj, personDetections, 0.05f)) {
|
||||
AddConfirmedWeaponDetection(frame, detectedObj, output);
|
||||
_realWeaponCheck++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Detection doesn't overlap with any person
|
||||
_realWeaponCheck = std::max(0, _realWeaponCheck - 1);
|
||||
if (_realWeaponCheck <= 0) {
|
||||
_isRealWeaponFrame = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ANSCustomWD::AddConfirmedWeaponDetection(
|
||||
const cv::Mat& frame,
|
||||
ANSCENTER::Object& detectedObj,
|
||||
std::vector<ANSCENTER::Object>& output)
|
||||
{
|
||||
output.push_back(detectedObj);
|
||||
UpdateActiveROI(frame, detectedObj);
|
||||
_isWeaponDetected = true;
|
||||
_retainDetectedArea = 0;
|
||||
_isRealWeaponFrame = true;
|
||||
}
|
||||
|
||||
void ANSCustomWD::ProcessNewDetectedArea(
|
||||
const cv::Mat& frame,
|
||||
const std::string& camera_id,
|
||||
std::vector<ANSCENTER::Object>& output
|
||||
#ifdef FNS_DEBUG
|
||||
, cv::Mat& draw
|
||||
#endif
|
||||
) {
|
||||
// Decay _realWeaponCheck gradually instead of hard reset
|
||||
_isRealWeaponFrame = false;
|
||||
_realWeaponCheck = std::max(0, _realWeaponCheck - 1);
|
||||
|
||||
// Divide image and get priority region
|
||||
std::vector<ImageSection> sections = divideImage(frame);
|
||||
const int lowestPriority = getLowestPriorityRegion();
|
||||
|
||||
if (_currentPriority > lowestPriority || _currentPriority == 0) {
|
||||
_currentPriority = getHighestPriorityRegion();
|
||||
}
|
||||
else {
|
||||
_currentPriority++;
|
||||
}
|
||||
|
||||
_detectedArea = getRegionByPriority(_currentPriority);
|
||||
|
||||
#ifdef FNS_DEBUG
|
||||
cv::rectangle(draw, _detectedArea, cv::Scalar(0, 0, 255), 4); // RED
|
||||
#endif
|
||||
|
||||
// Validate _detectedArea is within frame bounds
|
||||
cv::Rect frameRect(0, 0, frame.cols, frame.rows);
|
||||
_detectedArea &= frameRect;
|
||||
|
||||
if (_detectedArea.width <= 50 || _detectedArea.height <= 50) {
|
||||
_detectedArea = cv::Rect();
|
||||
return;
|
||||
}
|
||||
|
||||
cv::Mat detectedROI = frame(_detectedArea);
|
||||
std::vector<ANSCENTER::Object> detectedObjects;
|
||||
int detectorResult = _detector->RunInference(detectedROI, camera_id.c_str(), detectedObjects);
|
||||
|
||||
if (detectedObjects.empty()) {
|
||||
_detectedArea = cv::Rect();
|
||||
return;
|
||||
}
|
||||
|
||||
// Use configurable threshold instead of hardcoded 0.35
|
||||
const float scanThreshold = std::min(_detectionScoreThreshold, 0.35f);
|
||||
ANSCENTER::Object* bestDetection = nullptr;
|
||||
float maxScore = 0.0f;
|
||||
|
||||
for (auto& detectedObj : detectedObjects) {
|
||||
detectedObj.box.x += _detectedArea.x;
|
||||
detectedObj.box.y += _detectedArea.y;
|
||||
detectedObj.cameraId = camera_id;
|
||||
|
||||
if (detectedObj.confidence <= scanThreshold) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (detectedObj.confidence > maxScore) {
|
||||
maxScore = detectedObj.confidence;
|
||||
bestDetection = &detectedObj;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bestDetection) {
|
||||
_detectedArea = cv::Rect();
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up tracking area around the best detection
|
||||
UpdateDetectedAreaFromObject(frame, *bestDetection);
|
||||
_isWeaponDetected = true;
|
||||
_retainDetectedArea = 0;
|
||||
|
||||
// Validate in the same frame — run filter to check for person overlap
|
||||
std::vector<ANSCENTER::Object> personDetections = RunFilterGetPersons(frame);
|
||||
bool filterHadResults = !personDetections.empty() || !_filter;
|
||||
|
||||
ProcessWeaponDetection(frame, *bestDetection, output, personDetections, filterHadResults
|
||||
#ifdef FNS_DEBUG
|
||||
, draw
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
void ANSCustomWD::UpdateDetectedAreaFromObject(const cv::Mat& frame, const ANSCENTER::Object& detectedObj) {
|
||||
const int imageSize = std::max(frame.cols, frame.rows);
|
||||
int cropSize;
|
||||
|
||||
if (imageSize > 1920) cropSize = 640;
|
||||
else if (imageSize > 1280) cropSize = 480;
|
||||
else if (imageSize > 640) cropSize = 320;
|
||||
else cropSize = 224;
|
||||
|
||||
// Cap cropSize to frame dimensions to avoid negative clamp range (UB)
|
||||
cropSize = std::min(cropSize, std::min(frame.cols, frame.rows));
|
||||
if (cropSize <= 0) {
|
||||
_detectedArea = cv::Rect();
|
||||
return;
|
||||
}
|
||||
|
||||
const int xc = detectedObj.box.x + detectedObj.box.width / 2;
|
||||
const int yc = detectedObj.box.y + detectedObj.box.height / 2;
|
||||
|
||||
int x1_new = std::clamp(xc - cropSize / 2, 0, frame.cols - cropSize);
|
||||
int y1_new = std::clamp(yc - cropSize / 2, 0, frame.rows - cropSize);
|
||||
|
||||
_detectedArea.x = x1_new;
|
||||
_detectedArea.y = y1_new;
|
||||
_detectedArea.width = std::min(cropSize, frame.cols - _detectedArea.x);
|
||||
_detectedArea.height = std::min(cropSize, frame.rows - _detectedArea.y);
|
||||
}
|
||||
// Functions for screen size division
|
||||
double ANSCustomWD::calculateDistanceToCenter(const cv::Point& center, const cv::Rect& rect) {
|
||||
cv::Point rectCenter(rect.x + rect.width / 2, rect.y + rect.height / 2);
|
||||
return std::sqrt(std::pow(rectCenter.x - center.x, 2) + std::pow(rectCenter.y - center.y, 2));
|
||||
}
|
||||
std::vector<ANSCustomWD::ImageSection> ANSCustomWD::divideImage(const cv::Mat& image) {
|
||||
if (image.empty()) {
|
||||
std::cerr << "Error: Empty image!" << std::endl;
|
||||
return cachedSections;
|
||||
}
|
||||
cv::Size currentSize(image.cols, image.rows);
|
||||
|
||||
// Check if the image size has changed
|
||||
if (currentSize == previousImageSize) {
|
||||
return cachedSections; // Return cached sections if size is the same
|
||||
}
|
||||
|
||||
// Update previous size
|
||||
previousImageSize = currentSize;
|
||||
cachedSections.clear();
|
||||
|
||||
int width = image.cols;
|
||||
int height = image.rows;
|
||||
int maxDimension = std::max(width, height);
|
||||
int numSections = 10;// std::max(1, numSections); // Ensure at least 1 section
|
||||
if (maxDimension <= 2560)numSections = 8;
|
||||
if (maxDimension <= 1280)numSections = 6;
|
||||
if (maxDimension <= 960)numSections = 4;
|
||||
if (maxDimension <= 640)numSections = 2;
|
||||
if (maxDimension <= 320)numSections = 1;
|
||||
|
||||
int gridRows = std::sqrt(numSections);
|
||||
int gridCols = (numSections + gridRows - 1) / gridRows; // Ensure all sections are covered
|
||||
|
||||
int sectionWidth = width / gridCols;
|
||||
int sectionHeight = height / gridRows;
|
||||
|
||||
cv::Point imageCenter(width / 2, height / 2);
|
||||
std::vector<std::pair<double, ImageSection>> distancePriorityList;
|
||||
|
||||
// Create sections and store their distance from the center
|
||||
for (int r = 0; r < gridRows; ++r) {
|
||||
for (int c = 0; c < gridCols; ++c) {
|
||||
int x = c * sectionWidth;
|
||||
int y = r * sectionHeight;
|
||||
int w = (c == gridCols - 1) ? width - x : sectionWidth;
|
||||
int h = (r == gridRows - 1) ? height - y : sectionHeight;
|
||||
|
||||
ImageSection section(cv::Rect(x, y, w, h));
|
||||
double distance = calculateDistanceToCenter(imageCenter, section.region);
|
||||
distancePriorityList.emplace_back(distance, section);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort sections based on distance from center, then top-to-bottom, then left-to-right
|
||||
std::sort(distancePriorityList.begin(), distancePriorityList.end(),
|
||||
[](const std::pair<double, ImageSection>& a, const std::pair<double, ImageSection>& b) {
|
||||
if (std::abs(a.first - b.first) > 1e-5) {
|
||||
return a.first < b.first; // Sort by closest distance to center
|
||||
}
|
||||
// If distance is the same, prioritize top to bottom, then left to right
|
||||
return a.second.region.y == b.second.region.y
|
||||
? a.second.region.x < b.second.region.x
|
||||
: a.second.region.y < b.second.region.y;
|
||||
});
|
||||
|
||||
// Assign priority
|
||||
int priority = 1;
|
||||
for (auto& entry : distancePriorityList) {
|
||||
entry.second.priority = priority++;
|
||||
cachedSections.push_back(entry.second);
|
||||
}
|
||||
|
||||
return cachedSections;
|
||||
}
|
||||
int ANSCustomWD::getHighestPriorityRegion() {
|
||||
if (!cachedSections.empty()) {
|
||||
return cachedSections.front().priority; // First element has the highest priority
|
||||
}
|
||||
return 0; // Return empty rect if no sections exist
|
||||
}
|
||||
int ANSCustomWD::getLowestPriorityRegion() {
|
||||
if (!cachedSections.empty()) {
|
||||
return cachedSections.back().priority; // Last element has the lowest priority
|
||||
}
|
||||
return 0; // Return empty rect if no sections exist
|
||||
}
|
||||
cv::Rect ANSCustomWD::getRegionByPriority(int priority) {
|
||||
for (const auto& section : cachedSections) {
|
||||
if (section.priority == priority) {
|
||||
return section.region;
|
||||
}
|
||||
}
|
||||
return cv::Rect(); // Return empty rect if priority not found
|
||||
}
|
||||
void ANSCustomWD::UpdateNoDetectionCondition() {
|
||||
_isRealWeaponFrame = false;
|
||||
_realWeaponCheck = 0;
|
||||
if (_isWeaponDetected) {
|
||||
_retainDetectedArea++;
|
||||
if (_retainDetectedArea >= RETAINFRAMES) {// Reset detected area after 80 frames
|
||||
_detectedArea.width = 0;
|
||||
_detectedArea.height = 0;
|
||||
_retainDetectedArea = 0;
|
||||
_isWeaponDetected = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_detectedArea.width = 0;
|
||||
_detectedArea.height = 0;
|
||||
_retainDetectedArea = 0;
|
||||
}
|
||||
}
|
||||
|
||||
float ANSCustomWD::calculateIoU(const cv::Rect& box1, const cv::Rect& box2) {
|
||||
int x1 = std::max(box1.x, box2.x);
|
||||
int y1 = std::max(box1.y, box2.y);
|
||||
int x2 = std::min(box1.x + box1.width, box2.x + box2.width);
|
||||
int y2 = std::min(box1.y + box1.height, box2.y + box2.height);
|
||||
|
||||
int intersectionArea = std::max(0, x2 - x1) * std::max(0, y2 - y1);
|
||||
int box1Area = box1.width * box1.height;
|
||||
int box2Area = box2.width * box2.height;
|
||||
int unionArea = box1Area + box2Area - intersectionArea;
|
||||
if (unionArea <= 0) return 0.0f;
|
||||
|
||||
return static_cast<float>(intersectionArea) / unionArea;
|
||||
}
|
||||
bool ANSCustomWD::IsOverlapping(const ANSCENTER::Object& obj, const std::vector<ANSCENTER::Object>& objectList, float iouThreshold)
|
||||
{
|
||||
for (const auto& otherObj : objectList)
|
||||
{
|
||||
float iou = calculateIoU(obj.box, otherObj.box);
|
||||
//std::cout << "IoU: " << iou << std::endl;
|
||||
if (iou > iouThreshold)
|
||||
{
|
||||
return true; // Overlapping found
|
||||
}
|
||||
}
|
||||
return false; // No overlapping object found
|
||||
}
|
||||
|
||||
void ANSCustomWD::UpdateActiveROI(const cv::Mat& frame, ANSCENTER::Object detectedObj) {
|
||||
int cropSize = 640;
|
||||
int imagegSize = std::max(frame.cols, frame.rows);
|
||||
if (imagegSize > 1920) cropSize = 640;
|
||||
else if (imagegSize > 1280) cropSize = 480;
|
||||
else if (imagegSize > 640) cropSize = 320;
|
||||
else cropSize = 224;
|
||||
|
||||
// Cap cropSize to frame dimensions to avoid negative coordinates
|
||||
cropSize = std::min(cropSize, std::min(frame.cols, frame.rows));
|
||||
if (cropSize <= 0) {
|
||||
_detectedArea = cv::Rect();
|
||||
return;
|
||||
}
|
||||
|
||||
int xc = detectedObj.box.x + detectedObj.box.width / 2;
|
||||
int yc = detectedObj.box.y + detectedObj.box.height / 2;
|
||||
int x1_new = std::clamp(xc - cropSize / 2, 0, frame.cols - cropSize);
|
||||
int y1_new = std::clamp(yc - cropSize / 2, 0, frame.rows - cropSize);
|
||||
_detectedArea.x = x1_new;
|
||||
_detectedArea.y = y1_new;
|
||||
_detectedArea.width = std::min(cropSize, frame.cols - _detectedArea.x);
|
||||
_detectedArea.height = std::min(cropSize, frame.rows - _detectedArea.y);
|
||||
}
|
||||
|
||||
bool ANSCustomWD::ConfigureParameters(CustomParams& param)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
try {
|
||||
// Clear current parameters
|
||||
param.ROI_Config.clear();
|
||||
param.ROI_Options.clear();
|
||||
param.Parameters.clear();
|
||||
param.ROI_Values.clear();
|
||||
|
||||
//// Fill ROI_Config
|
||||
//if (this->_params.ROI_Config.empty()) {
|
||||
// // Traffic Light ROIs
|
||||
// param.ROI_Config.push_back({ true, false, false, 0, 10, "TrafficLight_One", "All Corners" });
|
||||
// param.ROI_Config.push_back({ true, false, false, 0, 10, "TrafficLight_Two", "All Corners" });
|
||||
// param.ROI_Config.push_back({ true, false, false, 0, 10, "TrafficLight_Three", "All Corners" });
|
||||
|
||||
// // Vehicle Detector Zones
|
||||
// param.ROI_Config.push_back({ true, true, false, 0, 10, "CarZone_One", "All Corners" });
|
||||
// param.ROI_Config.push_back({ true, true, false, 0, 10, "CarZone_Two", "All Corners" });
|
||||
// param.ROI_Config.push_back({ true, true, false, 0, 10, "CarZone_Three", "All Corners" });
|
||||
|
||||
// // Cross Line ROIs
|
||||
// param.ROI_Config.push_back({ false, false, true, 0, 10, "CrossLine_One", "All Corners" });
|
||||
// param.ROI_Config.push_back({ false, false, true, 0, 10, "CrossLine_Two", "All Corners" });
|
||||
// param.ROI_Config.push_back({ false, false, true, 0, 10, "CrossLine_Three", "All Corners" });
|
||||
//}
|
||||
//else {
|
||||
// // Reuse existing ROI_Config
|
||||
// param.ROI_Config = this->_params.ROI_Config;
|
||||
//}
|
||||
|
||||
//// Safely reuse ROI_Values only if valid
|
||||
//size_t suspiciousCap = this->_params.ROI_Values.capacity();
|
||||
//if (!this->_params.ROI_Values.empty() && suspiciousCap < 10000) {
|
||||
// for (const auto& roi : this->_params.ROI_Values) {
|
||||
// CustomROIValue roiValue;
|
||||
// roiValue.Name = roi.Name;
|
||||
// roiValue.ROIPoints = roi.ROIPoints;
|
||||
// roiValue.ROIMatch = roi.ROIMatch;
|
||||
// roiValue.Option = roi.Option;
|
||||
// roiValue.OriginalImageSize = roi.OriginalImageSize;
|
||||
// param.ROI_Values.push_back(roiValue);
|
||||
// }
|
||||
//}
|
||||
//else {
|
||||
// // Use default harcoded values
|
||||
// param.ROI_Values = {
|
||||
// // TrafficLight
|
||||
// {"Centre Point", {{700, 100}, {950, 100}, {950, 200}, {700, 200}}, "Left side", "TrafficLight_One_1", 1920},
|
||||
// {"Centre Point", {{1000, 100}, {2000, 100}, {2000, 200}, {1000, 200}}, "Left side", "TrafficLight_Two_1", 1920},
|
||||
// {"Centre Point", {{2100, 100}, {2200, 100}, {2200, 200}, {2100, 200}}, "Left side", "TrafficLight_Three_1", 1920},
|
||||
// // VehicleDetector
|
||||
// {"Centre Point", {{700, 650}, {950, 650}, {950, 700}, {600, 700}}, "Inside ROI", "CarZone_One_1", 1920},
|
||||
// {"Centre Point", {{950, 650}, {1900, 650}, {1900, 770}, {950, 700}}, "Inside ROI", "CarZone_Two_1", 1920},
|
||||
// {"Centre Point", {{1900, 650}, {2150, 650}, {2150, 770}, {1900, 770}}, "Inside ROI", "CarZone_Three_1", 1920},
|
||||
// // CrossLine
|
||||
// {"Centre Point", {{600, 670}, {2150, 750}}, "Above", "CrossLine_One_1", 1920},
|
||||
// {"Centre Point", {{600, 670}, {2150, 750}}, "Left side", "CrossLine_Two_1", 1920},
|
||||
// {"Centre Point", {{600, 670}, {2150, 750}}, "Left side", "CrossLine_Three_1", 1920}
|
||||
// };
|
||||
//}
|
||||
|
||||
//// Add ALPR parameter if ALPR is available
|
||||
//if (_ALPRVisible) {
|
||||
// CustomParameter stALPRParam;
|
||||
// stALPRParam.Name = "ALPR";
|
||||
// stALPRParam.DataType = "Boolean";
|
||||
// stALPRParam.NoOfDecimals = 0;
|
||||
// stALPRParam.MaxValue = 0;
|
||||
// stALPRParam.MinValue = 0;
|
||||
// stALPRParam.StartValue = "false";
|
||||
// stALPRParam.ListItems.clear();
|
||||
// stALPRParam.DefaultValue = "false";
|
||||
// stALPRParam.Value = _useALPR ? "true" : "false";
|
||||
// param.Parameters.push_back(stALPRParam);
|
||||
//}
|
||||
|
||||
//// Add Display TL parameter
|
||||
//CustomParameter stTLParam;
|
||||
//stTLParam.Name = "Show Traffic Light";
|
||||
//stTLParam.DataType = "Boolean";
|
||||
//stTLParam.NoOfDecimals = 0;
|
||||
//stTLParam.MaxValue = 0;
|
||||
//stTLParam.MinValue = 0;
|
||||
//stTLParam.StartValue = "false";
|
||||
//stTLParam.ListItems.clear();
|
||||
//stTLParam.DefaultValue = "false";
|
||||
//stTLParam.Value = _displayTL ? "true" : "false";
|
||||
//param.Parameters.push_back(stTLParam);
|
||||
|
||||
////Add Traffic Light AI parameter
|
||||
//CustomParameter stTrafficLightAI;
|
||||
//stTrafficLightAI.Name = "TrafficLightAI";
|
||||
//stTrafficLightAI.DataType = "Boolean";
|
||||
//stTrafficLightAI.NoOfDecimals = 0;
|
||||
//stTrafficLightAI.MaxValue = 0;
|
||||
//stTrafficLightAI.MinValue = 0;
|
||||
//stTrafficLightAI.StartValue = "false";
|
||||
//stTrafficLightAI.ListItems.clear();
|
||||
//stTrafficLightAI.DefaultValue = "false";
|
||||
//stTrafficLightAI.Value = m_bTrafficLightAI ? "true" : "false";
|
||||
//param.Parameters.push_back(stTrafficLightAI);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error in ConfigureParamaters: " << e.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
catch (...) {
|
||||
std::cerr << "Unknown error in ConfigureParamaters." << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
91
ANSCustomWeaponDetection/ANSCustomCodeWeaponDetection.h
Normal file
91
ANSCustomWeaponDetection/ANSCustomCodeWeaponDetection.h
Normal file
@@ -0,0 +1,91 @@
|
||||
#include "ANSLIB.h"
|
||||
#define RETAINFRAMES 80
|
||||
#define FILTERFRAMES 10
|
||||
|
||||
class CUSTOM_API ANSCustomWD : public IANSCustomClass
|
||||
{
|
||||
struct ImageSection {
|
||||
cv::Rect region;
|
||||
int priority;
|
||||
ImageSection(const cv::Rect& r) : region(r), priority(0) {}
|
||||
};
|
||||
private:
|
||||
using ANSLIBPtr = std::unique_ptr<ANSCENTER::ANSLIB, decltype(&ANSCENTER::ANSLIB::Destroy)>;
|
||||
ANSLIBPtr _detector{ nullptr, &ANSCENTER::ANSLIB::Destroy };
|
||||
ANSLIBPtr _filter{ nullptr, &ANSCENTER::ANSLIB::Destroy };
|
||||
int engineType;
|
||||
std::string _detectorModelName;
|
||||
std::string _detectorClassName;
|
||||
int _detectorModelType; // Assuming 4 represents TensorRT YoloV11
|
||||
int _detectorDetectionType; // Assuming 1 represents object detection
|
||||
|
||||
std::string _filterModelName;
|
||||
std::string _filterClassName;
|
||||
std::string _filterLabelMap;
|
||||
|
||||
int _filterModelType; // Assuming 1 represents OpenVINO YoloV12
|
||||
int _filterDetectionType; // Assuming 1 represents object detection
|
||||
|
||||
std::recursive_mutex _mutex;
|
||||
cv::Rect _detectedArea;// Area where weapon is detected
|
||||
int _retainDetectedArea{ 0 };
|
||||
bool _isWeaponDetected{ false };
|
||||
float _detectionScoreThreshold{ 0.5 };
|
||||
cv::Size previousImageSize = cv::Size(0, 0);
|
||||
std::vector<ImageSection> cachedSections;
|
||||
int _currentPriority{ 0 }; // None
|
||||
int _realWeaponCheck{ 0 };
|
||||
bool _isRealWeaponFrame{ false };
|
||||
void UpdateNoDetectionCondition();
|
||||
std::vector<ANSCENTER::Object> RunFilterGetPersons(const cv::Mat& frame);
|
||||
// Function to seperate screen size
|
||||
double calculateDistanceToCenter(const cv::Point& center, const cv::Rect& rect);
|
||||
std::vector<ImageSection> divideImage(const cv::Mat& image);
|
||||
int getHighestPriorityRegion();
|
||||
int getLowestPriorityRegion();
|
||||
cv::Rect getRegionByPriority(int priority);
|
||||
|
||||
// Utilities
|
||||
float calculateIoU(const cv::Rect& box1, const cv::Rect& box2);
|
||||
bool IsOverlapping(const ANSCENTER::Object& obj, const std::vector<ANSCENTER::Object>& objectList, float iouThreshold);
|
||||
void UpdateActiveROI(const cv::Mat& frame, ANSCENTER::Object detectedObj);
|
||||
std::vector<ANSCENTER::Object> ProcessExistingDetectedArea(
|
||||
const cv::Mat& frame, const std::string& camera_id
|
||||
#ifdef FNS_DEBUG
|
||||
, cv::Mat& draw
|
||||
#endif
|
||||
);
|
||||
bool ProcessWeaponDetection(
|
||||
const cv::Mat& frame,
|
||||
ANSCENTER::Object& detectedObj,
|
||||
std::vector<ANSCENTER::Object>& output,
|
||||
const std::vector<ANSCENTER::Object>& personDetections,
|
||||
bool filterHadResults
|
||||
#ifdef FNS_DEBUG
|
||||
, cv::Mat& draw
|
||||
#endif
|
||||
);
|
||||
void AddConfirmedWeaponDetection(
|
||||
const cv::Mat& frame,
|
||||
ANSCENTER::Object& detectedObj,
|
||||
std::vector<ANSCENTER::Object>& output);
|
||||
|
||||
void ProcessNewDetectedArea(
|
||||
const cv::Mat& frame,
|
||||
const std::string& camera_id,
|
||||
std::vector<ANSCENTER::Object>& output
|
||||
#ifdef FNS_DEBUG
|
||||
, cv::Mat& draw
|
||||
#endif
|
||||
);
|
||||
void UpdateDetectedAreaFromObject(const cv::Mat& frame, const ANSCENTER::Object& detectedObj);
|
||||
public:
|
||||
bool Initialize(const std::string& modelDiretory,float detectionScoreThreshold, std::string& labelMap)override;
|
||||
bool OptimizeModel(bool fp16)override;
|
||||
std::vector<CustomObject> RunInference(const cv::Mat& input)override;
|
||||
std::vector<CustomObject> RunInference(const cv::Mat& input, const std::string& camera_id)override;
|
||||
bool Destroy()override;
|
||||
virtual bool ConfigureParameters(CustomParams& param) override;
|
||||
ANSCustomWD();
|
||||
~ANSCustomWD();
|
||||
};
|
||||
60
ANSCustomWeaponDetection/CMakeLists.txt
Normal file
60
ANSCustomWeaponDetection/CMakeLists.txt
Normal file
@@ -0,0 +1,60 @@
|
||||
project(ANSCustomWeaponDetection LANGUAGES CXX)
|
||||
|
||||
# ---------- sources ----------
|
||||
set(SOURCES
|
||||
ANSCustomCodeWeaponDetection.cpp
|
||||
dllmain.cpp
|
||||
pch.cpp
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
ANSCustomCodeWeaponDetection.h
|
||||
framework.h
|
||||
pch.h
|
||||
)
|
||||
|
||||
# ---------- shared library (DLL) ----------
|
||||
add_library(${PROJECT_NAME} SHARED ${SOURCES} ${HEADERS})
|
||||
|
||||
# ---------- preprocessor definitions ----------
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||
ANSCUSTOMCODE_EXPORTS
|
||||
_WINDOWS
|
||||
_USRDLL
|
||||
WIN32_LEAN_AND_MEAN
|
||||
NOMINMAX
|
||||
$<$<CONFIG:Debug>:_DEBUG>
|
||||
$<$<CONFIG:Release>:NDEBUG>
|
||||
)
|
||||
|
||||
# ---------- include directories ----------
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
C:/Projects/ANLS/ANSLIB/ANSLIB
|
||||
C:/ANSLibs/opencv/include
|
||||
)
|
||||
|
||||
# ---------- library directories & linking ----------
|
||||
target_link_directories(${PROJECT_NAME} PRIVATE
|
||||
C:/ProgramData/ANSCENTER/Shared
|
||||
C:/ANSLibs/opencv/x64/vc17/lib
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
ANSLIB
|
||||
opencv_world4130
|
||||
)
|
||||
|
||||
# ---------- compiler options (MSVC) ----------
|
||||
if(MSVC)
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE
|
||||
/W3 # Warning level 3
|
||||
/sdl # SDL checks
|
||||
/permissive- # Conformance mode
|
||||
$<$<CONFIG:Release>:/O2 /Oi /GL> # Optimize + intrinsics + whole-program opt
|
||||
)
|
||||
|
||||
target_link_options(${PROJECT_NAME} PRIVATE
|
||||
$<$<CONFIG:Release>:/OPT:REF /OPT:ICF /LTCG> # Optimize refs, COMDAT folding, link-time codegen
|
||||
)
|
||||
endif()
|
||||
27
ANSCustomWeaponDetection/dllmain.cpp
Normal file
27
ANSCustomWeaponDetection/dllmain.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "pch.h"
|
||||
#include "ANSCustomCodeWeaponDetection.h"
|
||||
|
||||
|
||||
BOOL APIENTRY DllMain( HMODULE hModule,
|
||||
DWORD ul_reason_for_call,
|
||||
LPVOID lpReserved
|
||||
)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// expose the class to the outside world
|
||||
extern "C" __declspec(dllexport) IANSCustomClass* Create() {
|
||||
return new ANSCustomWD();
|
||||
}
|
||||
7
ANSCustomWeaponDetection/framework.h
Normal file
7
ANSCustomWeaponDetection/framework.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
#define NOMINMAX // Prevent windows.h from defining min/max macros
|
||||
// which break std::min / std::max (C2589)
|
||||
// Windows Header Files
|
||||
#include <windows.h>
|
||||
5
ANSCustomWeaponDetection/pch.cpp
Normal file
5
ANSCustomWeaponDetection/pch.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
// pch.cpp: source file corresponding to the pre-compiled header
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.
|
||||
13
ANSCustomWeaponDetection/pch.h
Normal file
13
ANSCustomWeaponDetection/pch.h
Normal file
@@ -0,0 +1,13 @@
|
||||
// pch.h: This is a precompiled header file.
|
||||
// Files listed below are compiled only once, improving build performance for future builds.
|
||||
// This also affects IntelliSense performance, including code completion and many code browsing features.
|
||||
// However, files listed here are ALL re-compiled if any one of them is updated between builds.
|
||||
// Do not add files here that you will be updating frequently as this negates the performance advantage.
|
||||
|
||||
#ifndef PCH_H
|
||||
#define PCH_H
|
||||
|
||||
// add headers that you want to pre-compile here
|
||||
#include "framework.h"
|
||||
|
||||
#endif //PCH_H
|
||||
Reference in New Issue
Block a user