add molun car code

newClassify
bobpan 5 years ago
parent f518d476b5
commit 3d2a85cf60

File diff suppressed because it is too large Load Diff

@ -0,0 +1,610 @@
/*! \file CVUtils.h
\brief Some functions extend opencv classes
Created: 2015/06/22, author: Jin Bingwen.
*/
#ifndef __CVUtils_h_
#define __CVUtils_h_
#include "StdUtils.h"
#include <opencv2/opencv.hpp>
#include <opencv2/opencv_modules.hpp>
#include <vector>
#include <iostream>
#include <fstream>
#include <map>
#include "pointpair.h"
#if defined(_DEBUG) && defined(_VIEW_INTERNAL_MAT)
#define DEBUG_VIEW_INTERNAL_MAT
#endif
#if defined(_DEBUG) && defined(_DETAIL_LOG)
#define DEBUG_DETAIL_LOG
#endif
#define M_PI CV_PI
#define INTER_POINT_Y(p0x, p0y, p1x, p1y, p2x) ((p1y - p0y)/(p1x - p0x)*(p2x - p0x) + p0y)
#define INTER_POINT_X(p0x, p0y, p1x, p1y, p2y) ((p1x - p0x)/(p1y - p0y)*(p2y - p0y) + p0x)
using std::vector;
using std::pair;
using namespace cv;
typedef Size_<double> Sized;
typedef Size_<float> Sizef;
namespace CyclopsUtils {
// Hold an empty dummy mat which is useful when you want to return a Mat() incase of some failure,
// or pass a Mat() as defualt function parameter.
// This will save some computation cost for initialization and deallocation of a local variable.
extern Mat gDummyMat;
// Hold an empty dummy Scalar which is useful when you want to return a Scalar() incase of some failure,
// or pass a Scalar() as defualt function parameter.
// This will save some computation cost for initialization and deallocation of a local variable.
extern Scalar gDummyScalar;
string templateMatchMethod2Str(int method);
// Carmack fast InvSqrt!
inline float InvSqrt(float x)
{
float xhalf = 0.5f*x;
int i = *(int*)&x;
i = 0x5f3759df - (i >> 1); // 计算第一个近似根
x = *(float*)&i;
x = x*(1.5f - xhalf*x*x); // 牛顿迭代法
return x;
}
void printIndent(int indentNum, int indent = 4);
template<typename _Ty>
void printProperty(int indentNum, string label, _Ty val)
{
printIndent(indentNum);
std::cout << label << ": " << val << "; " << std::endl;
}
void printMatInfo(const Mat& m, int baseIndentNum = 0, int indent = 4);
void alignEachRowRightBound(Mat& img, int tarCen, vector<int>& dxVec,
int gradientKernelWidth = 12,
int smoothBoundaryKernelWidth = 32,
float smoothBoundaryWeight = 3.0,
int backgroundThre = 40);
void autoAlignEachRowGloballyWithIndependentBoundaries(Mat& img);
void autoAlignEachRowWithWeightedXCen(Mat& img, int tarCenX, vector<int>& dxVec);
void autoAlignEachRowWithAutoThreshold(Mat& img, int tarCenX, vector<int>& dxVec);
void alignEachRow(Mat& img, const vector<int>& dxVec);
void autoAlignEachRowGloballyWithWidthConstraint(Mat& img, int tarCenX, vector<int>& dxVec);
void autoAlignEachRowLocally(Mat& img, int searchRadius = 5, int rowWidth = 1, const Mat& preImg = Mat());
void openOper(Mat& img, int w);
void openOper(Mat& img, const Mat& kernel);
void closeOper(Mat& img, int w);
void closeOper(Mat& img, const Mat& kernel);
void localCloseOper(Mat& img, vector< vector<Point> >& contours, float longerScale);
Mat genCircleMask(int rows, int cols, int type,
Point cen, double r,
const Scalar& color, int thickness, int lineType = 8, int shift = 0);
void closeShapesToConvex(const Mat& mask, Mat& closedMask);
double longShortRatio(const Size& s);
float lpContourArea(const vector<Point>& contour);
float circleDegree(const vector<Point>& contour);
enum LogicOper
{
LP_LOGIC_LARGER,
LP_LOGIC_SMALLER
};
Mat gridThre(const Mat& img, int gridWidth, int gridHeight, float threStdDevScale, LogicOper oper /*= LP_LOGIC_SMALLER*/, float majority /*= 0.8*/);
Mat gridWhiteThre(const Mat& img, int gridWidth, int gridHeight, float threStdDevScale, float majority /*= 0.8*/);
Mat gridBlackThre(const Mat& img, int gridWidth, int gridHeight, float threStdDevScale, float majority = 0.8);
void converToType(cv::Mat& mat, int mattype);
void genScharrImage(Mat& img);
void genSobelImage(Mat& img, Mat* pSobelx = NULL, Mat* pSobely = NULL);
void genSobelDir(Mat& img);
Mat genSobelDir(const Mat& sobelX, const Mat& sobelY);
Mat genDirColorImg(const Mat& img, Mat* pValueMat = NULL);
template<typename _T>
void histMeanStddev(const Mat& hist, double* pMean, double* pStddev)
{
_T* pData = (_T*)hist.data;
double sum = 0;
int count = 0;
for (int i = 0; i < hist.cols; ++i)
{
if (!pData[i])
{
continue;
}
count += pData[i];
sum += pData[i]*i;
}
if (count == 1)
{
if (pMean)
{
*pMean = sum;
}
if (pStddev)
{
*pStddev = 0;
}
return;
}
*pMean = sum / (double)count;
sum = 0;
for (int i = 0; i < hist.cols; ++i)
{
if (!pData[i])
{
continue;
}
double d = i - *pMean;
sum += d*d*pData[i];
}
*pStddev = sqrt(sum / (double)(count - 1));
}
bool localMeanVarNorm(const Mat& srcMat, Mat& dstMat, int winRadius, double tarMean = 120, double tarStd = 30);
Mat genXDeriLineKernel(int w, int type, double absVal);
Mat genXDeriMat(const Mat& img, int w, double absVal);
/************************************************************************/
/* Normalize */
/************************************************************************/
Mat normEachRow(const Mat& img);
enum PixelSelectionMethod
{
LP_PIXEL_SELECT_ALL,
LP_PIXEL_SELECT_MAJORITY,
LP_PIXEL_SELECT_MAJORITY_FAST
};
Mat cocentricNorm(const Mat& img, Point2f center, const Mat& mask, float dstMeanVal);
Mat meanNorm(const Mat& img, const Mat& mask, float dstMeanVal,
float majority = 1.0,
PixelSelectionMethod method = LP_PIXEL_SELECT_ALL);
Mat minMaxNorm(const Mat& img, double tarMin, double tarMax, const Mat& mask = Mat());
Mat normCanvas(const Mat& img);
/**********************************************************************/
Mat genGradientDir4EachRow(const Mat& img);
void findMatElementsEquals(const Mat& img, vector<Point>& pointVec, float val, int xPadding);
double localMatSum(const Mat& mat, const Rect& roi);
void findEdgePointsEachRow(const Mat& img, vector<Point>& edgePointVec, int xPadding);
template<typename _Ty>
Mat sumEachRow(const Mat& iMat)
{
_ASSERTE(iMat.channels() == 1);
Mat oVec(iMat.rows, 1, DataType<_Ty>::type);
for (int i = 0; i < iMat.rows; ++i)
{
Mat rowVec = iMat.row(i);
Scalar s = cv::sum(rowVec);
oVec.at<_Ty>(i) = s.val[0]/rowVec.cols;
}
return oVec;
}
template<typename _Ty, int cn>
Mat sumEachRowN(const Mat& iMat)
{
_ASSERTE(iMat.channels() == cn);
Mat oVec(iMat.rows, 1, CV_MAKETYPE(DataType<_Ty>::type, cn));
for (int i = 0; i < iMat.rows; ++i)
{
Mat rowVec = iMat.row(i);
Scalar s = cv::sum(rowVec);
Vec<_Ty, cn>& d = oVec.at<Vec<_Ty, cn> >(i);
for (int j = 0; j < cn; ++j)
d[j] = s[j] / iMat.cols;
}
return oVec;
}
Mat sumEachRow2(const Mat& img);
template<typename _Ty>
Mat sumEachCol(const Mat& iMat)
{
_ASSERTE(iMat.channels() == 1);
Mat oVec(1, iMat.cols, DataType<_Ty>::type);
for (int i = 0; i < iMat.cols; ++i)
{
Mat colVec = iMat.col(i);
Scalar s = cv::sum(colVec);
oVec.at<_Ty>(i) = s.val[0]/colVec.rows;
}
return oVec;
}
template<typename _Ty, int cn>
Mat sumEachColN(const Mat& iMat)
{
_ASSERTE(iMat.channels() == cn);
Mat oVec(1, iMat.cols, CV_MAKETYPE(DataType<_Ty>::type, cn));
for (int i = 0; i < iMat.cols; ++i)
{
Mat colVec = iMat.col(i);
Scalar s = cv::sum(colVec);
Vec<_Ty, cn>& d = oVec.at<Vec<_Ty, cn> >(i);
for (int j = 0; j < cn; ++j)
d[j] = s[j] / colVec.rows;
}
return oVec;
}
Mat sumEachCol2(const Mat& img);
/************************************************************************/
/* Threshold */
/************************************************************************/
Mat thresholdEachRowLocally(const Mat& img, int localRange = 501, int C = 10);
Mat thresholdEachRow(const Mat& img, float threScale = 0.5);
Mat thresholdEachRow(const Mat& img, const Mat& rowThre, float threScale);
// As OpenCV doesn't provide any API to get automatic threshold value only without actually apply the threshold,
// however in some cases, we need this value only to apply our own threshold type.
// So, copied out the private getThreshVal_Otsu_8u and getThreshVal_Triangle_8u function here,
// and we provide the uniformed getAutoThresValue function to call the above two.
// Pass in THRESH_OTSU for Otsu threshold, and THRESH_TRIANGLE for the other.
double getAutoThresValue(const Mat& img, int type = THRESH_OTSU);
/**********************************************************************/
void segEachRow(const Mat& img, vector<PointPair>& pointPairVec, int xPadding);
void fillPointPairVal(const Mat& img, std::string filePath, vector<PointPair>& pointPairVec);
Mat getFirstChannel(const Mat& img);
Mat getChannel(const Mat& img, int i);
void setChannel(Mat& img, int i, Mat chnMat);
void mulEachRow(Mat& img, const Mat& scaleRow);
void genRandomPoints(const Mat& img, vector<Point>& points, RNG rng, int sampleCnt = 1000);
void plot8uVec(const Mat& vec, float scale = 1.0);
void plot32fVec(const Mat& vec, float scale = 1.0);
Mat calcHist(const Mat& img, const Mat& mask, int histSize = 256, int minVal = 0, int maxVal = 256);
template<typename T>
Mat calcHist(const vector<T>& vals, unsigned int histSize, T* pMinVal = nullptr, T* pMaxVal = nullptr)
{
_ASSERTE(histSize > 1);
if (histSize <= 1) return gDummyMat;
int minValIdx = -1;
int maxValIdx = -1;
int valSize = vals.size();
if (!pMinVal || !pMaxVal) {
for (int i = 0; i < valSize; ++i)
{
const T& v = vals[i];
if (minValIdx < 0 || v < vals[minValIdx]) {
minValIdx = i;
}
if (maxValIdx < 0 || v > vals[maxValIdx]) {
maxValIdx = i;
}
}
}
T minVal = pMinVal ? *pMinVal : vals[minValIdx];
T maxVal = pMaxVal ? *pMaxVal : vals[maxValIdx];
if (minVal == maxVal) return gDummyMat;
double histStep = (maxVal - minVal) / (histSize-1); // ensure max value is in analysis.
Mat hist(2, histSize, CV_32S, Scalar(0));
int* axis = (int*)hist.ptr(1);
for (int i = 0; i < histSize; ++i)
axis[i] = histStep * i + minVal;
int* data = (int*)hist.ptr(0);
for (int i = 0; i < valSize; ++i)
{
const T& v = vals[i];
int histIdx = (v - minVal) / histStep;
data[histIdx]++;
}
return hist;
}
Mat resize(const Mat& img, float s, int interMethod = cv::INTER_LINEAR);
void writeFile(const Mat& mat, std::string filePath, std::string matName = "mat");
Mat readFile(std::string filePath, std::string matName);
void gaussianBlurEachRow(const Mat& src, Mat& dst, int ksize = 3);
void medianBlurEachRow(const Mat& src, Mat& dst, int ksize = 3);
double maxLaplacianX(const Mat& rowMat, float scale = 1.0);
double minLaplacianX(const Mat& rowMat, float scale = 1.0);
void Laplacian1D(const Mat& src, Mat& dst, int ddepth, int ksize = 1);
void filterKeyPointsByNeighborDistance(vector<KeyPoint>& vec, float dis);
void filterKeyPointsByRotationInvariants(vector<KeyPoint>& vec, const Mat& img,
FeatureDetector* pDesExtractor, float tor);
double localIC_Angle(const Mat& img, Point2f center, int patchSize);
double localAngle_WeightedCen(const Mat& img, Point2f center);
double localAngle_(const Mat& img, Point2f center, Mat mask);
class GroupMatcher : public BFMatcher
{
public:
CV_WRAP GroupMatcher(int normType = NORM_L2, bool crossCheck = false);
virtual ~GroupMatcher() {}
virtual bool isMaskSupported() const { return false; }
virtual Ptr<DescriptorMatcher> clone(bool emptyTrainData = false) const;
// no longer supported in opencv3.4.1
#if (CV_MAJOR_VERSION < 3)
AlgorithmInfo* info() const;
#endif
protected:
virtual void knnMatchImpl(const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int k,
const vector<Mat>& masks = vector<Mat>(), bool compactResult = false);
virtual void radiusMatchImpl(const Mat& queryDescriptors, vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks = vector<Mat>(), bool compactResult = false);
int normType;
bool crossCheck;
};
void upperMajorityMask(const Mat& img, Mat& mask, float majority);
void majorityHistLower(const Mat& img, const Mat& mask, Mat& hist, float majority);
void majorityHistUpper(const Mat& img, const Mat& mask, Mat& hist, float majority);
Mat lowerMajorityMask(const Mat& img, const Mat& mask, float majority);
void meanStdDev(const Mat& img, const Mat& mask, double* pMean, double* pStdDev, float majority, int type = 0);
Mat getRoiImg(const Mat& img, Point2i cen, int radius);
Mat getRoiImg(const Mat& img, vector<Point2i> ptVec, int radius);
Mat filterY(const Mat& img, float* kernel, int size, int ddepth = CV_32FC1);
Mat getContourBoundedRoiImg(const Mat& src, const vector<Point>& contour, Rect* pRoiRect = NULL);
void filterContours(Mat hsvColorThresImg,
int minArea, double minLength, double minLongShortRatio,
double maxCircleDegree, vector<Point>* pAreaMaxContour = NULL);
void filterContours(Mat& img, double minArea);
void filterContours(const vector< vector<Point> >& contours, const Mat& srcImg,
vector< vector<Point> >& filteredContours, int minArea, double minLength,
double minLongShortRatio, double maxCircleDegree, vector<Point>* pAreaMaxContour = NULL);
int filterSmallAndFindMaxContours(vector< vector<Point> >& contours, double minArea);
Mat genInRangeMask(const Mat& src, std::map<int, pair<Scalar, Scalar>>& colorRanges);
void genContrastImg(const Mat& img, Mat& contrastImg, const Mat& kernelMat);
void genContrastImg(const Mat& img, Mat& contrastImg, int ksize);
void genYContrastImg(const Mat& img, Mat& constrastImg, int ksize);
void genHalfContrastImg(const Mat& img, Mat& contrastImg, const Mat& kernelMat);
void genHalfContrastImg(const Mat& img, Mat& contrastImg, int ksize);
void cvtColor(Mat& img, int t);
Mat rotateImage(const Mat& img, Point2f cen, float degree);
Mat normAngle(const Mat& img, Mat mask);
Mat genSimpleXGradient(const Mat& m);
void uniformLocalMinExtremas(const Mat& vec, Mat& localExtremaValVec,
vector<int>& idxVec, int n, int refineRange);
void equalizeHist(const Mat& src, Mat& dst, const Mat& mask = gDummyMat);
void removeHighlights(const Mat& src, Mat& dst, float thre);
void genSectorSumVec(const Mat& img, const Mat& weightMat, Mat& hist,
Mat& weightHist, const Point2f& cen, float angleStep,
Mat* pAngMat = NULL, Mat* pMagMat = NULL);
void applySectorScales(Mat& img, const Mat& hist, const Mat& angMat);
void normSectors(Mat& img, Mat weightMat, const Point2f& cen, float angleStep,
float tarVal);
void normSectors_tarImg(Mat& img, Mat weightMat, const Point2f& cen, float angleStep,
const Mat& tarImg);
void normSectorsMeans_tarHist(Mat& img, const Mat& hist, const Point2f& cen,
float angleStep, const Mat& tarHist, const Mat& angMat);
Point2f imgCen(const Mat& img);
class luffyCircle;
cv::Mat findCircleObject(const Mat &src, const Mat& backgroundImg,
int nThres = 20, luffyCircle *pCircle = NULL);
bool ensureGrayImg(const Mat& img, Mat& gray);
/************************************************************************/
/* Color Space */
/************************************************************************/
double hsvDis(const Vec3b& v0, const Vec3b& v1);
Mat genHSVColorDisMat(const Mat& m0, const Mat& m1);
Mat genHSVColorDisMat(const Mat& m, Vec3b baseColor);
Mat genHSVColorDisMat(const Mat& m);
Mat genBlueGreenRatioMat(const Mat& m, float alpha = 166.32, float beta = -16.414);
Mat genColorFuncDisMat(const Mat& m, float a = 0.001, float b = 0.4061, float c = -5.6845);
template<typename T>
float validate_diff(T result, T target, float err_tol)
{
T diff = result - target;
float normDiff = cv::norm(diff);
return normDiff <= err_tol ? 0 : normDiff;
}
template<typename T>
T getImgSubPix(const Mat& img, float x, float y)
{
_ASSERTE(!img.empty());
_ASSERTE(x >= 0 && y >= 0 && x <= img.cols - 1 && y <= img.rows - 1);
int x0 = floor(x);
int y0 = floor(y);
bool noSubX = x0 == x;
bool noSubY = y0 == y;
if (noSubX && noSubY) {
return img.at<T>(y0, x0);
}
else if (noSubX) {
int y1 = y0 + 1;
double c = y1 - y;
double d = 1. - c;
const T* p0 = (const T*)img.ptr(y0);
const T* p1 = (const T*)img.ptr(y1);
T ret = p0[x0] * c + p1[x0] * d;
return ret;
}
else if (noSubY) {
int x1 = x0 + 1;
double a = x1 - x;
double b = 1. - a;
const T* p0 = (const T*)img.ptr(y0);
T ret = a * p0[x0] + b * p0[x1];
return ret;
}
else {
int x1 = x0 + 1;
int y1 = y0 + 1;
double a = x1 - x;
double b = 1. - a;
double c = y1 - y;
double d = 1. - c;
const T* p0 = (const T*)img.ptr(y0);
const T* p1 = (const T*)img.ptr(y1);
T ret = (a * p0[x0] + b * p0[x1]) * c +
(a * p1[x0] + b * p1[x1]) * d;
return ret;
}
}
template<typename T>
vector<T> cvtMat2Vec(const Mat& img)
{
_ASSERTE(!img.empty());
int count = img.cols * img.rows;
std::vector<T> vals(count);
if (img.isContinuous()) {
memcpy((T*)vals.data(), (T*)img.data, sizeof(T)*count);
}
else {
int rows = img.rows;
T* vals_p = vals.data();
size_t rowStep = sizeof(T)*img.cols;
for (int i = 0; i < rows; ++i) {
memcpy(vals_p, img.ptr<T>(i), rowStep);
vals_p += img.cols;
}
}
return vals;
}
template<typename T, typename S>
std::list<T> cvtMat2List(const Mat& img)
{
_ASSERTE(!img.empty());
int rows = img.rows;
int cols = img.cols;
int count = cols * rows;
std::list<T> vals;
for (int i = 0; i < rows; ++i) {
const S* img_p = img.ptr<S>(i);
for (int j = 0; j < cols; ++j) {
vals.push_back((S)(img_p[j]));
}
}
return vals;
}
template<typename T>
T getNthElement(const Mat& img, int n)
{
_ASSERTE(!img.empty());
int count = img.cols * img.rows;
_ASSERTE(n < count);
std::vector<T> vals = cvtMat2Vec<T>(img);
std::nth_element(vals.begin(), vals.begin() + n, vals.end());
return vals[n];
}
int getLocalMaximun(const Mat& vec, double thres, std::vector<int>* pMaxIdxVec);
int getLocalMinimun(const Mat& vec, double thres, std::vector<int>* pMaxIdxVec);
enum EnumAbnormalType {
AbTypePositive = 0,
AbTypeNegative,
AbTypeBoth
};
int detectAbnormal(const Mat& vec, int kernelSize, EnumAbnormalType abnormalType, double abnormalThreshold,
Mat* pScores = nullptr, std::vector<int>* pAbIdxVec = nullptr);
};
using namespace CyclopsUtils;
#include "GeomUtils.h"
#endif // __CVUtils_h_

@ -0,0 +1,243 @@
#include "DynamicProgramSearch.h"
#include <list>
using std::list;
void dynamicProgramY(Mat& disMat, int n /*= 2*/)
{
for (int y = 1; y < disMat.rows; ++y)
{
for (int x = 0; x < disMat.cols; ++x)
{
// find max neighbor
int sy = y - 1;
float maxVal = -1;
int sj = x - n >= 0 ? x - n : 0;
int ej = x + n <= disMat.cols - 1 ? x + n : disMat.cols - 1;
for (int j = sj; j <= ej; j++)
{
float val = disMat.at<float>(sy, j);
if (maxVal < val)
{
maxVal = val;
}
}
disMat.at<float>(y, x) += maxVal;
}
}
}
void dynamicProgramX(Mat& disMat, int n /*= 2*/)
{
for (int x = 1; x < disMat.cols; ++x)
{
int sx = x - 1;
for (int y = 0; y < disMat.rows; ++y)
{
// find max neighbor
float maxVal = -1;
int si = max(y - n, 0);
int ei = min(y + n, disMat.rows - 1);
for (int i = si; i <= ei; i++)
{
float val = disMat.at<float>(i, sx);
if (maxVal < val)
{
maxVal = val;
}
}
disMat.at<float>(y, x) += maxVal;
}
}
}
void findMaxXPath(const Mat& disMat, vector<int>& vec, vector<float>& energyVec,
int n /*= 2*/)
{
int sy = 0;
int ey = disMat.rows - 1;
vec.resize(disMat.cols, -1);
energyVec.resize(disMat.cols, -1);
for (int x = disMat.cols - 1; x >= 0; --x)
{
float maxVal = -1;
int maxIdx = -1;
for (int y = sy; y <= ey; ++y)
{
float val = disMat.at<float>(y, x);
if (maxVal < val)
{
maxVal = val;
maxIdx = y;
}
}
vec[x] = maxIdx;
energyVec[x] = maxVal;
sy = maxIdx - n;
ey = maxIdx + n;
sy = (sy >= 0 ? sy : 0);
ey = (ey < disMat.rows ? ey : disMat.rows - 1);
}
}
void findMaxYPath(const Mat& disMat, vector<int>& vec, vector<float>& energyVec, int n /*= 2*/)
{
int sx = 0;
int ex = disMat.cols - 1;
vec.resize(disMat.rows, -1);
energyVec.resize(disMat.rows, -1);
for (int y = disMat.rows - 1; y >= 0; --y)
{
float maxVal = -1;
int maxIdx = -1;
for (int x = sx; x <= ex; ++x)
{
float val = disMat.at<float>(y, x);
if (maxVal < val)
{
maxVal = val;
maxIdx = x;
}
}
vec[y] = maxIdx;
energyVec[y] = maxVal;
sx = maxIdx - n;
ex = maxIdx + n;
sx = (sx >= 0 ? sx : 0);
ex = (ex < disMat.cols ? ex : disMat.cols - 1);
}
}
void recoverPathEnergy(vector<float>& vec)
{
assert(!vec.empty());
auto i = vec.rbegin();
auto j = i;
++j;
while (j != vec.rend())
{
*i = *i - *j;
++i;
++j;
}
}
void dynamicProgramYWithSmooth(Mat& disMat,
int n /*= 2*/, float sw /*= 1.0*/,
const Mat& smoothMat /*= Mat()*/)
{
for (int y = 1; y < disMat.rows; ++y)
{
for (int x = 0; x < disMat.cols; ++x)
{
// find max neighbor
int sy = y - 1;
float maxVal = -1;
int sj = x - n >= 0 ? x - n : 0;
int ej = x + n <= disMat.cols - 1 ? x + n : disMat.cols - 1;
float baseSVal = 0;
if (!smoothMat.empty())
{
baseSVal = smoothMat.at<float>(y, x);
}
for (int j = sj; j <= ej; j++)
{
float disVal = disMat.at<float>(sy, j);
float sVal = 0;
if (!smoothMat.empty())
{
sVal = smoothMat.at<float>(sy, j);
}
float energyVal = disVal + (255 - abs(sVal - baseSVal))*sw;
if (maxVal < energyVal)
{
maxVal = energyVal;
}
}
disMat.at<float>(y, x) += maxVal;
}
}
}
void dynamicProgramYWithSmooth(Mat& disMat0, Mat& disMat1,
const Mat& smoothMat0,
const Mat& smoothMat1,
int targetWidth,
int n /* = 2 */,
float sw /* = 1.0 */)
{
assert(targetWidth <= disMat0.cols + disMat1.cols);
for (int y = 1; y < disMat0.rows; ++y)
{
int sx = 0;
int ex = disMat0.cols;
if (targetWidth < disMat0.cols)
{
sx = disMat0.cols - targetWidth;
}
if (targetWidth > disMat1.cols)
{
ex = disMat0.cols - (targetWidth - disMat1.cols);
}
for (int x = sx; x < ex; ++x)
{
// find max neighbor
int sy = y - 1;
float maxVal = -1;
int sj0 = x - n >= 0 ? x - n : 0;
int ej0 = x + n <= disMat0.cols - 1 ? x + n : disMat0.cols - 1;
int sj1 = sj0 + targetWidth - disMat0.cols;
int ej1 = ej0 + targetWidth - disMat0.cols;
if (sj1 < 0)
{
sj0 += -sj1;
}
if (ej1 > disMat1.cols - 1)
{
ej0 -= ej1 - disMat1.cols + 1;
}
float baseSVal0 = 0;
if (!smoothMat0.empty())
{
baseSVal0 = smoothMat0.at<float>(y, x);
}
float baseSVal1 = 0;
if (!smoothMat1.empty())
{
baseSVal1 = smoothMat1.at<float>(y, x + targetWidth - smoothMat0.cols);
}
for (int j = sj0; j <= ej0; j++)
{
float disVal0 = disMat0.at<float>(sy, j);
int j1 = j + targetWidth - disMat0.cols;
float disVal1 = disMat1.at<float>(sy, j1);
float sVal0 = 0;
if (!smoothMat0.empty())
{
sVal0 = smoothMat0.at<float>(sy, j);
}
float sVal1 = 0;
if (!smoothMat1.empty())
{
sVal1 = smoothMat1.at<float>(sy, j1);
}
float energyVal = disVal0 + disVal1 +
(255 - abs(sVal0 - baseSVal0))*sw +
(255 - abs(sVal1 - baseSVal1))*sw;
if (maxVal < energyVal)
{
maxVal = energyVal;
}
}
disMat0.at<float>(y, x) += maxVal;
}
}
}

@ -0,0 +1,118 @@
#ifndef _DynamicProgramSearch_h_
#define _DynamicProgramSearch_h_
#include <opencv2/opencv.hpp>
#include "CVUtils.h"
using namespace cv;
using std::vector;
void dynamicProgramYWithSmooth(Mat& disMat, int n = 2, float sw = 1.0, const Mat& smoothMat = Mat());
void dynamicProgramYWithSmooth(Mat& disMat0, Mat& disMat1,
const Mat& smoothMat0, const Mat& smoothMat1, int targetWidth, int n, float sw);
void dynamicProgramY(Mat& disMat, int n = 2);
void dynamicProgramX(Mat& disMat, int n = 2);
void findMaxXPath(const Mat& disMat, vector<int>& vec, vector<float>& energyVec,
int n = 2);
void findMaxYPath(const Mat& disMat, vector<int>& vec, vector<float>& energyVec,
int n = 2);
void recoverPathEnergy(vector<float>& vec);
inline float ptLineDisY(const Vec4f& line, const Point& pt)
{
float p0x = line.val[2];
float p0y = line.val[3];
float p1x = line.val[0] + p0x;
float p1y = line.val[1] + p0y;
float px = pt.x;
float py = pt.y;
float y = p0y + (p1y - p0y) / (p1x - p0x) * (px - p0x);
float dy = y - py;
if (dy > 0)
{
return dy;
}
else
{
return -dy;
}
}
inline float ptLineDis(const Vec4f& line, const Point& pt)
{
float a = line.val[1];
float b = -line.val[0];
float c = -a*line.val[2] - b*line.val[3];
return (a * (float)pt.x + b * (float)pt.y + c) * InvSqrt(a*a + b*b);
}
inline float ptLineDis(const Vec4f& line, const Point2f& pt)
{
float a = line.val[1];
float b = -line.val[0];
float c = -a*line.val[2] - b*line.val[3];
return (a * pt.x + b * pt.y + c) * InvSqrt(a*a + b*b);
}
inline float ptLineDis(const Vec4f& line, const vector<Point>& ptVec)
{
float ret = 0;
for (int i = 0; i < ptVec.size(); ++i)
{
const Point& pt(ptVec[i]);
float d = ptLineDis(line, pt);
ret += abs(d);
}
return ret;
}
template<typename _T>
void drawPointsX(vector<int>& xVec, Mat& img, int sx, int sy, int color = 255, int w = 1)
{
for (int x = 0; x < xVec.size(); ++x)
{
int y = xVec[x] + sy;
img.at<_T>(y, x + sx) = color;
for (int i = 1; i <= w / 2; ++i)
{
if (y - i >= 0)
{
img.at<_T>(y - i, x + sx) = color;
}
if (y + i < img.rows)
{
img.at<_T>(y + i, x + sx) = color;
}
}
}
}
template<typename _T>
void drawPointsY(vector<int>& yVec, Mat& img, int sx, int sy, int color = 255, int w = 1)
{
for (int y = 0; y < yVec.size(); ++y)
{
int x = yVec[y] + sx;
img.at<_T>(y + sy, x) = color;
for (int i = 1; i <= w / 2; ++i)
{
if (x - i >= 0)
{
img.at<_T>(y + sy, x - i) = color;
}
if (x + i < img.cols)
{
img.at<_T>(y + sy, x + i) = color;
}
}
}
}
#endif // _DynamicProgramSearch_h_

File diff suppressed because it is too large Load Diff

@ -0,0 +1,380 @@
/*! \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 "CircleDetector.h"
#define COLS_SCALE (float)(1200.0/ 416.0)
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<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);
static 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)
{};
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();
}
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);
//! 将一个图像和标准图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; }
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 img0mBaseImgrimg0
\param Mat * pRImg1 img1mBaseImgrimg1
\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 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;
// 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;
};

@ -0,0 +1,146 @@
#include "MultiScaleImageCompareModel.h"
#include "ImageCompareModel.h"
// 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
#include "../3rd/cvplot/cvplot.h"
#endif
void MultiScaleImageCompareModel::setBaseLevel(const ImageCompareModel* pModel)
{
ImageCompareModel* p = new ImageCompareModel;
*p = *pModel;
p->genOutterMask();
clear();
mMultiScaleModels.resize(1);
mMultiScaleModels[0] = p;
}
void MultiScaleImageCompareModel::genMultiScale()
{
resetMaxLevel();
while (getLevel() >= 1)
{
if (mMultiScaleModels.size()>1)
{
ImageCompareModel* pScaledModel = mMultiScaleModels.back()->scale(mScaleStep);
mMultiScaleModels.push_back(pScaledModel);
downLevel();
}
else
downLevel();
}
}
const ImageCompareModel* MultiScaleImageCompareModel::getCmpModel(int level)
{
if (level >= 0 && level < mLevelNum)
{
return mMultiScaleModels[level];
}
else
{
return NULL;
}
}
void MultiScaleImageCompareModel::clear()
{
for_each(mMultiScaleModels.begin(), mMultiScaleModels.end(), [&](ImageCompareModel* p)
{
delete p;
});
}
void MultiScaleImageCompareModel::proc(void *pData)
{
ImageCompareData* pCmpData = (ImageCompareData*)pData;
MultiScaleImage* pMSImg = pCmpData->getMSI();
resetMaxLevel();
float startAngle = pCmpData->getStartAngle();
float endAngle = pCmpData->getEndAngle();
float angleRange = (endAngle - startAngle) / 4.0;
vector<Range> angleRangeVec(1, Range(0, 360));
ImageCompareModel::RotateData* pRotateData = pCmpData->getRotateData();
int nlevel = getLevel();
while (nlevel >= 0)
{
nlevel = getLevel();
if (nlevel<0)
break;
const ImageCompareModel* pModel = NULL;
if (nlevel >= mMultiScaleModels.size())
pModel = mMultiScaleModels[0];
else
pModel = mMultiScaleModels[nlevel];
assert(pModel);
Mat img = pMSImg->getImg(nlevel);
assert(!img.empty());
#ifdef DEBUG_VIEW_INTERNAL_MAT
Mat viewImg = img / 255.0;
imshow("img", viewImg);
#endif
if (angleRangeVec.size() == 1 && angleRangeVec.front().size() >= 360)
{
pModel->rotateMatchData(img, pRotateData, 1.0, startAngle, endAngle);
ImageCompareModel::genCandidateAngleRanges(pRotateData->mDisValVec, 1, angleRangeVec);
if (angleRangeVec.empty())
{
pRotateData->mDisValVec.clear();
pRotateData->mRImgVec.clear();
angleRangeVec.resize(1);
angleRangeVec[0] = Range(0, 360);
}
}
else
{
Mat disMat;
pModel->rotateMatchData(img, pRotateData, angleRangeVec, 1.0, disMat);
ImageCompareModel::genCandidateAngleRanges(disMat, 1, angleRangeVec, 0.5);
// pModel->rotateMatchData(img, pRotateData, 1.0, angleRangeVec);
//
// double bestAngle = pRotateData->bestAngle();
// angleRangeVec.clear();
// Range r;
// r.start = bestAngle - angleRange;
// r.end = bestAngle + angleRange;
// angleRangeVec.push_back(r);
}
#ifdef DEBUG_VIEW_INTERNAL_MAT
// Mat viewBestRImg = pRotateData->bestRImg() / 255.0;
// imshow("bestRImg", viewBestRImg);
#endif
// 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
#ifdef DEBUG_VIEW_PLOT
{
stringstream ss;
ss << "multi_scale_comp_plot" << getLevel();
CvPlot::clear(ss.str());
CvPlot::plot<float>(ss.str(), &(pRotateData->mDisValVec[0]),
pRotateData->mDisValVec.size());
}
{
stringstream ss;
ss << "multi_scale_comp_plot_0_360_" << getLevel();
CvPlot::clear(ss.str());
ImageCompareModel::RotateData testRotateData = *pRotateData;
pModel->rotateMatchData(img, &testRotateData, 1.0, startAngle, endAngle);
CvPlot::plot<float>(ss.str(), &(testRotateData.mDisValVec[0]),
testRotateData.mDisValVec.size());
}
#endif
#endif
downLevel();
}
}

@ -0,0 +1,74 @@
/*! \file MultiScaleImageCompareModel.h
\brief A brief file description.
A more elaborated file description.
Created: 2017/02/24, author: bang.jin.
*/
#ifndef __MultiScaleImageCompareModel_h_
#define __MultiScaleImageCompareModel_h_
#include "MultiScaleObj.h"
#include <vector>
#include "ImageCompareModel.h"
using std::vector;
class MultiScaleImageCompareModel : public MultiScaleObj
{
public:
MultiScaleImageCompareModel(int levelNum = 2, float scaleStep = 0.5)
: MultiScaleObj(levelNum, scaleStep)
{
}
~MultiScaleImageCompareModel()
{
clear();
}
void setBaseLevel(const ImageCompareModel* pModel);
void genMultiScale();
const ImageCompareModel* getCmpModel(int level);
void clear();
void proc(void *pData);
protected:
vector<ImageCompareModel*> mMultiScaleModels;
private:
};
class ImageCompareData
{
public:
ImageCompareData(MultiScaleImage* pMSI)
: mpMSI(pMSI)
, mStartAngle(0)
, mEndAngle(360)
, mAngleRange(90)
{}
MultiScaleImage* getMSI() const { return mpMSI; }
void setMSI(MultiScaleImage* val) { mpMSI = val; }
float getStartAngle() const { return mStartAngle; }
void setStartAngle(float val) { mStartAngle = val; }
float getEndAngle() const { return mEndAngle; }
void setEndAngle(float val) { mEndAngle = val; }
float getAngleRange() const { return mAngleRange; }
void setAngleRange(float val) { mAngleRange = val; }
ImageCompareModel::RotateData* getRotateData() { return &mRotateData; }
protected:
MultiScaleImage* mpMSI;
ImageCompareModel::RotateData mRotateData;
float mStartAngle, mEndAngle;
float mAngleRange;
};
#endif // __MultiScaleImageCompareModel_h_

@ -0,0 +1,64 @@
#include "../include/MultiScaleObj.h"
#include "../include/CVUtils.h"
MultiScaleObj::MultiScaleObj(int levelNum /*= 2*/, float scaleStep /*= 0.5*/)
: mLevelNum(levelNum)
, mScaleStep(scaleStep)
{
}
MultiScaleObj::~MultiScaleObj()
{
}
MultiScaleImage::MultiScaleImage()
: mpProc(NULL)
{
}
MultiScaleImage::~MultiScaleImage()
{
if (mpProc)
{
delete mpProc;
}
}
void MultiScaleImage::setBaseLevel(const Mat& img)
{
mMultiScaleImages.resize(1);
mMultiScaleImages[0] = img;
}
void MultiScaleImage::genMultiScale()
{
resetMaxLevel();
while (getLevel() >= 1)
{
mMultiScaleImages.push_back(resize(mMultiScaleImages.back(), mScaleStep, INTER_CUBIC));
downLevel();
}
}
void MultiScaleImage::proc(void *pData)
{
resetMaxLevel();
while (getLevel() >= 1)
{
const Mat& img = mMultiScaleImages[getLevel() - 1];
(*mpProc)(img, this, pData);
downLevel();
}
}
cv::Mat MultiScaleImage::getImg(int level)
{
if (level < 0 || level >= mLevelNum)
{
return Mat();
}
return mMultiScaleImages[level];
}

@ -0,0 +1,87 @@
/*! \file MultiScaleObj.h
Copyright (C) 2014 º¼ÖÝÀûçê¿Æ¼¼ÓÐÏÞ¹«Ë¾.
Created by bang.jin at 2017/02/20.
*/
#ifndef __MultiScaleObj_h_
#define __MultiScaleObj_h_
#include <vector>
#include <opencv2/opencv.hpp>
#include <opencv2/opencv_modules.hpp>
using std::vector;
using namespace cv;
class MultiScaleObj;
class MultiScaleImageProc
{
public:
virtual void operator()(const Mat& img, MultiScaleObj* pMSObj, void* pData) = 0;
};
class MultiScaleObj
{
public:
MultiScaleObj(int level = 2, float scaleStep = 0.5);
~MultiScaleObj();
int getLevelNum() const { return mLevelNum; }
void setLevelNum(int val) { mLevelNum = val; }
int getLevel() const { return mLevel; }
float getScaleStep() const { return mScaleStep; }
void setScaleStep(float iVal) { mScaleStep = iVal; }
int getMaxLevel() const { return getLevelNum() - 1; }
int resetMaxLevel()
{
setLevel(getMaxLevel());
return getLevel();
}
int downLevel()
{
mLevel--;
return mLevel;
}
virtual void proc(void *pData) = 0;
protected:
void setLevel(int val) { mLevel = val; }
int mLevelNum;
int mLevel;
float mScaleStep;
private:
};
class MultiScaleImage : public MultiScaleObj
{
public:
MultiScaleImage();
~MultiScaleImage();
void setBaseLevel(const Mat& img);
void genMultiScale();
virtual void proc(void *pData);
MultiScaleImageProc* getProc() const { return mpProc; }
void setProc(MultiScaleImageProc* iVal) { mpProc = iVal; }
Mat getImg(int level);
protected:
vector<Mat> mMultiScaleImages;
MultiScaleImageProc* mpProc;
private:
};
#endif // __MultiScaleObj_h_

@ -0,0 +1,457 @@
/*! \file StdUtils.h
\brief useful functions working with std functions.
Created: 2015/06/22, author: Jin Bingwen.
*/
#ifndef __StdUtils_h_
#define __StdUtils_h_
#if (defined(_MSC_VER) && _MSC_VER <= 1600)
#define LITTLE_CPP11 1
#else
#include <thread>
#endif
#if (defined _WINDOWS) || (defined WIN32)
#define USE_WIN_API 1
#endif
#include "CyclopsLock.h"
#include <vector>
#include <string>
#include <sstream>
#include <memory>
#include <map>
#include <fstream>
#include <algorithm>
#include "Asserte.h"
using std::string;
using std::vector;
using std::stringstream;
#if defined(LITTLE_CPP11)
#define GET_SIGN(x) (x < 0 ? true : false)
#else
#define GET_SIGN(x) std::signbit(x)
#endif
template<typename T, typename _Iter>
T sum(_Iter s, _Iter e)
{
if (s == e)
{
return T();
}
T ret = *s;
s++;
while (s != e)
{
ret += *s;
s++;
}
return ret;
}
template<typename T, typename _Iter>
_Iter findTopNPercentEle(_Iter s, _Iter e, float nPersent)
{
auto maxVal = std::max_element(s, e);
T threVal = (*maxVal)*(1.0f - nPersent);
while (s != e)
{
if (*s < threVal)
{
return s;
}
++s;
}
return s;
}
template<typename T, typename _Iter>
_Iter findSumTopNPercentEle(_Iter s, _Iter e, float nPersent)
{
T sumVal = sum<T, _Iter>(s, e);
T threVal = sumVal*nPersent;
sumVal = 0;
while (s != e)
{
sumVal += *s;
if (sumVal > threVal)
{
s++;
return s;
}
s++;
}
return e;
}
template<typename T>
void clearAndResetVec(vector<T>* vec, int n)
{
if (vec) {
vec->clear();
vec->reserve(n);
}
}
template<typename T>
void genIncVec(vector<T>& vec, T start, int count, T step)
{
for (int i = 0; i < count; ++i)
{
vec.push_back(start);
start += step;
}
}
class SortEle
{
public:
SortEle() : mSortVal(0), pEle(NULL) {}
SortEle(double val, const void* pData) : mSortVal(val), pEle(pData) {}
double mSortVal;
const void* pEle;
bool operator< (const SortEle& i)
{
return mSortVal < i.mSortVal;
}
bool operator>(const SortEle& i)
{
return mSortVal > i.mSortVal;
}
SortEle operator+ (const SortEle& i)
{
return SortEle(mSortVal + i.mSortVal, pEle);
}
void operator+= (const SortEle& i)
{
mSortVal += i.mSortVal;
}
SortEle operator- (const SortEle& i)
{
return SortEle(mSortVal - i.mSortVal, pEle);
}
operator float()
{
return (float)mSortVal;
}
operator double()
{
return mSortVal;
}
SortEle operator* (float f)
{
return SortEle(mSortVal*f, pEle);
}
void operator= (float f)
{
mSortVal = f;
}
};
template<typename _It, typename _Ty>
_It findRange(_It s, _It e, const _Ty& val)
{
if (s == e)
{
return s;
}
_It mi = (e - s) / 2 + s;
if (val < *mi)
{
return findRange(s, mi, val);
}
else if (val > *mi)
{
return findRange(mi + 1, e, val);
}
else
{
return mi;
}
}
template<typename _Ty, typename _It>
void add(_It s, _It e, const _Ty& val)
{
while (s != e)
{
*s += val;
s++;
}
}
template<typename _Ty, typename _It>
bool allInRange(_It s, _It e, _Ty minVal, _Ty maxVal)
{
while (s != e)
{
if (*s < minVal || *s > maxVal)
{
return false;
}
++s;
}
return true;
}
template<typename _Ty, typename _It>
bool anyInRange(_It s, _It e, _Ty minVal, _Ty maxVal)
{
while (s != e)
{
if (*s >= minVal && *s <= maxVal)
{
return true;
}
++s;
}
return false;
}
template<typename _Ty, typename _It>
bool anyIn(_It s, _It e, _Ty v)
{
while (s != e)
{
if (*s == v)
{
return true;
}
++s;
}
return false;
}
template<typename _T>
bool loadAValueFromFile(string filePath, _T& ret)
{
std::fstream fs;
fs.open(filePath, std::fstream::in);
if (!fs.is_open())
{
return false;
}
fs >> ret;
fs.close();
}
// search range is [si, ei), not include ei
template<typename _PairIter>
_PairIter max_first_element(_PairIter si, _PairIter ei)
{
if (si == ei)
{
return ei;
}
// exclude ei
_PairIter ret = --ei;
ei++;
auto maxVal = si->first;
si++;
while (si != ei)
{
if (maxVal < si->first)
{
maxVal = si->first;
ret = si;
}
++si;
}
return ret;
}
template<typename _Ty0, typename _Ty1>
string joinStr(_Ty0 s0, _Ty1 s1)
{
stringstream ss;
ss << s0 << s1;
return ss.str();
}
template<typename _Ty0, typename _Ty1, typename _Ty2>
string joinStr(_Ty0 s0, _Ty1 s1, _Ty2 s2)
{
stringstream ss;
ss << s0 << s1 << s2;
return ss.str();
}
template<typename _Ty0, typename _Ty1, typename _Ty2, typename _Ty3>
string joinStr(_Ty0 s0, _Ty1 s1, _Ty2 s2, _Ty3 s3)
{
stringstream ss;
ss << s0 << s1 << s2 << s3;
return ss.str();
}
#define _DECLARE_PARAMETER_MEM(type, name)\
protected:\
type m##name;
#define _DECLARE_PARAMETER_GETFUN(type, name)\
public:\
type get##name() const { return m##name; }
#define _DECLARE_PARAMETER_SETFUN(type, name)\
public:\
void set##name(type val) { m##name = val; }
#define _DECLARE_PARAMETER_SETFUN2(type, name, val1, val2)\
public:\
void set##name(type val) {\
assert(val >= val1 && val <= val2);\
if (val >= val1 && val <= val2) m##name = val; }
#define _DECLARE_PARAMETER_SETENUM(type, name)\
public:\
void set##name(type val) { m##name = val; }\
void set##name(int val) {\
set##name(static_cast<type>(val)); }
#define _DECLARE_PARAMETER_SETENUM2(type, name, val1, val2)\
public:\
void set##name(type val) {\
assert(val >= val1 && val <= val2);\
if (val >= val1 && val <= val2) m##name = val; }\
void set##name(int val) {\
set##name(static_cast<type>(val)); }
#define _DECLARE_PARAMETER_SETPAIR(type, name)\
public:\
void set##name(type val1, type val2) {\
if (val1 > val2) { m##name##Start = val2; m##name##End = val1; }\
else { m##name##Start = val1; m##name##End = val2; }\
}
#define _DECLARE_PARAMETER_SETPAIR2(type, name, val1, val2)\
public:\
void set##name(type value1, type value2) {\
assert(value1 >= val1 && value1 <= val2 && value2 >= val1 && value2 <= val2);\
if (value1 >= val1 && value1 <= val2 && value2 >= val1 && value2 <= val2) {\
if (value1 > value2) { m##name##Start = value2; m##name##End = value1; }\
else { m##name##Start = value1; m##name##End = value2; }\
}\
}
#define DECLARE_PARAMETER(type, name)\
_DECLARE_PARAMETER_MEM(type, name)\
_DECLARE_PARAMETER_GETFUN(type, name)\
_DECLARE_PARAMETER_SETFUN(type, name)
#define DECLARE_PARAMETER2(type, name, val1, val2)\
_DECLARE_PARAMETER_MEM(type, name)\
_DECLARE_PARAMETER_GETFUN(type, name)\
_DECLARE_PARAMETER_SETFUN2(type, name, val1 , val2)
#define DECLARE_PARAMETER_SET(type, name)\
_DECLARE_PARAMETER_MEM(type, name)\
_DECLARE_PARAMETER_SETFUN(type, name)
#define DECLARE_PARAMETER_SET2(type, name, val1, val2)\
_DECLARE_PARAMETER_MEM(type, name)\
_DECLARE_PARAMETER_SETFUN2(type, name, val1, val2)
#define DECLARE_PARAMETER_GET(type, name)\
_DECLARE_PARAMETER_MEM(type, name)\
_DECLARE_PARAMETER_GETFUN(type, name)
#define DECLARE_PARAMETER_ENUM(type, name)\
_DECLARE_PARAMETER_MEM(type, name)\
_DECLARE_PARAMETER_GETFUN(type, name)\
_DECLARE_PARAMETER_SETENUM(type, name)
#define DECLARE_PARAMETER_ENUM2(type, name, val1, val2)\
_DECLARE_PARAMETER_MEM(type, name)\
_DECLARE_PARAMETER_GETFUN(type, name)\
_DECLARE_PARAMETER_SETENUM2(type, name, val1, val2)
#define DECLARE_PARAMETER_PAIR(type, name)\
_DECLARE_PARAMETER_MEM(type, name##Start)\
_DECLARE_PARAMETER_MEM(type, name##End)\
_DECLARE_PARAMETER_GETFUN(type, name##Start)\
_DECLARE_PARAMETER_GETFUN(type, name##End)\
_DECLARE_PARAMETER_SETPAIR(type, name)
#define DECLARE_PARAMETER_PAIR2(type, name, val1, val2)\
_DECLARE_PARAMETER_MEM(type, name##Start)\
_DECLARE_PARAMETER_MEM(type, name##End)\
_DECLARE_PARAMETER_GETFUN(type, name##Start)\
_DECLARE_PARAMETER_GETFUN(type, name##End)\
_DECLARE_PARAMETER_SETPAIR2(type, name, val1, val2)
// Declare the provide class as a singleton.
// Get instance via Class::getInstance().
//
// Note: according to C++11 standard, static object initialization will
// be made only by one thread, other threads will wait till it complete.
// start from VS2014, this macro is thread-safe.
#define DECLARE_SINGLETON(type, ...)\
public:\
static type& getInstance() {\
static type inst(__VA_ARGS__);\
return inst;\
}
// Declare the provide class as a singleton.
// Get instance via Class::getInstance().
//
// Note: according to C++11 standard, static object initialization will
// be made only by one thread, other threads will wait till it complete.
// start from VS2014, this macro is thread-safe.
#define DECLARE_SINGLETON_NOPARA(type)\
public:\
static type& getInstance() {\
static type inst;\
return inst;\
}
// A simple version of object factory that use object name as key and hold object instances.
// It's thread-safe.
// Note: factory own the object instance, aka. own the object instance's memory, which means it will
// deallocate the memory when it self is destroyed (when the who application is shutdown).
// You don't need to delete the object instance yourself, and even worse, it will cause the double-delete crash.
template<typename T, typename TPtr,
typename std::enable_if<std::is_base_of<std::shared_ptr<T>, TPtr>::value>::type* = nullptr>
class ObjectFactory
{
DECLARE_SINGLETON_NOPARA(ObjectFactory)
public:
~ObjectFactory() {}
TPtr getObject(const char* name)
{
CyclopsLockGuard guard(&mLock);
auto it = mLookupTable.find(name);
if (it == mLookupTable.end()) {
// create new
TPtr ptr = std::make_shared<T>();
it = mLookupTable.insert(std::make_pair(name, ptr)).first;
}
return it->second;
}
private:
ObjectFactory() {}
std::map<string, TPtr> mLookupTable;
CyclopsLock mLock;
};
template<typename T>
inline T minMax(T val, T minVal, T maxVal) {
return std::min<T>(maxVal, std::max<T>(minVal, val));
}
#endif // __StdUtils_h_

@ -0,0 +1,297 @@
#include "TransSolver.h"
#include "CVUtils.h"
#define M_LOW_TOLERANCE 0.000001
Matx33d affineTrans(const vector<Point2d>& src, const vector<Point2d>& dst)
{
if (dst.empty() || src.empty()) {
return Mat();
}
Point2d pc, qc;
int smallerSize = src.size() < dst.size() ? src.size() : dst.size();
for (int i = 0; i < smallerSize; i++) {
pc += src[i];
qc += dst[i];
}
pc.x /= smallerSize;
pc.y /= smallerSize;
qc.x /= smallerSize;
qc.y /= smallerSize;
Matx21d pit;
Matx12d pi;
Matx12d qi;
Matx22d spitpi = Matx22d::zeros();
Matx22d pitpi;
Matx22d pitqi;
Matx22d spitqi = Matx22d::zeros();
for (int i = 0; i < src.size() && i < dst.size(); i++) {
Point2d qpi = src[i] - pc;
Point2d qqi = dst[i] - qc;
pit(0) = qpi.x;
pit(1) = qpi.y;
pi(0) = qpi.x;
pi(1) = qpi.y;
qi(0) = qqi.x;
qi(1) = qqi.y;
pitpi = pit*pi;
spitpi = pitpi + spitpi;
pitqi = pit*qi;
spitqi = pitqi + spitqi;
}
Matx22d ispitpi;
ispitpi = spitpi.inv();
Matx22d M = ispitpi*spitqi;
double m11 = M(0, 0);
double m21 = M(0, 1);
double m12 = M(1, 0);
double m22 = M(1, 1);
Matx33d qm(m11, m12, 0, m21, m22, 0, 0, 0, 1);
Matx33d pcm(1.0, 0, -pc.x, 0, 1.0, -pc.y, 0, 0, 1);
Matx33d qcm(1.0, 0, qc.x, 0, 1.0, qc.y, 0, 0, 1);
Matx33d ret = qcm*qm*pcm;
return ret;
}
Matx33d rigidTrans(const vector<Point2d>& src, const vector<Point2d>& dst)
{
if (dst.empty() || src.empty()) {
return Mat();
}
vector<double> weights(src.size(), 1.0 / src.size());
Point2d pc, qc;
double wsum = 0;
for (int i = 0; i < src.size(); i++) {
double w = 1.0 / src.size();
weights[i] = w;
pc += src[i] * w;
qc += dst[i] * w;
wsum += w;
}
pc.x /= wsum;
pc.y /= wsum;
qc.x /= wsum;
qc.y /= wsum;
double u = 0;
double u1, u2;
u1 = 0;
u2 = 0;
for (int i = 0; i < src.size() && i < dst.size(); i++) {
Point2d qpi = src[i] - pc;
Point2d qqi = dst[i] - qc;
Point2d pi(qpi.x, qpi.y);
Point2d qi(qqi.x, qqi.y);
u1 += pi.dot(qi)*weights[i];
Point2d pi_(pi.y, -pi.x);
u2 += qi.dot(pi_)*weights[i];
}
u = sqrt(u1*u1 + u2*u2);
if (u < M_LOW_TOLERANCE) {
u = M_LOW_TOLERANCE;
}
Matx22d R = Matx22d::zeros();
Matx22d r = Matx22d::zeros();
for (int i = 0; i < src.size() && i < dst.size(); i++) {
Point2d qpi = src[i] - pc;
Point2d qqi = dst[i] - qc;
Point2d pi(qpi.x, qpi.y);
Point2d qi(qqi.x, qqi.y);
Point2d pi_(pi.y, -pi.x);
Point2d qi_(qi.y, -qi.x);
r(0, 0) = pi.dot(qi);
r(0, 1) = pi.dot(qi_);
r(1, 0) = pi_.dot(qi);
r(1, 1) = pi_.dot(qi_);
R = R + r * (weights[i] / u);
}
double m11 = R(0, 0);
double m21 = R(0, 1);
double m12 = R(1, 0);
double m22 = R(1, 1);
Matx33d qm(m11, m12, 0, m21, m22, 0, 0, 0, 1);
Matx33d pcm(1.0, 0, -pc.x, 0, 1.0, -pc.y, 0, 0, 1);
Matx33d qcm(1.0, 0, qc.x, 0, 1.0, qc.y, 0, 0, 1);
Matx33d ret = qcm*qm*pcm;
return ret;
}
bool cmpPointVec(const vector<Point2d>& vec0, const vector<Point2d>& vec1, const Matx33d& mat, double tor)
{
int smallerSize = vec0.size() < vec1.size() ? vec0.size() : vec1.size();
for (int i = 0; i < smallerSize; ++i)
{
Point2d p0, p1;
p0 = vec0[i];
p1 = vec1[i];
Point3d tp0 = mat*p0;
tp0.x /= tp0.z;
tp0.y /= tp0.z;
if (abs(p1.x - tp0.x) > tor || abs(p1.y - tp0.y) > tor)
{
return false;
}
}
return true;
}
void testTransSolver()
{
{
//rotation only
vector<Point2d> vec0, vec1;
vec0.push_back(Point2d());
vec0.push_back(Point2d(1, 0));
vec0.push_back(Point2d(1, 1));
vec0.push_back(Point2d(0, 1));
vec1.push_back(Point2d());
vec1.push_back(Point2d(0, 1));
vec1.push_back(Point2d(-1, 1));
vec1.push_back(Point2d(-1, 0));
Matx33d ret = affineTrans(vec0, vec1);
assert(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
ret = rigidTrans(vec0, vec1);
assert(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
}
{
// rotation and scale
vector<Point2d> vec0, vec1;
vec0.push_back(Point2d());
vec0.push_back(Point2d(1, 0));
vec0.push_back(Point2d(1, 1));
vec0.push_back(Point2d(0, 1));
vec1.push_back(Point2d());
vec1.push_back(Point2d(1, 1));
vec1.push_back(Point2d(0, 2));
vec1.push_back(Point2d(-1, 1));
Matx33d ret = affineTrans(vec0, vec1);
assert(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
}
{
// scale only
vector<Point2d> vec0, vec1;
vec0.push_back(Point2d());
vec0.push_back(Point2d(1, 0));
vec0.push_back(Point2d(1, 1));
vec0.push_back(Point2d(0, 1));
vec1.push_back(Point2d());
vec1.push_back(Point2d(2, 0));
vec1.push_back(Point2d(2, 2));
vec1.push_back(Point2d(0, 2));
Matx33d ret = affineTrans(vec0, vec1);
assert(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
}
{
// translation only
vector<Point2d> vec0, vec1;
vec0.push_back(Point2d());
vec0.push_back(Point2d(1, 0));
vec0.push_back(Point2d(1, 1));
vec0.push_back(Point2d(0, 1));
vec1.push_back(Point2d(1, 1));
vec1.push_back(Point2d(2, 1));
vec1.push_back(Point2d(2, 2));
vec1.push_back(Point2d(1, 2));
Matx33d ret = affineTrans(vec0, vec1);
assert(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
ret = rigidTrans(vec0, vec1);
assert(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
}
{
for (int i = 0; i < 10000; ++i)
{
// random rotation and translation
Mat mat23 = getRotationMatrix2D(Point2f(), rand() % 360, 1.0);
mat23.at<double>(0, 2) = (rand() % 1000) / 1000.0;
mat23.at<double>(1, 2) = (rand() % 1000) / 1000.0;
Matx33d matx = Matx33d::eye();
Mat mat(3, 3, CV_64FC1, matx.val);
mat23.copyTo(mat.rowRange(0, 2));
vector<Point2d> vec0, vec1;
vec0.push_back(Point2d());
vec0.push_back(Point2d(1, 0));
vec0.push_back(Point2d(1, 1));
vec0.push_back(Point2d(0, 1));
vec1 = vec0;
transPoints(vec1, matx);
Matx33d ret = affineTrans(vec0, vec1);
assert(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
ret = rigidTrans(vec0, vec1);
assert(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
}
}
{
// skew only
vector<Point2d> vec0, vec1;
vec0.push_back(Point2d());
vec0.push_back(Point2d(1, 0));
vec0.push_back(Point2d(1, 1));
vec0.push_back(Point2d(0, 1));
vec1.push_back(Point2d());
vec1.push_back(Point2d(1, 0));
vec1.push_back(Point2d(2, 1));
vec1.push_back(Point2d(1, 1));
Matx33d ret = affineTrans(vec0, vec1);
assert(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
}
{
// random affine
for (int i = 0; i < 10000; ++i)
{
vector<Point2d> vec0, vec1;
vec0.push_back(Point2d());
vec0.push_back(Point2d(1, 0));
vec0.push_back(Point2d(1, 1));
vec0.push_back(Point2d(0, 1));
Matx33d trans = Matx33d::eye();
Mat mat(3, 3, CV_64FC1, trans.val);
randu(mat.rowRange(0, 2), 0, 1.0);
vec1 = vec0;
transPoints(vec1, trans);
Matx33d ret = affineTrans(vec0, vec1);
assert(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
}
}
}

@ -0,0 +1,26 @@
/*! \file TransSolver.h
\brief A brief file description.
A more elaborated file description.
Created: 2015/11/15, author: bang.jin.
*/
#ifndef __TransSolver_h_
#define __TransSolver_h_
#include <vector>
#include <opencv2/opencv.hpp>
using namespace cv;
using std::vector;
Matx33d affineTrans(const vector<Point2d>& src, const vector<Point2d>& dst);
Matx33d rigidTrans(const vector<Point2d>& src, const vector<Point2d>& dst,
Mat* pCenRotScaleMat = NULL);
void testTransSolver();
#endif // __TransSolver_h_

@ -0,0 +1,182 @@
#include "cvdrawutils.h"
#include "CVUtils.h"
#include "DetectRoi.h"
cv::Mat drawLines(const Mat& img, const vector<PointfPair>& segPointVec)
{
#ifdef LP_DISABLE_DRAW
return Mat();
#else
Mat canvas = normCanvas(img);
for (size_t i = 0; i < segPointVec.size(); ++i)
{
const PointfPair& pointPair = segPointVec[i];
cv::line(canvas, pointPair.first, pointPair.second, Scalar(255, 255, 255, 0.5));
}
return canvas;
#endif
}
cv::Mat drawLines(const Mat& img, const vector<PointPair>& segPointVec)
{
#ifdef LP_DISABLE_DRAW
return Mat();
#else
Mat canvas = normCanvas(img);
for (size_t i = 0; i < segPointVec.size(); ++i)
{
const PointPair& pointPair = segPointVec[i];
cv::line(canvas, pointPair.first, pointPair.second, Scalar(255, 255, 255, 0.5));
}
return canvas;
#endif
}
void drawLine(Mat& canvas, const Vec4f& l, const Scalar& color)
{
line(canvas, Point(l.val[0], l.val[1]), Point(l.val[2], l.val[3]), color);
}
void drawPoint(Mat& canvas, int x, int y, const Scalar& color, int size)
{
int halfSize = size / 2;
Rect rect(x - halfSize, y - halfSize, size, size);
#if (CV_MAJOR_VERSION >= 3)
rectangle(canvas, rect, color, FILLED);
#else
rectangle(canvas, rect, color, CV_FILLED);
#endif
}
Scalar getRandomColor()
{
RNG& r = cv::theRNG();
return Scalar(r.next() % 256, r.next() % 256, r.next() % 256, 255);
}
Mat getColorCanvas(const Mat& img, float resizeFactor)
{
Mat canvas;
if (resizeFactor == 1.) canvas = img.clone();
else resize(img, canvas, Size(), resizeFactor, resizeFactor);
if (canvas.channels() == 1) cvtColor(canvas, canvas, CV_GRAY2BGR);
return canvas;
}
cv::Mat drawPoints(const Mat& img, const vector<Point>& pointVec, int grayVal /*= 255*/, int xRadius /*= 0*/, int yRadius /*= 0*/)
{
#ifdef LP_DISABLE_DRAW
return Mat();
#else
Mat canvas = normCanvas(img);
for (size_t i = 0; i < pointVec.size(); ++i)
{
Point pt(pointVec[i]);
if (xRadius == 0 && yRadius == 0)
{
canvas.at<uchar>(pt.y, pt.x) = grayVal;
}
else
{
Rect rect(pt.x - xRadius, pt.y - yRadius, xRadius * 2 + 1, yRadius * 2 + 1);
rectangle(canvas, rect, Scalar(grayVal, grayVal, grayVal));
}
}
return canvas;
#endif
}
cv::Mat drawPointsWithKeyPoints(const Mat& img, const vector<Point>& pointVec)
{
#ifdef LP_DISABLE_DRAW
return Mat();
#else
vector<KeyPoint> keypointVec;
for (size_t i = 0; i < pointVec.size(); ++i)
{
Point pt = pointVec[i];
keypointVec.push_back(KeyPoint(Point2f((float)pt.x, (float)pt.y), 1));
}
Mat canvas = normCanvas(img);
drawKeypoints(canvas, keypointVec, canvas);
return canvas;
#endif
}
void drawRotateRect(Mat& canvas, const RotatedRect& rr, const Scalar& color, int angleLen /*= 30*/)
{
// center
drawPoint(canvas, rr.center.x, rr.center.y, color);
// draw pose rect
Point2f pts[4];
rr.points(pts);
for (int i = 0; i < 3; ++i)
line(canvas, pts[i], pts[i + 1], color);
line(canvas, pts[3], pts[0], color);
// draw angle
int xshift = cos(rr.angle / 180 * CV_PI) * angleLen;
int yshift = sin(rr.angle / 180 * CV_PI) * angleLen;
Point2f p(rr.center.x + xshift, rr.center.y + yshift);
line(canvas, Point(rr.center.x, rr.center.y), p, color);
}
#if (CV_MAJOR_VERSION >= 3)
Mat highlightRoi(const Mat& roiImg, const Rect& r, const vector<Point2f>& roiVertexes)
{
assert(r.width == roiImg.cols && r.height == roiImg.rows);
Mat mask = DetectRoi::genMask(r, roiVertexes);
Mat canvas(roiImg.rows, roiImg.cols, CV_8UC4);
// bgr
static int from_to1_gay[] = { 0,0,0,1,0,2 };
static int from_to1_color[] = { 0,0,1,1,2,2 };
int* from_to1 = nullptr;
int channelCount = roiImg.channels();
if (channelCount == 1)
from_to1 = from_to1_gay;
else if (channelCount == 3 || channelCount == 4)
from_to1 = from_to1_color;
else {
assert(false && "highlightRoi: unexpected channel.");
return gDummyMat;
}
mixChannels(roiImg, canvas, from_to1, 3);
// a
static int from_to2[] = { 0,3 };
mixChannels(mask, canvas, from_to2, 1);
return canvas;
}
#endif
void drawPointDir(Mat& canvas, const Point& p, float angle, const Scalar& color, const Scalar& centerColor, int len, int thick)
{
float halfLen = len / 2.f;
float yStep = sin((90 + angle) / 180 * CV_PI) * halfLen;
float xStep = cos((90 + angle) / 180 * CV_PI) * halfLen;
line(canvas, Point(p.x - xStep, p.y - yStep), Point(p.x + xStep, p.y + yStep), color, thick, CV_AA);
#if (CV_MAJOR_VERSION >= 3)
rectangle(canvas, p, p, centerColor, FILLED);
#else
rectangle(canvas, p, p, centerColor, CV_FILLED);
#endif
}
void drawPatternPose(Mat& canvas, const Size& templateSize, const Vec4f& pose, const Scalar& color)
{
int cols = templateSize.width;
int rows = templateSize.height;
RotatedRect rr(Point2f(pose[0], pose[1]), Size2f(pose[3] * cols, pose[3] * cols), pose[2]);
drawRotateRect(canvas, rr, color);
}

@ -0,0 +1,38 @@
#ifndef cvdrawutils_h__
#define cvdrawutils_h__
#include <opencv2/opencv.hpp>
#include <opencv2/opencv_modules.hpp>
#include <vector>
#include "pointpair.h"
using std::vector;
using namespace cv;
Scalar getRandomColor();
Mat getColorCanvas(const Mat& img, float resizeFactor = 1.);
Mat drawPoints(const Mat& img, const vector<Point>& pointVec, int grayVal = 255, int xRadius = 0, int yRadius = 0);
Mat drawPointsWithKeyPoints(const Mat& img, const vector<Point>& pointVec);
Mat drawLines(const Mat& img, const vector<PointfPair>& segPointVec);
Mat drawLines(const Mat& img, const vector<PointPair>& segPointVec);
void drawLine(Mat& canvas, const Vec4f& l, const Scalar& color);
void drawPoint(Mat& canvas, int x, int y, const Scalar& color, int size = 1);
void drawRotateRect(Mat& canvas, const RotatedRect& rr, const Scalar& color, int angleLen = 30);
Mat highlightRoi(const Mat& roiImg, const Rect& r, const vector<Point2f>& roiVertexes);
void drawPointDir(Mat& canvas, const Point& p, float angle, const Scalar& color,
const Scalar& centerColor, int len = 5, int thick = 1);
void drawPatternPose(Mat& canvas, const Size& templateSize, const Vec4f& pose, const Scalar& color);
#endif // cvdrawutils_h__

@ -0,0 +1,217 @@
#include "cvmatutils.h"
#include <list>
#include <iostream>
using std::list;
using std::iostream;
cv::Mat getTranslateMatrix2D(float dx, float dy)
{
Mat ret(3, 2, CV_32FC1);
ret.setTo(0);
float* p = (float*)ret.data;
p[0] = 1.0;
p[2] = dx;
p[4] = 1.0;
p[5] = dy;
return ret;
}
void cutMargin(Mat& img, int w)
{
Rect r(w, w, img.cols - 2 * w, img.rows - 2 * w);
img = Mat(img, r);
}
cv::Mat duplicateChannels(const Mat& mat, int n)
{
assert(mat.channels() == 1);
vector<Mat> matVec(n, mat);
Mat ret;
merge(matVec, ret);
return ret;
}
cv::Scalar sum(const Mat& mat, const Mat& mask)
{
// double minVal, maxVal;
// minMaxIdx(mask, &minVal, &maxVal);
// mask /= maxVal;
Mat mask3ch = duplicateChannels(mask, 3);
Mat maskedMat = mask3ch & mat;
return sum(maskedMat);
}
string toStr(const Mat& m)
{
stringstream ss;
switch (m.type())
{
case CV_8UC1:
for (int i = 0; i < m.rows; ++i)
{
uchar* pRowData = m.row(i).data;
for (int j = 0; j < m.cols; ++j)
{
ss << (int)pRowData[j] << " ";
}
ss << "\n";
}
break;
case CV_32FC1:
for (int i = 0; i < m.rows; ++i)
{
float* pRowData = (float*)m.row(i).data;
for (int j = 0; j < m.cols; ++j)
{
ss << (float)pRowData[j] << " ";
}
ss << "\n";
}
break;
case CV_8UC4:
for (int i = 0; i < m.rows; ++i)
{
uchar* pRowData = m.row(i).data;
for (int j = 0; j < m.cols * 4; ++j)
{
ss << (int)pRowData[j] << " ";
}
ss << "\n";
}
break;
case CV_8UC3:
for (int i = 0; i < m.rows; ++i)
{
uchar* pRowData = m.row(i).data;
for (int j = 0; j < m.cols * 3; ++j)
{
ss << (int)pRowData[j] << " ";
}
ss << "\n";
}
break;
}
return ss.str();
}
cv::Mat fromStr(const string& str, int rows, int cols, int type)
{
Mat ret;
stringstream ss(str);
int count = 0;
switch (type)
{
case CV_8UC1:
ret.create(rows, cols, CV_8UC1);
for (int i = 0; i < rows; ++i)
{
uchar* pRowData = ret.row(i).data;
for (int j = 0; j < cols; ++j)
{
if (ss.eof())
{
return Mat();
}
int v;
ss >> v;
(*pRowData++) = v;
}
}
break;
case CV_32FC1:
ret.create(rows, cols, CV_32FC1);
for (int i = 0; i < rows; ++i)
{
float* pRowData = (float*)ret.row(i).data;
for (int j = 0; j < cols; ++j)
{
if (ss.eof())
{
return Mat();
}
float v;
ss >> v;
(*pRowData++) = v;
}
}
break;
case CV_32FC3:
ret.create(rows, cols / 3, CV_32FC3);
for (int i = 0; i < rows; ++i)
{
float* pRowData = (float*)ret.row(i).data;
for (int j = 0; j < cols; ++j)
{
if (ss.eof())
{
return Mat();
}
float v;
ss >> v;
(*pRowData++) = v;
}
}
break;
case CV_32FC4:
ret.create(rows, cols / 4, CV_32FC4);
for (int i = 0; i < rows; ++i)
{
float* pRowData = (float*)ret.row(i).data;
for (int j = 0; j < cols; ++j)
{
if (ss.eof())
{
return Mat();
}
float v;
ss >> v;
(*pRowData++) = v;
}
}
break;
case CV_8UC4:
ret.create(rows, cols / 4, CV_8UC4);
for (int i = 0; i < rows; ++i)
{
uchar* pRowData = ret.row(i).data;
for (int j = 0; j < cols; ++j)
{
if (ss.eof())
{
return Mat();
}
int v;
ss >> v;
(*pRowData++) = v;
}
}
break;
case CV_8UC3:
ret.create(rows, cols / 3, CV_8UC3);
for (int i = 0; i < rows; ++i)
{
uchar* pRowData = ret.row(i).data;
for (int j = 0; j < cols; ++j)
{
if (ss.eof())
{
return Mat();
}
int v;
ss >> v;
(*pRowData++) = v;
}
}
break;
default:
return Mat();
break;
}
return ret;
}

@ -0,0 +1,26 @@
#ifndef _cvmatutils_h_
#define _cvmatutils_h_
#include <opencv2/opencv.hpp>
#include <opencv2/opencv_modules.hpp>
#include <vector>
#include <string>
#include <iosfwd>
using namespace cv;
using std::vector;
using std::string;
using std::stringstream;
Mat getTranslateMatrix2D(float dx, float dy);
void cutMargin(Mat& img, int w);
Mat duplicateChannels(const Mat& mat, int n);
Scalar sum(const Mat& mat, const Mat& mask);
string toStr(const Mat& m);
Mat fromStr(const string& str, int rows, int cols, int type);
#endif // _cvmatutils_h_

@ -0,0 +1,78 @@
#ifndef pointpair_h__
#define pointpair_h__
#include <opencv2/opencv.hpp>
#include <opencv2/opencv_modules.hpp>
#include <vector>
using std::vector;
using std::pair;
using namespace cv;
template<typename _Point>
class _PointPair : public pair<_Point, _Point>
{
public:
_PointPair()
: pair<_Point, _Point>(), mAver(0) {}
_PointPair(const _Point& p0, const _Point& p1)
: pair<_Point, _Point>(p0, p1), mAver(0) {}
_PointPair(const _PointPair<_Point>& pointPair)
: pair<_Point, _Point>(pointPair.first, pointPair.second)
, mAver(pointPair.aver())
, mStr(pointPair.getStr())
{}
void setAver(float val) { mAver = val; }
float aver() const { return mAver; }
std::string getStr() const { return mStr; }
void setStr(std::string iVal) { mStr = iVal; }
protected:
float mAver;
std::string mStr;
private:
};
typedef _PointPair<Point> PointPair;
typedef _PointPair<Point2f> PointfPair;
void convertPointPair2PointfPair(const vector<PointPair>& vec0,
vector<PointfPair>& vec1);
template<typename _Point>
double pointDis(const _Point& i, const _Point& j)
{
return norm(i - j);
}
template<typename _PointPair>
double pointPairXDis(const _PointPair& i, const _PointPair& j)
{
return (abs(i.first.x - j.first.x) +
abs(i.second.x - j.second.x)) / 2.0;
}
double pointPairDis(const PointPair& i, const PointPair& j);
template<typename _PointPair>
double pointPairLen(const _PointPair& p)
{
return pointDis(p.first, p.second);
}
template<typename _PointPair>
void addYToPointPairs(vector<_PointPair>& vec, int y)
{
for (auto i = vec.begin(); i != vec.end(); ++i)
{
i->first.y += y;
i->second.y += y;
}
}
#endif // pointpair_h__
Loading…
Cancel
Save