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.
3062 lines
81 KiB
C++
3062 lines
81 KiB
C++
#include "CVUtils.h"
|
|
#include "DynamicProgramSearch.h"
|
|
#include "StdUtils.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 "TransSolver.h"
|
|
|
|
namespace CyclopsUtils1 {
|
|
|
|
Mat gDummyMat;
|
|
Scalar gDummyScalar;
|
|
|
|
bool localMeanVarNorm(const Mat& srcMat, Mat& dstMat, int winRadius, double tarMean/* = 120*/, double tarStd /*= 30*/)
|
|
{
|
|
int width = srcMat.cols;
|
|
int height = srcMat.rows;
|
|
|
|
if (winRadius * 2 + 1 >= width || winRadius * 2 + 1 >= height)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
dstMat = srcMat.clone();
|
|
|
|
Mat sumMat, sqSumMat;
|
|
cv::integral(srcMat, sumMat, sqSumMat, CV_64F);
|
|
int area = (2 * winRadius + 1)*(2 * winRadius + 1);
|
|
for (int i = winRadius; i < height - winRadius; i++)
|
|
{
|
|
const uchar *pData = srcMat.ptr(i);
|
|
uchar *tarData = dstMat.ptr(i);
|
|
double *pSumY1 = (double *)sumMat.ptr(i - winRadius);
|
|
double *pSumY2 = (double *)sumMat.ptr(i + winRadius + 1);
|
|
double *pSqSumY1 = (double *)sqSumMat.ptr(i - winRadius);
|
|
double *pSqSumY2 = (double *)sqSumMat.ptr(i + winRadius + 1);
|
|
for (int j = winRadius; j < width - winRadius; j++)
|
|
{
|
|
double sumX1Y1 = pSumY1[j - winRadius];
|
|
double sumX2Y2 = pSumY2[j + winRadius + 1];
|
|
double sumX1Y2 = pSumY2[j - winRadius];
|
|
double sumX2Y1 = pSumY1[j + winRadius + 1];
|
|
double sqSumX1Y1 = pSqSumY1[j - winRadius];
|
|
double sqSumX2Y2 = pSqSumY2[j + winRadius + 1];
|
|
double sqSumX1Y2 = pSqSumY2[j - winRadius];
|
|
double sqSumX2Y1 = pSqSumY1[j + winRadius + 1];
|
|
double val = sumX2Y2 + sumX1Y1 - sumX1Y2 - sumX2Y1;
|
|
double meanVal = val / area;
|
|
double sqSum = sqSumX2Y2 + sqSumX1Y1 - sqSumX1Y2 - sqSumX2Y1;
|
|
double stdVar = sqrt((sqSum - 2 * val*meanVal + area * meanVal * meanVal) / area);
|
|
double s = tarStd / stdVar;
|
|
double t = tarMean - meanVal * s;
|
|
|
|
int value = pData[j] * s + t;
|
|
|
|
if (value > 255)
|
|
{
|
|
tarData[j] = 255;
|
|
}
|
|
else
|
|
{
|
|
tarData[j] = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
void meanvarnorm(Mat& src, Mat &dst, double avg, double var, Mat mask /*= Mat()*/)
|
|
{
|
|
Scalar m, v;
|
|
cv::meanStdDev(src, m, v, mask);
|
|
// (I - m)*var/v + avg = I*var/v - m*var/v + avg
|
|
double s = var / v.val[0];
|
|
double t = avg - m.val[0] * var / v.val[0];
|
|
dst = src * s + t;
|
|
}
|
|
|
|
Mat gridThre(const Mat& img, int gridWidth, int gridHeight, float threStdDevScale,
|
|
LogicOper oper /*= LP_LOGIC_SMALLER*/,
|
|
float majority /*= 0.8*/)
|
|
{
|
|
Mat ret = Mat::zeros(img.size(), CV_8UC1);
|
|
int gridXNum = img.cols / gridWidth + 1;
|
|
int gridYNum = img.rows / gridHeight + 1;
|
|
for (int gy = 0; gy < gridYNum - 1; ++gy)
|
|
{
|
|
int y = gy*gridHeight;
|
|
for (int gx = 0; gx < gridXNum - 1; ++gx)
|
|
{
|
|
int x = gx*gridWidth;
|
|
Rect roi(x, y, gridWidth, gridHeight);
|
|
Mat roiImg(img, roi);
|
|
double mean, stddev;
|
|
meanStdDev(roiImg, Mat(), &mean, &stddev, majority);
|
|
Mat threImg;
|
|
switch (oper)
|
|
{
|
|
case LP_LOGIC_SMALLER:
|
|
threImg = roiImg < (mean + stddev*threStdDevScale);
|
|
break;
|
|
case LP_LOGIC_LARGER:
|
|
threImg = roiImg > (mean + stddev*threStdDevScale);
|
|
break;
|
|
}
|
|
|
|
threImg.copyTo(Mat(ret, roi));
|
|
}
|
|
}
|
|
for (int gy = 0; gy < gridYNum; ++gy)
|
|
{
|
|
int y = gy*gridHeight;
|
|
Rect roi(img.cols - gridWidth, y, gridWidth, gridHeight);
|
|
roi &= Rect(0, 0, img.cols, img.rows);
|
|
if (roi.width == 0 || roi.height == 0)
|
|
{
|
|
continue;
|
|
}
|
|
Mat roiImg(img, roi);
|
|
double mean, stddev;
|
|
meanStdDev(roiImg, Mat(), &mean, &stddev, majority);
|
|
Mat threImg;
|
|
switch (oper)
|
|
{
|
|
case LP_LOGIC_SMALLER:
|
|
threImg = roiImg < (mean + stddev*threStdDevScale);
|
|
break;
|
|
case LP_LOGIC_LARGER:
|
|
threImg = roiImg > (mean + stddev*threStdDevScale);
|
|
break;
|
|
}
|
|
threImg.copyTo(Mat(ret, roi));
|
|
}
|
|
return ret;
|
|
}
|
|
Mat gridWhiteThre(const Mat& img, int gridWidth, int gridHeight, float threStdDevScale, float majority /*= 0.8*/)
|
|
{
|
|
return gridThre(img, gridWidth, gridHeight, threStdDevScale, LP_LOGIC_LARGER, majority);
|
|
}
|
|
Mat gridBlackThre(const Mat& img, int gridWidth, int gridHeight, float threStdDevScale, float majority /*= 0.8*/)
|
|
{
|
|
return gridThre(img, gridWidth, gridHeight, threStdDevScale, LP_LOGIC_SMALLER, majority);
|
|
|
|
Mat ret = Mat::zeros(img.size(), CV_8UC1);
|
|
int gridXNum = img.cols / gridWidth + 1;
|
|
int gridYNum = img.rows / gridHeight + 1;
|
|
for (int gy = 0; gy < gridYNum - 1; ++gy)
|
|
{
|
|
int y = gy*gridHeight;
|
|
for (int gx = 0; gx < gridXNum - 1; ++gx)
|
|
{
|
|
int x = gx*gridWidth;
|
|
Rect roi(x, y, gridWidth, gridHeight);
|
|
Mat roiImg(img, roi);
|
|
double mean, stddev;
|
|
meanStdDev(roiImg, Mat(), &mean, &stddev, majority);
|
|
Mat threImg = roiImg < (mean + stddev*threStdDevScale);
|
|
threImg.copyTo(Mat(ret, roi));
|
|
}
|
|
}
|
|
for(int gy = 0; gy < gridYNum; ++gy)
|
|
{
|
|
int y = gy*gridHeight;
|
|
Rect roi((gridXNum - 1)*gridWidth, y, gridWidth, gridHeight);
|
|
roi &= Rect(0, 0, img.cols, img.rows);
|
|
if (roi.width == 0 || roi.height == 0)
|
|
{
|
|
continue;
|
|
}
|
|
Mat roiImg(img, roi);
|
|
double mean, stddev;
|
|
meanStdDev(roiImg, Mat(), &mean, &stddev, (float)0.8);
|
|
Mat threImg = roiImg < (mean + stddev*threStdDevScale);
|
|
threImg.copyTo(Mat(ret, roi));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void converToType(cv::Mat& mat, int mattype)
|
|
{
|
|
cv::Mat _mat;
|
|
mat.convertTo(_mat, mattype);
|
|
mat = _mat;
|
|
}
|
|
|
|
void genScharrImage(Mat& img)
|
|
{
|
|
Mat sobelx, sobely;
|
|
Sobel(img, sobelx, CV_32FC1, 1, 0, CV_SCHARR);
|
|
Sobel(img, sobely, CV_32FC1, 0, 1, CV_SCHARR);
|
|
img = sobelx.mul(sobelx) + sobely.mul(sobely);
|
|
Mat tempImg;
|
|
img.convertTo(tempImg, CV_32FC1);
|
|
Mat tempImg0;
|
|
sqrt(tempImg, tempImg0);
|
|
img = tempImg0;
|
|
}
|
|
|
|
void genSobelImage(Mat& img, Mat* pSobelx /*= NULL*/, Mat* pSobely /*= NULL*/)
|
|
{
|
|
Mat sobelx, sobely;
|
|
Sobel(img, sobelx, CV_32FC1, 1, 0, BORDER_REPLICATE);
|
|
Sobel(img, sobely, CV_32FC1, 0, 1, BORDER_REPLICATE);
|
|
img = sobelx.mul(sobelx) + sobely.mul(sobely);
|
|
Mat tempImg;
|
|
img.convertTo(tempImg, CV_32FC1);
|
|
Mat tempImg0;
|
|
sqrt(tempImg, tempImg0);
|
|
img = tempImg0;
|
|
|
|
if (pSobelx)
|
|
{
|
|
*pSobelx = sobelx;
|
|
}
|
|
if (pSobely)
|
|
{
|
|
*pSobely = sobely;
|
|
}
|
|
}
|
|
|
|
cv::Mat genXDeriLineKernel(int w, int type, double absVal)
|
|
{
|
|
assert(w % 2 == 1);
|
|
|
|
Mat leftKernel = Mat::ones(1, w / 2, type)*absVal;
|
|
Mat rightKernel = Mat::ones(1, w / 2, type)*absVal*(-1);
|
|
Mat kernel = Mat::zeros(1, w, type);
|
|
leftKernel.copyTo(kernel.colRange(0, w / 2));
|
|
rightKernel.copyTo(kernel.colRange(w / 2 + 1, w));
|
|
return kernel;
|
|
}
|
|
|
|
cv::Mat genXDeriMat(const Mat& img, int w, double absVal)
|
|
{
|
|
assert(w % 2 == 1);
|
|
int kernelRadius = w / 2;
|
|
|
|
Mat xKernel = genXDeriLineKernel(kernelRadius * 2 + 1, CV_32FC1, 1);
|
|
Mat xDeriMat;
|
|
sepFilter2D(img, xDeriMat, CV_32FC1, xKernel, Mat::ones(1, 1, CV_32FC1));
|
|
|
|
return xDeriMat;
|
|
}
|
|
|
|
cv::Mat normEachRow(const Mat& img)
|
|
{
|
|
Mat ret(img.rows, img.cols, img.type());
|
|
for (int y = 0; y < img.rows; y++)
|
|
{
|
|
Mat rowMat = img.row(y);
|
|
double minVal, maxVal;
|
|
cv::minMaxIdx(rowMat, &minVal, &maxVal);
|
|
rowMat = rowMat / maxVal;
|
|
rowMat.copyTo(ret.row(y));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
cv::Mat genGradientDir4EachRow(const Mat& img)
|
|
{
|
|
assert(img.cols >= 2);
|
|
|
|
Mat xDeriMat = genXDeriMat(img, 3, 1);
|
|
Mat threMat;
|
|
threshold(xDeriMat, threMat, 0, 1, cv::THRESH_BINARY);
|
|
|
|
return threMat;
|
|
}
|
|
|
|
void findMatElementsEquals(const Mat& img, vector<Point>& pointVec, float val, int xPadding)
|
|
{
|
|
assert(img.type() == CV_32FC1);
|
|
|
|
for (int y = 0; y < img.rows; ++y)
|
|
{
|
|
float* pRowData = (float*)img.row(y).data;
|
|
float* pRowDataStart = pRowData;
|
|
float* pRowDataEnd = pRowData + img.cols - xPadding;
|
|
pRowData += xPadding;
|
|
while (pRowData != pRowDataEnd)
|
|
{
|
|
if (abs(*pRowData - val) < 0.0001)
|
|
{
|
|
pointVec.push_back(Point(pRowData - pRowDataStart, y));
|
|
}
|
|
pRowData++;
|
|
}
|
|
}
|
|
}
|
|
|
|
double localMatSum(const Mat& mat, const Rect& roi)
|
|
{
|
|
Mat localMat(mat, roi);
|
|
return sum(localMat).val[0];
|
|
}
|
|
|
|
cv::Mat normCanvas(const Mat& img)
|
|
{
|
|
Mat canvas;
|
|
double maxVal, minVal;
|
|
minMaxIdx(img, &minVal, &maxVal);
|
|
if (maxVal <= 1.0)
|
|
{
|
|
img.convertTo(canvas, CV_8UC1, 255);
|
|
}
|
|
else
|
|
{
|
|
img.convertTo(canvas, CV_8UC1);
|
|
}
|
|
return canvas;
|
|
}
|
|
|
|
void findEdgePointsEachRow(const Mat& img, vector<Point>& edgePointVec, int xPadding)
|
|
{
|
|
// img is a binary image.
|
|
// an edge point in a row satisfies:
|
|
// 1) left neighbor pixels are all black (0) or white (1);
|
|
// 2) right neighbor pixels are all white or black.
|
|
|
|
// find candidate turning points
|
|
Mat sumKernelMat = Mat::ones(1, 2, img.type());
|
|
Mat sumMat;
|
|
sepFilter2D(img, sumMat, img.type(), sumKernelMat, Mat::ones(1, 1, img.type()));
|
|
vector<Point> turningPointVec;
|
|
|
|
// for pixels ... 0, 0, 1, 1 ..., turning point is
|
|
// *
|
|
findMatElementsEquals(sumMat, turningPointVec, 1.0, xPadding);
|
|
|
|
// filter turning points
|
|
vector<Point> filteredTurningPointVec;
|
|
int localRadius = 30;
|
|
int localSumTor = 5;
|
|
for (size_t i = 0; i < turningPointVec.size(); ++i)
|
|
{
|
|
Point pt(turningPointVec[i]);
|
|
Rect leftRoi(pt.x - localRadius, pt.y, localRadius, 1);
|
|
double leftSum = localMatSum(img, leftRoi);
|
|
Rect rightRoi(pt.x, pt.y, localRadius, 1);
|
|
double rightSum = localMatSum(img, rightRoi);
|
|
if (leftSum > rightSum && abs(leftSum - localRadius) < localSumTor && rightSum < 0.0001)
|
|
{
|
|
filteredTurningPointVec.push_back(pt);
|
|
}
|
|
else if (leftSum < rightSum && leftSum < 0.0001 && abs(rightSum - localRadius) < localSumTor)
|
|
{
|
|
filteredTurningPointVec.push_back(pt);
|
|
}
|
|
}
|
|
|
|
|
|
//Mat canvas = drawPoints(img, filteredTurningPointVec, 125, 10);
|
|
|
|
|
|
edgePointVec = filteredTurningPointVec;
|
|
}
|
|
|
|
Mat sumEachRow2(const Mat& img)
|
|
{
|
|
#define _sumEachRow2(t)\
|
|
if (img.channels() == 1) return sumEachRow<t>(img);\
|
|
else if (img.channels() == 2) return sumEachRowN<t, 2>(img);\
|
|
else if (img.channels() == 3) return sumEachRowN<t, 3>(img);\
|
|
else if (img.channels() == 4) return sumEachRowN<t, 4>(img);\
|
|
else { _ASSERTE(false && "not implemented"); return gDummyMat; }
|
|
|
|
switch (img.depth())
|
|
{
|
|
case CV_8U:
|
|
_sumEachRow2(unsigned char);
|
|
case CV_16S:
|
|
_sumEachRow2(short);
|
|
case CV_32S:
|
|
_sumEachRow2(int);
|
|
case CV_32F:
|
|
_sumEachRow2(float);
|
|
case CV_64F:
|
|
_sumEachRow2(double);
|
|
default:
|
|
_ASSERTE(false && "not implemented");
|
|
}
|
|
return gDummyMat;
|
|
}
|
|
|
|
Mat sumEachCol2(const Mat& img)
|
|
{
|
|
#define _sumEachCol2(t)\
|
|
if (img.channels() == 1) return sumEachCol<t>(img);\
|
|
else if (img.channels() == 2) return sumEachColN<t, 2>(img);\
|
|
else if (img.channels() == 3) return sumEachColN<t, 3>(img);\
|
|
else if (img.channels() == 4) return sumEachColN<t, 4>(img);\
|
|
else { _ASSERTE(false && "not implemented"); return gDummyMat; }
|
|
|
|
switch (img.depth())
|
|
{
|
|
case CV_8U:
|
|
_sumEachCol2(unsigned char);
|
|
case CV_16S:
|
|
_sumEachCol2(short);
|
|
case CV_32S:
|
|
_sumEachCol2(int);
|
|
case CV_32F:
|
|
_sumEachCol2(float);
|
|
case CV_64F:
|
|
_sumEachCol2(double);
|
|
default:
|
|
_ASSERTE(false && "not implemented");
|
|
}
|
|
return gDummyMat;
|
|
}
|
|
|
|
cv::Mat thresholdEachRowLocally(const Mat& img, int localRange /*= 501*/, int C /*= 10*/)
|
|
{
|
|
Mat ret = img.clone();
|
|
for (int y = 0; y < img.rows; ++y)
|
|
{
|
|
Mat rowMat = img.row(y);
|
|
uchar* pData = (uchar*)rowMat.data;
|
|
cv::adaptiveThreshold(rowMat, ret.row(y), 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, localRange, C);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
cv::Mat thresholdEachRow(const Mat& img, float threScale /*= 0.5*/)
|
|
{
|
|
Mat ret = img.clone();
|
|
for (int y = 0; y < img.rows; ++y)
|
|
{
|
|
Mat rowMat = img.row(y);
|
|
double rowSum = sum(rowMat).val[0];
|
|
double rowAvg = rowSum / img.cols * threScale;
|
|
Mat rowThre = ret.row(y);
|
|
threshold(rowMat, rowThre, rowAvg, 255, THRESH_BINARY);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
cv::Mat thresholdEachRow(const Mat& img, const Mat& rowThre, float threScale)
|
|
{
|
|
Mat ret = Mat::zeros(img.rows, img.cols, img.type());
|
|
for (int y = 0; y < img.rows; ++y)
|
|
{
|
|
Mat retRow = ret.row(y);
|
|
uchar* pRetData = retRow.data;
|
|
Mat imgRow = img.row(y);
|
|
uchar* pData = imgRow.data;
|
|
double* pThreData = (double*)rowThre.data;
|
|
for (int x = 0; x < img.cols; ++x)
|
|
{
|
|
if (*pData < (*pThreData)*threScale)
|
|
{
|
|
*pRetData = 0;
|
|
}
|
|
else
|
|
{
|
|
*pRetData = 255;
|
|
}
|
|
pData++;
|
|
pThreData++;
|
|
pRetData++;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void convertPointPair2PointfPair(const vector<PointPair>& vec0, vector<PointfPair>& vec1)
|
|
{
|
|
vec1.clear();
|
|
for (auto i = vec0.begin(); i != vec0.end(); ++i)
|
|
{
|
|
PointfPair pointfPair(i->first, i->second);
|
|
pointfPair.setAver(i->aver());
|
|
pointfPair.setStr(i->getStr());
|
|
vec1.push_back(pointfPair);
|
|
}
|
|
}
|
|
|
|
double pointPairDis(const PointPair& i, const PointPair& j)
|
|
{
|
|
double dis0 = pointDis(i.first, j.first);
|
|
double dis1 = pointDis(i.second, j.second);
|
|
return (dis0 + dis1) / 2.0;
|
|
}
|
|
|
|
void segEachRow(const Mat& img, vector<PointPair>& pointPairVec, int xPadding)
|
|
{
|
|
for (int y = 0; y < img.rows; ++y)
|
|
{
|
|
Mat rowMat = img.row(y);
|
|
uchar* pRowData = rowMat.data;
|
|
uchar* pRowStart = pRowData;
|
|
uchar* pRowEnd = rowMat.data + img.cols - xPadding;
|
|
pRowData += xPadding;
|
|
int leftX = -1;
|
|
while (pRowData != pRowEnd)
|
|
{
|
|
uchar val = *pRowData;
|
|
if (val == 0 && leftX == -1)
|
|
{
|
|
leftX = pRowData - pRowStart;
|
|
}
|
|
else if (val != 0 && leftX >= 0)
|
|
{
|
|
Point leftPt(leftX, y);
|
|
Point rightPt(pRowData - pRowStart - 1, y);
|
|
PointPair pointPair(leftPt, rightPt);
|
|
pointPairVec.push_back(pointPair);
|
|
leftX = -1;
|
|
}
|
|
pRowData++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void fillPointPairVal(const Mat& img, std::string filePath, vector<PointPair>& pointPairVec)
|
|
{
|
|
for (auto i = pointPairVec.begin(); i != pointPairVec.end(); ++i)
|
|
{
|
|
Mat mat(img, Rect(i->first.x, i->first.y, i->second.x - i->first.x, 1));
|
|
i->setAver(sum(mat).val[0] / (double)mat.cols);
|
|
std::stringstream ss;
|
|
ss << filePath << " x: " << i->first.x << " y: " << i->first.y;
|
|
i->setStr(ss.str());
|
|
}
|
|
}
|
|
|
|
cv::Mat getFirstChannel(const Mat& img)
|
|
{
|
|
vector<Mat> channels;
|
|
split(img, channels);
|
|
return channels.front();
|
|
}
|
|
Mat getChannel(const Mat& img, int i)
|
|
{
|
|
if (i < 0 || i >= img.channels())
|
|
{
|
|
return Mat();
|
|
}
|
|
vector<Mat> channels;
|
|
split(img, channels);
|
|
|
|
return channels[i];
|
|
}
|
|
|
|
void mulEachRow(Mat& img, const Mat& scaleRow)
|
|
{
|
|
for (int i = 0; i < img.rows; ++i)
|
|
{
|
|
Mat row = img.row(i);
|
|
uchar* pRowData = (uchar*)row.data;
|
|
double* pScaleData = (double*)scaleRow.data;
|
|
for (int j = 0; j < row.cols; ++j)
|
|
{
|
|
*pRowData *= *pScaleData;
|
|
++pRowData;
|
|
++pScaleData;
|
|
}
|
|
}
|
|
}
|
|
|
|
void genRandomPoints(const Mat& img, vector<Point>& points, RNG rng, int sampleCnt /* = 1000 */)
|
|
{
|
|
sampleCnt = cv::min(sampleCnt, img.rows * img.cols );
|
|
for (int i = 0; i < sampleCnt; ++i)
|
|
{
|
|
Vec3b* pData = (Vec3b*)img.row(rng.uniform(0, img.rows)).data;
|
|
Vec3b& d = pData[rng.uniform(0, img.cols)];
|
|
|
|
Point p;
|
|
p.x = d[0];
|
|
p.y = d[1];
|
|
points.push_back(p);
|
|
}
|
|
}
|
|
|
|
void plot8uVec(const Mat& vec, float scale /*= 1.0*/)
|
|
{
|
|
|
|
Mat canvas = Mat::zeros(100 * scale, 100 * scale, CV_8UC1);
|
|
Rect rect(0, 0, canvas.cols * scale, canvas.rows * scale);
|
|
|
|
int size = cv::max(vec.rows, vec.cols);
|
|
uchar* pVecData = vec.data;
|
|
for (int i = 0; i < size; ++i)
|
|
{
|
|
Point pt(i * scale, canvas.rows - 1 - pVecData[i] * scale);
|
|
if (rect.contains(pt))
|
|
{
|
|
canvas.at<uchar>(pt.y, pt.x) = 255;
|
|
}
|
|
}
|
|
imshow("plot8uVec", canvas);
|
|
}
|
|
|
|
void plot32fVec(const Mat& vec, float scale /*= 1.0*/)
|
|
{
|
|
Mat canvas = Mat::zeros(100 * scale, 100 * scale, CV_8UC1);
|
|
Rect rect(0, 0, canvas.cols * scale, canvas.rows * scale);
|
|
int size = cv::max(vec.rows, vec.cols);
|
|
float* pVecData = (float*)vec.data;
|
|
for (int i = 0; i < size; ++i)
|
|
{
|
|
Point pt(i * scale, canvas.rows - 1 - pVecData[i] * scale);
|
|
if (rect.contains(pt))
|
|
{
|
|
canvas.at<uchar>(pt.y, pt.x) = 255;
|
|
}
|
|
}
|
|
imshow("plot32fVec", canvas);
|
|
}
|
|
|
|
cv::Mat calcHist(const Mat& img, const Mat& mask, int histSize /*= 256*/, int minVal, int maxVal)
|
|
{
|
|
Mat histMat;
|
|
float fRange[] = { minVal, maxVal };
|
|
const float* fHistRange = { fRange };
|
|
|
|
cv::calcHist(&img, 1, NULL, mask, histMat, 1, &histSize, &fHistRange);
|
|
return histMat;
|
|
}
|
|
|
|
Mat resize(const Mat& img, float s, int interMethod /*= cv::INTER_LINEAR*/)
|
|
{
|
|
Mat ret;
|
|
Size newSize(img.cols*s, img.rows*s);
|
|
if (newSize.width == 0 || newSize.height == 0)
|
|
{
|
|
return img;
|
|
}
|
|
resize(img, ret, Size(), s, s, interMethod);
|
|
return ret;
|
|
}
|
|
|
|
void writeFile(const Mat& mat, std::string filePath, std::string matName /*= "mat"*/)
|
|
{
|
|
FileStorage fs;
|
|
fs.open(filePath, FileStorage::WRITE);
|
|
fs << matName << mat;
|
|
fs.release();
|
|
}
|
|
|
|
cv::Mat readFile(std::string filePath, std::string matName)
|
|
{
|
|
FileStorage fs;
|
|
fs.open(filePath, FileStorage::READ);
|
|
Mat ret;
|
|
fs[matName] >> ret;
|
|
return ret;
|
|
}
|
|
|
|
void gaussianBlurEachRow(const Mat& src, Mat& dst, int ksize /*= 3*/)
|
|
{
|
|
dst = Mat::zeros(src.rows, src.cols, src.type());
|
|
for (int y = 0; y < src.rows; y++)
|
|
{
|
|
Mat dstRow = dst.row(y);
|
|
Mat srcRow = src.row(y);
|
|
cv::GaussianBlur(srcRow, dstRow, cv::Size(ksize, 1), 1.0);
|
|
}
|
|
}
|
|
|
|
void medianBlurEachRow(const Mat& src, Mat& dst, int ksize /*= 3*/)
|
|
{
|
|
dst = Mat::zeros(src.rows, src.cols, src.type());
|
|
for (int y = 0; y < src.rows; y++)
|
|
{
|
|
Mat dstRow = dst.row(y);
|
|
Mat srcRow = src.row(y);
|
|
cv::medianBlur(srcRow, dstRow, 1);
|
|
}
|
|
}
|
|
|
|
double maxLaplacianX(const Mat& rowMat, float scale /*= 1.0*/)
|
|
{
|
|
Mat sRowMat;
|
|
Mat lapMat;
|
|
|
|
if (scale != 1.0)
|
|
{
|
|
resize(rowMat, sRowMat, Size(), scale, 1.0, INTER_CUBIC);
|
|
Laplacian(sRowMat, lapMat, CV_32FC1, 3);
|
|
}
|
|
else
|
|
{
|
|
Laplacian(rowMat, lapMat, CV_32FC1);
|
|
}
|
|
double minVal, maxVal;
|
|
int minIdx, maxIdx;
|
|
minMaxIdx(lapMat, &minVal, &maxVal, &minIdx, &maxIdx);
|
|
return maxIdx / scale;
|
|
}
|
|
|
|
double minLaplacianX(const Mat& rowMat, float scale /*= 1.0*/)
|
|
{
|
|
Mat sRowMat;
|
|
Mat lapMat;
|
|
|
|
if (scale != 1.0)
|
|
{
|
|
resize(rowMat, sRowMat, Size(), scale, 1.0, INTER_CUBIC);
|
|
Laplacian(sRowMat, lapMat, CV_32FC1, 3);
|
|
}
|
|
else
|
|
{
|
|
Laplacian(rowMat, lapMat, CV_32FC1);
|
|
}
|
|
double minVal, maxVal;
|
|
int minIdx, maxIdx;
|
|
minMaxIdx(lapMat, &minVal, &maxVal, &minIdx, &maxIdx);
|
|
return minIdx / scale;
|
|
}
|
|
|
|
void Laplacian1D(const Mat& src, Mat& dst, int ddepth, int ksize /*= 1*/)
|
|
{
|
|
assert(src.rows == 1);
|
|
Mat kernel;
|
|
float K1[3] = { 1, -2, 1 };
|
|
float K3[5] = { 1, 2, -6, 2, 1 };
|
|
float K5[7] = { 1, 1, 1, -6, 1, 1, 1 };
|
|
float K7[9] = { 1, 1, 1, 1, -8, 1, 1, 1, 1 };
|
|
float K9[11] = { 1, 1, 1, 1, 1, -10, 1, 1, 1, 1, 1 };
|
|
switch (ksize)
|
|
{
|
|
case 1:
|
|
kernel = Mat(1, 3, CV_32F, K1);
|
|
break;
|
|
case 3:
|
|
kernel = Mat(1, 5, CV_32F, K3);
|
|
break;
|
|
case 5:
|
|
kernel = Mat(1, 7, CV_32F, K5);
|
|
break;
|
|
case 7:
|
|
kernel = Mat(1, 9, CV_32F, K7);
|
|
break;
|
|
case 9:
|
|
kernel = Mat(1, 11, CV_32F, K9);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
filter2D(src, dst, ddepth, kernel);
|
|
}
|
|
|
|
|
|
void _filterKeyPointsByNeighborDistance(vector<KeyPoint>& vec, float ndis)
|
|
{
|
|
vector<KeyPoint> ret;
|
|
for (size_t i = 0; i < vec.size(); ++i)
|
|
{
|
|
KeyPoint kp0 = vec[i];
|
|
bool isDiscard = false;
|
|
for (size_t j = i + 1; j < vec.size(); ++j)
|
|
{
|
|
KeyPoint kp1 = vec[j];
|
|
float dis = pointDis(kp0.pt, kp1.pt);
|
|
if (dis < ndis)
|
|
{
|
|
if (kp0.response < kp1.response)
|
|
{
|
|
isDiscard = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!isDiscard)
|
|
{
|
|
ret.push_back(kp0);
|
|
}
|
|
}
|
|
vec = ret;
|
|
}
|
|
|
|
void filterKeyPointsByNeighborDistance(vector<KeyPoint>& vec, float ndis)
|
|
{
|
|
_filterKeyPointsByNeighborDistance(vec, ndis);
|
|
vec = vector<KeyPoint>(vec.rbegin(), vec.rend());
|
|
_filterKeyPointsByNeighborDistance(vec, ndis);
|
|
}
|
|
|
|
float IC_Angle_u8(const Mat& image, const int half_k, Point2f pt,
|
|
const vector<int> & u_max)
|
|
{
|
|
int m_01 = 0, m_10 = 0;
|
|
|
|
const uchar* center = &image.at<uchar>(cvRound(pt.y), cvRound(pt.x));
|
|
|
|
// Treat the center line differently, v=0
|
|
for (int u = -half_k; u <= half_k; ++u)
|
|
m_10 += u * center[u];
|
|
|
|
// Go line by line in the circular patch
|
|
int step = (int)image.step1();
|
|
for (int v = 1; v <= half_k; ++v)
|
|
{
|
|
// Proceed over the two lines
|
|
int v_sum = 0;
|
|
int d = u_max[v];
|
|
for (int u = -d; u <= d; ++u)
|
|
{
|
|
int val_plus = center[u + v*step], val_minus = center[u - v*step];
|
|
v_sum += (val_plus - val_minus);
|
|
m_10 += u * (val_plus + val_minus);
|
|
}
|
|
m_01 += v * v_sum;
|
|
}
|
|
|
|
return fastAtan2((float)m_01, (float)m_10);
|
|
}
|
|
|
|
float IC_Angle_f32(const Mat& image, const int half_k, Point2f pt,
|
|
const vector<int> & u_max)
|
|
{
|
|
float m_01 = 0, m_10 = 0;
|
|
|
|
const float* center = &image.at<float>(cvRound(pt.y), cvRound(pt.x));
|
|
|
|
// Treat the center line differently, v=0
|
|
for (int u = -half_k; u <= half_k; ++u)
|
|
m_10 += u * center[u];
|
|
|
|
// Go line by line in the circular patch
|
|
int step = (int)image.step1();
|
|
for (int v = 1; v <= half_k; ++v)
|
|
{
|
|
// Proceed over the two lines
|
|
float v_sum = 0;
|
|
int d = u_max[v];
|
|
for (int u = -d; u <= d; ++u)
|
|
{
|
|
float val_plus = center[u + v*step], val_minus = center[u - v*step];
|
|
v_sum += (val_plus - val_minus);
|
|
m_10 += u * (val_plus + val_minus);
|
|
}
|
|
m_01 += v * v_sum;
|
|
}
|
|
|
|
return fastAtan2((float)m_01, (float)m_10);
|
|
}
|
|
|
|
float IC_Angle(const Mat& image, const int half_k, Point2f pt,
|
|
const vector<int> & u_max)
|
|
{
|
|
if (image.type() == CV_8UC1)
|
|
{
|
|
return IC_Angle_u8(image, half_k, pt, u_max);
|
|
}
|
|
else if (image.type() == CV_32FC1)
|
|
{
|
|
return IC_Angle_f32(image, half_k, pt, u_max);
|
|
}
|
|
else
|
|
{
|
|
std::cout << "image type " << image.type() << " is not supported" << std::endl;
|
|
return FLT_MAX;
|
|
}
|
|
}
|
|
|
|
void computeOrientation(const Mat& image, vector<KeyPoint>& keypoints,
|
|
int halfPatchSize, const vector<int>& umax)
|
|
{
|
|
// Process each keypoint
|
|
for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),
|
|
keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)
|
|
{
|
|
keypoint->angle = IC_Angle(image, halfPatchSize, keypoint->pt, umax);
|
|
}
|
|
}
|
|
|
|
void genUMax(vector<int>& umax, int halfPatchSize)
|
|
{
|
|
umax.resize(halfPatchSize + 2);
|
|
|
|
int v, v0, vmax = cvFloor(halfPatchSize * sqrt(2.f) / 2 + 1);
|
|
int vmin = cvCeil(halfPatchSize * sqrt(2.f) / 2);
|
|
for (v = 0; v <= vmax; ++v)
|
|
umax[v] = cvRound(sqrt((double)halfPatchSize * halfPatchSize - v * v));
|
|
|
|
// Make sure we are symmetric
|
|
for (v = halfPatchSize, v0 = 0; v >= vmin; --v)
|
|
{
|
|
while (umax[v0] == umax[v0 + 1])
|
|
++v0;
|
|
umax[v] = v0;
|
|
++v0;
|
|
}
|
|
}
|
|
|
|
void filterKeyPointsByRotationInvariants(vector<KeyPoint>& vec, const Mat& img,
|
|
FeatureDetector* pFeatDetector, float tor)
|
|
{
|
|
Point2f cen(img.cols / 2.0, img.rows / 2.0);
|
|
|
|
// no longer supported in opencv3.4.1
|
|
#if (CV_MAJOR_VERSION < 3)
|
|
int patchSize = pFeatDetector->getInt("patchSize");
|
|
#else
|
|
int patchSize = pFeatDetector->descriptorSize();
|
|
#endif
|
|
int halfPatchSize = patchSize / 2;
|
|
vector<int> umax;
|
|
genUMax(umax, halfPatchSize);
|
|
|
|
for (int i = 0; i < 360; ++i)
|
|
{
|
|
Mat trans = getRotationMatrix2D(cen, i, 1.0);
|
|
Mat transedImg;
|
|
warpAffine(img, transedImg, trans, Size(img.cols, img.rows));
|
|
computeOrientation(img, vec, halfPatchSize, umax);
|
|
}
|
|
}
|
|
|
|
double localIC_Angle(const Mat& img, Point2f center, int patchSize)
|
|
{
|
|
int halfPatchSize = patchSize / 2;
|
|
vector<int> umax;
|
|
genUMax(umax, halfPatchSize);
|
|
|
|
return IC_Angle(img, halfPatchSize, center, umax);
|
|
}
|
|
|
|
double localAngle_WeightedCen(const Mat& img, Point2f center)
|
|
{
|
|
#ifdef DEBUG_VIEW_INTERNAL_MAT
|
|
Mat vImg = img/255.0;
|
|
#endif
|
|
Point2f weightedCen(0, 0);
|
|
float sum = 0;
|
|
for (int y = 0; y < img.rows; ++y)
|
|
{
|
|
float* pRowData = (float*)img.row(y).data;
|
|
for (int x = 0; x < img.cols; ++x)
|
|
{
|
|
float val = pRowData[x];
|
|
if (val == 0)
|
|
{
|
|
continue;
|
|
}
|
|
weightedCen.x += x*val;
|
|
weightedCen.y += y*val;
|
|
sum += val;
|
|
}
|
|
}
|
|
weightedCen.x /= sum;
|
|
weightedCen.y /= sum;
|
|
|
|
weightedCen = weightedCen - center;
|
|
|
|
return fastAtan2(weightedCen.y, weightedCen.x);
|
|
}
|
|
|
|
double localAngle_(const Mat& img, Point2f center, Mat mask)
|
|
{
|
|
#ifdef DEBUG_VIEW_INTERNAL_MAT
|
|
Mat vImg = img / 255.0;
|
|
#endif
|
|
Scalar meanScalar, stddevScalar;
|
|
meanStdDev(img, meanScalar, stddevScalar, mask);
|
|
Mat meanNormImg = img - meanScalar.val[0];
|
|
float thre = stddevScalar.val[0] * 4;
|
|
meanNormImg.setTo(0, meanNormImg < thre);
|
|
meanNormImg.setTo(1, meanNormImg > thre);
|
|
return localAngle_WeightedCen(meanNormImg, center);
|
|
}
|
|
|
|
void upperMajorityMask(const Mat& img, Mat& mask, float majority)
|
|
{
|
|
Mat hist;
|
|
majorityHistUpper(img, Mat(), hist, majority);
|
|
float* pHistData = (float*)hist.data;
|
|
int histSize = hist.rows > hist.cols ? hist.rows : hist.cols;
|
|
float* pEndHistData = pHistData + histSize;
|
|
int idx = 0;
|
|
while (pHistData != pEndHistData)
|
|
{
|
|
if (*pHistData)
|
|
{
|
|
break;
|
|
}
|
|
idx++;
|
|
pHistData++;
|
|
}
|
|
mask = (img >= idx);
|
|
}
|
|
|
|
void lowerMajorityHist(const Mat& img, const Mat& mask, Mat& hist, float majority)
|
|
{
|
|
hist = calcHist(img, mask);
|
|
|
|
// suppose there is only one peak
|
|
float* pHistData = (float*)hist.data;
|
|
float curRatio = 1.0;
|
|
int totalNum = sum(hist).val[0];
|
|
|
|
float* pFront = pHistData;
|
|
float* pTail = pHistData + 254;
|
|
while (pFront != pTail)
|
|
{
|
|
if (*pTail == 0)
|
|
{
|
|
pTail--;
|
|
continue;
|
|
}
|
|
|
|
{
|
|
curRatio -= *pTail / totalNum;
|
|
*pTail = 0;
|
|
pTail--;
|
|
}
|
|
if (curRatio <= majority)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void majorityHistLower(const Mat& img, const Mat& mask, Mat& hist, float majority)
|
|
{
|
|
hist = calcHist(img, mask);
|
|
|
|
// suppose there is only one peak
|
|
float* pHistData = (float*)hist.data;
|
|
float curRatio = 1.0;
|
|
int totalNum = sum(hist).val[0];
|
|
|
|
float* pFront = pHistData;
|
|
float* pTail = pHistData + 254;
|
|
while (pFront != pTail)
|
|
{
|
|
if (*pTail == 0)
|
|
{
|
|
pTail--;
|
|
continue;
|
|
}
|
|
|
|
{
|
|
curRatio -= *pTail / totalNum;
|
|
*pTail = 0;
|
|
pTail--;
|
|
}
|
|
if (curRatio <= majority)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void majorityHistUpper(const Mat& img, const Mat& mask, Mat& hist, float majority)
|
|
{
|
|
hist = calcHist(img, mask);
|
|
|
|
// suppose there is only one peak
|
|
float* pHistData = (float*)hist.data;
|
|
float curRatio = 1.0;
|
|
int totalNum = sum(hist).val[0];
|
|
|
|
float* pFront = pHistData;
|
|
float* pTail = pHistData + 254;
|
|
while (pFront != pTail)
|
|
{
|
|
if (*pFront == 0)
|
|
{
|
|
pFront++;
|
|
continue;
|
|
}
|
|
|
|
{
|
|
curRatio -= *pFront / totalNum;
|
|
*pFront = 0;
|
|
pFront++;
|
|
}
|
|
if (curRatio <= majority)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
void majorityHist(const Mat& img, const Mat& mask, Mat& hist, float majority)
|
|
{
|
|
hist = calcHist(img, mask);
|
|
|
|
// suppose there is only one peak
|
|
float* pHistData = (float*)hist.data;
|
|
float curRatio = 1.0;
|
|
int totalNum = sum(hist).val[0];
|
|
|
|
float* pFront = pHistData;
|
|
float* pTail = pHistData + 254;
|
|
while (pFront != pTail)
|
|
{
|
|
if (*pFront == 0)
|
|
{
|
|
pFront++;
|
|
continue;
|
|
}
|
|
if (*pTail == 0)
|
|
{
|
|
pTail--;
|
|
continue;
|
|
}
|
|
if (*pFront < *pTail)
|
|
{
|
|
curRatio -= *pFront / totalNum;
|
|
*pFront = 0;
|
|
pFront++;
|
|
}
|
|
else
|
|
{
|
|
curRatio -= *pTail / totalNum;
|
|
*pTail = 0;
|
|
pTail--;
|
|
}
|
|
if (curRatio <= majority)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
Mat lowerMajorityMask(const Mat& img, const Mat& mask, float majority)
|
|
{
|
|
Mat hist;
|
|
lowerMajorityHist(img, mask, hist, majority);
|
|
float* pHistData = (float*)hist.data;
|
|
|
|
Mat ret = mask.clone();
|
|
|
|
for (int i = 0; i < img.rows; ++i)
|
|
{
|
|
uchar* pRowData = (uchar*)img.row(i).data;
|
|
uchar* pMaskRowData = (uchar*)ret.row(i).data;
|
|
for (int j = 0; j < img.cols; ++j)
|
|
{
|
|
uchar val = pRowData[j];
|
|
if (!pHistData[val])
|
|
{
|
|
pMaskRowData[j] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void meanStdDev(const Mat& img, const Mat& mask,
|
|
double* pMean, double* pStdDev, float majority, int type)
|
|
{
|
|
Mat hist;
|
|
switch (type)
|
|
{
|
|
default:
|
|
case 0:
|
|
majorityHist(img, mask, hist, majority);
|
|
break;
|
|
case 1:
|
|
majorityHistLower(img, mask, hist, majority);
|
|
break;
|
|
case 2:
|
|
majorityHistUpper(img, mask, hist, majority);
|
|
break;
|
|
}
|
|
|
|
// suppose there is only one peak
|
|
Mat rowHist;// = toRowVec(hist);
|
|
float* pHistData = (float*)rowHist.data;
|
|
|
|
histMeanStddev<float>(rowHist, pMean, pStdDev);
|
|
}
|
|
|
|
float interpolate(float* pY, int n, float stepX, float x)
|
|
{
|
|
int lIdx = (int)(x/stepX);
|
|
int rIdx = lIdx + 1;
|
|
if (rIdx > n - 1)
|
|
{
|
|
return pY[n - 1];
|
|
}
|
|
assert(lIdx >= 0 && lIdx < n && rIdx >= 0 && rIdx < n);
|
|
float s = (x - lIdx*stepX)/stepX;
|
|
float ly = pY[lIdx];
|
|
float ry = pY[rIdx];
|
|
return ly + (ry - ly)*s;
|
|
}
|
|
|
|
cv::Mat cocentricNorm(const Mat& img, Point2f center, const Mat& weightMat, float dstMeanVal)
|
|
{
|
|
assert(weightMat.empty() || weightMat.type() == CV_32FC1);
|
|
|
|
int w = img.cols;
|
|
int h = img.rows;
|
|
vector<Point2f> corners;
|
|
corners.push_back(Point2f(0, 0));
|
|
corners.push_back(Point2f(0, h));
|
|
corners.push_back(Point2f(w, h));
|
|
corners.push_back(Point2f(w, 0));
|
|
vector<double> cornerDisVec;
|
|
for_each(corners.begin(), corners.end(), [&](const Point2f& pt)
|
|
{
|
|
double dis = pointDis(center, pt);
|
|
cornerDisVec.push_back(dis);
|
|
});
|
|
|
|
auto farthestCornerDis = max_element(cornerDisVec.begin(), cornerDisVec.end());
|
|
float maxRadius = *farthestCornerDis;
|
|
|
|
int radiusNum = floorf(maxRadius);
|
|
//radiusNum = 20;
|
|
float radiusStep = (maxRadius / radiusNum);
|
|
Mat cocentricSumMat = Mat::zeros(1, radiusNum, CV_32FC1);
|
|
float* pSumData = (float*)cocentricSumMat.data;
|
|
Mat cocentricWeightSumMat = Mat::zeros(1, radiusNum, CV_32FC1);
|
|
float* pWeightSumData = (float*)cocentricWeightSumMat.data;
|
|
Mat radiusMat(img.rows, img.cols, CV_32FC1);
|
|
|
|
for (int y = 0; y < h; y++)
|
|
{
|
|
const Mat& imgRow = img.row(y);
|
|
float* pImgRowData = (float*)imgRow.data;
|
|
float* pRadiusRowData = (float*)radiusMat.row(y).data;
|
|
|
|
float* pWeightRowData = NULL;
|
|
if (!weightMat.empty())
|
|
{
|
|
pWeightRowData = (float*)weightMat.row(y).data;
|
|
}
|
|
|
|
for (int x = 0; x < w; x++)
|
|
{
|
|
//std::cout << x << " " << y << std::endl;
|
|
float weight;
|
|
if (pWeightRowData)
|
|
{
|
|
weight = pWeightRowData[x];
|
|
}
|
|
else
|
|
{
|
|
weight = 1.0;
|
|
}
|
|
float val = pImgRowData[x] * weight;
|
|
float radius = pointDis(Point2f(x, y), center);
|
|
pRadiusRowData[x] = radius;
|
|
int radiusIdx0 = (int)(radius / radiusStep);
|
|
assert(radiusIdx0 >= 0);
|
|
int radiusIdx1 = radiusIdx0 + 1;
|
|
if (radiusIdx0 >= radiusNum - 1)
|
|
{
|
|
pSumData[radiusNum - 1] += val;
|
|
pWeightSumData[radiusNum - 1] += weight;
|
|
}
|
|
else
|
|
{
|
|
float s = (radius - radiusStep*radiusIdx0) / radiusStep;
|
|
pSumData[radiusIdx0] += val*s;
|
|
pSumData[radiusIdx1] += val*(1 - s);
|
|
pWeightSumData[radiusIdx0] += s*weight;
|
|
pWeightSumData[radiusIdx1] += (1 - s)*weight;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < radiusNum; ++i)
|
|
{
|
|
//float radius = (i*radiusStep + radiusStep) / 2;
|
|
if (pWeightSumData[i] == 0)
|
|
{
|
|
|
|
}
|
|
else
|
|
{
|
|
pSumData[i] /= pWeightSumData[i];
|
|
}
|
|
}
|
|
|
|
Mat retMat = Mat::zeros(img.rows, img.cols, img.type());
|
|
Mat normMask = Mat::zeros(img.rows, img.cols, img.type());
|
|
for (int y = 0; y < h; y++)
|
|
{
|
|
float* pImgRowData = (float*)img.row(y).data;
|
|
float* pRetRowData = (float*)retMat.row(y).data;
|
|
float* pRadiusData = (float*)radiusMat.row(y).data;
|
|
float *pNormData = (float *)normMask.row(y).data;
|
|
for (int x = 0; x < w; x++)
|
|
{
|
|
float val = pImgRowData[x];
|
|
float radius = pRadiusData[x];
|
|
float mean = interpolate(pSumData, radiusNum, radiusStep, radius);
|
|
if (mean == 0)
|
|
{
|
|
continue;
|
|
}
|
|
float newVal = (float)val * dstMeanVal / mean;
|
|
|
|
// if (newVal - dstMeanVal > -30)
|
|
//{
|
|
pRetRowData[x] = newVal;
|
|
//pNormData[x] = 255;
|
|
// }
|
|
//else
|
|
//{
|
|
// continue;
|
|
//}
|
|
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_VIEW_INTERNAL_MAT
|
|
Mat viewRetMat = retMat/255.0;
|
|
#endif
|
|
/*Mat retNromMask = normMask.mul(retMat);
|
|
Mat normTest = retNromMask / 255.0;*/
|
|
|
|
return retMat;
|
|
//return normTest;
|
|
}
|
|
|
|
cv::Mat meanNorm(const Mat& img, const Mat& mask, float dstMeanVal, float majority /*= 1.0*/, PixelSelectionMethod method /*= LP_PIXEL_SELECT_ALL*/)
|
|
{
|
|
double meanVal = 0;
|
|
if (method == LP_PIXEL_SELECT_ALL)
|
|
{
|
|
meanVal = mean(img, mask).val[0];
|
|
}
|
|
else if (method == LP_PIXEL_SELECT_MAJORITY_FAST ||
|
|
method == LP_PIXEL_SELECT_MAJORITY)
|
|
{
|
|
meanStdDev(img, mask, &meanVal, NULL, majority);
|
|
}
|
|
return img + (int)(dstMeanVal - meanVal);
|
|
}
|
|
|
|
// cv::Mat getRoiImg(const Mat& img, vector<Point2i> ptVec, int radius)
|
|
// {
|
|
// Rect defectRoi(genRectCenRadius(ptVec, radius));
|
|
//
|
|
// defectRoi &= Rect(0, 0, img.cols, img.rows);
|
|
// return Mat(img, defectRoi);
|
|
// }
|
|
//
|
|
// cv::Mat getRoiImg(const Mat& img, Point2i cen, int radius)
|
|
// {
|
|
// Rect defectRoi(genRectCenRadius(cen, radius));
|
|
//
|
|
// defectRoi &= Rect(0, 0, img.cols, img.rows);
|
|
// return Mat(img, defectRoi);
|
|
// }
|
|
|
|
cv::Mat filterY(const Mat& img, float* kernel, int size, int ddepth /*= CV_32FC1*/)
|
|
{
|
|
Mat kernelMat(size, 1, CV_32FC1, kernel);
|
|
|
|
Mat ret;
|
|
cv::filter2D(img, ret, ddepth, kernelMat);
|
|
|
|
return ret;
|
|
}
|
|
|
|
float circleDegree(const vector<Point>& contour)
|
|
{
|
|
Rect br = boundingRect(contour);
|
|
float r = sqrt((float)(br.width*br.width / 4 + br.height*br.height / 4));
|
|
float circleArea = CV_PI*r*r;
|
|
float area = contourArea(contour);
|
|
if (circleArea < 0.00001)
|
|
{
|
|
return 0;
|
|
}
|
|
return area / circleArea;
|
|
}
|
|
|
|
float lpContourArea(const vector<Point>& contour)
|
|
{
|
|
double area = 0;
|
|
if (contour.size() == 1)
|
|
{
|
|
area = 1;
|
|
}
|
|
else if (contour.size() != 0)
|
|
{
|
|
area = contourArea(contour);
|
|
area += arcLength(contour, true);
|
|
}
|
|
return area;
|
|
}
|
|
|
|
cv::Mat minMaxNorm(const Mat& img, double tarMin, double tarMax, const Mat& mask /*= Mat()*/)
|
|
{
|
|
double minVal, maxVal;
|
|
minMaxIdx(img, &minVal, &maxVal, 0, 0, mask);
|
|
double s = ((tarMax - tarMin) / (maxVal - minVal));
|
|
return img * s + (tarMin - minVal * s);
|
|
}
|
|
|
|
double longShortRatio(const Size& s)
|
|
{
|
|
if (s.width > s.height)
|
|
{
|
|
if (s.height < 0.000001)
|
|
{
|
|
return 0;
|
|
}
|
|
return (double)s.width / (double)s.height;
|
|
}
|
|
else
|
|
{
|
|
if (s.width < 0.000001)
|
|
{
|
|
return 0;
|
|
}
|
|
return (double)s.height / (double)s.width;
|
|
}
|
|
}
|
|
|
|
void closeShapesToConvex(const Mat& mask, Mat& closedMask)
|
|
{
|
|
closedMask.create(mask.rows, mask.cols, mask.type());
|
|
closedMask.setTo(0);
|
|
|
|
Mat canvas;
|
|
mask.copyTo(canvas);
|
|
vector< vector<Point> > contours;
|
|
findContours(canvas, contours, RETR_LIST, cv::CHAIN_APPROX_NONE);
|
|
vector< vector<Point> > convextContours;
|
|
for (size_t i = 0; i < contours.size(); ++i)
|
|
{
|
|
const vector<Point>& contour = contours[i];
|
|
vector<Point> convexContour;
|
|
convexHull(contour, convexContour);
|
|
convextContours.push_back(convexContour);
|
|
}
|
|
fillPoly(closedMask, convextContours, Scalar(255, 255, 255));
|
|
}
|
|
|
|
cv::Mat genCircleMask(int rows, int cols, int type,
|
|
Point cen, double r, const Scalar& color, int thickness, int lineType /*= 8*/, int shift /*= 0*/)
|
|
{
|
|
Mat mask = Mat::zeros(rows, cols, type);
|
|
circle(mask, cen, r, color, thickness, lineType, shift);
|
|
return mask;
|
|
}
|
|
|
|
void openOper(Mat& img, int w)
|
|
{
|
|
Mat kernel(w, w, CV_8UC1);
|
|
kernel.setTo(1);
|
|
openOper(img, kernel);
|
|
}
|
|
|
|
void openOper(Mat& img, const Mat& kernel)
|
|
{
|
|
erode(img, img, kernel);
|
|
dilate(img, img, kernel);
|
|
}
|
|
|
|
void closeOper(Mat& img, int w)
|
|
{
|
|
Mat kernel(w, w, CV_8UC1);
|
|
kernel.setTo(1);
|
|
closeOper(img, kernel);
|
|
}
|
|
|
|
void closeOper(Mat& img, const Mat& kernel)
|
|
{
|
|
dilate(img, img, kernel);
|
|
erode(img, img, kernel);
|
|
}
|
|
|
|
CV_WRAP GroupMatcher::GroupMatcher(
|
|
int normType /*= NORM_L2*/, bool crossCheck /*= false*/)
|
|
: BFMatcher(normType, crossCheck)
|
|
{
|
|
|
|
}
|
|
|
|
Ptr<DescriptorMatcher> GroupMatcher::clone(bool emptyTrainData /*= false*/) const
|
|
{
|
|
return new GroupMatcher();
|
|
}
|
|
|
|
// no longer supported in opencv3.4.1
|
|
#if (CV_MAJOR_VERSION < 3)
|
|
AlgorithmInfo* GroupMatcher::info() const
|
|
{
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
void GroupMatcher::knnMatchImpl(const Mat& queryDescriptors,
|
|
vector<vector<DMatch> >& matches,
|
|
int k,
|
|
const vector<Mat>& masks /*= vector<Mat>()*/,
|
|
bool compactResult /*= false*/)
|
|
{
|
|
BFMatcher::knnMatchImpl(queryDescriptors, matches, k, masks,
|
|
compactResult);
|
|
|
|
|
|
}
|
|
|
|
void GroupMatcher::radiusMatchImpl(const Mat& queryDescriptors,
|
|
vector<vector<DMatch> >& matches,
|
|
float maxDistance,
|
|
const vector<Mat>& masks /*= vector<Mat>()*/,
|
|
bool compactResult /*= false*/)
|
|
{
|
|
BFMatcher::radiusMatchImpl(queryDescriptors,
|
|
matches, maxDistance, masks, compactResult);
|
|
}
|
|
|
|
|
|
Mat getContourBoundedRoiImg(const Mat& src, const vector<Point>& contour, Rect* pRoiRect /* = NULL*/)
|
|
{
|
|
Rect cr = boundingRect(contour);
|
|
Mat cmask(cr.height, cr.width, CV_8UC1);
|
|
cmask.setTo(0);
|
|
cv::fillPoly(cmask, vector< vector<Point> >(1, contour), Scalar(255, 255, 255), 8, 0, cr.tl()*(-1.0));
|
|
Mat localMask;
|
|
Mat(src, cr).copyTo(localMask);
|
|
if (pRoiRect)
|
|
{
|
|
*pRoiRect = cr;
|
|
}
|
|
return localMask & cmask;
|
|
}
|
|
|
|
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)
|
|
{
|
|
int areaMaxIdx = -1;
|
|
double maxarea = -1;
|
|
for (int j = 0; j < contours.size(); ++j)
|
|
{
|
|
const vector<Point>& contour = contours[j];
|
|
if (contour.size() < 5)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int area = -1;
|
|
if (!srcImg.empty())
|
|
{
|
|
Mat localMask = getContourBoundedRoiImg(srcImg, contour);
|
|
area = countNonZero(localMask);
|
|
}
|
|
else
|
|
{
|
|
area = contourArea(contour);
|
|
}
|
|
|
|
RotatedRect rr = minAreaRect(contour);
|
|
|
|
double l = max(rr.size.width, rr.size.height);
|
|
if (area < minArea && l < minLength)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
double cd = circleDegree(contour);
|
|
if (cd > maxCircleDegree)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
double lsr = longShortRatio(rr.size);
|
|
if (lsr < minLongShortRatio)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (area > maxarea)
|
|
{
|
|
maxarea = area;
|
|
areaMaxIdx = j;
|
|
}
|
|
|
|
filteredContours.push_back(contour);
|
|
}
|
|
|
|
if (areaMaxIdx >= 0 && pAreaMaxContour)
|
|
{
|
|
*pAreaMaxContour = contours[areaMaxIdx];
|
|
}
|
|
}
|
|
|
|
void filterContours(Mat hsvColorThresImg,
|
|
int minArea, double minLength, double minLongShortRatio,
|
|
double maxCircleDegree, vector<Point>* pAreaMaxContour /*= NULL*/)
|
|
{
|
|
vector< vector<Point> > contours;
|
|
Mat canvas = hsvColorThresImg.clone();
|
|
findContours(canvas, contours, RETR_LIST, CHAIN_APPROX_NONE);
|
|
canvas.setTo(0);
|
|
|
|
vector< vector<Point> > filteredContours;
|
|
filterContours(contours, hsvColorThresImg, filteredContours,
|
|
minArea, minLength, minLongShortRatio, maxCircleDegree, pAreaMaxContour);
|
|
|
|
// redraw
|
|
cv::fillPoly(canvas, filteredContours, Scalar(255, 255, 255));
|
|
hsvColorThresImg &= canvas;
|
|
}
|
|
|
|
|
|
int filterSmallAndFindMaxContours(vector< vector<Point> >& contours, double minArea)
|
|
{
|
|
vector< vector<Point> > filteredContours;
|
|
int maxindex = -1;
|
|
double maxarea = -1;
|
|
for (int j = 0; j < contours.size(); ++j)
|
|
{
|
|
const vector<Point>& contour = contours[j];
|
|
double area = contourArea(contour);
|
|
if (area > minArea)
|
|
{
|
|
filteredContours.push_back(contour);
|
|
}
|
|
|
|
if (area > maxarea)
|
|
{
|
|
maxarea = area;
|
|
maxindex = j;
|
|
}
|
|
}
|
|
contours = filteredContours;
|
|
return maxindex;
|
|
}
|
|
|
|
|
|
void filterContours(Mat& img, double minArea)
|
|
{
|
|
Mat canvas;
|
|
img.copyTo(canvas);
|
|
|
|
vector< vector<Point> > contours;
|
|
findContours(canvas, contours, RETR_LIST, cv::CHAIN_APPROX_NONE);
|
|
filterSmallAndFindMaxContours(contours, minArea);
|
|
canvas.setTo(0);
|
|
cv::fillPoly(canvas, contours, Scalar(255, 255, 255));
|
|
|
|
img = canvas;
|
|
}
|
|
|
|
|
|
Mat genInRangeMask(const Mat& src, std::map<int, pair<Scalar, Scalar>>& colorRanges)
|
|
{
|
|
Mat mask = Mat::zeros(src.rows, src.cols, CV_8UC1);
|
|
|
|
for (auto iter = colorRanges.begin(); iter != colorRanges.end(); ++iter)
|
|
{
|
|
int key = iter->first;
|
|
Scalar low = iter->second.first;
|
|
Scalar high = iter->second.second;
|
|
Mat imgThresholded;
|
|
inRange(src, low, high, imgThresholded);
|
|
mask |= imgThresholded;
|
|
}
|
|
|
|
return mask;
|
|
}
|
|
|
|
void localCloseOper(Mat& img, vector< vector<Point> >& contours, float longerScale)
|
|
{
|
|
Mat tmp = img.clone();
|
|
for (int i = 0; i < contours.size(); ++i)
|
|
{
|
|
const vector<Point>& contour = contours[i];
|
|
RotatedRect rr = minAreaRect(contour);
|
|
int longer = cv::max(rr.size.width, rr.size.height)*longerScale;
|
|
|
|
Rect br = boundingRect(contour);
|
|
br.x -= longer*1.5;
|
|
br.y -= longer*1.5;
|
|
br.width += longer * 3;
|
|
br.height += longer * 3;
|
|
|
|
br.x = cv::max(0, br.x);
|
|
br.y = cv::max(0, br.y);
|
|
br.width = cv::min(img.cols - br.x - 1, br.width);
|
|
br.height = cv::min(img.rows - br.y - 1, br.height);
|
|
|
|
Mat roiMat = Mat(img, br).clone();
|
|
closeOper(roiMat, Mat::ones(longer, 1, CV_8UC1));
|
|
br.x -= 1;
|
|
br.y -= 1;
|
|
br.x = cv::max(0, br.x);
|
|
br.y = cv::max(0, br.y);
|
|
|
|
roiMat.copyTo(Mat(tmp, br));
|
|
}
|
|
img |= tmp;
|
|
}
|
|
|
|
Mat genWithCopiedCols(const Mat& m, int multiples = 3)
|
|
{
|
|
Mat ret(m.rows, m.cols * multiples, m.type());
|
|
for (int i = 0; i < multiples; ++i)
|
|
{
|
|
m.copyTo(ret.colRange(i*m.cols, i*m.cols + m.cols));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
double matDisWithReversedRows(const Mat& m0, const Mat& m1)
|
|
{
|
|
assert(m0.rows == m1.rows);
|
|
assert(m0.cols == m1.cols);
|
|
|
|
int rows = m0.rows;
|
|
int cols = m0.cols;
|
|
|
|
Mat rowDisVec(rows, 1, CV_64FC1);
|
|
double* pRowDisVec = (double*)rowDisVec.data;
|
|
for (int i = 0; i < rows; ++i)
|
|
{
|
|
Mat row0 = 255 - m0.row(i);
|
|
Mat row1 = 255 - m1.row(rows - i - 1);
|
|
//Mat weightedRow0 = 255 - row0;
|
|
//*pRowDisVec = ((row0 - row1).dot(weightedRow0));
|
|
Mat row0f;
|
|
row0.convertTo(row0f, CV_32FC1);
|
|
Mat row1f;
|
|
row1.convertTo(row1f, CV_32FC1);
|
|
*pRowDisVec = compareHist(row0f, row1f, CV_COMP_CORREL);
|
|
pRowDisVec++;
|
|
}
|
|
|
|
Mat kernel = cv::getGaussianKernel(rows * 2, 1).rowRange(rows, rows * 2);
|
|
|
|
return rowDisVec.dot(kernel);
|
|
}
|
|
|
|
double matDis(const Mat& m0, const Mat& m1)
|
|
{
|
|
assert(m0.rows == m1.rows);
|
|
assert(m0.cols == m1.cols);
|
|
|
|
int rows = m0.rows;
|
|
int cols = m0.cols;
|
|
|
|
Mat rowDisVec(rows, 1, CV_64FC1);
|
|
double* pRowDisVec = (double*)rowDisVec.data;
|
|
for (int i = 0; i < rows; ++i)
|
|
{
|
|
Mat row0 = 255 - m0.row(i);
|
|
Mat row1 = 255 - m1.row(i);
|
|
|
|
Mat row0f;
|
|
row0.convertTo(row0f, CV_32FC1);
|
|
Mat row1f;
|
|
row1.convertTo(row1f, CV_32FC1);
|
|
*pRowDisVec = compareHist(row0f, row1f, CV_COMP_CORREL);
|
|
pRowDisVec++;
|
|
}
|
|
|
|
Mat kernel = cv::getGaussianKernel(rows * 2, 1).rowRange(rows, rows * 2);
|
|
|
|
return rowDisVec.dot(kernel);
|
|
}
|
|
|
|
int searchBestMatchDx(int searchRadius, int startCol, int endCol, const Mat& curRow, const Mat& preRowMid)
|
|
{
|
|
vector<double> valVec;
|
|
for (int dx = -searchRadius; dx <= searchRadius; dx++)
|
|
{
|
|
int curStartCol = startCol + dx;
|
|
int curEndCol = endCol + dx;
|
|
Mat curRowMid = curRow.colRange(curStartCol, curEndCol);
|
|
double val = -matDisWithReversedRows(curRowMid, preRowMid);
|
|
valVec.push_back(val);
|
|
}
|
|
auto minIter = std::min_element(valVec.begin(), valVec.end());
|
|
int bestDx = minIter - valVec.begin() - searchRadius;
|
|
|
|
return bestDx;
|
|
}
|
|
|
|
Mat searchBestMatchSubRowMat(const Mat& img, int startRow, int rowWidth, int searchRadius, const Mat& preRowMid)
|
|
{
|
|
Mat curRow = genWithCopiedCols(img.rowRange(startRow, rowWidth + startRow), 3);
|
|
int startCol = img.cols;
|
|
int endCol = img.cols * 2;
|
|
|
|
int bestDx = searchBestMatchDx(searchRadius, startCol, endCol, curRow, preRowMid);
|
|
|
|
return curRow.colRange(startCol + bestDx, endCol + bestDx);
|
|
}
|
|
|
|
void xEnergeyMat(const Mat& img, Mat& energyMat,
|
|
int kernelWidth = 12, int backgroundThre = 40, int method = 2)
|
|
{
|
|
switch (method)
|
|
{
|
|
case 0:
|
|
{
|
|
// sobel edge
|
|
Mat sobelMat;
|
|
cv::Sobel(img, sobelMat, CV_32FC1, 1, 0);
|
|
sobelMat = cv::abs(sobelMat);
|
|
energyMat = sobelMat;
|
|
}
|
|
break;
|
|
case 1:
|
|
{
|
|
// edge with first derivative
|
|
Mat kernel = cv::getGaussianKernel(kernelWidth, 4).t();
|
|
Mat halfKernel(kernel.colRange(0, kernel.cols / 2));
|
|
halfKernel *= -1.0;
|
|
filter2D(img, energyMat, CV_32FC1, kernel);
|
|
energyMat = abs(energyMat);
|
|
}
|
|
break;
|
|
case 2:
|
|
{
|
|
// edge with second derivative
|
|
Mat kernel = cv::getGaussianKernel(kernelWidth, 4).t();
|
|
Mat halfKernel(kernel.colRange(0, kernel.cols / 2));
|
|
halfKernel *= -1.0;
|
|
filter2D(img, energyMat, CV_32FC1, kernel);
|
|
energyMat = abs(energyMat);
|
|
|
|
Mat secondEnergyMat;
|
|
filter2D(energyMat, secondEnergyMat, CV_32FC1, kernel);
|
|
secondEnergyMat = abs(secondEnergyMat);
|
|
|
|
energyMat = secondEnergyMat;
|
|
}
|
|
break;
|
|
case 3:
|
|
{
|
|
// darker edge with first derivative
|
|
Mat kernel = cv::getGaussianKernel(kernelWidth, 4).t();
|
|
Mat gaussMat;
|
|
filter2D(img, gaussMat, CV_32FC1, kernel);
|
|
gaussMat = 255.0 - gaussMat;
|
|
Mat halfKernel(kernel.colRange(0, kernel.cols / 2));
|
|
halfKernel *= -1.0;
|
|
filter2D(img, energyMat, CV_32FC1, kernel);
|
|
energyMat = abs(energyMat) + gaussMat*0.2;
|
|
}
|
|
case 4:
|
|
{
|
|
// darker edge with thresholding
|
|
Mat threMat = img < backgroundThre;
|
|
converToType(threMat, CV_32FC1);
|
|
|
|
Mat kernel = cv::getGaussianKernel(kernelWidth, 4).t();
|
|
|
|
Mat halfKernel(kernel.colRange(0, kernel.cols / 2));
|
|
halfKernel *= -1.0;
|
|
filter2D(img, energyMat, CV_32FC1, kernel);
|
|
energyMat = abs(energyMat) + threMat;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void findBoundaryY(const Mat& img, vector<int>& vec, vector<float>& energyVec,
|
|
int gradientKernelWidth = 12,
|
|
int smoothKernelWidth = 32,
|
|
float smoothWeight = 3.0,
|
|
int backgroundThre = 40,
|
|
int energyType = 4)
|
|
{
|
|
// generate energy mat (sobel)
|
|
Mat energyMat;
|
|
xEnergeyMat(img, energyMat, gradientKernelWidth, backgroundThre, energyType);
|
|
|
|
int n = 4;
|
|
Mat energyMatBack = energyMat.clone();
|
|
|
|
Mat smoothImg, showMat;
|
|
{
|
|
Mat kernel = cv::getGaussianKernel(smoothKernelWidth, 4.0).t();
|
|
filter2D(img, smoothImg, CV_32FC1, kernel);
|
|
}
|
|
|
|
float w = pow(10, smoothWeight);
|
|
dynamicProgramYWithSmooth(energyMat, n, w/255.0, smoothImg);
|
|
findMaxYPath(energyMat, vec, energyVec, n);
|
|
|
|
recoverPathEnergy(energyVec);
|
|
}
|
|
|
|
void findBoundaryY(const Mat& img0, const Mat& img1, int targetWidth,
|
|
vector<int>& vec, vector<float>& energyVec)
|
|
{
|
|
// generate energy mat
|
|
Mat energyMat0, energyMat1;
|
|
xEnergeyMat(img0, energyMat0);
|
|
xEnergeyMat(img1, energyMat1);
|
|
|
|
// generate smooth mat (gauss)
|
|
Mat smoothImg0, smoothImg1, showMat;
|
|
{
|
|
Mat kernel = cv::getGaussianKernel(31, 4.0).t();
|
|
filter2D(img0, smoothImg0, CV_32FC1, kernel);
|
|
filter2D(img1, smoothImg1, CV_32FC1, kernel);
|
|
}
|
|
|
|
int n = 4;
|
|
float sw = 100.0 / 255.0;
|
|
dynamicProgramYWithSmooth(energyMat0, energyMat1,
|
|
smoothImg0, smoothImg1, targetWidth, n, sw);
|
|
|
|
findMaxYPath(energyMat0, vec, energyVec, n);
|
|
|
|
recoverPathEnergy(energyVec);
|
|
}
|
|
|
|
// assuming with two boundaries (left and right)
|
|
void autoAlignEachRowGloballyWithIndependentBoundaries(Mat& img)
|
|
{
|
|
// divide into two part: left and right
|
|
Mat leftImg(img.colRange(0, img.cols / 2));
|
|
Mat rightImg(img.colRange(img.cols / 2, img.cols));
|
|
|
|
// search a boundary for each part
|
|
vector<int> leftVec, rightVec;
|
|
vector<float> leftEnergyVec, rightEnergyVec;
|
|
findBoundaryY(leftImg, leftVec, leftEnergyVec);
|
|
findBoundaryY(rightImg, rightVec, rightEnergyVec);
|
|
|
|
// draw path
|
|
Mat canvas = img.clone();
|
|
drawPointsY<uchar>(leftVec, canvas, 0, 0, 255);
|
|
drawPointsY<uchar>(rightVec, canvas, canvas.cols / 2, 0, 255);
|
|
|
|
// align centers of each row
|
|
int rightBaseX = img.cols / 2;
|
|
int centerX = (leftVec[0] + rightVec[0] + rightBaseX) / 2;
|
|
Mat ret = img.clone();
|
|
ret.setTo(0);
|
|
|
|
// move each row to align
|
|
for (int i = 0; i < img.rows; ++i)
|
|
{
|
|
int curCenterX = (leftVec[i] + rightVec[i] + rightBaseX) / 2;
|
|
int dx = centerX - curCenterX;
|
|
Mat curRow = genWithCopiedCols(img.row(i), 3);
|
|
curRow.colRange(img.cols - dx, img.cols * 2 - dx).copyTo(ret.row(i));
|
|
}
|
|
|
|
ret.copyTo(img);
|
|
}
|
|
|
|
void autoAlignEachRowWithWeightedXCen(Mat& img, int tarCenX, vector<int>& dxVec)
|
|
{
|
|
dxVec.resize(img.rows, 0);
|
|
|
|
for (int i = 0; i < img.rows; ++i)
|
|
{
|
|
Mat r = img.row(i);
|
|
uchar* p = r.data;
|
|
uchar* ep = p + r.cols;
|
|
int j = 0;
|
|
float wx = 0;
|
|
float ws = 0;
|
|
while (p != ep)
|
|
{
|
|
float w = (*p) * (*p);
|
|
ws += w;
|
|
wx += j*w;
|
|
j++;
|
|
p++;
|
|
}
|
|
wx /= ws;
|
|
int dx = floorf(tarCenX - wx);
|
|
dxVec[i] = dx;
|
|
}
|
|
|
|
alignEachRow(img, dxVec);
|
|
}
|
|
|
|
void alignEachRow(Mat& img, const vector<int>& dxVec)
|
|
{
|
|
Mat ret = img.clone();
|
|
ret.setTo(0);
|
|
|
|
for (int i = 0; i < img.rows; ++i)
|
|
{
|
|
Mat curRow = genWithCopiedCols(img.row(i), 3);
|
|
int dx = dxVec[i];
|
|
curRow.colRange(img.cols - dx, img.cols * 2 - dx).copyTo(ret.row(i));
|
|
}
|
|
|
|
ret.copyTo(img);
|
|
}
|
|
|
|
void autoAlignEachRowGloballyWithWidthConstraint(Mat& img, int tarCenX, vector<int>& dxVec)
|
|
{
|
|
// divide into two part: left and right
|
|
Mat leftImg(img.colRange(0, img.cols / 2));
|
|
Mat rightImg(img.colRange(img.cols / 2, img.cols));
|
|
|
|
// search a boundary for each part
|
|
vector<int> leftVec, rightVec;
|
|
vector<float> leftEnergyVec, rightEnergyVec;
|
|
findBoundaryY(leftImg, leftVec, leftEnergyVec);
|
|
findBoundaryY(rightImg, rightVec, rightEnergyVec);
|
|
|
|
// draw path
|
|
Mat canvas = img.clone();
|
|
drawPointsY<uchar>(leftVec, canvas, 0, 0, 255);
|
|
drawPointsY<uchar>(rightVec, canvas, canvas.cols / 2, 0, 255);
|
|
|
|
float avrLeft = sum<int>(leftVec.begin(), leftVec.end()) / leftVec.size();
|
|
float avrRight = sum<int>(rightVec.begin(), rightVec.end()) / rightVec.size();
|
|
int tarWidth = avrRight + leftImg.cols - avrLeft;
|
|
|
|
leftVec.clear();
|
|
leftEnergyVec.clear();
|
|
findBoundaryY(leftImg, rightImg, tarWidth, leftVec, leftEnergyVec);
|
|
|
|
rightVec = leftVec;
|
|
add<int>(rightVec.begin(), rightVec.end(), tarWidth);
|
|
|
|
drawPointsY<uchar>(leftVec, canvas, 0, 0, 180);
|
|
drawPointsY<uchar>(rightVec, canvas, 0, 0, 180);
|
|
|
|
// align centers of each row
|
|
int centerX = leftVec[0] + tarWidth / 2;
|
|
if (tarCenX >= 0)
|
|
{
|
|
centerX = tarCenX;
|
|
}
|
|
Mat ret = img.clone();
|
|
ret.setTo(0);
|
|
|
|
// move each row to align
|
|
dxVec.resize(img.rows, 0);
|
|
for (int i = 0; i < img.rows; ++i)
|
|
{
|
|
int curCenterX = leftVec[i] + tarWidth / 2;
|
|
int dx = centerX - curCenterX;
|
|
dxVec[i] = dx;
|
|
Mat curRow = genWithCopiedCols(img.row(i), 3);
|
|
curRow.colRange(img.cols - dx, img.cols * 2 - dx).copyTo(ret.row(i));
|
|
}
|
|
|
|
ret.copyTo(img);
|
|
}
|
|
|
|
void autoAlignEachRowLocally(Mat& img, int searchRadius /*= 5*/, int rowWidth /*= 1*/, const Mat& preImg /*= Mat()*/)
|
|
{
|
|
Mat ret = img.clone();
|
|
ret.setTo(0);
|
|
|
|
Mat preRow;
|
|
if (!preImg.empty())
|
|
{
|
|
preRow = genWithCopiedCols(preImg.rowRange(0, rowWidth), 3);
|
|
}
|
|
else
|
|
{
|
|
preRow = genWithCopiedCols(img.rowRange(0, rowWidth), 3);
|
|
}
|
|
|
|
Mat preRowMid = preRow.colRange(img.cols, img.cols * 2);
|
|
if (!preImg.empty())
|
|
{
|
|
preRowMid = searchBestMatchSubRowMat(img, 0, rowWidth, searchRadius, preRowMid);
|
|
}
|
|
preRowMid.copyTo(ret.rowRange(0, rowWidth));
|
|
|
|
for (int i = rowWidth; i <= img.rows - rowWidth; i += rowWidth)
|
|
{
|
|
preRowMid = searchBestMatchSubRowMat(img, i, rowWidth, searchRadius, preRowMid);
|
|
|
|
preRowMid.copyTo(ret.rowRange(i, i + rowWidth));
|
|
}
|
|
|
|
ret.copyTo(img);
|
|
}
|
|
|
|
void autoAlignEachRowWithAutoThreshold(Mat& img, int tarCenX, vector<int>& dxVec)
|
|
{
|
|
dxVec.resize(img.rows, 0);
|
|
|
|
for (int i = 0; i < img.rows; ++i)
|
|
{
|
|
Mat row(img.row(i));
|
|
Mat trow;
|
|
threshold(row, trow, 0, 255, THRESH_OTSU);
|
|
uchar* p = trow.data;
|
|
uchar* ep = p + trow.cols;
|
|
int l, r;
|
|
while (p != ep)
|
|
{
|
|
if (*p)
|
|
{
|
|
l = p - trow.data;
|
|
break;
|
|
}
|
|
p++;
|
|
}
|
|
p = ep - 1;
|
|
do
|
|
{
|
|
if (*p)
|
|
{
|
|
r = p - trow.data;
|
|
break;
|
|
}
|
|
p--;
|
|
} while (p != trow.data);
|
|
int curCenX = floorf((l + r) / 2.0);
|
|
dxVec[i] = tarCenX - curCenX;
|
|
}
|
|
|
|
alignEachRow(img, dxVec);
|
|
}
|
|
|
|
void alignEachRowRightBound(Mat& img, int tarCen, vector<int>& dxVec,
|
|
int gradientKernelWidth /*= 12*/,
|
|
int smoothBoundaryKernelWidth /*= 32*/,
|
|
float smoothBoundaryWeight /*= 3.0*/,
|
|
int backgroundThre /*= 40*/)
|
|
{
|
|
// right part
|
|
Mat rightImg(img.colRange(img.cols / 2, img.cols));
|
|
|
|
// search a boundary
|
|
vector<int> rightVec;
|
|
vector<float> rightEnergyVec;
|
|
findBoundaryY(rightImg, rightVec, rightEnergyVec, gradientKernelWidth,
|
|
smoothBoundaryKernelWidth, smoothBoundaryWeight, backgroundThre);
|
|
|
|
// draw path
|
|
Mat canvas = img.clone();
|
|
drawPointsY<uchar>(rightVec, canvas, canvas.cols / 2, 0, 255);
|
|
|
|
// align centers of each row
|
|
int rightBaseX = img.cols / 2;
|
|
int centerX = tarCen;
|
|
Mat ret = img.clone();
|
|
ret.setTo(0);
|
|
|
|
// move each row to align
|
|
for (int i = 0; i < img.rows; ++i)
|
|
{
|
|
int dx = img.cols - rightVec[i] - rightBaseX - 1;
|
|
dxVec.push_back(dx);
|
|
Mat curRow = genWithCopiedCols(img.row(i), 3);
|
|
curRow.colRange(img.cols - dx, img.cols * 2 - dx).copyTo(ret.row(i));
|
|
}
|
|
|
|
ret.copyTo(img);
|
|
}
|
|
|
|
cv::Mat genDirColorImg(const Mat& img, Mat* pValueMat /*= NULL*/)
|
|
{
|
|
Mat hsvImg(img.rows, img.cols, CV_8UC3);
|
|
vector<Mat> channels(3);
|
|
channels[0] = img;
|
|
channels[1] = Mat::zeros(img.rows, img.cols, CV_8UC1);
|
|
channels[2] = channels[1];
|
|
channels[1].setTo(255);
|
|
if (pValueMat)
|
|
{
|
|
channels[2] = *pValueMat;
|
|
}
|
|
else
|
|
{
|
|
channels[2].setTo(255);
|
|
}
|
|
merge(channels, hsvImg);
|
|
Mat rgbImg;
|
|
cvtColor(hsvImg, rgbImg, COLOR_HSV2BGR);
|
|
return rgbImg;
|
|
}
|
|
|
|
void genSobelDir(Mat& img)
|
|
{
|
|
Mat srcImg = img.clone();
|
|
Mat sobelX, sobelY;
|
|
genSobelImage(srcImg, &sobelX, &sobelY);
|
|
img = genSobelDir(sobelX, sobelY);
|
|
}
|
|
|
|
cv::Mat genSobelDir(const Mat& sobelX, const Mat& sobelY)
|
|
{
|
|
Mat img(sobelX.rows, sobelX.cols, CV_8UC1);
|
|
for (int i = 0; i < img.rows; ++i)
|
|
{
|
|
Mat row = img.row(i);
|
|
float* pDataX = (float*)sobelX.row(i).data;
|
|
float* pDataY = (float*)sobelY.row(i).data;
|
|
uchar* pData = row.data;
|
|
for (int j = 0; j < img.cols; ++j)
|
|
{
|
|
float dx = pDataX[j];
|
|
float dy = pDataY[j];
|
|
float angle = 0;
|
|
if (dx == 0 && dy == 0)
|
|
{
|
|
angle = 0;
|
|
}
|
|
else
|
|
{
|
|
angle = atan2f(dy, dx);
|
|
if (angle < 0)
|
|
{
|
|
angle += 2.0 * M_PI;
|
|
}
|
|
}
|
|
pData[j] = floorf((angle / M_PI) * 90);
|
|
}
|
|
}
|
|
return img;
|
|
}
|
|
|
|
void genContrastImg(const Mat& img, Mat& contrastImg, const Mat& kernelMat)
|
|
{
|
|
Mat maxMat, minMat;
|
|
dilate(img, maxMat, kernelMat);
|
|
erode(img, minMat, kernelMat);
|
|
|
|
contrastImg = maxMat - minMat;
|
|
}
|
|
|
|
void genContrastImg(const Mat& img, Mat& contrastImg, int ksize)
|
|
{
|
|
Mat kernelMat(ksize, ksize, CV_8UC1);
|
|
genContrastImg(img, contrastImg, kernelMat);
|
|
}
|
|
|
|
void genYContrastImg(const Mat& img, Mat& constrastImg, int ksize)
|
|
{
|
|
Mat kernelMat(ksize, 1, CV_8UC1);
|
|
genContrastImg(img, constrastImg, kernelMat);
|
|
}
|
|
|
|
void genHalfContrastImg(const Mat& img, Mat& contrastImg, const Mat& kernelMat)
|
|
{
|
|
assert(img.type() == CV_8UC1);
|
|
|
|
uchar* pData = img.data;
|
|
int wstep = img.step;
|
|
int kernelXStep = kernelMat.cols;
|
|
int kernelYStep = kernelMat.rows;
|
|
|
|
contrastImg.create(img.rows / kernelXStep, img.cols / kernelYStep, CV_8UC1);
|
|
uchar* pContrastData = contrastImg.data;
|
|
int wContrastStep = contrastImg.step;
|
|
|
|
for (int y = 0; y < img.rows; y += kernelYStep)
|
|
{
|
|
uchar* pRowData = pData;
|
|
uchar* pContrastRowData = pContrastData;
|
|
for (int x = 0; x < img.cols; x += kernelXStep)
|
|
{
|
|
Mat roiMat(kernelMat.rows, kernelMat.cols, CV_8UC1, pRowData);
|
|
roiMat.step = wstep;
|
|
|
|
|
|
|
|
pRowData += kernelXStep;
|
|
pContrastRowData += 1;
|
|
}
|
|
pData += wstep*kernelYStep;
|
|
pContrastData += wContrastStep;
|
|
}
|
|
}
|
|
|
|
void genHalfContrastImg(const Mat& img, Mat& contrastImg, int ksize)
|
|
{
|
|
|
|
}
|
|
|
|
void cvtColor(Mat& img, int t)
|
|
{
|
|
Mat tImg;
|
|
cvtColor(img, tImg, t);
|
|
img = tImg;
|
|
}
|
|
|
|
cv::Mat rotateImage(const Mat& img, Point2f cen, float degree)
|
|
{
|
|
Mat t = getRotationMatrix2D(cen, degree, 1.0);
|
|
Mat rImg;
|
|
warpAffine(img, rImg, t, img.size(), INTER_CUBIC, BORDER_CONSTANT);
|
|
return rImg;
|
|
}
|
|
|
|
cv::Mat normAngle(const Mat& img, Mat mask)
|
|
{
|
|
Point2f cen(img.cols / 2.0, img.rows / 2.0);
|
|
double angle = localAngle_(img, cen, mask);
|
|
return rotateImage(img, cen, -angle);
|
|
}
|
|
|
|
float normAngle(float angle)
|
|
{
|
|
angle = angle - (int)(angle / 360.0) * 360;
|
|
if (angle < 0)
|
|
{
|
|
angle += 360.0;
|
|
}
|
|
return angle;
|
|
}
|
|
|
|
void printMatInfo(const Mat& m, int baseIndentNum /*= 0*/, int indent /*= 4*/)
|
|
{
|
|
printIndent(baseIndentNum, indent);
|
|
std::cout << "rows: " << m.rows << ", " <<
|
|
"cols: " << m.cols << ", " <<
|
|
"channels: " << m.channels() << ", " <<
|
|
"elementSize: " << m.elemSize() << "; ";
|
|
}
|
|
|
|
string templateMatchMethod2Str(int method)
|
|
{
|
|
switch (method)
|
|
{
|
|
case cv::TM_SQDIFF:
|
|
return "TM_SQDIFF";
|
|
case cv::TM_SQDIFF_NORMED:
|
|
return "TM_SQDIFF_NORMED";
|
|
case cv::TM_CCORR:
|
|
return "TM_CCORR";
|
|
case cv::TM_CCORR_NORMED:
|
|
return "TM_CCORR_NORMED";
|
|
case cv::TM_CCOEFF:
|
|
return "TM_CCOEFF";
|
|
case cv::TM_CCOEFF_NORMED:
|
|
return "TM_CCOEFF_NORMED";
|
|
default:
|
|
return "";
|
|
}
|
|
}
|
|
|
|
void printIndent(int indentNum, int indent /*= 4*/)
|
|
{
|
|
for (int i = 0; i < indentNum; i++)
|
|
{
|
|
for (int j = 0; j < indent; j++)
|
|
{
|
|
std::cout << " ";
|
|
}
|
|
}
|
|
}
|
|
|
|
Mat genSimpleXGradient(const Mat& m)
|
|
{
|
|
Mat kernel(1, 2, CV_32FC1);
|
|
float* p = (float*)kernel.data;
|
|
p[0] = -1;
|
|
p[1] = 1;
|
|
Mat dm;
|
|
filter2D(m, dm, CV_32FC1, kernel,
|
|
Point(-1, -1), 0, BORDER_REFLECT);
|
|
return dm;
|
|
}
|
|
|
|
void uniformLocalMinExtremas(const Mat& vec, Mat& localExtremaValVec,
|
|
vector<int>& idxVec, int n, int refineRange)
|
|
{
|
|
idxVec.resize(n);
|
|
|
|
float step = (float)(vec.cols - 1) / (float)n;
|
|
float* pVec = (float*)vec.data;
|
|
localExtremaValVec.create(1, n + 1, CV_32FC1);
|
|
float* pExtremaValVec = (float*)localExtremaValVec.data;
|
|
|
|
float* pVecData = (float*)vec.data;
|
|
for (int i = 0; i < n; ++i)
|
|
{
|
|
int idx = floorf(step*i);
|
|
int sIdx = idx - refineRange;
|
|
int eIdx = idx + refineRange;
|
|
sIdx = max(0, sIdx);
|
|
eIdx = min(vec.cols - 1, eIdx) + 1;
|
|
float* pExtrema = std::min_element(pVec + sIdx, pVec + eIdx);
|
|
idxVec[i] = pExtrema - pVec;
|
|
pExtremaValVec[i] = *pExtrema;
|
|
}
|
|
|
|
pExtremaValVec[n] = pExtremaValVec[0];
|
|
}
|
|
|
|
void equalizeHist(const Mat& src, Mat& dst, const Mat& mask /*= Mat()*/)
|
|
{
|
|
if (mask.empty())
|
|
{
|
|
cv::equalizeHist(src, dst);
|
|
return;
|
|
}
|
|
Mat hist = calcHist(src, mask);
|
|
hist *= 255.0 / countNonZero(mask);
|
|
|
|
float* pHistData = (float*)hist.data;
|
|
Mat mapHist = Mat::zeros(hist.rows, hist.cols, CV_32FC1);
|
|
float* pMapHistData = (float*)mapHist.data;
|
|
pMapHistData[0] = pHistData[0];
|
|
for (int i = 1; i < 256; ++i)
|
|
{
|
|
pMapHistData[i] = pMapHistData[i - 1] + pHistData[i];
|
|
}
|
|
|
|
dst.create(src.size(), src.type());
|
|
for (int y = 0; y < dst.rows; ++y)
|
|
{
|
|
uchar* pDstData = dst.row(y).data;
|
|
uchar* pSrcData = src.row(y).data;
|
|
for (int x = 0; x < dst.cols; ++x)
|
|
{
|
|
pDstData[x] = pMapHistData[pSrcData[x]];
|
|
}
|
|
}
|
|
}
|
|
|
|
void removeHighlights(const Mat& src, Mat& dst, float thre)
|
|
{
|
|
dst = Mat::zeros(src.size(), src.type());
|
|
Mat hightlightMask = src < thre;
|
|
src.copyTo(dst, hightlightMask);
|
|
}
|
|
|
|
void genSectorSumVec(const Mat& img, const Mat& weightMat, Mat& hist,
|
|
Mat& weightHist, const Point2f& cen, float angleStep,
|
|
Mat* pAngMat /*= NULL*/, Mat* pMagMat /*= NULL*/)
|
|
{
|
|
int count = floorf(360.0 / angleStep);
|
|
hist = Mat::zeros(1, count, CV_32FC1);
|
|
weightHist = Mat::zeros(1, count, CV_32FC1);
|
|
float* pData = (float*)img.data;
|
|
float* pWeightData = (float*)weightMat.data;
|
|
float* pHistData = (float*)hist.data;
|
|
float* pWeightHistData = (float*)weightHist.data;
|
|
float *x = new float[img.cols];
|
|
for (int i = 0; i < img.cols; ++i) x[i] = i - cen.x;
|
|
Mat xRow(1, img.cols, CV_32FC1, x);
|
|
Mat magMat(img.rows, img.cols, CV_32FC1);
|
|
Mat angMat(img.rows, img.cols, CV_32FC1);
|
|
for (int i = 0; i < img.rows; ++i)
|
|
{
|
|
Mat yRow = Mat::ones(xRow.size(), xRow.type())*(i - cen.y);
|
|
|
|
Mat magRow, angRow;
|
|
cartToPolar(xRow, yRow, magRow, angRow, true);
|
|
magRow.copyTo(magMat.row(i));
|
|
angRow.copyTo(angMat.row(i));
|
|
|
|
float* pAngleData = (float*)angRow.data;
|
|
float* pRadiusData = (float*)magRow.data;
|
|
for (int j = 0; j < img.cols; ++j)
|
|
{
|
|
if (i == 100 && j == 90)
|
|
{
|
|
int a = 0;
|
|
}
|
|
float val = pData[j];
|
|
float weight = pWeightData[j];
|
|
float radius = pRadiusData[j];
|
|
if (radius == 0)
|
|
{
|
|
continue;
|
|
}
|
|
float angle = pAngleData[j];
|
|
|
|
val *= weight;
|
|
|
|
float angleRange = 28.6479 / radius;
|
|
float langle = angle - angleRange;
|
|
float rangle = angle + angleRange;
|
|
|
|
int lIntAngle = (int)(langle);
|
|
int rIntAngle = (int)rangle;
|
|
int li = normAngle(lIntAngle);
|
|
int ri = normAngle(rIntAngle);
|
|
float totalRange = angleRange*2.0;
|
|
//pHistData[li] += val*(langle - ceil(langle)) / totalRange;
|
|
pHistData[li] += val*(1 - langle + lIntAngle) / totalRange;
|
|
//pWeightHistData[li] += weight*(langle - ceil(langle)) / totalRange;
|
|
pWeightHistData[li] += weight*(1 - langle + lIntAngle) / totalRange;
|
|
pHistData[ri] += val*(rangle - rIntAngle) / totalRange;
|
|
pWeightHistData[ri] += weight*(rangle - rIntAngle) / totalRange;
|
|
lIntAngle++;
|
|
#if defined(LITTLE_CPP11)
|
|
if (_isnan(pHistData[li]) || _isnan(pHistData[ri]))
|
|
#else
|
|
if (isnan(pHistData[li]) || isnan(pHistData[ri]))
|
|
#endif
|
|
{
|
|
waitKey();
|
|
}
|
|
while (lIntAngle < rIntAngle)
|
|
{
|
|
li = normAngle(lIntAngle);
|
|
pHistData[li] += val / totalRange;
|
|
pWeightHistData[li] += weight / totalRange;
|
|
lIntAngle++;
|
|
}
|
|
}
|
|
pData += img.step1();
|
|
pWeightData += weightMat.step1();
|
|
}
|
|
|
|
if (pAngMat)
|
|
{
|
|
*pAngMat = angMat;
|
|
}
|
|
if (pMagMat)
|
|
{
|
|
*pMagMat = magMat;
|
|
}
|
|
|
|
free(x);
|
|
}
|
|
|
|
Mat matDivideNonzero(const Mat& m0, const Mat& m1)
|
|
{
|
|
// m0/m1
|
|
Mat m1copy = m1.clone();
|
|
m0.copyTo(m1copy, m1copy < 0.0000001);
|
|
return m0 / m1copy;
|
|
}
|
|
|
|
|
|
Mat matDivideNonzero(float m0, const Mat& m1)
|
|
{
|
|
// m0/m1
|
|
Mat m1copy = m1.clone();
|
|
m1copy.setTo(m0, m1 < 0.0000001);
|
|
return m0 / m1copy;
|
|
}
|
|
|
|
|
|
void normSectors(Mat& _img, Mat weightMat, const Point2f& cen,
|
|
float angleStep, float tarVal)
|
|
{
|
|
if (weightMat.empty())
|
|
{
|
|
weightMat = Mat::ones(_img.size(), CV_32FC1);
|
|
}
|
|
Mat img = _img;
|
|
if (img.channels() != 1)
|
|
{
|
|
img = getFirstChannel(img);
|
|
}
|
|
if (img.type() != CV_32FC1)
|
|
{
|
|
converToType(img, CV_32FC1);
|
|
}
|
|
int count = floorf(360.0 / angleStep);
|
|
Mat hist = Mat::zeros(1, count, CV_32FC1);
|
|
Mat weightHist = Mat::zeros(1, count, CV_32FC1);
|
|
Mat magMat(img.rows, img.cols, CV_32FC1);
|
|
Mat angMat(img.rows, img.cols, CV_32FC1);
|
|
|
|
genSectorSumVec(img, weightMat, hist, weightHist, cen, angleStep,
|
|
&angMat, &magMat);
|
|
|
|
hist = matDivideNonzero(hist, weightHist);
|
|
|
|
#ifdef DEBUG_VIEW_INTERNAL_MAT
|
|
Mat vAngleMat = angMat / 360;
|
|
Mat vMagMat = magMat / sqrt(img.cols*img.cols / 4.0 + img.rows*img.rows / 4.0);
|
|
Mat vHist = hist;
|
|
vHist = minMaxNorm(vHist, 0, 1.0);
|
|
Mat vWeightHist = weightHist;
|
|
vWeightHist = minMaxNorm(vWeightHist, 0, 1.0);
|
|
#endif
|
|
|
|
Mat scaleHist = matDivideNonzero(tarVal, hist);
|
|
|
|
applySectorScales(img, scaleHist, angMat);
|
|
|
|
_img = img;
|
|
}
|
|
|
|
void normSectors_tarImg(Mat& img, Mat weightMat, const Point2f& cen, float angleStep,
|
|
const Mat& tarImg)
|
|
{
|
|
Mat hist, weightHist, angMat, magMat;
|
|
genSectorSumVec(img, weightMat, hist, weightHist, cen, angleStep,
|
|
&angMat, &magMat);
|
|
|
|
#ifdef DEBUG_VIEW_INTERNAL_MAT
|
|
Mat vHist0 = minMaxNorm(hist, 0, 1.0);
|
|
Mat vWeightHist = minMaxNorm(weightHist, 0, 1.0);
|
|
#endif
|
|
|
|
hist = matDivideNonzero(hist, weightHist);
|
|
|
|
#ifdef DEBUG_VIEW_INTERNAL_MAT
|
|
Mat vHist1 = hist / 255.0;
|
|
#endif
|
|
|
|
Mat tarHist;
|
|
genSectorSumVec(tarImg, weightMat, tarHist, weightHist, cen, angleStep);
|
|
tarHist = matDivideNonzero(tarHist, weightHist);
|
|
|
|
#ifdef DEBUG_VIEW_INTERNAL_MAT
|
|
Mat vTarHist = tarHist/255.0;
|
|
#endif
|
|
|
|
normSectorsMeans_tarHist(img, hist, cen, angleStep, tarHist, angMat);
|
|
}
|
|
|
|
void normSectorsMeans_tarHist(Mat& img, const Mat& hist,
|
|
const Point2f& cen, float angleStep, const Mat& tarHist, const Mat& angMat)
|
|
{
|
|
Mat scaleHist = matDivideNonzero(tarHist, hist);
|
|
|
|
#ifdef DEBUG_VIEW_INTERNAL_MAT
|
|
Mat vScaleHist = minMaxNorm(scaleHist, 0, 1.0);
|
|
#endif
|
|
applySectorScales(img, scaleHist, angMat);
|
|
}
|
|
|
|
void applySectorScales(Mat& img, const Mat& scaleHist, const Mat& angMat)
|
|
{
|
|
float* pData = (float*)img.data;
|
|
float* pHistData = (float*)scaleHist.data;
|
|
for (int i = 0; i < img.rows; ++i)
|
|
{
|
|
Mat angRow = angMat.row(i);
|
|
|
|
float* pAngleData = (float*)angRow.data;
|
|
for (int j = 0; j < img.cols; ++j)
|
|
{
|
|
float angle = pAngleData[j];
|
|
int base = (int)angle;
|
|
float s = pHistData[base];
|
|
pData[j] *= s;
|
|
}
|
|
pData += img.step1();
|
|
}
|
|
}
|
|
|
|
cv::Point2f imgCen(const Mat& img)
|
|
{
|
|
return Point2f(img.cols / 2.0, img.rows / 2.0);
|
|
}
|
|
|
|
cv::Mat getForeImage(const Mat & src, const Mat &backgroundImg)
|
|
{
|
|
Mat resizedBackgroundImg = backgroundImg;
|
|
if (backgroundImg.size() != src.size()) {
|
|
resize(backgroundImg, resizedBackgroundImg, src.size());
|
|
}
|
|
return (src - resizedBackgroundImg);
|
|
}
|
|
#define ALG_RESIZE_IMAGE_WIDTH 416.0
|
|
//
|
|
// cv::Mat findCircleObject(const Mat &src, const Mat& backgroundImg,
|
|
// int nThres /*= 20*/, luffy_base::luffyCircle *pCircle /*= NULL*/)
|
|
// {
|
|
// if (src.empty() || backgroundImg.empty() || src.rows < 500) {
|
|
// return Mat();
|
|
// }
|
|
//
|
|
// assert(backgroundImg.type() == CV_8UC1);
|
|
// Mat imgTmp, imgBinary;
|
|
// const cv::Size cSize = cv::Size(ALG_RESIZE_IMAGE_WIDTH, floorf(ALG_RESIZE_IMAGE_WIDTH / (float)src.cols*(float)src.rows));
|
|
// cv::resize(src, imgTmp, cSize);
|
|
// Mat foregroundImg = getForeImage(imgTmp, backgroundImg);
|
|
//
|
|
// using namespace luffy_base;
|
|
// luffy_threshold::Threshold(foregroundImg, imgBinary, nThres);
|
|
//
|
|
// Mat dilatedImgBin;
|
|
// dilate(imgBinary, dilatedImgBin, Mat::ones(7, 7, CV_32FC1));
|
|
// erode(dilatedImgBin, imgBinary, Mat::ones(7, 7, CV_32FC1));
|
|
//
|
|
// openOper(imgBinary, Mat::ones(1, 13, CV_32FC1));
|
|
//
|
|
// vector<vector<Point>> conts;
|
|
// cv::findContours(imgBinary, conts, RETR_EXTERNAL, CHAIN_APPROX_NONE);
|
|
// imgBinary.setTo(0);
|
|
// for (int i = 0; i < conts.size(); i++) {
|
|
// const vector<Point> &pt = conts.at(i);
|
|
// if (pt.size() < 20) {
|
|
// continue;
|
|
// }
|
|
// Rect rt = boundingRect(pt);
|
|
// if (rt.width < 5 || rt.height < 5) {
|
|
// continue;
|
|
// }
|
|
// drawContours(imgBinary, conts, i, Scalar::all(255), -1);
|
|
// }
|
|
// Mat hit; vector<Point> pts;
|
|
// luffy_hit::firstHit4Circle(imgBinary, hit, pts, Point(cSize.width / 2, cSize.height / 2), 0, cSize.width / 2, 360, luffy_hit::emHitOut2In);
|
|
//
|
|
// //luffy_imageProc::RansacParam rs(0.02, 2.5, 70, 100, 220);
|
|
// luffy_imageProc::RansacParam rs(0.001, 1.5, 2240, 100, 420);
|
|
// vector<Point> pts2 = luffy_imageProc::fitModelbyRansac(pts, luffy_imageProc::emModelCircle, &rs);
|
|
//
|
|
// #ifdef _DEBUG
|
|
// Mat imgColor;
|
|
// cv::cvtColor(imgTmp, imgColor, CV_GRAY2BGR);
|
|
// for (int i = 0; i < pts.size(); i++) {
|
|
// imgColor.at<cv::Vec3b>(pts.at(i))[0] = 255;//B
|
|
// imgColor.at< cv::Vec3b >(pts.at(i))[1] = 0;//G
|
|
// imgColor.at< cv::Vec3b >(pts.at(i))[2] = 0;//R
|
|
// }
|
|
// for (int i = 0; i < pts2.size(); i++) {
|
|
// imgColor.at<cv::Vec3b>(pts2.at(i))[0] = 0;//B
|
|
// imgColor.at< cv::Vec3b >(pts2.at(i))[1] = 0;//G
|
|
// imgColor.at< cv::Vec3b >(pts2.at(i))[2] = 255;//R
|
|
// }
|
|
// #endif
|
|
//
|
|
// float fRadius;
|
|
// Point2f ptCenter;
|
|
// bool bFind = luffy_imageProc::lsCircleFit(pts2, fRadius, ptCenter);
|
|
//
|
|
// if (!bFind) {
|
|
// return Mat();
|
|
// }
|
|
// Mat dst;
|
|
// const int nOffset = 1;
|
|
// fRadius += nOffset;
|
|
// Rect rt(ptCenter.x - fRadius + nOffset, ptCenter.y - fRadius + nOffset, 2 * fRadius, 2 * fRadius);
|
|
// rt &= Rect(0, 0, imgTmp.cols, imgTmp.rows);
|
|
// Mat test;
|
|
// imgTmp(rt).copyTo(test);
|
|
// static int nCount = cv::getTickCount();
|
|
// //QString str = "d://image//" + QString::number(cv::getTickCount());
|
|
// //QString strTest = str + "_1.jpg";
|
|
// //cv::imwrite(strTest.toLatin1().data(), test);
|
|
// //
|
|
// if (pCircle) {
|
|
// float fScale = src.cols / ALG_RESIZE_IMAGE_WIDTH;
|
|
// Mat matBig = src - backgroundImg;
|
|
// pCircle->fRadius = fRadius * fScale;
|
|
// pCircle->ptCenter = Point(ptCenter.x * fScale, ptCenter.y * fScale);
|
|
// Mat matBinary;
|
|
// luffy_threshold::Threshold(matBig, matBinary, nThres);
|
|
// Mat hit; vector<Point> pts;
|
|
// luffy_hit::firstHit4Circle(matBinary, hit, pts, pCircle->ptCenter, 0, pCircle->fRadius + 10, 360, luffy_hit::emHitOut2In);
|
|
// luffy_imageProc::RansacParam rs(0.01, 2.5, 100, 100, 220);
|
|
// vector<Point> pts2 = luffy_imageProc::fitModelbyRansac(pts, luffy_imageProc::emModelCircle, &rs);
|
|
// float fRadius2;
|
|
// Point2f ptCenter2;
|
|
// bool bFind = luffy_imageProc::lsCircleFit(pts2, fRadius2, ptCenter2);
|
|
// if (bFind) {
|
|
// pCircle->fRadius = fRadius2;
|
|
// pCircle->ptCenter = ptCenter2;
|
|
// Rect rt(ptCenter2.x - fRadius2 + nOffset, ptCenter2.y - fRadius2 + nOffset, 2 * fRadius2, 2 * fRadius2);
|
|
// src(rt).copyTo(dst);
|
|
// int nWidth = ((int)((double)dst.cols / fScale / 4)) * 4;
|
|
// cv::resize(dst, dst, cv::Size(nWidth, nWidth));
|
|
// //QString strTest = str + "_2.jpg";
|
|
// //cv::imwrite(strTest.toLatin1().data(), dst);
|
|
// }
|
|
// }
|
|
// if (dst.empty()) {
|
|
// imgTmp(rt).copyTo(dst);
|
|
// }
|
|
//
|
|
// return dst;
|
|
// }
|
|
|
|
bool ensureGrayImg(const Mat& img, Mat& gray)
|
|
{
|
|
if (img.channels() == 3)
|
|
{
|
|
cvtColor(img, gray, COLOR_BGR2GRAY);
|
|
return true;
|
|
}
|
|
else if (img.channels() == 4)
|
|
{
|
|
cvtColor(img, gray, COLOR_BGRA2GRAY);
|
|
return true;
|
|
}
|
|
else if (img.channels() == 1)
|
|
{
|
|
gray = img;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
assert(false && "unsupported image conversion.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
double hsvDis(const Vec3b& v0, const Vec3b& v1)
|
|
{
|
|
float h0 = v0[0] * 2.0 * 0.01745;
|
|
float s0 = v0[1] * (v0[2] / 255.0) * 2.0;
|
|
float h1 = v1[0] * 2.0 * 0.01745;
|
|
float s1 = v1[1] * (v1[2] / 255.0) * 2.0;
|
|
|
|
double dVal = norm(Vec3f(cos(h0)*s0, sin(h0)*s0, v0[2]),
|
|
Vec3f(cos(h1)*s1, sin(h1)*s1, v1[2]));
|
|
|
|
return dVal;
|
|
}
|
|
|
|
cv::Mat genHSVColorDisMat(const Mat& m0, const Mat& m1)
|
|
{
|
|
Scalar mean, stddev;
|
|
meanStdDev(m1, mean, stddev);
|
|
Vec3b baseColor(mean.val[0], mean.val[1], mean.val[2]);
|
|
return genHSVColorDisMat(m0, baseColor);
|
|
}
|
|
|
|
cv::Mat genHSVColorDisMat(const Mat& m, Vec3b baseColor)
|
|
{
|
|
Mat ret = Mat::zeros(m.size(), CV_32FC1);
|
|
uchar* pRet = ret.data;
|
|
uchar* pData0 = m.data;
|
|
for (int i = 0; i < m.rows; ++i)
|
|
{
|
|
float* pRet32f = (float*)pRet;
|
|
Vec3b* pData32f0 = (Vec3b*)pData0;
|
|
for (int j = 0; j < m.cols; ++j)
|
|
{
|
|
Vec3b& v0 = pData32f0[j];
|
|
pRet32f[j] = (float)hsvDis(v0, baseColor);
|
|
}
|
|
|
|
pRet += ret.step;
|
|
pData0 += m.step;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
cv::Mat genHSVColorDisMat(const Mat& m)
|
|
{
|
|
Scalar mean, stddev;
|
|
meanStdDev(m, mean, stddev);
|
|
return genHSVColorDisMat(m, Vec3b(mean.val[0], mean.val[1], mean.val[2]));
|
|
}
|
|
|
|
cv::Mat genBlueGreenRatioMat(const Mat& m, float alpha /*= 50.0*/, float beta /*= -17.0*/)
|
|
{
|
|
vector<Mat> channels;
|
|
split(m, channels);
|
|
Mat blue32f, green32f;
|
|
channels[0].convertTo(blue32f, CV_32FC1);
|
|
channels[1].convertTo(green32f, CV_32FC1, 1.0, -beta);
|
|
Mat ratioMat = green32f / blue32f;
|
|
Mat ratioMat8u;
|
|
ratioMat.convertTo(ratioMat8u, CV_8UC1, alpha);
|
|
return ratioMat8u;
|
|
}
|
|
|
|
Mat genColorFuncDisMat(const Mat& m, float a, float b, float c)
|
|
{
|
|
vector<Mat> channels;
|
|
split(m, channels);
|
|
Mat blue32f, green32f;
|
|
channels[0].convertTo(blue32f, CV_32FC1);
|
|
channels[1].convertTo(green32f, CV_32FC1);
|
|
Mat sqrBlue = blue32f.mul(blue32f, a);
|
|
Mat bBlue = blue32f*b;
|
|
//Mat dis = ((sqrBlue + bBlue) - green32f);
|
|
Mat dis = (green32f - sqrBlue + c) / blue32f;
|
|
Mat ret;
|
|
dis.convertTo(ret, CV_8UC1, 255);
|
|
return ret;
|
|
}
|
|
|
|
void setChannel(Mat& img, int i, Mat chnMat)
|
|
{
|
|
vector<Mat> channels;
|
|
split(img, channels);
|
|
channels[i] = chnMat;
|
|
merge(channels, img);
|
|
}
|
|
|
|
|
|
void transPoints(const vector<Point2f>& vec, vector<Point2f>& oVec, const Matx23f& mat)
|
|
{
|
|
oVec.resize(vec.size());
|
|
for (size_t i = 0; i < vec.size(); ++i)
|
|
{
|
|
Point2f p = vec[i];
|
|
Point2f tp = mat * Vec3f(p.x, p.y, 1.0);
|
|
oVec[i] = Point2f(tp.x, tp.y);
|
|
}
|
|
}
|
|
|
|
void transPoints(vector<Point2d>& vec, const Matx33d& mat)
|
|
{
|
|
for (size_t i = 0; i < vec.size(); ++i)
|
|
{
|
|
Point2d p = vec[i];
|
|
Point3d tp = mat * p;
|
|
vec[i] = Point2d(tp.x / tp.z, tp.y / tp.z);
|
|
}
|
|
}
|
|
|
|
void transPoints(vector<Point2d>& vec, const Mat& mat)
|
|
{
|
|
Matx33d matx = Matx33d::eye();
|
|
Mat matx_(3, 3, CV_64FC1, matx.val);
|
|
|
|
if (mat.rows == 2 && mat.cols == 3)
|
|
{
|
|
mat.copyTo(matx_.rowRange(0, 2));
|
|
}
|
|
else if (mat.rows == 3 && mat.cols == 3)
|
|
{
|
|
mat.copyTo(matx_);
|
|
}
|
|
else
|
|
{
|
|
std::cout << "not supported transformation mat with its size as " \
|
|
<< mat.rows << "x" << mat.cols << std::endl;
|
|
return;
|
|
}
|
|
transPoints(vec, matx);
|
|
}
|
|
|
|
Matx23f getRotationMatrix23f(Point2f center, float angle, float scale, float xOffset, float yOffset)
|
|
{
|
|
Matx23f rotMat;
|
|
float* pRotVal = rotMat.val;
|
|
float a_pi = -angle * CV_PI / 180;
|
|
float alpha = cos(a_pi)*scale;
|
|
float beta = sin(a_pi)*scale;
|
|
pRotVal[0] = alpha;
|
|
pRotVal[1] = beta;
|
|
pRotVal[2] = -alpha * center.x - beta * center.y + xOffset;
|
|
pRotVal[3] = -beta;
|
|
pRotVal[4] = alpha;
|
|
pRotVal[5] = beta * center.x - alpha * center.y + yOffset;
|
|
|
|
return rotMat;
|
|
}
|
|
|
|
void getRigidTransform_(const Point2f& u1, const Point2f& u2, const Point2f& v1, const Point2f& v2,
|
|
double& x0, double& y0, double& angle, double& scale)
|
|
{
|
|
vector<Point2d> srcPtVec(2), dstPtVec(2);
|
|
srcPtVec[0] = Point2d(u1.x, u1.y);
|
|
srcPtVec[1] = Point2d(u2.x, u2.y);
|
|
dstPtVec[0] = Point2d(v1.x, v1.y);
|
|
dstPtVec[1] = Point2d(v2.x, v2.y);
|
|
|
|
Mat srMat;
|
|
Matx33d t = rigidTrans(srcPtVec, dstPtVec, &srMat);
|
|
|
|
double t0 = t.val[0];
|
|
double t1 = t.val[1];
|
|
double l = sqrt(t0 * t0 + t1 * t1);
|
|
if (abs(l) > FLT_EPSILON)
|
|
{
|
|
t0 /= l;
|
|
t1 /= l;
|
|
scale = 1.0 / l;
|
|
angle = acos(t0) / CV_PI * 180;
|
|
angle = normAngle(angle);
|
|
}
|
|
else
|
|
{
|
|
angle = 0;
|
|
scale = 1.0;
|
|
}
|
|
|
|
Point2d vCen = v1 + v2;
|
|
|
|
x0 = vCen.x / 2.;
|
|
y0 = vCen.y / 2.;
|
|
}
|
|
|
|
void getRigidTransform(const Point2f& u1, const Point2f& u2, const Point2f& v1, const Point2f& v2,
|
|
double& x0, double& y0, double& angle, double& scale)
|
|
{
|
|
float theta_u = atan2f(u2.y - u1.y, u2.x - u1.x);
|
|
float theta_v = atan2f(v2.y - v1.y, v2.x - v1.x);
|
|
angle = (theta_v - theta_u) * 180 / CV_PI;
|
|
angle = normAngle(angle);
|
|
|
|
float sin_a = sin(angle / 180 * CV_PI);
|
|
float cos_a = cos(angle / 180 * CV_PI);
|
|
|
|
double f = (u2.x - u1.x) * cos_a - (u2.y - u1.y) * sin_a;
|
|
if (abs(f) > FLT_EPSILON)
|
|
{
|
|
scale = (v2.x - v1.x) / f;
|
|
}
|
|
else
|
|
{
|
|
scale = sqrt(pow(u2.x - u1.x, 2) + pow(u2.y - u1.y, 2)) / sqrt(pow(v2.x - v1.x, 2) + pow(v2.y - v1.y, 2));
|
|
}
|
|
|
|
x0 = v1.x - u1.x * scale * cos_a + u1.y * scale * sin_a;
|
|
y0 = v1.y - u1.x * scale * sin_a - u1.y * scale * cos_a;
|
|
}
|
|
|
|
void getRigidTransform(const Point2f& cen1, const Point2f& u1, const Point2f& u2,
|
|
const Point2f& cen2, const Point2f& v1, const Point2f& v2,
|
|
double& x0, double& y0, double& angle, double& scale)
|
|
{
|
|
getRigidTransform(u1 - cen1, u2 - cen1, v1 - cen2, v2 - cen2, x0, y0, angle, scale);
|
|
}
|
|
|
|
void getRigidTransform(const Point2f& cen1, const Point2f& u1, float ua,
|
|
const Point2f& cen2, const Point2f& v1, float va,
|
|
double& x0, double& y0, double& angle, double& scale)
|
|
{
|
|
getRigidTransform(u1 - cen1, ua, v1 - cen2, va, x0, y0, angle, scale);
|
|
}
|
|
|
|
void getRigidTransform(const Point2f& u1, float ua,
|
|
const Point2f& v1, float va,
|
|
double& x0, double& y0, double& angle, double& scale)
|
|
{
|
|
getRigidTransform(u1, u1 + Point2f(cos(ua / 180 * CV_PI), sin(ua / 180 * CV_PI)),
|
|
v1, v1 + Point2f(cos(va / 180 * CV_PI), sin(va / 180 * CV_PI)),
|
|
x0, y0, angle, scale);
|
|
}
|
|
|
|
void getRigidTransform(const Point2f& cen1, const Point2f& u1, float ua,
|
|
const Point2f& cen2, const Point2f& v1, float va,
|
|
float s,
|
|
double& x0, double& y0, double& angle, double& scale)
|
|
{
|
|
getRigidTransform(u1 - cen1, ua, v1 - cen2, va, s, x0, y0, angle, scale);
|
|
}
|
|
|
|
void getRigidTransform(const Point2f& u1, float ua,
|
|
const Point2f& v1, float va,
|
|
float s,
|
|
double& x0, double& y0, double& angle, double& scale)
|
|
{
|
|
getRigidTransform(u1, u1 + Point2f(cos(ua / 180 * CV_PI), sin(ua / 180 * CV_PI)),
|
|
v1, v1 + Point2f(cos(va / 180 * CV_PI), sin(va / 180 * CV_PI)) * s,
|
|
x0, y0, angle, scale);
|
|
}
|
|
|
|
Mat applyPerspectiveTransform(const Mat& img, std::vector<Point2f>& transVertexes, int flags)
|
|
{
|
|
if (img.empty()) return gDummyMat;
|
|
|
|
int w = img.cols, h = img.rows;
|
|
#if defined(LITTLE_CPP11)
|
|
std::vector<Point2f> pnts1(4);
|
|
pnts1[0] = Point2f(0, 0);
|
|
pnts1[1] = Point2f(w - 1, 0);
|
|
pnts1[2] = Point2f(w - 1, h - 1);
|
|
pnts1[3] = Point2f(0, h - 1);
|
|
#else
|
|
std::vector<Point2f> pnts1 = {
|
|
Point2f(0, 0), Point2f(w - 1, 0), Point2f(w - 1, h - 1), Point2f(0, h - 1)
|
|
};
|
|
#endif
|
|
Mat H = findHomography(pnts1, transVertexes);
|
|
Mat img_warp;
|
|
warpPerspective(img, img_warp, H, img.size(), flags);
|
|
return img_warp;
|
|
}
|
|
|
|
|
|
};
|