//2015.11.23
#include <iostream>
#include <vector>
#include <stack>
#include "Defines.h"
#include "OneSeg.h"
//定义点
typedef struct _MyPoint
{
bool flag;
double x;
double y;
_MyPoint(double _x = 0.0, double _y = 0.0, bool _flag = false)
{
x = _x;
y = _y;
flag = _flag;
}
}MyPoint;
double CalcAngle(MyPoint first, MyPoint cen, MyPoint second)
{
double dx1, dx2, dy1, dy2;
double angle;
dx1 = first.x - cen.x;
dy1 = first.y - cen.y;
dx2 = second.x - cen.x;
dy2 = second.y - cen.y;
double c = (double)sqrt(dx1 * dx1 + dy1 * dy1) * (double)sqrt(dx2 * dx2 + dy2 * dy2);
if (c == 0) return -1;
angle = (double)acos((dx1 * dx2 + dy1 * dy2) / c);
return angle / PAI * 180;
}
/* cp 在此是四个元素的数组:
cp[0] 为起点,或上图中的 P0
cp[1] 为第一控制点,或上图中的 P1
cp[2] 为第二控制点,或上图中的 P2
cp[3] 为结束点,或上图中的 P3
t 为参数值,0 <= t <= 1 */
MyPoint PointOnCubicBezier(MyPoint* cp, double t)
{
double ax, bx, cx; double ay, by, cy;
double tSquared, tCubed; MyPoint result;
/* 计算多项式系数 */
cx = 3.0 * (cp[1].x - cp[0].x);
bx = 3.0 * (cp[2].x - cp[1].x) - cx;
ax = cp[3].x - cp[0].x - cx - bx;
cy = 3.0 * (cp[1].y - cp[0].y);
by = 3.0 * (cp[2].y - cp[1].y) - cy;
ay = cp[3].y - cp[0].y - cy - by;
/* 计算t位置的点值 */
tSquared = t * t;
tCubed = tSquared * t;
result.x = (ax * tCubed) + (bx * tSquared) + (cx * t) + cp[0].x;
result.y = (ay * tCubed) + (by * tSquared) + (cy * t) + cp[0].y;
return result;
}
/* ComputeBezier 以控制点 cp 所产生的曲线点,填入 MyPoint 结构数组。
调用方必须分配足够的空间以供输出,<sizeof(MyPoint) numberOfPoints> */
void ComputeBezier( MyPoint* cp, int numberOfPoints, MyPoint* curve )
{
double dt; int i;
dt = 1.0 / ( numberOfPoints - 1 );
for( i = 0; i < numberOfPoints; i++)
curve[i] = PointOnCubicBezier( cp, i*dt );
}
int FindSplit(std::vector<MyPoint>& V, int& i, int& j, int* f, double* dist)
{
if (i + 1 > j)
{
return -1;
}
*f = i + 1;
COneSeg os(V[i].x, V[i].y, V[j].x, V[j].y);
*dist = os.GapFromPoint(cv::Point2d(V[*f].x, V[*f].y));
for (int m = *f + 1; m < j; ++m)
{
double dTmpGap = os.GapFromPoint(cv::Point2d(V[m].x, V[m].y));
if (dTmpGap > *dist)
{
*dist = dTmpGap;
*f = m;
}
}
return 0;
}
int DouglasPeucker(std::vector<MyPoint>& V, int &i, int &j, double e)
{
if (V.size() <= 2)
{
//不需要处理
return -1;
}
double dist = 9999;
int f = 0; //最大距离的点的序号
std::stack<int> tempVertex; //STL实现的栈
tempVertex.push(j);
do
{
//循环i和j之间距离直线ij最大的点
FindSplit(V, i, j, &f, &dist);
if (dist > e) //大于阈值
{
V[f].flag = true;
j = f; //更新B值
tempVertex.push(f); //记录最大距离点,放入栈中存储
continue;
}
else
{
if (!tempVertex.empty() && j != tempVertex.top()) //判断后一点是否和当前栈顶相等
{
i = f;
j = tempVertex.top();
continue;
}
else
{
if (j != V.size()) //判断最后一个点是否和当前线段的B点重合
{
i = j;
if (!tempVertex.empty())
{
std::deque<int> cont = tempVertex._Get_container();
if (cont.size() > 1) //栈中至少还有两个点
{
j = cont[cont.size() - 2];
}
else if (cont.size() == 1) //栈中只有一个点
{
j = cont[cont.size() - 1];
}
tempVertex.pop();
continue;
}
}
}
}
} while (!tempVertex.empty());
//保留首尾,去掉无用的点
for (int i = 1; i < V.size() - 1;)
{
if (V[i].flag == false)
{
V.erase(V.begin() + i);
}
else
{
i++;
}
}
return int(tempVertex.size());
}
int GetLineKeyPt(double* pX, double* pY, const int nNum, double** pOutX, double** pOutY, int *pOutNum, double dGap = 10.0)
{
std::vector<MyPoint> V;
for (int m = 0; m < nNum; ++m)
{
V.push_back(MyPoint(pX[m], pY[m]));
}
int i = 0;
int j = int(V.size() - 1);
DouglasPeucker(V, i, j, dGap);
//输出
*pOutNum = (int)V.size();
*pOutX = new double[*pOutNum];
*pOutY = new double[*pOutNum];
for (int i = 0; i < *pOutNum; ++i)
{
(*pOutX)[i] = V[i].x;
(*pOutY)[i] = V[i].y;
}
V.clear();
return 0;
}
double Length(MyPoint p1, MyPoint p2)
{
return sqrt((p1.y - p2.y) * (p1.y - p2.y) + (p1.x - p2.x) * (p1.x - p2.x));
}
double Slope(MyPoint p1, MyPoint p2)
{
if (std::fabs(p1.x - p2.x) <= 1e-7)
{
return DBL_MAX;
}
return (p1.y - p2.y) / (p1.x - p2.x);
}
double GapFromPoint(MyPoint p1, MyPoint p2, const MyPoint& pt )
{
double k = Slope(p1, p2);
if (k == DBL_MAX)
{
//垂直的时候直接返回差值
return fabs(p1.x - pt.x);
}
double b = p1.y - k * p1.x;
return fabs(k * pt.x - pt.y + b) / sqrt( 1 + k * k);
}
bool IsPointInHalfLine(MyPoint p1, MyPoint p2, const MyPoint& pt, const double dDiff /*= 1.0*/ )
{
return GapFromPoint(p1, p2, pt) <= dDiff;
}
MyPoint GetCenter(MyPoint p1, MyPoint p2)
{
return MyPoint((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
}
bool GetPointInSeg(MyPoint p1, MyPoint p2, MyPoint& pRes, const double dGap /*= 10*/ )
{
if (Length(p1, p2) < dGap)
{
return false;
}
if (p1.y == p2.y)
{
if (p1.x < p2.x)
{
pRes = MyPoint(p2.x - dGap, p1.y);
}
else
{
pRes = MyPoint(p2.x + dGap, p1.y);
}
return true;
}
if (p1.x == p2.x)
{
if (p1.y < p2.y)
{
pRes = MyPoint(p1.x, p2.y - dGap);
}
else
{
pRes = MyPoint(p1.x, p2.y + dGap);
}
return true;
}
double dResX = p2.x - (p2.x - p1.x) * dGap / Length(p1, p2);
double dResY = p1.y - (p1.y - p2.y)*(Length(p1, p2) - dGap)/Length(p1, p2);
pRes = MyPoint(dResX, dResY);
return true;
}
double TwoPointsGap(const double x1, const double y1, const double x2, const double y2)
{
return sqrt((y1 - y2) * (y1 - y2) + (x1 - x2) * (x1 - x2));
}
double TwoPointsGap(const MyPoint& p1, const MyPoint& p2)
{
return TwoPointsGap(p1.x, p1.y, p2.x, p2.y);
}
int RemovePointByGap(double** pX, double** pY, int* nNum, const double& dGap = 1.0)
{
int nOldNum = *nNum;
if (nOldNum < 3)
{
return -1;
}
//获取输入
std::vector<MyPoint> vPts(nOldNum);
std::vector<MyPoint> vOut;
for (int i = 0; i < nOldNum; ++i)
{
vPts[i] = MyPoint((*pX)[i], (*pY)[i]);
}
//取顺序的两个点
MyPoint ptStart, ptCurrent;
ptStart = vPts[0];
ptCurrent = vPts[1];
vOut.push_back(ptStart);
for (int i = 1; i < nOldNum - 1; ++i)
{
ptCurrent = vPts[i];
double dLength = Length(ptStart, ptCurrent);
if (dLength > dGap)
{
vOut.push_back(ptCurrent);
ptStart = ptCurrent;
}
}
vOut.push_back(MyPoint((*pX)[nOldNum - 1], (*pY)[nOldNum - 1]));
//释放原来的值
delete[] (*pX);
delete[] (*pY);
*pX = NULL;
*pY = NULL;
*nNum = (int)vOut.size();
*pX = new double[*nNum];
*pY = new double[*nNum];
for (int i = 0; i < *nNum; ++i)
{
(*pX)[i] = vOut[i].x;
(*pY)[i] = vOut[i].y;
}
//注意这里不能delete,因为外部还会使用
return 0;
}
int CurveBrokenLine(std::vector<cv::Point>& vPts)
{
int nNum = vPts.size();
MyPoint* pt = new MyPoint[nNum];
for (int i = 0; i < nNum; ++i)
{
pt[i] = MyPoint(vPts[i].x, vPts[i].y);
}
for (int i = 0; i < nNum - 3; ++i)
{
ComputeBezier(pt, 4, pt);
}
for (int i = 0; i < nNum; ++i)
{
vPts[i] = cv::Point(pt[i].x, pt[i].y);
}
delete[] pt;
pt = nullptr;
return 0;
}
int SmoothBrokenLine(double* pX, double* pY, const int nNum)
{
if (nNum < 4)
{
return -1;
}
const int nBezierNum = 4;
int nTime = nNum / 4;
MyPoint* pPts = new MyPoint[nBezierNum];
MyPoint* pOut = new MyPoint[nBezierNum];
for (int i = 0; i < nTime; ++i)
{
for (int j = 0; j < nBezierNum; ++j)
{
pPts[j] = MyPoint(pX[i * 4 + j], pY[i * 4 + j]);
}
ComputeBezier(pPts, 4, pOut);
for (int j = 0; j < nBezierNum; ++j)
{
pX[i * 4 + j] = pOut[j].x;
pY[i * 4 + j] = pOut[j].y;
}
}
//最后4个结果再次拟合
for (int j = 0; j < nBezierNum; ++j)
{
pPts[j] = MyPoint(pX[nNum - 4 + j], pY[nNum - 4 + j]);
}
ComputeBezier(pPts, 4, pOut);
for (int j = 0; j < nBezierNum; ++j)
{
pX[nNum - 4 + j] = pOut[j].x;
pY[nNum - 4 + j] = pOut[j].y;
}
delete[] pPts;
delete[] pOut;
pPts = NULL;
pOut = NULL;
return 0;
}