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.

1915 lines
54 KiB
C++

/*! \file ImageCompareModel.cpp
Copyright (C) 2014 Hangzhou Leaper.
Created by bang.jin at 2017/02/04.
*/
#include "ImageCompareModel.h"
#include "CVUtils.h"
#include "MultiScaleImageCompareModel.h"
#include <fstream>
#include <iostream>
//#define DEBUG_VIEW_INTERNAL_MAT
#define IMGCMP_STR_NAME "name"
#define IMGCMP_STR_IMAGE_COMPARE_MODEL "imageCompareModel"
#define IMGCMP_STR_ALIGN_BASE_IMAGE "alignBaseImage"
#define IMGCMP_STR_COMPARE_BASE_IMAGE "compareBaseImage"
#define IMGCMP_STR_WEIGHT_MAT "weightMat"
#define IMGCMP_STR_MATCH_VAL_SCALE "matchValScale"
#define IMGCMP_STR_TARGET_MEAN_VAL "targetMeanVal"
#define IMGCMP_STR_TARGET_STDDEV_VAL "targetStddevVal"
#define IMGCMP_STR_REPEAT_NUM "repeatNum"
#define IMGCMP_STR_TRUE_SAMPLE_DIS_MEAN "trueSampleDisMean"
#define IMGCMP_STR_TRUE_SAMPLE_DIS_STDDEV "trueSampleDisStddev"
#define IMGCMP_STR_TRUE_SAMPLE_DIS_MIN "trueSampleDisMin"
#define IMGCMP_STR_TRUE_SAMPLE_DIS_MAX "trueSampleDisMax"
#define IMGCMP_STR_DIS_THRE "disThre"
#define IMGCMP_STR_FALSE_SAMPLE_MIN_DIS "falseSampleMinDis"
#define IMGCMP_STR_TRAIN_DIS "trainDis"
#define IMGCMP_STR_SMALLEST_MATCH_DIS "falseMinDis"
#define IMGCMP_STR_AVER_DIAMETER "averageDiameter"
#define IMGCMP_STR_INSIDE_WEIGHT "insideWeightimage"
#define IMGCMP_STR_INSIDE_AVE_IMAGE "insideBaseimage"
#define IMGCMP_STR_INSIDE_COMP_AVE_IMAGE "insideCompareBaseImage"
#define IMGCMP_STR_INSIDE_RADIUS "insideRadius"
#define IMGCMP_STR_INSIDE_TEMPL "innerTempl"
#define IMGCMP_STR_INSIDE_CIRCLE_NUM "insideCircleNum"
// the number of circle
#define IMGCMP_CACHE_MAX_SIZE 100
#define MIN_CIRCLE_RADII 5
#define MAX_CIRCLE_RADII 110
#define MIN_INSIDE_RATIO 0.012
#define MAX_INSIDE_RATIO 0.023
int ImageCompareModel::m_parallelFlag = 0;
const float sF = 0.45;
Mat _EnhanImg_sharpen(Mat img) {
int mGainValue = 3;
float amount = mGainValue * 0.5f;
Mat blurred;
int mSmoothValue = 3;
GaussianBlur(img, blurred, Size(), mSmoothValue);
Mat lowContrastMask = abs(img - blurred) < 5;
Mat orResult = img * (1 + amount) + blurred * (-amount);
img.copyTo(orResult, lowContrastMask);
Mat openMat = orResult.clone();
return openMat;
}
cv::Point2f ImageCompareModel::refineCircleCen(const Mat& img, Point2f cen)
{
int r = 2;
vector<double> vec;
double minDiff = DBL_MAX;
Point2f bestCenter;
vector<float> bestDftVec;
for (float y = cen.y - r; y <= cen.y + r; y += 0.5)
{
for (float x = cen.x - r; x <= cen.x + r; x += 0.5)
{
Point2f newCenter(x, y);
vector<float> vec;
selfRotationSimilarityFeature(img, vec, newCenter);
vector<float> dftVec;
dft(vec, dftVec);
float diffVal = sum<float>(vec.begin(), vec.end());
vec.push_back(diffVal);
if (minDiff > diffVal)
{
minDiff = diffVal;
bestCenter = newCenter;
bestDftVec = dftVec;
}
}
}
return bestCenter;
}
cv::Mat ImageCompareModel::genWeightImage(const Mat& img, Point2f center,
float innerR /*= -1*/, float outterR /*= -1*/)
{
if (innerR == -1)
{
// default is 30
innerR = img.rows*0.175;
}
if (outterR == -1)
{
// default is max radius - 10
outterR = img.rows*0.475;
}
Mat weightImg;
weightImg.create(img.size(), CV_32FC1);
weightImg.setTo(0);
uchar* pData = weightImg.data;
for (int y = 0; y < weightImg.rows; y++)
{
float* pFData = (float*)weightImg.row(y).data;
for (int x = 0; x < weightImg.cols; x++)
{
float& val = pFData[x];
float dx = center.x - x;
float dy = center.y - y;
float r = sqrt(dx*dx + dy*dy);
if (r > outterR || r < innerR)
{
val = 0;
}
else
{
val = min(outterR - r, r - innerR);
}
}
}
return weightImg;
}
void ImageCompareModel::selfRotationSimilarityFeature(
const Mat& img, vector<float>& vec, Point2f center /*= Point2f(-1, -1)*/)
{
float angleStep = 1.0;
if (center.x < 0 || center.y < 0)
{
center = Point2f(img.cols / 2.0, img.rows / 2.0);
}
double bestVal = DBL_MAX;
Mat bestRImg;
Mat fImg;
img.convertTo(fImg, CV_32FC1);
Mat weightImg = genWeightImage(img, center);
fImg = fImg.mul(weightImg);
double sumVal = sum(weightImg).val[0];
Mat baseImg;
Mat t = getRotationMatrix2D(center, 1.0, 1.0);
Mat rImg;
warpAffine(fImg, rImg, t, fImg.size(), CV_INTER_CUBIC);
t = getRotationMatrix2D(center, -1.0, 1.0);
warpAffine(rImg, baseImg, t, rImg.size(), CV_INTER_CUBIC);
for (float a = 0; a <= 360.0; a += angleStep)
{
Mat t = getRotationMatrix2D(center, a, 1.0);
Mat rImg;
warpAffine(fImg, rImg, t, fImg.size(), CV_INTER_CUBIC);
Mat diffImg = baseImg - rImg;
//diffImg = diffImg.mul(weightImg);
double diffVal = norm(diffImg)/sumVal;
vec.push_back(diffVal);
}
}
cv::Mat ImageCompareModel::genMask(const Mat& img, Point2f center,
float innerR /*= -1*/, float outterR /*= -1*/, int type /*= CV_32FC1*/)
{
Mat mask(img.size(), CV_8UC1);
mask.setTo(0);
if (innerR == -1)
{
// default is 30
innerR = img.rows*0.178;
//innerR = img.rows;
}
if (outterR == -1)
{
// default is max radius - 10
outterR = img.rows*0.425;
//outterR = img.rows;
}
circle(mask, center, outterR, Scalar(255), -1);
circle(mask, center, innerR, Scalar(0), -1);
if (type != CV_8UC1)
{
converToType(mask, type);
mask /= 255;
}
return mask;
}
void ImageCompareModel::genMask()
{
m32fMaskImg = genMask(mAlignBaseImg, Point2f(mAlignBaseImg.cols / 2.0, mAlignBaseImg.rows / 2.0));
m8uMaskImg = genMask(mAlignBaseImg, Point2f(mAlignBaseImg.cols / 2.0, mAlignBaseImg.rows / 2.0), -1.0, -1.0, CV_8UC1);
m32fInsideMaskImg = genInsideMask(mInSideBaseImg, Point2f(mInSideBaseImg.cols / 2.0, mInSideBaseImg.rows / 2.0));
m8uInsideMaskImg = genInsideMask(mInSideBaseImg, Point2f(mInSideBaseImg.cols / 2.0, mInSideBaseImg.rows / 2.0), -1.0, -1.0, CV_8UC1);
}
void ImageCompareModel::printInfo()
{
}
void ImageCompareModel::saveImages(string dirPath)
{
}
bool ImageCompareModel::save2file(string filePath)
{
FileStorage fs(filePath, FileStorage::WRITE);
if (!fs.isOpened())
{
return false;
}
fs << IMGCMP_STR_NAME << mName <<
IMGCMP_STR_ALIGN_BASE_IMAGE << mAlignBaseImg <<
IMGCMP_STR_COMPARE_BASE_IMAGE << mCompareBaseImg <<
IMGCMP_STR_WEIGHT_MAT << mWeightMat <<
IMGCMP_STR_INSIDE_AVE_IMAGE << mInSideBaseImg <<
IMGCMP_STR_INSIDE_COMP_AVE_IMAGE << mInsideCompareBaseImg <<
IMGCMP_STR_INSIDE_WEIGHT << mInsideWeightMat <<
IMGCMP_STR_INSIDE_TEMPL << innerTempl <<
IMGCMP_STR_MATCH_VAL_SCALE << mMatchValScale <<
IMGCMP_STR_TARGET_MEAN_VAL << mTargetMeanVal <<
IMGCMP_STR_TARGET_STDDEV_VAL << mTargetStddevVal <<
IMGCMP_STR_REPEAT_NUM << mRepeatNum <<
IMGCMP_STR_TRUE_SAMPLE_DIS_MEAN << mTrueSampleDisMean <<
IMGCMP_STR_TRUE_SAMPLE_DIS_STDDEV << mTrueSampleDisStddev <<
IMGCMP_STR_TRUE_SAMPLE_DIS_MIN << mTrueSampleDisMin <<
IMGCMP_STR_TRUE_SAMPLE_DIS_MAX << mTrueSampleDisMax <<
IMGCMP_STR_FALSE_SAMPLE_MIN_DIS << getFalseSampleMinDis() <<
IMGCMP_STR_DIS_THRE << mDisThre <<
IMGCMP_STR_TRAIN_DIS << mDisMat <<
IMGCMP_STR_AVER_DIAMETER << meanDiameter <<
IMGCMP_STR_INSIDE_RADIUS << rInner <<
IMGCMP_STR_INSIDE_CIRCLE_NUM << mInnerCircleNum;
setFilePath(filePath);
return true;
}
bool ImageCompareModel::readFromFile(string filePath)
{
try{
FileStorage fs(filePath, FileStorage::READ);
if (!fs.isOpened())
{
return false;
}
fs[IMGCMP_STR_ALIGN_BASE_IMAGE] >> mAlignBaseImg;
fs[IMGCMP_STR_COMPARE_BASE_IMAGE] >> mCompareBaseImg;
fs[IMGCMP_STR_WEIGHT_MAT] >> mWeightMat;
fs[IMGCMP_STR_INSIDE_AVE_IMAGE] >> mInSideBaseImg;
fs[IMGCMP_STR_INSIDE_COMP_AVE_IMAGE] >> mInsideCompareBaseImg;
fs[IMGCMP_STR_INSIDE_WEIGHT] >> mInsideWeightMat;
fs[IMGCMP_STR_INSIDE_TEMPL] >> innerTempl;
mMatchValScale = (double)fs[IMGCMP_STR_MATCH_VAL_SCALE];
FileNode fn = fs[IMGCMP_STR_TARGET_MEAN_VAL];
if (!fn.empty())
{
mTargetMeanVal = (int)fn;
}
fn = fs[IMGCMP_STR_TARGET_STDDEV_VAL];
if (!fn.empty())
{
mTargetStddevVal = (int)fn;
}
fn = fs[IMGCMP_STR_REPEAT_NUM];
if (!fn.empty())
{
mRepeatNum = (int)fn;
}
else
{
//mRepeatNum = computeRepeatNum();
}
fn = fs[IMGCMP_STR_NAME];
if (!fn.empty())
{
mName = (string)fn;
}
fn = fs[IMGCMP_STR_TRUE_SAMPLE_DIS_MEAN];
if (!fn.empty())
{
mTrueSampleDisMean = (double)fn;
}
fn = fs[IMGCMP_STR_TRUE_SAMPLE_DIS_STDDEV];
if (!fn.empty())
{
mTrueSampleDisStddev = (double)fn;
}
fn = fs[IMGCMP_STR_TRUE_SAMPLE_DIS_MIN];
if (!fn.empty())
{
mTrueSampleDisMin = (double)fn;
}
fn = fs[IMGCMP_STR_TRUE_SAMPLE_DIS_MAX];
if (!fn.empty())
{
mTrueSampleDisMax = (double)fn;
}
fn = fs[IMGCMP_STR_FALSE_SAMPLE_MIN_DIS];
if (!fn.empty())
{
setFalseSampleMinDis((double)fn);
}
fn = fs[IMGCMP_STR_DIS_THRE];
if (!fn.empty())
{
mDisThre = (double)fn;
}
fn = fs[IMGCMP_STR_AVER_DIAMETER];
if (!fn.empty())
{
meanDiameter = (int)fn;
}
fn = fs[IMGCMP_STR_INSIDE_RADIUS];
if (!fn.empty())
{
rInner = (float)fn;
}
fn = fs[IMGCMP_STR_INSIDE_CIRCLE_NUM];
if (!fn.empty())
{
mInnerCircleNum = (double)fn;
}
setFilePath(filePath);
genMask();
return true;
}
catch (std::exception &e) {
return false;
}
}
//图像预处理
void ImageCompareModel::preProcessImage(Mat& img, Mat &insideImg) const
{
preProcessImage(img, m8uMaskImg, mTargetMeanVal, mTargetStddevVal, mHighlightsThreshold);
if (insideImg.empty())
{
return;
}
preProcessImage(insideImg, m8uInsideMaskImg, mTargetMeanVal, mTargetStddevVal, mHighlightsThreshold);
}
void ImageCompareModel::preProcessImage(Mat& img, const Mat& mask, double tarMean, double tarStddev, int highlightsThreshold) const
{
if (img.channels() > 1)
{
img = getFirstChannel(img);
}
if (img.type() != CV_32FC1)
{
converToType(img, CV_32FC1);
}
//高斯模糊 处理噪点
Mat gaussImg;
GaussianBlur(img, gaussImg, Size(3, 3), 5.0);
img = gaussImg;
//膨胀 扩大掩膜模板
Mat dilatedMask;
Mat kernel = Mat::ones(Size(3, 3), CV_8UC1);
dilate(mask, dilatedMask, kernel);
Mat hightlightsMask = img < highlightsThreshold;
Mat imgMask = hightlightsMask & dilatedMask;
//计算图像像素均值 对检测图像做均衡
Scalar meanScalar, stddevScalar;
meanStdDev(img, meanScalar, stddevScalar, imgMask);
img = (img - meanScalar.val[0]) * tarStddev / stddevScalar.val[0] + tarMean;
//归一化处理图像 减少图像间差异
converToType(imgMask, CV_32FC1);
imgMask /= 255.0;
Mat imgNorm = cocentricNorm(img, Point2f(img.cols / 2.0, img.rows / 2.0), imgMask, 125);
img = imgNorm;
}
void ImageCompareModel::preProcessImage(Mat& img, const Mat& mask, const Mat& weightMat, double dstMean, double dstStddev, int highlightsThreshold) const
{
if (img.channels() > 1)
{
img = getFirstChannel(img);
}
if (img.type() != CV_32FC1)
{
converToType(img, CV_32FC1);
}
Mat gaussImg;
GaussianBlur(img, gaussImg, Size(3, 3), 5.0);
img = gaussImg;
Mat hightlightsMask = img < highlightsThreshold;
Mat imgMask = hightlightsMask & mask & (weightMat > 0);
Scalar meanScalar, stddevScalar;
meanStdDev(img, meanScalar, stddevScalar, imgMask);
img = (img - meanScalar.val[0]) * dstStddev / stddevScalar.val[0] + dstMean;
converToType(hightlightsMask, CV_32FC1);
Mat imgNorm = cocentricNorm(img, Point2f(img.cols / 2.0, img.rows / 2.0), weightMat.mul(hightlightsMask), 125);
img = imgNorm;
}
double ImageCompareModel::compare(Mat img0, Mat img1, Mat* pRImg0, Mat* pRImg1)
{
if (img0.size() != img1.size() && img0.size() != mAlignBaseImg.size())
{
return -1;
}
double bestMatchVal = DBL_MAX;
int bestAngle = -1;
Mat bestImg1;
double bestDD = 0;
double w = 0;
Point2f center((float)img1.cols / 2.0, (float)img1.rows / 2.0);
Mat rImg0 = rotateMatch(img0).mBestRImg;
Mat rImg1 = rotateMatch(img1).mBestRImg;
Mat dMat = rImg0 - rImg1;
converToType(dMat, CV_32FC1);
dMat = dMat.mul(mWeightMat);
if (pRImg0)
{
*pRImg0 = rImg0;
}
if (pRImg1)
{
*pRImg1 = rImg1;
}
return genMatchValue(dMat);
}
//匹配比较
double ImageCompareModel::compare(Mat srcImage, Mat* pRImg /*= NULL*/, int levelNum /*= 1*/,
bool isFilterSize /*= true*/, int flag, double md_diameter, double md_height)
{
if (mIsEnableCache)
{
auto cacheIter = mDisCache.find(srcImage.data);
if (cacheIter != mDisCache.end())
{
return cacheIter->second;
}
}
//srcImage = _EnhanImg_sharpen(srcImage);
int x_Axis = srcImage.cols / 2 - MAX_CIRCLE_RADII;
int y_Axis = srcImage.rows / 2 - MAX_CIRCLE_RADII;
int r_Axis = 2 * MAX_CIRCLE_RADII;
// int inR = srcImage.cols / 2 * sF;
// int x_Axis = srcImage.cols / 2 - inR;
// int y_Axis = srcImage.rows / 2 - inR;
// int r_Axis = 2 * inR;
if (x_Axis <= 0 || y_Axis <= 0)
return DBL_MAX;
if (r_Axis <= 0)
return DBL_MAX;
if (r_Axis >= srcImage.cols || r_Axis >= srcImage.rows)
return DBL_MAX;
if (srcImage.cols < (srcImage.cols / 2 + MAX_CIRCLE_RADII) || srcImage.rows < (srcImage.rows / 2 + MAX_CIRCLE_RADII))
return DBL_MAX;
Rect rect(x_Axis, y_Axis, r_Axis, r_Axis);
Mat srcCenterMat;
srcImage(rect).copyTo(srcCenterMat);
Mat img, centerMat;
resizeMat(srcImage, img);
resizeMat(srcCenterMat, centerMat);
Mat rawImg = img.clone();
int nRadiusDiff = 15;//过滤图像尺寸和模板尺寸不匹配的动作
if (mAlignBaseImg.size() != img.size() || mInSideBaseImg.size() !=centerMat.size())
{
if (isFilterSize
&& abs(mAlignBaseImg.size().width - img.size().width) > nRadiusDiff
|| abs(mInSideBaseImg.size().width - centerMat.size().width) > 3)
{
return DBL_MAX;
}
resize(img, img, mAlignBaseImg.size());
resize(centerMat, centerMat, mInSideBaseImg.size());
}
//抠出需要检测的图像
Mat matchImg = img.clone();//辐条部位
Mat matchInsideImg = centerMat.clone();//圆心部位
preProcessImage(matchImg, matchInsideImg);//图像预处理
// Mat s = matchImg / 255.0;
// imwrite("11.png", s);
if (!mpMultiScaleModel)
{
initMultiScaleModel();
}
m_parallelFlag = 0;
RotateMatchResult rmr = rotateMatch(matchImg, levelNum);//旋转图像,计算最佳旋转角度
Mat rImg = rmr.mBestRImg;
m_parallelFlag = 1;
RotateMatchResult rmmInside = rotateMatch(matchInsideImg, 1);//旋转图像,计算最佳旋转角度
Mat rInsideImg = rmmInside.mBestRImg;
if (rImg.empty() || rInsideImg.empty())
{
return DBL_MAX;
}
double bestAngle = rmr.mBestAngle;
Mat cmpImg = rotateImage(img, Point2f(img.cols / 2.0, img.rows / 2.0), bestAngle);//旋转图像
// remove highlights 去除高光影响
Mat hightlightsMask = cmpImg < mHighlightsThreshold;
converToType(hightlightsMask, CV_32FC1);
hightlightsMask /= 255.0;
Mat unifiedMask = m32fMaskImg.mul(hightlightsMask).mul(mWeightMat);
preProcessImage(cmpImg, m8uMaskImg, mWeightMat, mTargetMeanVal, mTargetStddevVal, mHighlightsThreshold);
cmpImg.setTo(0, cmpImg < 0);
converToType(cmpImg, CV_32FC1);
normSectors_tarImg(cmpImg, unifiedMask, imgCen(cmpImg), 1, mCompareBaseImg);
double bestInsideAngle = rmmInside.mBestAngle;
Mat camInsideMat = rotateImage(centerMat, Point2f(centerMat.cols / 2.0, centerMat.rows / 2.0), bestInsideAngle);
Mat highLightInsideMask = camInsideMat < mHighlightsThreshold;
converToType(highLightInsideMask, CV_32FC1);
highLightInsideMask /= 255.0;
Mat unifiedInsideMask = m32fInsideMaskImg.mul(highLightInsideMask).mul(mInsideWeightMat);
preProcessImage(camInsideMat, m8uInsideMaskImg, mInsideWeightMat, mTargetMeanVal, mTargetStddevVal, mHighlightsThreshold);
Mat vcamInside = camInsideMat / 255.0;
camInsideMat.setTo(0, camInsideMat < 0);
//mInsideCompareBaseImg.setTo(0, mInsideCompareBaseImg < 0);
normSectors_tarImg(camInsideMat, unifiedInsideMask, imgCen(camInsideMat), 1, mInsideCompareBaseImg);
mInsideCompareBaseImg.setTo(0, mInsideCompareBaseImg < 0);
cmpImg.setTo(0, cmpImg < 10);
//camInsideMat.setTo(0, camInsideMat < 10);
if (pRImg)
{
*pRImg = cmpImg;
}
m_parallelFlag = 0;
Mat s1 = cmpImg / 255.0;
Mat s2 = mCompareBaseImg / 255.0;
Mat s3 = unifiedMask / 255.0;
Mat s4 = rawImg / 255.0;
double ret = genMatchValue(cmpImg, mCompareBaseImg, unifiedMask, flag, rawImg.rows, md_diameter, md_height);//得出相似值
m_parallelFlag = 1;
double retInside = genMatchValue(camInsideMat, mInsideCompareBaseImg, unifiedInsideMask, 0, rawImg.rows, md_diameter, md_height);//得出相似值
ret = 0.6*ret + 0.4*retInside;
if (ret > mDisThre)
{
return DBL_MAX;
}
if (mIsEnableCache)
{
mDisCache[img.data] = ret;
if (mDisCache.size() > IMGCMP_CACHE_MAX_SIZE)
{
mDisCache.clear();
}
}
return ret;
}
void ImageCompareModel::train(const vector<Mat>& vec)
{
if (0 == vec.size()) {
return;
}
vector<Mat> centerMatVec;
vector<Mat> tmpVec;
const int vecSize = vec.size();
float insideCircleNumSum = 0;
for (int i = 0; i < vecSize; i++)
{
Mat originalMat = vec[i];
//originalMat = _EnhanImg_sharpen(originalMat);
Vec3f bestCircle;
Point2f p = Point2f(originalMat.cols / 2.0, originalMat.rows / 2.0);
float startX = originalMat.cols / 2.0 - MAX_CIRCLE_RADII;//找最大圆外径,固定直径
float startY = originalMat.rows / 2.0 - MAX_CIRCLE_RADII;
Rect rect(startX, startY, MAX_CIRCLE_RADII * 2, MAX_CIRCLE_RADII * 2);
// int inR = originalMat.cols / 2 * sF;
// int startX = originalMat.cols / 2.0 - inR;
// int startY = originalMat.rows / 2.0 - inR;
// int r_Axis = 2 * inR;
// Rect rect(startX, startY, r_Axis, r_Axis);
Mat origianlCenterMat;
originalMat(rect).copyTo(origianlCenterMat);
centerMatVec.push_back(origianlCenterMat);
tmpVec.push_back(vec[i]);
}
mInnerCircleNum = insideCircleNumSum / vecSize;
vector<Mat> scaledCenterVec;
resizeVecMat(centerMatVec, scaledCenterVec);
if (scaledCenterVec.size() <= 0)
return;
if (tmpVec.size() <= 0)
return;
vector<Mat> imgVec;
resizeVecMat(tmpVec, imgVec);
mAlignBaseImg = imgVec.front();
mInSideBaseImg = scaledCenterVec.front();
genMask();
preProcessImage(mAlignBaseImg, mInSideBaseImg);//预处理 光照均衡
Mat sumOutsideMat = Mat::zeros(mAlignBaseImg.size(), CV_32FC1);
Mat sumInsideMat = Mat::zeros(mInSideBaseImg.size(), CV_32FC1);
Mat minOutsideMat(mAlignBaseImg.size(), mAlignBaseImg.type());
Mat minInsideMat(mInSideBaseImg.size(), mInSideBaseImg.type());
minOutsideMat.setTo(FLT_MAX);
minInsideMat.setTo(FLT_MAX);
vector<Mat> rImgVec;
vector<Mat> rInsideImgVec;
vector<RotateMatchResult> rmrVec;
vector<RotateMatchResult> rmrVecInside;
vector<Mat> resizedImgVec;
vector<Mat> resizedCenterVec;
vector<int> diametersVec;
for (int i = 0; i < imgVec.size(); ++i)
{
diametersVec.push_back(imgVec[i].rows);
Mat img = imgVec[i].clone();
if (img.size() != mAlignBaseImg.size())
{
Mat resizedImg;
resize(img, resizedImg, mAlignBaseImg.size());
img = resizedImg;
}
Mat centerMat = scaledCenterVec[i];
if (centerMat.size() != mInSideBaseImg.size())
{
Mat resizedCenterMat;
resize(centerMat, resizedCenterMat, minInsideMat.size());
centerMat = resizedCenterMat;
}
resizedImgVec.push_back(img);
resizedCenterVec.push_back(centerMat);
preProcessImage(img, centerMat);
m_parallelFlag = 0;
RotateMatchResult rmr = rotateMatch(img);//旋转匹配 输出匹配完成的角度及图片
rmrVec.push_back(rmr);
m_parallelFlag = 1;
RotateMatchResult rmrInside = rotateMatch(centerMat);
rmrVecInside.push_back(rmrInside);
Mat rImg = rmr.mBestRImg;
Mat rInsideImg = rmrInside.mBestRImg;
rInsideImgVec.push_back(rInsideImg);
minOutsideMat = min(minOutsideMat, rImg);//生成权重图 去毛刺
sumOutsideMat += rImg;
minInsideMat = min(minInsideMat, rInsideImg);
sumInsideMat += rInsideImg;
}
meanDiameter = mean(diametersVec)[0];
Mat avgMat = sumOutsideMat / imgVec.size();
mAlignBaseImg = avgMat;
Mat avgInsideMat = sumInsideMat / imgVec.size();
mInSideBaseImg = avgInsideMat;
//mWeightMat = minMat;
mWeightMat = minOutsideMat;
mWeightMat /= 255.0;
mInsideWeightMat = minInsideMat;
mInsideWeightMat /= 255.0;
//对权重进行 归一化
meanvarnorm(mWeightMat, mWeightMat, mTargetMeanVal, 150, m8uMaskImg);
mWeightMat.setTo(0, mWeightMat < 10);
Scalar meanScalar, stddevScalar;
meanStdDev(mInsideWeightMat, meanScalar, stddevScalar, m8uInsideMaskImg);
mInsideWeightMat = mInsideWeightMat* (127 / meanScalar.val[0]);
mInsideWeightMat.setTo(0, mInsideWeightMat < 0);
{
if(mRepeatNum <= 0)
{
mRepeatNum = computeRepeatNum();
}
selfRotateMin(mWeightMat, mWeightMat, mRepeatNum);
}
sumOutsideMat.setTo(0);
sumInsideMat.setTo(0);
for (int i = 0; i < rmrVec.size(); ++i)
{
double bestAngle = rmrVec[i].mBestAngle;
Mat img = resizedImgVec[i];
Mat rotatedImg = rotateImage(img, Point2f(img.cols/2.0, img.rows/2.0), bestAngle);
preProcessImage(rotatedImg, m8uMaskImg, mWeightMat, mTargetMeanVal, mTargetStddevVal, mHighlightsThreshold);
double bestInsidePart = rmrVecInside[i].mBestAngle;
//Mat i_Mat = resizedImgVec[i];
Mat centerMat = resizedCenterVec[i];
Mat rotatedInsideMat = rotateImage(centerMat, Point2f(centerMat.cols / 2.0, centerMat.rows / 2.0), bestInsidePart);
preProcessImage(rotatedInsideMat, m8uInsideMaskImg, mInsideWeightMat, mTargetMeanVal, mTargetStddevVal, mHighlightsThreshold);
sumOutsideMat += rotatedImg;
//rotatedInsideMat.setTo(0, rotatedInsideMat < 0);
sumInsideMat += rotatedInsideMat;
}
mCompareBaseImg = sumOutsideMat / resizedImgVec.size();//得到最终匹配图
mInsideCompareBaseImg = sumInsideMat / resizedImgVec.size();
trueSampleWeightRecon(resizedImgVec, resizedCenterVec, rmrVec, rmrVecInside);
mWeightMat = falseReConMat.clone();
mInsideWeightMat = insideWeightRecon.clone();
}
void ImageCompareModel::trueSampleWeightRecon(const vector<Mat>& resizedVec,
const vector<Mat>& resizedCenterVec,
vector<RotateMatchResult> rmrVec,
vector<RotateMatchResult> rmrVecInside)
{
falseReConMat = mWeightMat.clone();
insideWeightRecon = mInsideWeightMat.clone();
for (int i = 0; i < resizedVec.size(); i++)
{
const Mat &img = resizedVec[i];
double bestAngle = rmrVec[i].mBestAngle;
//preProcessImage(processedImg, insideMatchImg);
Mat rotatedImg = rotateImage(img, Point2f(img.cols / 2.0, img.rows / 2.0), bestAngle);
Mat hightlightsMask = rotatedImg < mHighlightsThreshold;
converToType(hightlightsMask, CV_32FC1);
hightlightsMask /= 255.0;
Mat unifiedMask = m32fMaskImg.mul(hightlightsMask).mul(mWeightMat);
preProcessImage(rotatedImg, m8uMaskImg, mWeightMat, mTargetMeanVal, mTargetStddevVal, mHighlightsThreshold);
rotatedImg.setTo(0, rotatedImg < 0);
converToType(rotatedImg, CV_32FC1);
normSectors_tarImg(rotatedImg, unifiedMask, imgCen(rotatedImg), 1, mCompareBaseImg);
#ifdef DEBUG_VIEW_INTERNAL_MAT
Mat vRotatedImg1 = rotatedImg / 255.0;
#endif
rotatedImg.setTo(0, rotatedImg < 10);
////// FOR INSIDE CIRCLE
double insideBestAngle = rmrVecInside[i].mBestAngle;
const Mat & centerMat = resizedCenterVec[i];
Mat rotatedInsideImg = rotateImage(centerMat, Point2f(centerMat.cols / 2.0, centerMat.rows / 2.0), insideBestAngle);
Mat highLightInsideMask = rotatedInsideImg < mHighlightsThreshold;
converToType(highLightInsideMask, CV_32FC1);
highLightInsideMask /= 255.0;
Mat unifiedInsideMask = m32fInsideMaskImg.mul(highLightInsideMask).mul(mInsideWeightMat);
preProcessImage(rotatedInsideImg, m8uInsideMaskImg, mInsideWeightMat, mTargetMeanVal, mTargetStddevVal, mHighlightsThreshold);
Mat vcamInside = rotatedInsideImg / 255.0;
rotatedInsideImg.setTo(0, rotatedInsideImg < 0);
//mInsideCompareBaseImg.setTo(0, mInsideCompareBaseImg < 0);
normSectors_tarImg(rotatedInsideImg, unifiedInsideMask, imgCen(rotatedInsideImg), 1, mInsideCompareBaseImg);
trueWeightRecon(rotatedImg, mCompareBaseImg, rotatedInsideImg, mInsideCompareBaseImg);
}
}
void ImageCompareModel::trueWeightRecon(const Mat& rImg, const Mat& baseImg,
const Mat& insideRimg, const Mat& insideBaseImg)
{
Mat dMat = abs(rImg - baseImg);
#ifdef DEBUG_VIEW_INTERNAL_MAT
Mat tt = dMat / 255.0;
Mat m1 = rImg / 255.0;
Mat b1 = baseImg / 255.0;
#endif // DEBUG_VIEW_INTERNAL_MAT
//Mat mData = dMat.mul(m32fMaskImg).mul(mWeightMat);
Mat mData;
dMat.copyTo(mData, dMat > 1000);
double outterVal = sum(mData).val[0];
if (outterVal != 0)
{
mData = minMaxNorm(mData, 0, 255.0);
}
Mat i_dMat = abs(insideRimg - insideBaseImg);
Mat i_mData;
i_mData.setTo(0);
//Mat test = i_dMat / 255;
//Mat imgtest = i_dMat.mul(m32fInsideMaskImg) / 255;
Mat innerDmat = i_dMat.mul(m32fInsideMaskImg).mul(mInsideWeightMat);
innerDmat = minMaxNorm(innerDmat, 0, 255.0);
weightMapping(mData, 255.0, innerDmat, 255.0);
}
void ImageCompareModel::weightMapping(const Mat& mData, double maxVal, const Mat& i_mData, double i_maxVal)
{
for (int i = 0; i < mData.rows; i++)
{
float* pData = (float*)mData.row(i).data;
float*pWeight = (float*)falseReConMat.row(i).data;
for (int j = 0; j < mData.cols; j++)
{
double pixVal = pData[j];
if (pixVal != 0)
{
double affineCoeff = descendFunction(pixVal, maxVal);
pWeight[j] *= affineCoeff;
}
}
}
for (int i = 0; i < i_mData.rows; i++)
{
float* i_pData = (float*)i_mData.row(i).data;
float*i_pWeight = (float*)insideWeightRecon.row(i).data;
for (int j = 0; j < i_mData.cols; j++)
{
double i_pixVal = i_pData[j];
if (i_pixVal != 0)
{
double affineCoeff = descendFunction(i_pixVal, i_maxVal);
i_pWeight[j] *= affineCoeff;
}
}
}
}
double ImageCompareModel::descendFunction(double pixVal, double maxVal)
{
return (-1.0 / maxVal)*pixVal + 1.0;
//return -1 * pow(pixVal, 2) + 1;
}
void ImageCompareModel::calculateAllParams(const vector<Mat>& imgVec)
{
mDisThre = DBL_MAX;
Mat disMat(1, imgVec.size(), CV_64FC1);
for (int i = 0; i < imgVec.size(); ++i)
{
const Mat& img = imgVec[i];
double dis = compare(img, NULL, 1, false, 0);
disMat.at<double>(i) = dis;
//disMat.copyTo(mDisMat);
}
if (disMat.cols == 1)
{
mTrueSampleDisStddev = 0;
mTrueSampleDisMean = disMat.at<double>(0);
}
else
{
Scalar m, s;
meanStdDev(disMat, m, s);
mTrueSampleDisStddev = s.val[0];
mTrueSampleDisMean = m.val[0];
}
disMat.copyTo(mDisMat);
minMaxIdx(disMat, &mTrueSampleDisMin, &mTrueSampleDisMax);
}
void ImageCompareModel::computeDisThre(const vector<Mat>& imgVec, double* pMinDis, double md_diameter, double md_height)
{
if (imgVec.empty())
{
return;
}
mDisThre = DBL_MAX;
vector<double> disVec;
std::map<double, cv::Mat> imgs;
for_each(imgVec.begin(), imgVec.end(), [&](Mat img)
{
double dis = compare(img, NULL, 3, false, 1, md_diameter, md_height);
disVec.push_back(dis);
imgs[dis] = img;
});
double minDis = *(min_element(disVec.begin(), disVec.end()));
setFalseSampleMinDis(minDis);
if (pMinDis)
{
*pMinDis = minDis;
}
if (minDis > mTrueSampleDisMax)
{
mDisThre = mTrueSampleDisMax*0.3 + minDis*0.7;
//mDisThre = mTrueSampleDisMax*0.15 + minDis*0.85 + mTrueSampleDisStddev;
//mDisThre = minDis;
if (mDisThre > mTrueSampleDisMax*1.8)
{
mDisThre = mTrueSampleDisMax*1.8;
}
}
else
{
mDisThre = DBL_MAX;
std::cout << "max distance in training > min distance in testing" << std::endl;
}
}
void ImageCompareModel::genUniformSepIdx(int num, int startIdx, int endIdx, vector<int>& cenVec)
{
float step = (endIdx - startIdx) / (num);
for (int i = 0; i < num; i++)
{
cenVec.push_back(floorf(step*i + startIdx));
}
}
void ImageCompareModel::genAngleRanges(vector<int> cenVec, vector<Range>& rangeVec, int localRange)
{
rangeVec.clear();
for_each(cenVec.begin(), cenVec.end(), [&](int cen)
{
Range r;
r.start = cen - localRange;
r.end = cen + localRange + 1;
rangeVec.push_back(r);
});
}
void ImageCompareModel::genCandidateAngleRanges(vector<float> disVec,
float angleStep, vector<Range>& rangeVec)
{
auto minDisIter = min_element(disVec.begin(), disVec.end());
int startIdx = minDisIter - disVec.begin();
rotate(disVec.begin(), minDisIter, disVec.end());
Mat disVecMat(1, disVec.size(), CV_32FC1, &(disVec[0]));
Mat normDisVecMat;
meanvarnorm(disVecMat, normDisVecMat, 0, 1.0);
set<unsigned int> canNums;
genCandidateRepeatNums(canNums);
vector<int> cenVec;
int repeatNum = recognizeLowerExtremes(normDisVecMat, canNums, 5, &cenVec);
if (repeatNum < 0)
{
rangeVec.clear();
return;
cenVec.clear();
cenVec.push_back(disVec.size() / 2);
genAngleRanges(cenVec, rangeVec, disVec.size() / 2);
}
else
{
if (cenVec.size() >= 2 && diffAngleRaw(cenVec.front(), cenVec.back()) < 2)
{
cenVec.pop_back();
}
for_each(cenVec.begin(), cenVec.end(), [&](int& cen)
{
cen += startIdx;
cen *= angleStep;
cen = normAngle(cen);
});
for (auto i = cenVec.begin(); i != cenVec.end(); ++i)
{
if (*i < startIdx*angleStep)
{
rotate(cenVec.begin(), i, cenVec.end());
break;
}
}
genAngleRanges(cenVec, rangeVec, 10);
}
}
void ImageCompareModel::genCandidateAngleRanges(const Mat& disMat,
float angleStep, vector<Range>& rangeVec, float rangeScale /*= 1*/)
{
for (int i = 0; i < disMat.rows; ++i)
{
Mat disVec = disMat.row(i);
double minVal, maxVal;
Point minPt, maxPt;
minMaxLoc(disVec, &minVal, &maxVal, &minPt, &maxPt);
Range &r = rangeVec[i];
float cenAngle = r.start + angleStep*minPt.x;
float angleRangeWidth = r.end - r.start;
r.start = floorf(cenAngle - angleRangeWidth * 0.5 * rangeScale);
r.end = floorf(cenAngle + angleRangeWidth * 0.5 * rangeScale);
}
}
void ImageCompareModel::selfRotateMin(const Mat& img, Mat& weightMat, int repeatNum)
{
Point2f cen(img.cols / 2.0, img.rows / 2.0);
float angleStep = 360.0 / repeatNum;
Mat dSumMat = Mat::ones(img.size(), img.type()) * 255;
//Mat dSumMat = Mat::ones(img.size(), img.type());
for (int i = 1; i < repeatNum; ++i)
{
Mat rotatedImg = rotateImage(img, cen, i*angleStep);
Mat dilatedImg;
Mat kernel = Mat::ones(3, 3, CV_32FC1);
dilate(rotatedImg, dilatedImg, kernel);
dSumMat = min(dSumMat, dilatedImg);
}
weightMat = dSumMat;
}
double ImageCompareModel::genMatchValue(const Mat& dMat) const
{
double v = norm(dMat, NORM_L2);
double ret = scaleMatchValue(v);
return ret;
}
double ImageCompareModel::genMatchValue2(const Mat& rImg, const Mat& baseImg, const Mat& maskImg) const
{
Mat dMat = rImg - baseImg;
dMat = abs(dMat.mul(maskImg));
Mat minmaxScaleMat;
Mat mMask;
if (m_parallelFlag != 1)
{
minmaxScaleMat = minMaxNorm(dMat, 0, 255, m8uMaskImg);
converToType(minmaxScaleMat, CV_8UC1);
mMask = lowerMajorityMask(minmaxScaleMat, m8uMaskImg, 0.98);
// Mat dd = mMask / 255.0;
// Mat ddmask = m8uMaskImg / 255.0;
}
else
{
minmaxScaleMat = minMaxNorm(dMat, 0, 255, m8uInsideMaskImg);
converToType(minmaxScaleMat, CV_8UC1);
mMask = lowerMajorityMask(minmaxScaleMat, m8uInsideMaskImg, 0.97);
}
converToType(mMask, CV_32FC1);
mMask /= 255.0;
dMat = dMat.mul(mMask);
Mat vii = mMask.mul(maskImg);
Mat ii = mMask.mul(maskImg) / 255.0;
double s = sum(mMask.mul(maskImg)).val[0];
double ret = genMatchValue(dMat) / s;
return ret;
}
double ImageCompareModel::genMatchValue(const Mat& rImg, const Mat& baseImg,
const Mat& maskImg, int flag, int diameter, double md_diameter, double md_height) const
{
Mat v1 = rImg / 255.0;//展示图
Mat v2 = baseImg / 255.0;//展示图
Mat v3 = maskImg / 255.0;//展示图
Mat dMat = rImg - baseImg;
dMat = abs(dMat.mul(maskImg));
Mat dV = dMat / 255.0;
Mat minmaxScaleMat;
Mat mMask;
if (m_parallelFlag != 1)
{
minmaxScaleMat = minMaxNorm(dMat, 0, 255, m8uMaskImg);
converToType(minmaxScaleMat, CV_8UC1);
mMask = lowerMajorityMask(minmaxScaleMat, m8uMaskImg, 0.98);
}
else
{
minmaxScaleMat = minMaxNorm(dMat, 0, 255, m8uInsideMaskImg);
converToType(minmaxScaleMat, CV_8UC1);
mMask = lowerMajorityMask(minmaxScaleMat, m8uInsideMaskImg, 0.97);
}
converToType(mMask, CV_32FC1);
mMask /= 255.0;
dMat = dMat.mul(mMask);
Mat sunMat = mMask.mul(maskImg);
// Mat ii = mMask.mul(maskImg) / 255.0;
double s = sum(sunMat).val[0];
double ret = genMatchValue(dMat) / s;
if (flag == 1)
{
double matchedVal = ret;
ret = penltyCoeff(matchedVal, meanDiameter, diameter, md_diameter, md_height);//做惩罚
}
return ret;
}
double ImageCompareModel::scaleMatchValue(double val) const
{
return val*mMatchValScale;
}
ImageCompareModel* ImageCompareModel::scale(float s)
{
Mat baseImage;
resize(mAlignBaseImg, baseImage, Size(), s, s, INTER_CUBIC);
Mat weightMat;
resize(mWeightMat, weightMat, Size(), s, s, INTER_CUBIC);
ImageCompareModel* pRet = new ImageCompareModel;
*pRet = *this;
pRet->setBaseImg(baseImage);
pRet->setWeightMat(weightMat);
pRet->genOutterMask();
return pRet;
}
ImageCompareModel::RotateMatchResult ImageCompareModel::rotateMatch(const Mat& img, int levelNum /*= 1*/, float angleStep /*= 1.0*/,
float startAngle /*= 0*/, float endAngle /*= 360*/) const
{
RotateData* pData = new RotateData;
vector<Range> angleRangeVec;
if (levelNum > 1)
{
MultiScaleImage msi;
msi.setLevelNum(3);
msi.setBaseLevel(img);
msi.genMultiScale();
ImageCompareData imgCmpData(&msi);
imgCmpData.setStartAngle(startAngle);
imgCmpData.setEndAngle(endAngle);
mpMultiScaleModel->proc(&imgCmpData);
*pData = *imgCmpData.getRotateData();
}
else
{
rotateMatchData(img, pData, angleStep, startAngle, endAngle);
}
RotateMatchResult rmr;
if (pData->mDisValVec.empty())
{
delete pData;
return rmr;
}
else
{
size_t bestIndex = min_element(pData->mDisValVec.begin(), pData->mDisValVec.end()) - pData->mDisValVec.begin();
Mat ret = pData->mRImgVec[bestIndex];
rmr.mBestRImg = ret;
rmr.mBestAngle = pData->bestAngle();
delete pData;
return rmr;
}
}
void ImageCompareModel::rotateMatchData(const Mat& _img, RotateData* pData,
float angleStep /*= 1.0*/, float startAngle /*= 0*/, float endAngle /*= 360*/) const
{
if (_img.empty())
return;
Point2f center(_img.cols / 2.0, _img.rows / 2.0);
int nNum = (endAngle - startAngle) / angleStep;
RotateData& data = *pData;
data.init(_img.clone(), center, angleStep, nNum);
data.mStartAngle = startAngle;
data.mEndAngle = endAngle;
parallel_for_(Range(0, nNum), ImageCompareModelInvoker(this, pData));
}
void ImageCompareModel::rotateMatchData(const Mat& img, RotateData* pData,
float angleStep /*= 1.0*/, const vector<Range>& angleRangeVec) const
{
float minDis = FLT_MAX;
RotateData bestData;
for_each(angleRangeVec.begin(), angleRangeVec.end(), [&](const Range& r)
{
float startAngle = r.start;
float endAngle = r.end;
rotateMatchData(img, pData, angleStep, startAngle, endAngle);
float localMinDis = *min_element(pData->mDisValVec.begin(), pData->mDisValVec.end());
if (localMinDis < minDis)
{
minDis = localMinDis;
bestData = *pData;
}
});
*pData = bestData;
}
void ImageCompareModel::rotateMatchData(const Mat& img, RotateData* pData,
const vector<Range>& angleRangeVec, float angleStep, Mat& disMat) const
{
if (angleRangeVec.empty())
{
return;
}
disMat.create(angleRangeVec.size(), angleRangeVec.front().size(), CV_32FC1);
pData->mRImgVec.resize(angleRangeVec.size());
pData->mDisValVec.resize(angleRangeVec.size(), FLT_MAX);
double minDis = DBL_MAX;
for (int i = 0; i < angleRangeVec.size(); ++i)
{
Range r = angleRangeVec[i];
Mat disVec = disMat.row(i);
RotateData rotateData;
float startAngle = r.start;
float endAngle = r.end;
rotateMatchData(img, &rotateData, angleStep, startAngle, endAngle);
std::copy(rotateData.mDisValVec.begin(), rotateData.mDisValVec.end(),
(float*)disVec.data);
double localMinDis, localMaxDis;
Point minPt, maxPt;
minMaxLoc(disVec, &localMinDis, &localMaxDis, &minPt, &maxPt);
if (localMinDis < minDis)
{
minDis = localMinDis;
*pData = rotateData;
}
}
}
void ImageCompareModel::genCandidateRepeatNums(set<unsigned int>& canNums)
{
canNums.clear();
for (int i = 4; i < 25; ++i)
{
canNums.insert(i);
}
}
bool isValidExtremas(const Mat& vec, const vector<int>& lessExtremaIdxVec,
const vector<int>& moreExtremaIdxVec)
{
float* pVec = (float*)vec.data;
auto lessIdxIter = lessExtremaIdxVec.begin();
auto moreIdxIter = moreExtremaIdxVec.begin();
while (lessIdxIter != lessExtremaIdxVec.end())
{
auto leftLessIdxIter = lessIdxIter;
auto leftMoreIdxIter = moreIdxIter;
auto rightLessIdxIter = lessIdxIter;
rightLessIdxIter++;
if (rightLessIdxIter == lessExtremaIdxVec.end())
{
break;
}
auto rightMoreIdxIter = leftMoreIdxIter;
while (*rightLessIdxIter > *rightMoreIdxIter)
{
rightMoreIdxIter++;
}
float leftLessExtrema = pVec[*leftLessIdxIter];
float rightLessExtrema = pVec[*rightLessIdxIter];
float localMaxVal = *std::max_element(pVec + *leftLessIdxIter,
pVec + *rightLessIdxIter + 1);
float valRange = localMaxVal - min(leftLessExtrema, rightLessExtrema);
float valTor = valRange*0.05;
float slope = (rightLessExtrema - leftLessExtrema) / (*rightLessIdxIter - *leftLessIdxIter);
leftMoreIdxIter++;
while (leftMoreIdxIter != rightMoreIdxIter)
{
float interVal = leftLessExtrema + slope*(*leftMoreIdxIter - *leftLessIdxIter);
float curExtremaVal = pVec[*leftMoreIdxIter];
if (curExtremaVal < interVal + valTor)
{
return false;
}
leftMoreIdxIter++;
}
lessIdxIter++;
moreIdxIter = rightMoreIdxIter;
}
return true;
}
int ImageCompareModel::recognizeLowerExtremes(const Mat& vec,
set<unsigned int> canNums, int extremeRefineRange /*= 5*/, vector<int> *pExtremaIdxVec /*= 0*/)
{
unsigned int size = vec.cols;
set<unsigned int> validNs;
float* pVec = (float*)vec.data;
double vecMinVal, vecMaxVal;
minMaxIdx(vec, &vecMinVal, &vecMaxVal);
map<int, vector<int> > repeatNumLocalExtremaIdxMap;
map<int, Mat> repeatNumLocalExtremaValMap;
for (auto iter = canNums.rbegin(); iter != canNums.rend(); ++iter)
{
unsigned int n = *iter;
Mat extremaValVec;
vector<int> extremaIdxVec;
uniformLocalMinExtremas(vec, extremaValVec, extremaIdxVec,
n, extremeRefineRange);
float* pExtremaVecVec = (float*)extremaValVec.data;
float curVal, nxtVal;
bool isValid = true;
Mat preLocalVec;
for (unsigned int i = 0; i < extremaIdxVec.size() - 1; ++i)
{
unsigned int curIdx = extremaIdxVec[i];
unsigned int nxtIdx = extremaIdxVec[i + 1];
curVal = pVec[curIdx];
nxtVal = pVec[nxtIdx];
// no smaller value between two neighbor extremes
if (anyInRange(pVec + curIdx + 1, pVec + nxtIdx,
-FLT_MAX, min(curVal, nxtVal)))
{
isValid = false;
break;
}
if (!preLocalVec.empty())
{
Mat curLocalVec = vec.colRange(curIdx, nxtIdx + 1);
if (curLocalVec.size() != preLocalVec.size())
{
resize(curLocalVec, curLocalVec, preLocalVec.size());
}
float sim = compareHist(curLocalVec, preLocalVec, CV_COMP_CORREL);
if (sim < 0)
{
isValid = false;
break;
}
}
preLocalVec = vec.colRange(curIdx, nxtIdx + 1);
}
if (!isValid)
{
continue;
}
set<int> nToBeErased;
for_each(validNs.begin(), validNs.end(), [&](int preN)
{
if (preN % n != 0 || !isValid)
{
return;
}
vector<int> preExtremaIdxVec = repeatNumLocalExtremaIdxMap[preN];
if (isValidExtremas(vec, extremaIdxVec, preExtremaIdxVec))
{
nToBeErased.insert(preN);
}
else
{
isValid = false;
}
});
if (!isValid)
{
continue;
}
if (nToBeErased.size() == validNs.size())
{
validNs.clear();
}
else
{
for_each(nToBeErased.begin(), nToBeErased.end(), [&](int toBeErasedN)
{
validNs.erase(toBeErasedN);
});
}
validNs.insert(n);
repeatNumLocalExtremaIdxMap[n] = extremaIdxVec;
repeatNumLocalExtremaValMap[n] = extremaValVec;
if (pExtremaIdxVec)
{
*pExtremaIdxVec = extremaIdxVec;
}
}
if (validNs.size() == 0)
{
return -1;
}
if (validNs.size() > 1)
{
return -2;
}
return *(validNs.begin());
}
int ImageCompareModel::recognizeRepeatedLocalExtremas(const Mat& vec,
const set<unsigned int>& canNums, int refineRange /*= 5*/,
vector<int> *pIdxVec /*= 0*/)
{
assert(vec.type() == CV_32FC1);
float* pVecData = (float*)vec.data;
for_each(canNums.begin(), canNums.end(), [&](int n)
{
Mat localMinExtremaValVec;
vector<int> localMinExtremaIdxVec;
uniformLocalMinExtremas(vec, localMinExtremaValVec, localMinExtremaIdxVec,
n, refineRange);
float* pExtremaVecVec = (float*)localMinExtremaValVec.data;
// get locally top 10% values
});
return -1;
}
int ImageCompareModel::computeRepeatNum(const Mat& _img)
{
Mat img;
cv::GaussianBlur(_img, img, Size(3, 3), 5.0);
vector<float> vec;
selfRotationSimilarityFeature(img, vec, Point2f(img.cols / 2.0, img.rows / 2.0));
#ifdef DEBUG_VIEW_INTERNAL_MAT
Mat viewImg = img / 255.0 /255.0;
Mat view_Img = _img / 255.0 /255.0;
#endif
vector<float> normVec(vec);
Mat vecMat(1, vec.size(), CV_32FC1, &(vec[0]));
Mat normVecMat(1, normVec.size(), CV_32FC1, &(normVec[0]));
meanvarnorm(vecMat, normVecMat, 0, 1.0);
#ifdef DEBUG_VIEW_PLOT
Mat dftVec;
dft(normVec, dftVec);
dftVec = abs(dftVec);
Mat dVecMat = normVecMat.clone();
genSobelImage(dVecMat);
Mat dDftVec;
dft(dVecMat, dDftVec);
Mat ddVecMat = dVecMat.clone();
genSobelImage(ddVecMat);
Mat ddDftVec;
dft(ddVecMat, ddDftVec);
// no need to code for plotting in visual studio later than 2017,
// install the ArrayPlotter extension to see the data distribution
#if (_MSC_VER < 1910) // vs2017
CvPlot::plot<float>("normVec", (float*)&(normVec[0]), normVec.size());
CvPlot::plot<float>("dVec", (float*)dVecMat.data, dVecMat.cols);
CvPlot::plot<float>("ddVec", (float*)ddVecMat.data, ddVecMat.cols);
CvPlot::plot<float>("ddft", (float*)dDftVec.data, 50);
CvPlot::plot<float>("dft", (float*)dftVec.data, 50);
CvPlot::plot<float>("dddft", (float*)ddDftVec.data, 50);
#endif
waitKey();
#endif
set<unsigned int> canNums;
genCandidateRepeatNums(canNums);
return recognizeLowerExtremes(normVecMat, canNums);
}
int ImageCompareModel::computeRepeatNum()
{
if (mWeightMat.empty() || mAlignBaseImg.empty())
{
return 0;
}
Mat img = mAlignBaseImg.mul(mWeightMat);
#ifdef DEBUG_VIEW_INTERNAL_MAT
Mat viewImg = img / 255.0 / 255.0;
#endif
return computeRepeatNum(img);
}
void ImageCompareModel::initMultiScaleModel()
{
if (mpMultiScaleModel)
{
delete mpMultiScaleModel;
}
mpMultiScaleModel = new MultiScaleImageCompareModel;
mpMultiScaleModel->setLevelNum(3);
mpMultiScaleModel->setBaseLevel(this);
mpMultiScaleModel->genMultiScale();
}
void ImageCompareModelInvoker::operator()(const cv::Range& range) const
{
int i0 = range.start;
int i1 = range.end;
m_pModel->parallelDetect(i0, m_pData);
}
void ImageCompareModel::parallelDetect(int index, void *p) const
{
RotateData *pData = (RotateData *)p;
Mat t = getRotationMatrix2D(pData->mCenter, pData->angle(index), 1.0);
Mat rImg;
warpAffine(pData->mImgSrc, rImg, t, pData->mImgSrc.size());
// need add insideImage base image;
// ....
Mat imgRes;
if (m_parallelFlag!=1)
{
if (rImg.size() != mAlignBaseImg.size())
{
resize(rImg, rImg, mAlignBaseImg.size());
}
imgRes = rImg - mAlignBaseImg;
if (!m32fMaskImg.empty())
{
if (m32fMaskImg.size() != imgRes.size())
resize(imgRes, imgRes, m32fMaskImg.size());
imgRes = imgRes.mul(m32fMaskImg);
if (!mWeightMat.empty())
{
if (mWeightMat.size() != imgRes.size())
resize(imgRes, imgRes, mWeightMat.size());
imgRes.mul(mWeightMat);
}
}
}
else
{
if (rImg.size() != mInSideBaseImg.size())
{
resize(rImg, rImg, mInSideBaseImg.size());
}
imgRes = rImg - mInSideBaseImg;
if (!m32fInsideMaskImg.empty())
{
if (m32fInsideMaskImg.size() != imgRes.size())
resize(imgRes, imgRes, m32fInsideMaskImg.size());
imgRes = imgRes.mul(m32fInsideMaskImg);
if (!mInsideWeightMat.empty())
{
if (mInsideWeightMat.size() != imgRes.size())
resize(imgRes, imgRes, mInsideWeightMat.size());
imgRes.mul(mInsideWeightMat);
}
}
}
#ifdef DEBUG_VIEW_INTERNAL_MAT
Mat vRImg, vBaseImg;
vRImg = rImg / 255.0;
vBaseImg = mAlignBaseImg / 255.0;
Mat vImgRes = imgRes / 255.0;
Mat vWeightMat = mWeightMat / 255.0;
Mat viewInsideMat = mInSideBaseImg / 255.0;
#endif
double val = 0;
if(!imgRes.empty())
val = norm(imgRes);
#ifdef DEBUG_VIEW_INTERNAL_MAT
vImgRes = imgRes / 255.0;
#endif
pData->mDisValVec[index] = val;
pData->mRImgVec[index] = rImg;
}
double ImageCompareModel::filterTrainImage(const Mat&img)
{
if (img.empty())
{
return DBL_MAX;
}
mDisThre = DBL_MAX;
double dis = compare(img, NULL, 3, false);
/*if (dis < mTrueSampleDisMax)
{
dis = mTrueSampleDisMax;
}
double absDiff = abs(dis - mTrueSampleDisMax);*/
return dis;
}
void ImageCompareModel::weightOptimization(const vector<Mat>& falseSamples)
{
if (falseSamples.size() <= 0)
return;
reConMat = mWeightMat.clone();
vector<Mat> falseVec;
resizeVecMat(falseSamples, falseVec);
for (int i = 0; i < falseVec.size(); i++)
{
Mat falseSample = falseVec[i].clone();
preWeightReconstruction(falseSample, reConMat);
}
mWeightMat = reConMat;
}
void ImageCompareModel::preWeightReconstruction(Mat img, Mat &reConImg)
{
if (mAlignBaseImg.size().height <= 0 || mAlignBaseImg.size().width <= 0)
{
//mAlignBaseImg = img.clone();
return;
}
if (mAlignBaseImg.size() != img.size())
{
resize(img, img, mAlignBaseImg.size());
}
Mat matchImg = img.clone();
preProcessImage(matchImg, Mat());
if (!mpMultiScaleModel)
{
initMultiScaleModel();
}
RotateMatchResult rmr = rotateMatch(matchImg, 1);
Mat rImg = rmr.mBestRImg;
#ifdef DEBUG_VIEW_INTERNAL_MAT
Mat ttt = mAlignBaseImg / 255.0;
Mat vRImg = rImg / 255.0;
#endif
double bestAngle = rmr.mBestAngle;
Mat cmpImg = rotateImage(img, Point2f(img.cols / 2.0, img.rows / 2.0),
bestAngle);
// remove highlights
Mat hightlightsMask = cmpImg < mHighlightsThreshold;
converToType(hightlightsMask, CV_32FC1);
hightlightsMask /= 255.0;
Mat unifiedMask = m32fMaskImg.mul(hightlightsMask).mul(mWeightMat);
preProcessImage(cmpImg, m8uMaskImg, mWeightMat, mTargetMeanVal,
mTargetStddevVal, mHighlightsThreshold);
cmpImg.setTo(0, cmpImg < 0);
converToType(cmpImg, CV_32FC1);
normSectors_tarImg(cmpImg, unifiedMask, imgCen(cmpImg), 1,
mCompareBaseImg);
#ifdef DEBUG_VIEW_INTERNAL_MAT
Mat vRotatedImg0 = cmpImg / 255.0;
#endif
#ifdef DEBUG_VIEW_INTERNAL_MAT
Mat vRotatedImg1 = mCompareBaseImg / 255.0;
#endif
cmpImg.setTo(0, cmpImg < 10);
weightReconstruction(cmpImg, mCompareBaseImg, unifiedMask, reConImg);
}
void ImageCompareModel::weightReconstruction(const Mat& rImg, const Mat& baseImg, const Mat& maskImg, Mat &reConImg)
{
Mat dMat = abs(rImg - baseImg);
Mat test = dMat / 255.0;
Mat norImg = Mat::ones(dMat.size(), dMat.type()) * 255;
dMat = min(dMat, norImg);
Mat w(dMat.size(), dMat.type(), Scalar::all(0));
dMat.copyTo(w, dMat < 0.5);
w = minMaxNorm(w, 0, 1.0);
for (int i = 0; i < reConImg.rows; i++)
{
float* pData = (float*)reConImg.row(i).data;
float* pWeight = (float*)w.row(i).data;
for (int j = 0; j < reConImg.cols; j++)
{
if (pWeight[j]!=0)
{
//pData[j] *= pWeight[j];
pWeight[j] = -1 * pow(pWeight[j] - 1, 2) + 1;
pData[j] *= pWeight[j];
}
}
}
}
double ImageCompareModel::penltyCoeff(double matchedVal, int diameterMean, int targetDiameter, double modelDiamter, double modelHeight) const
{
int diff = abs(diameterMean - targetDiameter);
double modelDiameterDiff = abs(modelDiamter - realWidth);
double modelHeightDiff = abs(modelHeight - realHeight);
if (diff >=10)
{
return matchedVal*(diff / 100.0 + 1) * (modelHeightDiff / 100.0 + 1) * (modelDiameterDiff / 100.0 + 1);
}
else
{
return matchedVal;
}
}
cv::Mat ImageCompareModel::genInsideMask(const Mat& img, Point2f center, float innerR /*= -1*/, float outterR /*= -1*/, int type /*= CV_32FC1*/)
{
Mat mask(img.size(), CV_8UC1);
mask.setTo(0);
if (innerR == -1)
{
// default is 30
innerR = img.rows*0.178;
//innerR = img.rows;
}
if (outterR == -1)
{
// default is max radius - 10
outterR = img.rows*0.425;
}
circle(mask, center, outterR, Scalar(255), -1);
circle(mask, center, innerR, Scalar(0), -1);
if (type != CV_8UC1)
{
converToType(mask, type);
mask /= 255;
}
return mask;
}
float ImageCompareModel::allocateInnerRadius(cv::Mat subImage)
{
if (subImage.size() != mAlignBaseImg.size())
{
resize(subImage, subImage, mAlignBaseImg.size());
}
cv::Mat binaryImg = subImage > 30;
const int innerRadius = binaryImg.rows*0.05;
const int outterRadius = binaryImg.cols*0.22;
const Point center(binaryImg.cols / 2.0, binaryImg.rows / 2.0);
Mat mask(binaryImg.size(), CV_8UC1);
mask.setTo(0);
circle(mask, center, outterRadius, Scalar(1), -1);
Mat dilatedImgBin;
Mat erodeMat;
dilate(binaryImg, dilatedImgBin, Mat::ones(9, 9, CV_32FC1));
erode(dilatedImgBin, erodeMat, Mat::ones(9, 9, CV_32FC1));
openOper(erodeMat, Mat::ones(1, 13, CV_32FC1));
circle(erodeMat, center, innerRadius, Scalar(255), -1);
Mat tarMat = mask.mul(erodeMat) > 0;
vector<Point> maxCon;
vector<vector<Point>> contours;
findContours(tarMat, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
maxCon = contours.front();
for (const vector<Point>& contour : contours)
{
if (contour.size() > maxCon.size())
{
maxCon = contour;
}
}
float minDis = FLT_MAX;
Point initialPoint = maxCon.front();
for (int i = 0; i < maxCon.size(); i++)
{
const Point& p = maxCon[i];
float dis = pointDis(p, center);
if (dis < minDis)
{
minDis = dis;
initialPoint = p;
}
}
return (minDis - 2);
}
float ImageCompareModel::allocateRadius(cv::Mat subImage)
{
if (subImage.size() != mAlignBaseImg.size())
{
resize(subImage, subImage, mAlignBaseImg.size());
}
cv::Mat binaryImg = subImage > 30;
const int innerRadius = binaryImg.rows*0.05;
const int outterRadius = binaryImg.cols*0.22;
const Point center(binaryImg.cols / 2.0, binaryImg.rows / 2.0);
Mat mask(binaryImg.size(), CV_8UC1);
mask.setTo(0);
circle(mask, center, outterRadius, Scalar(1), -1);
Mat dilatedImgBin;
Mat erodeMat;
dilate(binaryImg, dilatedImgBin, Mat::ones(9, 9, CV_32FC1));
erode(dilatedImgBin, erodeMat, Mat::ones(9, 9, CV_32FC1));
openOper(erodeMat, Mat::ones(1, 13, CV_32FC1));
circle(erodeMat, center, innerRadius, Scalar(255), -1);
Mat tarMat = mask.mul(erodeMat) > 0;
vector<Point> maxCon;
vector<vector<Point>> contours;
findContours(tarMat, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
maxCon = contours.front();
for (const vector<Point>& contour : contours)
{
if (contour.size() > maxCon.size())
{
maxCon = contour;
}
}
float minDis = FLT_MAX;
Point initialPoint = maxCon.front();
for (int i = 0; i < maxCon.size(); i++)
{
const Point& p = maxCon[i];
float dis = pointDis(p, center);
if (dis < minDis)
{
minDis = dis;
initialPoint = p;
}
}
return (minDis - 2);
}
void ImageCompareModel::genOutterMask()
{
m32fMaskImg = genMask(mAlignBaseImg, Point2f(mAlignBaseImg.cols / 2.0, mAlignBaseImg.rows / 2.0));
m8uMaskImg = genMask(mAlignBaseImg, Point2f(mAlignBaseImg.cols / 2.0, mAlignBaseImg.rows / 2.0), -1.0, -1.0, CV_8UC1);
}
void ImageCompareModel::resizeVecMat(vector<Mat> srcVec, vector<Mat> &dstVec)
{
if (srcVec.size() <= 0)
return;
dstVec.resize(srcVec.size());
int nWidth = ((int)((float)srcVec.front().cols / COLS_SCALE / 4)) * 4;
for (int i = 0; i < srcVec.size(); i++)
{
const Mat& mat = srcVec[i];
Mat dstMat;
cv::resize(mat, dstMat, cv::Size(nWidth, nWidth));
dstVec[i] = dstMat;
}
}
void ImageCompareModel::resizeMat(Mat src, Mat &dst)
{
int nWidth = ((int)((float)src.cols / COLS_SCALE / 4)) * 4;
cv::resize(src, dst, cv::Size(nWidth, nWidth));
}