|
|
/*! \file ImageCompareModel.h
|
|
|
|
|
|
Copyright (C) 2014 Hangzhou Leaper.
|
|
|
|
|
|
Created by bang.jin at 2017/02/04.
|
|
|
|
|
|
*/
|
|
|
|
|
|
#pragma once
|
|
|
#include <opencv2/opencv.hpp>
|
|
|
#include <opencv2/opencv_modules.hpp>
|
|
|
#include <string>
|
|
|
#include <map>
|
|
|
#include <set>
|
|
|
#include <fstream>
|
|
|
#include "MultiScaleObj.h"
|
|
|
#include "ICompareModel.h"
|
|
|
|
|
|
#include "ED.h"
|
|
|
#include "EDCircles.h"
|
|
|
#include "EDColor.h"
|
|
|
#include "EDCircles.h"
|
|
|
#include "EDLines.h"
|
|
|
|
|
|
/*#include "CircleDetector.h"*/
|
|
|
#define COLS_SCALE (float)(1200.0/ 416.0)
|
|
|
//#define COLS_SCALE (float)(1200.0/ 600.0)
|
|
|
using std::set;
|
|
|
using std::map;
|
|
|
using std::fstream;
|
|
|
using std::string;
|
|
|
using namespace cv;
|
|
|
|
|
|
class MultiScaleImageCompareModel;
|
|
|
class ImageCompareModel : public ICompareModel
|
|
|
{
|
|
|
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<Mat> mRImgVec;
|
|
|
vector<float> 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<float>& vec, Point2f cen = Point2f(-1, -1));
|
|
|
Mat genMask(const Mat& img, Point2f center, float innerR = -1, float outterR = -1, int type = CV_32FC1);
|
|
|
|
|
|
Mat genInsideMask(const Mat& img, Point2f center, float innerR = -1, float outterR = -1, int type = CV_32FC1);
|
|
|
|
|
|
static void genCandidateRepeatNums(set<unsigned int>& canNums);
|
|
|
virtual int computeRepeatNum(const Mat& img);
|
|
|
static int recognizeLowerExtremes(const Mat& vec, set<unsigned int> canNums, int extremeRefineRange = 5, vector<int> *pExtemeIdxVec = 0);
|
|
|
static int recognizeRepeatedLocalExtremas(const Mat& vec, const set<unsigned int>& canNums, int extremeRefineRange = 5, vector<int> *pExtremeIdxVec = 0);
|
|
|
static void genUniformSepIdx(int num, int startIdx, int endIdx, vector<int>& cenVec);
|
|
|
static void genAngleRanges(vector<int> cenVec, vector<Range>& rangeVec, int localRange);
|
|
|
static void genCandidateAngleRanges(vector<float> disVec, float angleStep, vector<Range>& rangeVec);
|
|
|
static void genCandidateAngleRanges(const Mat& disMat, float angleStep, vector<Range>& rangeVec, float rangeScale = 1);
|
|
|
static void selfRotateMin(const Mat& img, Mat& weightMat, int repeatNum);
|
|
|
float allocateInnerRadius(cv::Mat subImage);
|
|
|
float allocateRadius(cv::Mat subImage);
|
|
|
void resizeVecMat(vector<Mat> srcVec, vector<Mat> &dstVec);
|
|
|
void resizeMat(Mat src, Mat &dst);
|
|
|
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),
|
|
|
realWidth(0),
|
|
|
realHeight(0),
|
|
|
rInner(DBL_MAX),
|
|
|
mInnerCircleNum(0.0)
|
|
|
{};
|
|
|
ImageCompareModel(const ImageCompareModel& model)
|
|
|
: mAlignBaseImg(model.getBaseImg().clone())
|
|
|
, mWeightMat(model.getWeightMat().clone())
|
|
|
, mMatchValScale(model.getMatchValScale())
|
|
|
, mInSideBaseImg(model.getInsideBaseImg().clone())
|
|
|
, mInsideWeightMat(model.getInsideWeightImg().clone())
|
|
|
{
|
|
|
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();
|
|
|
mInSideBaseImg = model.getInsideBaseImg().clone();
|
|
|
mInsideWeightMat = model.getInsideWeightImg().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();
|
|
|
rInner = model.getInnerR();
|
|
|
mInnerCircleNum = model.getInnerCircleNum();
|
|
|
}
|
|
|
|
|
|
void genMask();
|
|
|
void genOutterMask();
|
|
|
void preProcessImage(Mat& img, Mat &insideImg) 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<Mat>& falseSamples);
|
|
|
//! <20><>һ<EFBFBD><D2BB>ͼ<EFBFBD><CDBC><EFBFBD>ͱ<EFBFBD>ͼmBaseImg<6D><67><EFBFBD><EFBFBD><EFBFBD><EFBFBD>תƥ<D7AA>䣬<EFBFBD><E4A3AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ò<EFBFBD><C3B2><EFBFBD>ֵ
|
|
|
/*!
|
|
|
\param Mat img <20><><EFBFBD><EFBFBD>ͼ<EFBFBD><CDBC>
|
|
|
\param Mat * pRImg <20><><EFBFBD><EFBFBD>ͼ<EFBFBD><CDBC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>תƥ<D7AA><C6A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
\param bool isFilterSize <20>Ƿ<EFBFBD><C7B7><EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD><CDBC><EFBFBD>ߴ<EFBFBD><DFB4><EFBFBD><EFBFBD><EFBFBD>
|
|
|
\return double <20><><EFBFBD><EFBFBD>ֵ
|
|
|
\see compare(Mat, Mat, Mat*, Mat*)
|
|
|
*/
|
|
|
double compare(Mat srcImage, Mat* pRImg = NULL, int levelNum = 1, bool isFilterSize = true, int flag = 0,
|
|
|
double md_diameter = 0, double md_height = 0);
|
|
|
|
|
|
//! ѵ<><D1B5><EFBFBD>õ<EFBFBD><C3B5><EFBFBD>ͼ<D7BC><CDBC>mBaseImg
|
|
|
/*!
|
|
|
\param const vector<Mat> & imgVec <20><><EFBFBD><EFBFBD>ͬ<EFBFBD><CDAC><EFBFBD><EFBFBD>ѵ<EFBFBD><D1B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ
|
|
|
\return void
|
|
|
*/
|
|
|
void train(const vector<Mat>& vec);
|
|
|
void calculateAllParams(const vector<Mat>& imgVec);
|
|
|
void trueSampleWeightRecon(const vector<Mat>& resizedVec, const vector<Mat>& resizedCenterVec, vector<RotateMatchResult> rmrVec,
|
|
|
vector<RotateMatchResult> rmrVecInside);
|
|
|
void trueWeightRecon(const Mat& rImg, const Mat& baseImg, const Mat& insideRimg, const Mat& insideBaseImg);
|
|
|
void weightMapping(const Mat& weight, double maxVal, const Mat& i_mData, double i_maxVal);
|
|
|
//void falseWeightmapping(Mat &img);
|
|
|
double descendFunction(double pixVal, double maxVal);
|
|
|
double penltyCoeff(double matchedVal, int diameterMean, int targetDiameterm, double modelDiamter, double modelHeight) const;
|
|
|
//! <20><><EFBFBD>ݸ<EFBFBD><DDB8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ж<EFBFBD><D0B6><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>µ<EFBFBD>ͼ<EFBFBD><CDBC><EFBFBD>Ƿ<EFBFBD><C7B7><EFBFBD><EFBFBD>ڸ<EFBFBD><DAB8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
/*!
|
|
|
\param const vector<Mat> & imgVec <20><><EFBFBD><EFBFBD><EFBFBD>ڸ<EFBFBD><DAB8><EFBFBD><EFBFBD><EFBFBD>ѵ<EFBFBD><D1B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ
|
|
|
\return void
|
|
|
*/
|
|
|
void computeDisThre(const vector<Mat>& imgVec, double* pMinDis = NULL, double md_diameter = 0, double md_height = 0);
|
|
|
|
|
|
double filterTrainImage(const Mat &img);
|
|
|
|
|
|
//! <20><><EFBFBD>м<EFBFBD><D0BC><EFBFBD>rotate
|
|
|
/*!
|
|
|
\param int index <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD>index
|
|
|
\param void *p <20><><EFBFBD><EFBFBD>ָ<EFBFBD><D6B8>
|
|
|
*/
|
|
|
void parallelDetect(int index, void *p) const;
|
|
|
|
|
|
|
|
|
//! <20><>ȡ<EFBFBD><C8A1>ͼ<D7BC><CDBC>
|
|
|
/*!
|
|
|
\return cv::Mat <20><>ͼ<D7BC><CDBC>
|
|
|
*/
|
|
|
Mat getInsideBaseImg() const { return mInSideBaseImg; }
|
|
|
Mat getInsideWeightImg() const { return mInsideWeightMat; }
|
|
|
float getInnerR() const { return rInner; }
|
|
|
double getInnerCircleNum() const { return mInnerCircleNum; }
|
|
|
|
|
|
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; }
|
|
|
//! <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>һ<EFBFBD><D2BB>ִ<EFBFBD><D6B4>readFromFileʱ<65><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>·<EFBFBD><C2B7>
|
|
|
/*!
|
|
|
\return string <20>ļ<EFBFBD>·<EFBFBD><C2B7>
|
|
|
*/
|
|
|
string getFilePath() const { return mFilePath; }
|
|
|
|
|
|
double getDisThre() const { return mDisThre; }
|
|
|
void setDisThre(double val) { mDisThre = val; }
|
|
|
|
|
|
//! <20><><EFBFBD>û<EFBFBD><C3BB>档<EFBFBD><E6A1A3><EFBFBD><EFBFBD><EFBFBD>Ὣÿ<E1BDAB>ε<EFBFBD><CEB5><EFBFBD>compare<72>ӿ<EFBFBD><D3BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>img<6D><67><EFBFBD><EFBFBD><EFBFBD><EFBFBD>dataָ<61><D6B8><EFBFBD><EFBFBD>Ϊkey<65>洢
|
|
|
// <20><><EFBFBD><EFBFBD>ֵ<EFBFBD><D6B5>
|
|
|
/*!
|
|
|
\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<Range>& 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<Range>& 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();
|
|
|
void setRealWidth(int val){ realWidth = val; }
|
|
|
void setRealHeight(int val){ realHeight = val; }
|
|
|
int getRealWidth(){ return realWidth; }
|
|
|
int getRealHeight(){ return realHeight; }
|
|
|
void printLog(string root, string log, double n = 0) const;
|
|
|
|
|
|
protected:
|
|
|
//! <20><><EFBFBD><EFBFBD>δ<EFBFBD><CEB4><EFBFBD>ɣ<EFBFBD><C9A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD><CDBC><EFBFBD>ֱ<EFBFBD><D6B1>ͱ<EFBFBD>ͼmBaseImg<6D><67><EFBFBD><EFBFBD><EFBFBD><EFBFBD>תƥ<D7AA>䣬<EFBFBD><E4A3AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ò<EFBFBD><C3B2><EFBFBD>ֵ
|
|
|
/*!
|
|
|
\param Mat img0 <20><><EFBFBD>Ƚϵ<C8BD>ͼ<EFBFBD><CDBC>img0
|
|
|
\param Mat img1 <20><><EFBFBD>Ƚϵ<C8BD>ͼ<EFBFBD><CDBC>img1
|
|
|
\param Mat * pRImg0 <20><>img0<67>ͱ<EFBFBD>ͼmBaseImg<6D><67>תƥ<D7AA><C6A5><EFBFBD>Ľ<EFBFBD><C4BD><EFBFBD>rimg0
|
|
|
\param Mat * pRImg1 <20><>img1<67>ͱ<EFBFBD>ͼmBaseImg<6D><67>תƥ<D7AA><C6A5><EFBFBD>Ľ<EFBFBD><C4BD><EFBFBD>rimg1
|
|
|
\return double <20><><EFBFBD><EFBFBD>ֵ<EFBFBD><D6B5>ȡֵ<C8A1><D6B5>СΪ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, double md_diameter, double md_height) 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;
|
|
|
Mat insideWeightRecon;
|
|
|
Mat innerTempl;
|
|
|
double mDisThre{ DBL_MAX };
|
|
|
string mName;
|
|
|
double falseMinDis;
|
|
|
int meanDiameter;
|
|
|
int realWidth;
|
|
|
int realHeight;
|
|
|
// INSIDE PART
|
|
|
Mat mInSideBaseImg, mInsideCompareBaseImg;
|
|
|
Mat mInsideWeightMat;
|
|
|
Mat m32fInsideMaskImg, m8uInsideMaskImg;
|
|
|
float rInner;
|
|
|
float x_aix;
|
|
|
float y_aix;
|
|
|
double mInnerCircleNum;
|
|
|
|
|
|
|
|
|
|
|
|
// some training data
|
|
|
double mTrueSampleDisStddev, mTrueSampleDisMean, mTrueSampleDisMax{DBL_MAX}, mTrueSampleDisMin;
|
|
|
double mFalseSampleMinDis;
|
|
|
|
|
|
// temp data
|
|
|
string mFilePath;
|
|
|
void initMultiScaleModel();
|
|
|
MultiScaleImageCompareModel* mpMultiScaleModel;
|
|
|
|
|
|
// cache
|
|
|
bool mIsEnableCache;
|
|
|
map<uchar*, double> mDisCache;
|
|
|
public:
|
|
|
static int m_parallelFlag;
|
|
|
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;
|
|
|
}; |