/*! * \file CircleDetector.h * \date 2018/08/06 * * \author Lin, Chi * Contact: lin.chi@hzleaper.com * * * \note */ #ifndef __CircleDetector_h_ #define __CircleDetector_h_ #include "StdUtils.h" #include "CVUtils.h" #include "CyclopsEnums.h" #include "CyclopsModules.h" struct PeakCircleParamPack; enum PeakAlgoAccLevel; /*! \brief Located single or multiple circles in the given image and ROI. * * We support several algorithms in this detector, choose one suitable for your scenario. * If you are pretty sure where the circle center is, use PeakCircle; * otherwise, you should use either HoughCircle or EDCircle depending on the performance and accuracy requirement. * * 1) If you are using Cyclops as static library, * 1.1) and you want global factory to manage the detector for you, initialize the detector * via CircleDetector::getInstance(). * Example: * \code{.cpp} * CircleDetector::Ptr cdPtr = CircleDetector::getInstance("detect sth"); * cdPtr->setAlgoType(CircleDetector::PeakCircle); // use peakcircle algorithm * cdPtr->setLineType(Polarity::Black2White); // set polarity * cdPtr->setLineBasis(Findby::Best); // set findby strategy * cdPtr->setRadii(90, 110); // set radii range * Vec3f bestCircle; // result circle * float bestScore = cdPtr->detectBest(img, Point2f(200, 300), // give estimated center point, and run the detection * bestCircle, nullptr); * * // delete it later * CircleDetector::deleteInstance("detect sth"); * \endcode * * 1.2) or, if you wish to manage the detector yourself: * \code{.cpp} * CircleDetector::Ptr ldPtr = std::make_shared(); // remember to hold the smart pointer * // balabala, same as above * // ... * \endcode * * 2) If you are using Cyclops as dynamic library, * initialize and manipulate the detector via CyclopsModules APIs. * Example: * \code{.cpp} * // get a long-term detector, and give it an unique name * CircleDetector::Ptr cdPtr = GetModuleInstance("detect sth"); * // get for temporary usage * CircleDetector::Ptr localCdPtr = GetModuleInstance(); * * // delete it later * DeleteModuleInstance("detect sth"); * \endcode * * see SingleCircleTest and MultiCircleTest for unit test */ class CircleDetector : public ICyclopsModuleInstance { public: enum CircleDetectAlgoType { /*! Classic algorithm using hough transform, similar as Matlab's imfindcircles */ HoughCircle = 0, /*! Approach based on sub-pixel peaks. Limitation: detect only almost concentric circles near center of image */ PeakCircle = 1, /*! Approach adopted from "EDCircles: A Real-time Parameter-free Circle Detector with a False Detection Control" \link http://ceng.anadolu.edu.tr/cv/edcircles/ */ EDCircle = 2, }; /*! \fn setPolarity * Define polarity of circle edge, Polarity::Black2White to Polarity::Either, default to Polarity::Either, see also getPolarity() * \fn getPolarity * Get value of polarity of circle edge, see also setPolarity() */ DECLARE_PARAMETER2(Polarity, Polarity, Polarity::Black2White, Polarity::Either) /*! \fn setFindBy * Define find-by stategy of detector, FindBy::Best to FindBy::Last, default to FindBy::Best, see also getFindBy() * \fn getFindBy * Get value of find-by stategy of detector, see also setFindBy() */ DECLARE_PARAMETER2(FindBy, FindBy, FindBy::Best, FindBy::All) /*! \fn setACThres * Define minimum acceptable score, 0 to 100, default to 0, see also getACThres() * \fn getACThres * Get value of minimum acceptable score, see also setACThres() */ DECLARE_PARAMETER2(int, ACThres, 0, 100) /*! \fn setEdgeWidth * Define edge width, default to 3, see also getEdgeWidth() * The value should match the real edge width, which means the range that the edge transit from black to white or vice versa. * \fn getEdgeWidth * Get value of edge width, see also setEdgeWidth() */ DECLARE_PARAMETER(int, EdgeWidth) /*! \fn setAlgoType * Define which algorithm we should use for detection, default to PeakCircle, see also getAlgoType() * \fn getAlgoType * Get which algorithm we are using for detection, see also setAlgoType() */ DECLARE_PARAMETER2(CircleDetectAlgoType, AlgoType, HoughCircle, PeakCircle) /*! \fn setRadii * Define the radii range [val1, val2] * \fn getRadiiStart * Get value of minimum circle radii * \fn getRadiiEnd * Get value of maximum circle radii */ DECLARE_PARAMETER_PAIR(unsigned int, Radii) /*! \fn setAccLevel * Define accuracy level of the algorithm, default to -1 means hardcoded defaults, see also getAccLevel() * Note: this value only works for PeakCircle. * \fn getAccLevel * Get value of accuracy level of the algorithm, see also setAccLevel() */ DECLARE_PARAMETER(int, AccLevel) /*! \fn setNormBy * Define how to normalized scores, default to NormBy::RegionMax, see also getNormBy() * \fn getNormBy * Get value of how to normalized scores, see also setNormBy() */ DECLARE_PARAMETER(NormBy, NormBy) public: CircleDetector(Polarity polarity, FindBy findby, CircleDetectAlgoType algoType) : mPolarity(polarity), mFindBy(findby), mAlgoType(algoType), mACThres(0), mEdgeWidth(3), mAccLevel(-1), mNormBy(NormBy::RegionMax) {} CircleDetector() : mPolarity(Polarity::Either), mFindBy(FindBy::Best), mAlgoType(PeakCircle), mACThres(0), mEdgeWidth(3), mAccLevel(-1), mNormBy(NormBy::RegionMax) {} virtual ~CircleDetector() {} //! Smart pointer to hold an instance of CircleDetector typedef std::shared_ptr Ptr; DECL_GET_INSTANCE(CircleDetector::Ptr) /*! Detect the best single circle edge using the detector * @param img input image for detection * @param bestCircle output circle: center.x, center.y, radii * @param allScores output, represent the score distribution among defined radii range, * pass null if you don't want it * @param mask input mask for exclude some pixel from detection * @return score of the best one, 0 if we found nothing good */ virtual float detectBest(const Mat& img, Vec3f& bestCircle, vector* allScores = nullptr, Mat* mask = nullptr); /** @overload */ virtual float detectBest(const Mat& img, const Point2f& estCenter, Vec3f& bestCircle, vector* allScores = nullptr); /*! Detect multiple circle edge using the detector * @param img input image for detection * @param bestCircles output circles: center.x, center.y, radii * @param bestScoresoutput scores of several best circles * @param allScores output, represent the score distribution among defined radii range, * pass null if you don't want it * @param minCount minimum number of circles there should be * @param maxCount maximum number of circles there should be * @param mask input mask for exclude some pixel from detection * @return real number of lines found in the image, it may exceed the provided limitation */ virtual int detectMulti(const Mat& img, vector* bestCircles, vector* bestScores, vector* allScores = nullptr, int minCount = 0, int maxCount = 10, Mat* mask = nullptr); /** @overload */ virtual int detectMulti(const Mat& img, const Point2f& estCenter, vector* bestCircles, vector* bestScores, vector* allScores = nullptr, int minCount = 0, int maxCount = 10); private: void genPeakCircleParamPack(PeakCircleParamPack& pack, PeakAlgoAccLevel defaultAccLevel); }; #endif // CircleDetector_h_