说明
采集软件中使用折线图显示采集的信息,如果折线是自己绘制的,就需要不断向折线的数组中写入数据,然后再刷新到页面上。但采样的点过多就会导致折线绘制速度过慢,所以需要对折线中的部分数据进行剔除,保障数据刷新的速度。
数据压缩过程中需要剔除掉其中的一些特征不太明显的点,如连续的串点在同一条直线上,那么除去直线头尾以外的所有点,绘制的效果依然差不多,即梯度变化不大的点就是特征不明显的点,应该被剔除,所以基本的方法就是剔除掉二阶导绝对值较小的点。
效果展示
折线压缩过程中保持着8192个点,压缩的过程中折线会不可避免的出现一定的变形,但折线中特征最明显一些点会全部保留下来。另外点在压缩的需要零点几秒的时间,无法立刻完成显示的。
程序
struct comPoint {
comPoint()
{
x = 0.0f;
y = 0.0f;
}
float x;
float y;
};
struct Diff2Point
{
Diff2Point()
{
diff2 = 0.0f;
point.x = 0.0f;
point.y = 0.0f;
}
float diff2;
comPoint point;
};
enum Direction
{
POSITIVE=1, //从小到大排序
REVERSE //从大到小排序
};
float diff(comPoint point1,comPoint point2) //求point2处的梯度
{
return (point2.y - point1.y) / (point2.x - point1.x);
}
float diff2(comPoint point1, comPoint point2,comPoint point3) //求point2处的二阶导
{
return (diff(point3, point2) - diff(point2, point1)) / ((point3.x -point1.x)/2);
}
//快速排序法
void quicksortHelper(std::vector<Diff2Point>& a, int start, int end, Direction direction)
{
if (start >= end ||start<0) return;
unsigned int l = start, r = end;
float pivot = a[(end - start) / 2 + start].diff2;
if(direction==POSITIVE)
{
while (l <= r)
{
while (l <= r && a[r].diff2 > pivot) r--;
while (l <= r && a[l].diff2 < pivot) l++;
if (l <= r) std::swap(a[l++], a[r--]);
}
}
else
{
while (l <= r)
{
while (l <= r && a[r].diff2 < pivot) r--;
while (l <= r && a[l].diff2 > pivot) l++;
if (l <= r) std::swap(a[l++], a[r--]);
}
}
quicksortHelper(a, start, r,direction);
quicksortHelper(a, l, end,direction);
}
//快速排序,使得梯度从大到小进行排序
void quicksort(std::vector<Diff2Point>& arry, Direction direction)
{
quicksortHelper(arry, 0, arry.size() - 1,direction);
}
void quicksortHelper(std::vector<comPoint>& a, int start, int end, Direction direction)
{
if (start >= end ||start<0) return;
unsigned int l = start, r = end;
float pivot = a[(end - start) / 2 + start].x;
if (direction == POSITIVE)
{
while (l <= r)
{
while (l <= r && a[r].x > pivot) r--;
while (l <= r && a[l].x < pivot) l++;
if (l <= r) std::swap(a[l++], a[r--]);
}
}
else
{
while (l <= r)
{
while (l <= r && a[r].x < pivot) r--;
while (l <= r && a[l].x > pivot) l++;
if (l <= r) std::swap(a[l++], a[r--]);
}
}
quicksortHelper(a, start, r,direction);
quicksortHelper(a, l, end, direction);
}
//快速排序,将点按照从小到大的方式进行排序
void quicksort(std::vector<comPoint>& arry, Direction direction)
{
quicksortHelper(arry, 0, arry.size() - 1,direction);
}
/******************************************
将数据信息vec数组压缩到count个数据
大致原理是根据二阶导数计算判断数值的变化幅度,
二阶导绝对值过小则意味变化不明显,该处的特征可以删除,根据二阶导来尽可能地保留数据中地有效信息
*****************************************/
std::vector<comPoint> DataCompress(std::vector<comPoint> vec, int count)
{
if (vec.empty())
{
return std::vector<comPoint>(); //返回一个空的值
}
if (vec.size() <= count) //数据量过小,达不到压缩的标准
{
return vec;
}
std::vector<Diff2Point> diff2points;
unsigned int vecsize = vec.size(); //参与计算二阶导的点为vecsize-2个点
for (unsigned int i = 1; i < vecsize - 1; i++)
{
Diff2Point diffpoint;
diffpoint.diff2 =abs( diff2(vec[i - 1], vec[i], vec[i + 1]));
diffpoint.point = vec[i];
}
quicksort(diff2points, REVERSE); //按照从大到小的排序方法进行排序
std::vector<comPoint> vec2;
vec2.push_back(vec[0]);
for (unsigned int i = 0; i < count - 2; i++) //此处需要从diffpoint中读取count-2个点到vec2中,二阶导过小则不添加到
{
vec2.push_back(diff2points[i].point);
}
vec2.push_back(vec[vec.size() - 1]);
quicksort(vec2,POSITIVE);
return vec2;
}
最后
这部分程序是按照这个想法写的,程序中可能会存在些BUG,绘制折线时点的数量不易太少,否则很快便会出现折线变形。