/*! * \file PatternDetector.h * \date 2018/03/23 * * \author Lin, Chi * Contact: lin.chi@hzleaper.com * * * \note */ #ifndef __PatternDetector_h_ #define __PatternDetector_h_ #include "StdUtils.h" #include "CVUtils.h" #include "PatternDescriptor.h" #include "CyclopsLock.h" #include "DetectRoi.h" #include "CyclopsModules.h" struct PeakPatternParamPack; enum PeakAlgoAccLevel; /*! \brief Locate single or multiple instances of the trained pattern in the given image and ROI. * * 1) If you are using Cyclops as static library, * 1.1) and you want global factory to manage the trained pattern and detector for you, initialize the detector * via PatternDetector::getInstance(). * Example: * \code{.cpp} * PatternDetector::Ptr detectorPtr = PatternDetector::getInstance("detect sth"); * if (!detectorPtr->isTrained()) { // do training if not trained yet * bool ret = detectorPtr->train(trainImg); * if (!ret) return false; * } * detectorPtr->setScale(0); // no scale * detectorPtr->setAngle(15); // -15 ~ +15 * Vec4f pose; // result pose * float bestScore = detectorPtr->detectBest(detectImg, roiVertexes, pose); * * // delete it later * PatternDetector::deleteInstance("detect sth"); * \endcode * * 1.2) or, if you wish to manage the detector yourself: * \code{.cpp} * PatternDetector::Ptr detectorPtr = 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 * PatternDetector::Ptr pdPtr = GetModuleInstance("detect sth"); * // get for temporary usage * PatternDetector::Ptr pdPtr = GetModuleInstance(); * \endcode * * see SinglePatternTest and MultiPatternTest for unit test */ class PatternDetector : public ICyclopsModuleInstance { /*! \fn setACThres * Define minimum acceptable score, 0 to 100, default to 70, see also getACThres() * \fn getACThres * Get value of minimum acceptable score, see also setACThres() */ DECLARE_PARAMETER2(int, ACThres, 0, 100) /*! \fn setCoarseACThres * Define minimum acceptable score to filter result in early stage, default to 0, means we'll decide it by training. * Works when turn on strict-scoring, we'll use this score to filter candidates first * see also getCoarseACThres() * \fn getCoarseACThres * Get value of minimum accpetable score in coarse stage, see also setCoarseACThres() */ DECLARE_PARAMETER2(int, CoarseACThres, 0, 100) /*! \fn setAngle * Define angle range for matching search, 0 to 180, default to 15 means +-15, see also getAngle() * \fn getAngle * Get value of angle range for matching search, see also setAngle() */ DECLARE_PARAMETER2(int, Angle, 0, 180) /*! \fn setScale * Define scale range for matching search, 0 to 50, default to 1 means 99% to 101%, see also getScale() * \fn getScale * Get value of scale range for matching search, see also setScale() */ DECLARE_PARAMETER2(int, Scale, 0, 50) /*! \fn setOverlap * Define minimum overlap percentage of each pattern for matches, 1 to 100, default to 50 means 50%, see also getOverlapa() * \fn getOverlap * Get value of minimum overlap percentage of each pattern for matches, see also setOverlapa() */ DECLARE_PARAMETER2(int, Overlap, 1, 100) /*! \fn setIgnorePolarity * Define whether to ignore polarity for matches, default to false, see also getIgnorePolarity() * \fn getIgnorePolarity * Get whether to ignore polarity for matches, see also setIgnorePolarity() */ DECLARE_PARAMETER(bool, IgnorePolarity) /*! \fn setIgnoreMissing * Define whether to ignore missing part for scoring, default to true, see also getIgnoreMissing() * \fn getIgnoreMissing * Get value of whether to ignore missing part for scoring, see also setIgnoreMissing() */ DECLARE_PARAMETER(bool, IgnoreMissing) /*! \fn setXOffset * Define x offset of matches, default to 0, see also getXOffset() * \fn getXOffset * Get value of x offset of matches, see also setXOffset(), setOffset() */ DECLARE_PARAMETER(float, XOffset) /*! \fn setYOffset * Define y offset of matches, default to 0, see also getYOffset() * \fn getYOffset * Get value of y offset of matches, see also setYOffset(), setOffset() */ DECLARE_PARAMETER(float, YOffset) /*! \fn setThreadNum * Define maximum thread number we could use for detection, default to -1 for no limiatation, see also getThreadNum() * \fn getThreadNum * Get value of maximum thread number we could use for detection, see also setThreadNum() */ DECLARE_PARAMETER(int, ThreadNum) /*! \fn setDetailLevel * Define level of detail of trained pattern template, 0 to 1, default 0.5, higher for more detail, see also getDetailLevel() * \fn getDetailLevel * Get value of level of detail of trained pattern template, see also setDetailLevel() */ DECLARE_PARAMETER2(float, DetailLevel, 0, 1) /*! \fn setAccLevel * Define accuracy level of the algorithm, default to -1 means hardcoded defaults, see also getAccLevel() * Note: this value only works for PeakPattern. * \fn getAccLevel * Get value of accuracy level of the algorithm, see also setAccLevel() */ DECLARE_PARAMETER(int, AccLevel) /*! \fn setCoarseStepX * Define coarse stage along x axis (horizontally), 1 to 16, default to 0, * means use default values related to accuracy level and hardware concurrency, see also getCoarseStepX() * \fn getCoarseStepX * Get value of coarse stage along x axis (horizontally), see also setCoarseStepX() */ DECLARE_PARAMETER2(int, CoarseStepX, 0, 16) /*! \fn setCoarseStepY * Define coarse stage along y axis (vertically), 1 to 16, default to 0, * means use default values related to accuracy level and hardware concurrency, see also getCoarseStepY() * \fn getCoarseStepY * Get value of coarse stage along y axis (horizontally), see also setCoarseStepY() */ DECLARE_PARAMETER2(int, CoarseStepY, 0, 16) /*! \fn setEnableDetectMutex * Define whether to enable the mutex in detection, default to true, see also getEnableDetectMutex() * If turn it off, user need to be careful of the multi-threading problem, in case too much threads are created at one time. * \fn getEnableDetectMutex * Get value of whether to enable the mutex in detection, see also setEnableDetectMutex() */ DECLARE_PARAMETER(bool, EnableDetectMutex) /*! \fn setBias * Define whether detection will prefer 0-angle and 1-scale, the algorithm will start searching from biased value and stop early if score decreased. * default to false, see also getBias() * \fn getBias * Get value of whether detection will prefer 0-angle and 1-scale, see also setBias() */ DECLARE_PARAMETER(bool, Bias) /*! \fn setUseCache * Define whether to use cache to store some internal structure, default to true, see also getUseCache() * It will speed-up the detection while consume extra memory depending on your search angle and scale. Accuracy is not affected. * \fn getUseCache * Get value of whether to use cache to store some internal structure, see also setUseCache() */ DECLARE_PARAMETER(bool, UseCache) /*! \fn setStrictScore * Define whether to re-score the detect results on original source image, default to false, see also getStrictScore() * \fn getStrictScore * Get value of whether to re-score the detect results on original source image, see also setStrictScore() */ DECLARE_PARAMETER(bool, StrictScore) /*! \fn setGrayValueWeight * Define the weight of gray value score in detection, 0 ~ 100, default to 0, see also getGrayValueWeight() * \fn getGrayValueWeight * Get value of the weight of gray value score in detection, see also setGrayValueWeight() */ DECLARE_PARAMETER2(int, GrayValueWeight, 0, 100) public: PatternDetector() : mACThres(70), mCoarseACThres(0), mAngle(15), mScale(1), mOverlap(50), mIgnorePolarity(false), mIgnoreMissing(true), mXOffset(0), mYOffset(0), mThreadNum(0), mDetailLevel(0.5), mAccLevel(-1), mCoarseStepX(0), mCoarseStepY(0), mEnableDetectMutex(true), mBias(false), mUseCache(true), mStrictScore(false), mGrayValueWeight(0) {} virtual ~PatternDetector() {} /*! \fn serializeToMemory * Serialize the pattern detector into a in-memory string, see also deserializeFromMemory() * @param str used to take the output serialization result * @return true for succeed, false for fail * \fn serializeToFile * Serialize the pattern detector into a text file, see also deserializeFromFile() * @param filename file name (full path) where we will write the data * @return true for succeed, false for fail * \fn deserializeFromMemory * Deserialize the pattern detector from in-memory string, see also serializeToMemory() * @param str in-memory string * @return true for succeed, false for fail * \fn deserializeFromFile * Deserialize the pattern detector from a text file, see also serializeToFile() * @param filename file name (full path) where we will read the data * @return true for succeed, false for fail */ DECL_SERIALIZE_FUNCS //! Smart pointer to hold an instance of PatternDetector typedef std::shared_ptr Ptr; DECL_GET_INSTANCE(PatternDetector::Ptr) /*! Define x and y offset of matches, it's in the coordinate of template, * which means if the match result scales, the offsets scales; if the match result rotates, the offset rotates. */ void setOffset(float x, float y) { setXOffset(x); setYOffset(y); } /*! Train the detector with a provided image. * @param templateImg the template image for training, a gray image is preferred, otherwise we'll convert it to gray * @param pMask the mask for which pixels should be excluded * @return true for successfully trained, false for failure, say no key point is detected in the training image. */ virtual bool train(const Mat& templateImg, const Mat* pMask = nullptr); /** @overload */ virtual bool train(const Mat& templateImg, const vector& roi); // train the template within given ROI /*! Tell whether this detector is trained or not, see also train() * @return true for already trained */ virtual bool isTrained(); /* ! Reset the detector as never trained */ virtual void reset(); /*! Prune training result via provided key point list which should be removed from template * @param kpPruneList indexes of key points * @return true if succeed */ virtual bool prune(const Mat& templateImg, const vector& kpPruneList); /*! Get the trained center of trained template, it's the anchor point of rotation * @return center */ virtual Point2f templateCenter(); /*! Detect the best matching instance using the detector. * @param img input image for detection * @param bestPattern output pose of the best instance: x, y (without offset), angle (in degree), scale * @param allScores output, represent the matching score distribution of the entire image, * 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, Vec4f& bestPattern, Mat* allScores = nullptr, Mat* mask = nullptr ); /** @overload */ virtual float detectBest(const Mat& img, const vector& roi, Vec4f& bestPattern, Mat* allScores = nullptr ); /** @overload */ virtual float detectBest(const Mat& img, DetectRoi& droi, Vec4f& bestPattern, Mat* allScores = nullptr ); /*! Detect multiple best matching instances using the detector. * @param img input image for detection * @param bestPatterns output pose of several best instances: x, y (without offset), angle (in degree), scale * @param bestScores output scores of several best instances * @param allScores output, represent the matching score distribution of the entire image, * pass null if you don't want it * @param minCount expected minimum count of the found instances * @param maxCount expected maximum count of the found instances * @param mask input mask for exclude some pixel from detection * @return count of the found instances, it may below minCount but wouldn't exceed maxCount. return 0 if we found nothing good. */ virtual int detectMulti(const Mat& img, vector* bestPatterns, vector* bestScores, Mat* allScores = nullptr, int minCount = 0, int maxCount = 10, Mat* mask = nullptr ); /** @overload */ virtual int detectMulti(const Mat& img, const vector& roi, vector* bestPatterns, vector* bestScores, Mat* allScores, int minCount, int maxCount ); /** @overload */ virtual int detectMulti(const Mat& img, DetectRoi& droi, vector* bestPatterns, vector* bestScores, Mat* allScores, int minCount, int maxCount ); /*! Draw pattern on the provided image * @param img on what we should draw * @param pose the detected pose got from detectBest: x, y, angle (in degree), scale * @param withDir true if draw the key points' direction, false for singe points * @param factor to scale canvas for easy identification, default to 1.0 means no scale * @param colorPeak color for the key points, default to random color * @param colorDir color for the direction path, default to random color * @return canvas */ virtual Mat drawPattern(const Mat& img, const Vec4f& pose, bool withDir = false, float factor = 1.0, Scalar colorPeak = gDummyScalar, Scalar colorDir = gDummyScalar); /*! Draw pattern on a transparent canvas, align to the center, without scale * @param pose the detected pose got from detectBest: x, y, angle (in degree), scale * @param withDir true if draw the key points' direction, false for singe points * @param colorPeak color for the key points, default to random color * @param colorDir color for the direction path, default to random color * @return canvas */ virtual Mat drawPattern(const Vec4f& pose, bool withDir = false, Scalar colorPeak = gDummyScalar, Scalar colorDir = gDummyScalar); /*! Draw pattern on a transparent canvas, with provided width and height, and provided shift, without scale * @param pose the detected pose got from detectBest: x, y, angle (in degree), scale * @param w width of the result canvas * @param h height of the result canvas * @param shifts shifts of the instance position * @param withDir true if draw the key points' direction, false for singe points * @param colorPeak color for the key points, default to random color * @param colorDir color for the direction path, default to random color * @return canvas */ virtual Mat drawPattern(const Vec4f& pose, int w, int h, Point2f shifts, bool withDir = false, Scalar colorPeak = gDummyScalar, Scalar colorDir = gDummyScalar); /*! Draw patterns on the provided image * @param img on what we should draw * @param poses the detected poses got from detectMulti: x, y, angle (in degree), scale * @param withDir true if draw the key points' direction, false for singe points * @param factor to scale canvas for easy identification, default to 1.0 means no scale * @param colorPeak color for the key points, default to random color * @param colorDir color for the direction path, default to random color * @return canvas */ virtual Mat drawPatterns(const Mat& img, const vector& poses, bool withDir = false, float factor = 1.0, Scalar colorPeak = gDummyScalar, Scalar colorDir = gDummyScalar); /*! Draw patterns on a transparent canvas, with provided width and height, and provided shift, without scale * @param poses the detected pose got from detectMulti: x, y, angle (in degree), scale * @param w width of the result canvas * @param h height of the result canvas * @param shifts shifts of the instance position * @param withDir true if draw the key points' direction, false for singe points * @param colorPeak color for the key points, default to random color * @param colorDir color for the direction path, default to random color * @return canvas */ virtual Mat drawPatterns(const vector& poses, int w, int h, Point2f shifts, bool withDir = false, Scalar colorPeak = gDummyScalar, Scalar colorDir = gDummyScalar); /*! Represent the pattern of provided pose with patches of lines showing couture's direction * @param pose the detected pose got from detectMulti or detectBest: x, y, angle (in degree), scale * @param dirLen individual line's length, 0 ~ 5.0, negative value means we'll get an automatic value according to the distribution of template's key points. */ virtual std::vector getPatternAsLines(const Vec4f& pose, float dirLen = 0); /*! Represent the pattern of provided pose with key points * @param pose the detected pose got from detectMulti or detectBest: x, y, angle (in degree), scale * @return coarseKps return the key points: x, y (int original image size), and angle */ virtual std::vector getPatternAsPoints(const Vec4f& pose); /*! Generate the required position applied the offset setting * @param result the detected pose got from detectBest or detectMulti. * @return position applied the offset setting */ virtual Point2f getOffsetCenter(const Vec4f& result); private: void genPeakPattenPack(PeakPatternParamPack& pack, PeakAlgoAccLevel defaultAccLevel); virtual bool serialize(FileStorage& fs); virtual bool deserialize(const FileNode& fs); private: PatternDescriptor::Ptr mpTemplateDesc; CyclopsLock mDescLock; static CyclopsLock mDetectMutex; }; #endif // PatternDetector_h_