#ifndef ANSFIRENSMOKE_H #define ANSFIRENSMOKE_H #pragma once #include "ANSEngineCommon.h" #include #include #include #include "ANSYOLOV10OVOD.h" #include "ANSYOLOV10RTOD.h" namespace ANSCENTER { class ImageProcessor { const int MAX_QUEUE_SIZE = 10; const int THRESHOLD = 8; public: // Add image to the queue and process if queue has 10 images std::vector addImageToQueue(const cv::Mat& image) { width = image.cols; height = image.rows; // Check if queue size is already 10, if so, remove the oldest image if (imageQueue.size() == MAX_QUEUE_SIZE) { imageQueue.pop(); } // Add the new image to the queue imageQueue.push(image.clone()); // Process images if we have exactly 10 images in the queue if (imageQueue.size() == MAX_QUEUE_SIZE) { return processQueueImages(); } // Return an empty vector if not yet ready to process return std::vector(); } private: int width, height; std::queue imageQueue; // Sum all images in the queue, create a mask, and return array of bounding rectangles std::vector processQueueImages() { // Initialize the sum image with zeros cv::Mat sumImage = cv::Mat::zeros(height, width, CV_32FC1); // Use a temporary queue to preserve the original images std::queue tempQueue = imageQueue; // Sum up all images in the queue while (!tempQueue.empty()) { cv::Mat img = tempQueue.front(); tempQueue.pop(); // Accumulate image pixels in sumImage sumImage += img; } // Threshold the summed image to create a binary mask cv::Mat mask; cv::threshold(sumImage, mask, THRESHOLD, 1, cv::THRESH_BINARY); // Convert mask to 8-bit to find contours mask.convertTo(mask, CV_8UC1); // Find contours on the mask std::vector> contours; cv::findContours(mask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); // Extract bounding rectangles for each contour std::vector boundingRects; for (const auto& contour : contours) { cv::Rect boundingRect = cv::boundingRect(contour); boundingRects.push_back(boundingRect); } return boundingRects; } }; class GLCM { public: explicit GLCM(const std::string& channel) : channel(channel) {} std::vector getGLCM(const cv::Mat& imageChannel, const std::vector& distances = { 5 }, const std::vector& angles = { 0.0f },// { 0.0f, CV_PI / 4.0f, CV_PI / 2.0f, 3.0f * CV_PI / 4.0f }, int levels = 256, bool symmetric = false, bool normed = false) { cv::Mat imageChannel8; imageChannel.convertTo(imageChannel8, CV_8U); return graycomatrix(imageChannel8, distances, angles, levels, symmetric, normed); } void getStockPropFromGLCM(const std::vector& glcmMatrices, const std::string& prop) { auto featureVector = graycoprops(glcmMatrices, prop); for (size_t i = 0; i < featureVector.size(); ++i) { props[prop + std::to_string(i + 1) + "_channel" + channel] = featureVector[i]; } } const std::map& getProps() const { return props; } private: std::string channel; std::map props; void normalizeGLCM(cv::Mat& glcm); std::vector graycomatrix(const cv::Mat& image, const std::vector& distances, const std::vector& angles, int levels = 256, bool symmetric = false, bool normed = false); std::vector graycoprops(const std::vector& glcmMatrices, const std::string& property); }; class HaralickFeatureExtractor { public: HaralickFeatureExtractor() { } ~HaralickFeatureExtractor() { } void Init(const std::string& colourSpace = "RGB", const std::vector& properties = { "energy","homogeneity","dissimilarity","contrast"}) { props = properties; for (char channel : colourSpace) { channels.push_back(std::string(1, channel)); } } cv::Mat resizeWithMinAspectRatio(const cv::Mat& inputImage, int minSize) { // Get original dimensions int originalWidth = inputImage.cols; int originalHeight = inputImage.rows; if ((originalWidth > minSize) && (originalHeight > minSize))return inputImage; // Calculate aspect ratio float aspectRatio = static_cast(originalWidth) / static_cast(originalHeight); // Calculate new dimensions based on the minimum size int newWidth, newHeight; if (originalWidth > originalHeight) { newHeight = minSize; newWidth = static_cast(minSize * aspectRatio); } else { newWidth = minSize; newHeight = static_cast(minSize / aspectRatio); } // Resize the image cv::Mat resizedImage; cv::resize(inputImage, resizedImage, cv::Size(newWidth, newHeight)); return resizedImage; } std::vector createFeatureVector(const cv::Mat& inputImage) { //0. Resize the image to a minimum size cv::Mat frame = resizeWithMinAspectRatio(inputImage, 200);// min is 20 //1. Convert to RGB format if necessary cv::Mat imageArray; if (inputImage.channels() == 3) { cv::cvtColor(frame, imageArray, cv::COLOR_BGR2RGB); // Convert BGR to RGB } else { return std::vector(); } // Split the image into individual channels std::vector imageChannels(3); cv::split(imageArray, imageChannels); // Initialize GLCM objects for each channel GLCM glcmChannel0("R"); // Red channel GLCM glcmChannel1("G"); // Green channel GLCM glcmChannel2("B"); // Blue channel // Calculate GLCM matrices for each channel auto glcmChannel0Matx = glcmChannel0.getGLCM(imageChannels[0]); auto glcmChannel1Matx = glcmChannel1.getGLCM(imageChannels[1]); auto glcmChannel2Matx = glcmChannel2.getGLCM(imageChannels[2]); // Compute properties for each channel for (const auto& prop : props) { glcmChannel0.getStockPropFromGLCM(glcmChannel0Matx, prop); glcmChannel1.getStockPropFromGLCM(glcmChannel1Matx, prop); glcmChannel2.getStockPropFromGLCM(glcmChannel2Matx, prop); } // Combine all properties into a single feature vector in the desired order std::vector featureVector; // Iterate over channels first (R, G, B) for (size_t channelIdx = 0; channelIdx < 3; ++channelIdx) { const GLCM* glcmChannel = nullptr; std::string channelName; // Select the appropriate channel if (channelIdx == 0) { glcmChannel = &glcmChannel0; channelName = "R"; } else if (channelIdx == 1) { glcmChannel = &glcmChannel1; channelName = "G"; } else if (channelIdx == 2) { glcmChannel = &glcmChannel2; channelName = "B"; } // Retrieve properties for the selected channel const auto& propsMap = glcmChannel->getProps(); // Iterate over properties for the current channel for (const auto& prop : props) { std::string key = prop + "1_channel" + channelName; if (propsMap.find(key) != propsMap.end()) { featureVector.push_back(propsMap.at(key)); } } } frame.release(); return featureVector; } private: std::vector props; std::vector channels; }; class ANSENGINE_API ANSFIRENSMOKE :public ANSODBase { public: virtual bool Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) override; virtual bool LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) override; virtual bool LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className,const std::string& modelFolder, std::string& labelMap)override; bool OptimizeModel(bool fp16, std::string& optimizedModelFolder); std::vector RunInference(const cv::Mat& input); std::vector RunInference(const cv::Mat& input, const std::string& camera_id); bool Destroy(); ~ANSFIRENSMOKE(); private: double _hsvThreshold; std::vector _smoke_colour; std::vector _fire_colour; std::string _classifierModelPath; ANSOYOLOV10OVOD _cpuObjectDetector; ANSYOLOV10RTOD _gpuObjectDetector; EngineType _engineType; ImageProcessor _smokeImageProcessor; ImageProcessor _fireImageProcessor; ANNHUBClassifier _annhub; const int SMOKE_THRESHOLD_SIZE = 4; const int HISTORY_SIZE = 30; const int MIN_MATCHES = 9; const float IOU_THRESHOLD = 0.4; const int MINIMUM_STABLE_DURATION = 30; const float MAX_MOVEMENT = 40.0; const float ALPHA = 0.7; cv::Rect _detectedArea;// Area where fire and smoke are detected cv::Rect _previousDetectedArea;// Area where fire and smoke are detected int _retainDetectedArea{ 0 }; bool _isFireNSmokeDetected{ false }; bool _classifierInitialized{ false }; HaralickFeatureExtractor _extractor; std::vector> _persistent_detections; std::deque> _stablization_queue; private: bool MajorityColourInFrame(const cv::Mat frame, Range range, float area_threshold = 0.8); bool MajorityColourInFrame(const cv::Mat frame, std::vector ranges, float area_threshold = 0.8); bool DetectFireNSmokeColourInFrame(const cv::Mat& frame, const cv::Rect bBox, const std::vector& ranges, float area_threshold); cv::Mat CreateBinaryImageWithRects(int width, int height, const std::vector& rects); cv::Point2f CalculateCentroid(const std::vector& group); cv::Point2f CalculateGroupCenter(const std::vector& group); std::vector CreateCoveringMaskRects(const cv::Mat& image, const std::vector& maskRects, float maxDistance); std::vector ProcecssDetection(const std::vector& detectedObjects, const std::string& camera_id, int threshold = 8); cv::Rect CreateMinimumSquareBoundingBox(const std::vector& detectedObjects, int minSize = 640); bool IsOverlap(const cv::Rect& target, const std::vector& rectArray); bool IsFireDetected(const cv::Mat image, const cv::Rect bBox); bool IsSmokeDetected(const cv::Mat image, const cv::Rect bBox); float calculateIOU(const cv::Rect& box_a, const cv::Rect& box_b); std::vector StablizeDetection(const std::vector& detectedObjects, const std::string& camera_id); }; } #endif