You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

388 lines
13 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*! \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