From ec24d1d1474e2acecd3dcf48df37d980e125141d Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Sat, 12 Sep 2015 20:14:13 -0400 Subject: [PATCH] Added support for OpenCL GPU acceleration --- config/openalpr.conf.in | 7 +- src/openalpr/CMakeLists.txt | 1 + src/openalpr/config.cpp | 2 + src/openalpr/config.h | 3 +- src/openalpr/detection/detectorfactory.cpp | 10 ++ src/openalpr/detection/detectorocl.cpp | 153 +++++++++++++++++++++ src/openalpr/detection/detectorocl.h | 59 ++++++++ 7 files changed, 231 insertions(+), 4 deletions(-) create mode 100644 src/openalpr/detection/detectorocl.cpp create mode 100644 src/openalpr/detection/detectorocl.h diff --git a/config/openalpr.conf.in b/config/openalpr.conf.in index 36800f2..cf3ee2a 100644 --- a/config/openalpr.conf.in +++ b/config/openalpr.conf.in @@ -36,9 +36,10 @@ max_detection_input_width = 1280 max_detection_input_height = 720 ; detector is the technique used to find license plate regions in an image. Value can be set to -; lbpcpu - default LBP-based detector uses the system CPU -; lbpgpu - LBP-based detector that uses Nvidia GPU to increase recognition speed. -; morphcpu - Experimental detector that detects white rectangles in an image. Does not require training. +; lbpcpu - default LBP-based detector uses the system CPU +; lbpgpu - LBP-based detector that uses Nvidia GPU to increase recognition speed. +; lbpopencl - LBP-based detector that uses OpenCL GPU to increase recognition speed. Requires OpenCV 3.0 +; morphcpu - Experimental detector that detects white rectangles in an image. Does not require training. detector = lbpcpu ; Bypasses plate detection. If this is set to 1, the library assumes that each region provided is a likely plate area. diff --git a/src/openalpr/CMakeLists.txt b/src/openalpr/CMakeLists.txt index e8556e6..1424ad7 100644 --- a/src/openalpr/CMakeLists.txt +++ b/src/openalpr/CMakeLists.txt @@ -8,6 +8,7 @@ set(lpr_source_files detection/detector.cpp detection/detectorcpu.cpp detection/detectorcuda.cpp + detection/detectorocl.cpp detection/detectorfactory.cpp detection/detectormorph.cpp licenseplatecandidate.cpp diff --git a/src/openalpr/config.cpp b/src/openalpr/config.cpp index da0a9ae..3cc3049 100644 --- a/src/openalpr/config.cpp +++ b/src/openalpr/config.cpp @@ -158,6 +158,8 @@ namespace alpr detector = DETECTOR_LBP_CPU; else if (detectorString.compare("lbpgpu") == 0) detector = DETECTOR_LBP_GPU; + else if (detectorString.compare("lbpopencl") == 0) + detector = DETECTOR_LBP_OPENCL; else if (detectorString.compare("morphcpu") == 0) detector = DETECTOR_MORPH_CPU; else diff --git a/src/openalpr/config.h b/src/openalpr/config.h index 59d6102..3475f33 100644 --- a/src/openalpr/config.h +++ b/src/openalpr/config.h @@ -146,7 +146,8 @@ namespace alpr { DETECTOR_LBP_CPU=0, DETECTOR_LBP_GPU=1, - DETECTOR_MORPH_CPU=2 + DETECTOR_MORPH_CPU=2, + DETECTOR_LBP_OPENCL=3 }; } diff --git a/src/openalpr/detection/detectorfactory.cpp b/src/openalpr/detection/detectorfactory.cpp index 5656e50..59ac3d2 100644 --- a/src/openalpr/detection/detectorfactory.cpp +++ b/src/openalpr/detection/detectorfactory.cpp @@ -1,5 +1,6 @@ #include "detectorfactory.h" #include "detectormorph.h" +#include "detectorocl.h" namespace alpr { @@ -21,6 +22,15 @@ namespace alpr return new DetectorCPU(config); #endif } + else if (config->detector == DETECTOR_LBP_OPENCL) + { + #if OPENCV_MAJOR_VERSION == 3 + return new DetectorOCL(config); + #else + std::cerr << "Error: OpenCL acceleration requires OpenCV 3.0. " << std::endl; + return new DetectorCPU(config); + #endif + } else if (config->detector == DETECTOR_MORPH_CPU) { return new DetectorMorph(config); diff --git a/src/openalpr/detection/detectorocl.cpp b/src/openalpr/detection/detectorocl.cpp new file mode 100644 index 0000000..8ee295b --- /dev/null +++ b/src/openalpr/detection/detectorocl.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2015 OpenALPR Technology, Inc. + * Open source Automated License Plate Recognition [http://www.openalpr.com] + * + * This file is part of OpenALPR. + * + * OpenALPR is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License + * version 3 as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . +*/ + +#include "detectorocl.h" + +#if OPENCV_MAJOR_VERSION == 3 + +using namespace cv; +using namespace std; + +namespace alpr +{ + + + DetectorOCL::DetectorOCL(Config* config) : Detector(config) { + + cv::ocl::setUseOpenCL(true); + + + + + if (!ocl::haveOpenCL()) + { + this->loaded = false; + cerr << "OpenCL not detected" << endl; + } + else if( this->plate_cascade.load( config->getCascadeRuntimeDir() + config->country + ".xml" ) ) + { + this->loaded = true; + } + else + { + this->loaded = false; + printf("--(!)Error loading CPU classifier\n"); + } + } + + + DetectorOCL::~DetectorOCL() { + } + + vector DetectorOCL::detect(Mat frame, std::vector regionsOfInterest) + { + + Mat frame_gray; + + if (frame.channels() > 2) + { + cvtColor( frame, frame_gray, CV_BGR2GRAY ); + } + else + { + frame.copyTo(frame_gray); + } + + + vector detectedRegions; + for (int i = 0; i < regionsOfInterest.size(); i++) + { + // Sanity check. If roi width or height is less than minimum possible plate size, + // then skip it + if ((regionsOfInterest[i].width < config->minPlateSizeWidthPx) || + (regionsOfInterest[i].height < config->minPlateSizeHeightPx)) + continue; + + Mat cropped = frame_gray(regionsOfInterest[i]); + vector subRegions = doCascade(cropped, regionsOfInterest[i].x, regionsOfInterest[i].y); + + for (int j = 0; j < subRegions.size(); j++) + detectedRegions.push_back(subRegions[j]); + } + return detectedRegions; + } + + vector DetectorOCL::doCascade(Mat orig_frame, int offset_x, int offset_y) + { + + int w = orig_frame.size().width; + int h = orig_frame.size().height; + + float scale_factor = computeScaleFactor(w, h); + + UMat openclFrame; + orig_frame.copyTo(openclFrame); + + vector plates; + + equalizeHist( openclFrame, openclFrame ); + + if (scale_factor != 1.0) + resize(openclFrame, openclFrame, Size(w * scale_factor, h * scale_factor)); + + //-- Detect plates + timespec startTime; + getTimeMonotonic(&startTime); + + float maxWidth = ((float) w) * (config->maxPlateWidthPercent / 100.0f) * scale_factor; + float maxHeight = ((float) h) * (config->maxPlateHeightPercent / 100.0f) * scale_factor; + Size minSize(config->minPlateSizeWidthPx * scale_factor, config->minPlateSizeHeightPx * scale_factor); + Size maxSize(maxWidth, maxHeight); + + plate_cascade.detectMultiScale( openclFrame, plates, config->detection_iteration_increase, config->detectionStrictness, + 0, + //0|CV_HAAR_SCALE_IMAGE, + minSize, maxSize ); + + + if (config->debugTiming) + { + timespec endTime; + getTimeMonotonic(&endTime); + cout << "LBP Time: " << diffclock(startTime, endTime) << "ms." << endl; + } + + for( unsigned int i = 0; i < plates.size(); i++ ) + { + plates[i].x = (plates[i].x / scale_factor); + plates[i].y = (plates[i].y / scale_factor); + plates[i].width = plates[i].width / scale_factor; + plates[i].height = plates[i].height / scale_factor; + + // Ensure that the rectangle isn't < 0 or > maxWidth/Height + plates[i] = expandRect(plates[i], 0, 0, w, h); + + plates[i].x = plates[i].x + offset_x; + plates[i].y = plates[i].y + offset_y; + } + + vector orderedRegions = aggregateRegions(plates); + + return orderedRegions; + + } + +} + +#endif \ No newline at end of file diff --git a/src/openalpr/detection/detectorocl.h b/src/openalpr/detection/detectorocl.h new file mode 100644 index 0000000..5daf784 --- /dev/null +++ b/src/openalpr/detection/detectorocl.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015 OpenALPR Technology, Inc. + * Open source Automated License Plate Recognition [http://www.openalpr.com] + * + * This file is part of OpenALPR. + * + * OpenALPR is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License + * version 3 as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . +*/ + +#ifndef OPENALPR_DETECTOROPENCL_H +#define OPENALPR_DETECTOROPENCL_H + +#include +#include +#include + +#if OPENCV_MAJOR_VERSION == 3 + +#include "opencv2/objdetect/objdetect.hpp" +#include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/core/core.hpp" +#include "opencv2/ml/ml.hpp" +#include "opencv2/core/ocl.hpp" + +#include "detector.h" + +namespace alpr +{ + + class DetectorOCL : public Detector { + public: + DetectorOCL(Config* config); + virtual ~DetectorOCL(); + + std::vector detect(cv::Mat frame, std::vector regionsOfInterest); + + private: + + cv::CascadeClassifier plate_cascade; + + std::vector doCascade(cv::Mat frame, int offset_x, int offset_y); + }; + +} + +#endif + +#endif /* OPENALPR_DETECTOROPENCL_H */ +