|
|
/*! \file ImageCompareModel.h
|
|
|
|
|
|
Copyright (C) 2014 Hangzhou Leaper.
|
|
|
|
|
|
Created by bang.jin at 2017/02/04.
|
|
|
|
|
|
*/
|
|
|
|
|
|
#ifndef _IMAGECOMPAREMODEL_H_
|
|
|
#define _IMAGECOMPAREMODEL_H_
|
|
|
|
|
|
#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"
|
|
|
|
|
|
#define COLS_SCALE (float)(1200.0/ 416.0)
|
|
|
//#define COLS_SCALE (float)(1.5)
|
|
|
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 weightOptimization(const vector<Mat>& falseSamples);
|
|
|
//! 将一个图像和标准图mBaseImg进行旋转匹配,再相减求得差异值
|
|
|
/*!
|
|
|
\param Mat img 输入图像
|
|
|
\param Mat * pRImg 输入图像的旋转匹配结果
|
|
|
\param bool isFilterSize 是否根据图像尺寸过滤
|
|
|
\return double 差异值
|
|
|
\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);
|
|
|
|
|
|
//! 训练得到标准图像mBaseImg
|
|
|
/*!
|
|
|
\param const vector<Mat> & imgVec 属于同类的训练样本图
|
|
|
\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;
|
|
|
//! 根据负样本计算差异阈值,用于判断任意一个新的图像是否属于该类别。
|
|
|
/*!
|
|
|
\param const vector<Mat> & imgVec 不属于该类的训练样本图
|
|
|
\return void
|
|
|
*/
|
|
|
void computeDisThre(const vector<Mat>& imgVec, double* pMinDis = NULL, double md_diameter = 0, double md_height = 0);
|
|
|
|
|
|
double filterTrainImage(const Mat &img);
|
|
|
|
|
|
//! 并行计算rotate
|
|
|
/*!
|
|
|
\param int index 当前并行index
|
|
|
\param void *p 数据指针
|
|
|
*/
|
|
|
void parallelDetect(int index, void *p) const;
|
|
|
|
|
|
|
|
|
//! 获取标准图像
|
|
|
/*!
|
|
|
\return cv::Mat 标准图像
|
|
|
*/
|
|
|
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; }
|
|
|
//! 获取最近一次执行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<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:
|
|
|
//! (尚未完成)将两个图像分别和标准图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, double md_diameter, double md_height) const;
|
|
|
double genMatchValue(const Mat& dMat) const;
|
|
|
double genMatchValue2(const Mat& rImg, const Mat& baseImg, const Mat& maskImg) 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;
|
|
|
};
|
|
|
|
|
|
#endif
|
|
|
|