/*! \file ImageCompareModel.h Copyright (C) 2014 Hangzhou Leaper. Created by bang.jin at 2017/02/04. */ #pragma once #include #include #include #include #include #include #include "MultiScaleObj.h" using std::set; using std::map; using std::fstream; using std::string; using namespace cv; class MultiScaleImageCompareModel; class ImageCompareModel { public: class RotateMatchResult { public: Mat mBestRImg; double mBestAngle; }; class RotateData { public: RotateData() {}; RotateData(const Mat &img, Point pt, double dAngleStep, int size) : mImgSrc(img) , mStartAngle(0) , mEndAngle(360) { init(img, pt, dAngleStep, size); }; void init(const Mat& img, Point pt, double dAngleStep, int size) { mImgSrc = img; mStartAngle = 0; mEndAngle = 360.0; mRImgVec.clear(); mRImgVec.resize(size); mDisValVec.clear(); mDisValVec.resize(size, FLT_MAX); mCenter = pt; mAngleStep = dAngleStep; } double angle(int index) { return index*mAngleStep + mStartAngle; } double bestAngle() { if (mDisValVec.empty()) { return -DBL_MAX; } size_t bestIndex = min_element(mDisValVec.begin(), mDisValVec.end()) - mDisValVec.begin(); double bestAngle = angle(bestIndex); return bestAngle; } Mat bestRImg() { if (mRImgVec.empty()) { return Mat(); } size_t bestIndex = min_element(mDisValVec.begin(), mDisValVec.end()) - mDisValVec.begin(); return mRImgVec[bestIndex]; } double mAngleStep; Mat mImgSrc; Point mCenter; vector mRImgVec; vector mDisValVec; float mStartAngle, mEndAngle; }; public: static cv::Point2f refineCircleCen(const Mat& img, Point2f cen); static Mat genWeightImage(const Mat& img, Point2f center, float innerR = -1, float outterR = -1); static void selfRotationSimilarityFeature(const Mat& img, vector& vec, Point2f cen = Point2f(-1, -1)); static Mat genMask(const Mat& img, Point2f center, float innerR = -1, float outterR = -1, int type = CV_32FC1); static void genCandidateRepeatNums(set& canNums); static int computeRepeatNum(const Mat& img); static int recognizeLowerExtremes(const Mat& vec, set canNums, int extremeRefineRange = 5, vector *pExtemeIdxVec = 0); static int recognizeRepeatedLocalExtremas(const Mat& vec, const set& canNums, int extremeRefineRange = 5, vector *pExtremeIdxVec = 0); static void genUniformSepIdx(int num, int startIdx, int endIdx, vector& cenVec); static void genAngleRanges(vector cenVec, vector& rangeVec, int localRange); static void genCandidateAngleRanges(vector disVec, float angleStep, vector& rangeVec); static void genCandidateAngleRanges(const Mat& disMat, float angleStep, vector& rangeVec, float rangeScale = 1); static void selfRotateMin(const Mat& img, Mat& weightMat, int repeatNum); public: ImageCompareModel() : mMatchValScale(1.0), mTargetMeanVal(127), mTargetStddevVal(50), mHighlightsThreshold(256), mRepeatNum(0), mName("unnamed"), mDisThre(DBL_MAX), mTrueSampleDisMin(DBL_MAX), mTrueSampleDisMax(DBL_MIN), mTrueSampleDisMean(-1), mTrueSampleDisStddev(-1), mIsEnableCache(false), mpMultiScaleModel(NULL), meanDiameter(0) {}; ImageCompareModel(const ImageCompareModel& model) : mAlignBaseImg(model.getBaseImg().clone()) , mWeightMat(model.getWeightMat().clone()) , mMatchValScale(model.getMatchValScale()) { genMask(); } ~ImageCompareModel() {}; void printInfo(); void saveImages(string dirPath); bool save2file(string filePath); bool readFromFile(string filePath); void operator = (const ImageCompareModel& model) { mAlignBaseImg = model.getBaseImg().clone(); mWeightMat = model.getWeightMat().clone(); mMatchValScale = model.getMatchValScale(); mTargetMeanVal = model.getTargetMeanVal(); mTargetStddevVal = model.getTargetStddevVal(); mName = model.getName(); mTrueSampleDisStddev = model.getDisStdDev(); mTrueSampleDisMean = model.getDisMean(); mTrueSampleDisMax = model.getDisMax(); mTrueSampleDisMin = model.getDisMin(); meanDiameter = model.getMeanDiamter(); } void genMask(); void preProcessImage(Mat& img) const; void preProcessImage(Mat& img, const Mat& mask, double dstMean, double dstStddev, int highlightsThreshold) const; void preProcessImage(Mat& img, const Mat& mask, const Mat& weightMat, double dstMean, double dstStddev, int highlightsThreshold) const; void preProcessImage0(Mat& img) const; void weightOptimization(const vector& falseSamples); //! 将一个图像和标准图mBaseImg进行旋转匹配,再相减求得差异值 /*! \param Mat img 输入图像 \param Mat * pRImg 输入图像的旋转匹配结果 \param bool isFilterSize 是否根据图像尺寸过滤 \return double 差异值 \see compare(Mat, Mat, Mat*, Mat*) */ double compare(Mat img, Mat* pRImg = NULL, int levelNum = 1, bool isFilterSize = true, int flag = 0); //! 训练得到标准图像mBaseImg /*! \param const vector & imgVec 属于同类的训练样本图 \return void */ void train(const vector& imgVec); void calculateAllParams(const vector& imgVec); void trueSampleWeightRecon(const vector& resizedVec, vector rmrVec); void trueWeightRecon(const Mat& rImg, const Mat& baseImg); void weightMapping(const Mat& weight, double maxVal); //void falseWeightmapping(Mat &img); double descendFunction(double pixVal, double maxVal); double penltyCoeff(double matchedVal, int diameterMean, int targetDiameter) const; //! 根据负样本计算差异阈值,用于判断任意一个新的图像是否属于该类别。 /*! \param const vector & imgVec 不属于该类的训练样本图 \return void */ void computeDisThre(const vector& imgVec, double* pMinDis = NULL); double filterTrainImage(const Mat &img); //! 并行计算rotate /*! \param int index 当前并行index \param void *p 数据指针 */ void parallelDetect(int index, void *p) const; //! 获取标准图像 /*! \return cv::Mat 标准图像 */ Mat getBaseImg() const { return mAlignBaseImg; } void setBaseImg(const Mat& img) { mAlignBaseImg = img; } Mat getWeightMat() const { return mWeightMat; } void setWeightMat(Mat val) { mWeightMat = val; } double getMatchValScale() const { return mMatchValScale; } void setMatchValScale(double val) { mMatchValScale = val; } int getTargetStddevVal() const { return mTargetStddevVal; } void setTargetStddevVal(int val) { mTargetStddevVal = val; } int getTargetMeanVal() const { return mTargetMeanVal; } void setTargetMeanVal(int val) { mTargetMeanVal = val; } int getRepeatNum() const { return mRepeatNum; } void setRepeatNum(int val) { mRepeatNum = val; } string getName() const { return mName; } void setName(string val) { mName = val; } double getDisStdDev() const { return mTrueSampleDisStddev; } double getDisMean() const { return mTrueSampleDisMean; } double getDisMin() const { return mTrueSampleDisMin; } double getDisMax() const { return mTrueSampleDisMax; } double getFalseSampleMinDis() const { return mFalseSampleMinDis; } double getMeanDiamter() const { return meanDiameter; } //! 获取最近一次执行readFromFile时输入的文件路径 /*! \return string 文件路径 */ string getFilePath() const { return mFilePath; } double getDisThre() const { return mDisThre; } void setDisThre(double val) { mDisThre = val; } //! 启用缓存。缓存会将每次调用compare接口输入的img参数的data指针作为key存储 // 差异值。 /*! \return void */ void setIsEnableCache(bool val) { mIsEnableCache = val; } bool getIsEnableCache() const { return mIsEnableCache; } void clearDisCache() { mDisCache.clear(); } ImageCompareModel* scale(float s); RotateMatchResult rotateMatch(const Mat& img, int levelNum = 1, float angleStep = 1.0, float startAngle = 0, float endAngle = 360) const; void rotateMatchData(const Mat& img, RotateData* pData, const vector& angleRangeVec, float angleStep, Mat& disMat) const; void rotateMatchData(const Mat& img, RotateData* pData, float angleStep = 1.0, float startAngle = 0, float endAngle = 360) const; void rotateMatchData(const Mat& img, RotateData* pData, float angleStep, const vector& angleRangeVec) const; cv::Mat getM8uMaskImg() const { return m8uMaskImg; } void setM8uMaskImg(cv::Mat val) { m8uMaskImg = val; } cv::Mat getM32fMaskImg() const { return m32fMaskImg; } void setM32fMaskImg(cv::Mat val) { m32fMaskImg = val; } int computeRepeatNum(); protected: //! (尚未完成)将两个图像分别和标准图mBaseImg进行旋转匹配,再相减求得差异值 /*! \param Mat img0 待比较的图像img0 \param Mat img1 带比较的图像img1 \param Mat * pRImg0 将img0和标准图mBaseImg旋转匹配的结果rimg0 \param Mat * pRImg1 将img1和标准图mBaseImg旋转匹配的结果rimg1 \return double 差异值,取值大小为mMatchValScale*norm(rimg0 - rimg1)/(cols*rows) */ double compare(Mat img0, Mat img1, Mat* pRImg0 = NULL, Mat* pRImg1 = NULL); void setFilePath(string val) { mFilePath = val; } double genMatchValue(const Mat& rImg, const Mat& baseImg, const Mat& maskImg, int flag, int diameter) const; double genMatchValue(const Mat& dMat) const; double scaleMatchValue(double val) const; void weightReconstruction(const Mat& rImg, const Mat& baseImg, const Mat& maskImg, Mat &reConImg); void preWeightReconstruction(Mat img, Mat &reConImg); void setFalseSampleMinDis(double iVal) { mFalseSampleMinDis = iVal; } double mMatchValScale; int mTargetMeanVal, mTargetStddevVal, mHighlightsThreshold; int mRepeatNum; Mat mAlignBaseImg, mCompareBaseImg; Mat mWeightMat; Mat m32fMaskImg, m8uMaskImg; Mat mDisMat; Mat reConMat; Mat falseReConMat; double mDisThre; string mName; double falseMinDis; int meanDiameter; // some training data double mTrueSampleDisStddev, mTrueSampleDisMean, mTrueSampleDisMax, mTrueSampleDisMin; double mFalseSampleMinDis; // temp data string mFilePath; void initMultiScaleModel(); MultiScaleImageCompareModel* mpMultiScaleModel; // cache bool mIsEnableCache; map mDisCache; private: }; class ImageCompareModelInvoker : public cv::ParallelLoopBody { public: ImageCompareModelInvoker(const ImageCompareModel *pModel, void* pData) : m_pModel(pModel), m_pData(pData) {} private: const ImageCompareModel *m_pModel; void *m_pData; virtual void operator() (const cv::Range& range) const; };