ToB企服应用市场:ToB评测及商务社交产业平台

标题: .NET 白板誊写延迟-曲线拟合预测 [打印本页]

作者: 雁过留声    时间: 2024-10-10 21:32
标题: .NET 白板誊写延迟-曲线拟合预测
白板软件誊写速率是其最焦点的功能,注册StylusPlugin从触摸线程拿触摸点数据并在另一UI线程绘制渲染是比较稳妥的方案,具体的可以检察小伙伴德熙的2019-1-28-WPF-高性能笔 - lindexi - 博客园 (cnblogs.com)
上面StylusPlugin方案能提升在大屏现在如富创通、华欣触摸框的重要产物版本上,1帧16ms左右的誊写性能。除了这个跳过一些流程来减少延时,我们还能继承优化誊写性能么?答案肯定是可以的
本文我们介绍下誊写加快的一类实现方向,通过预测下一个甚至N个点,提前绘制字迹来降低誊写延迟。
曲线拟合预测

誊写预测,这里介绍下曲线拟合的方案:
取N个点拟合成一条曲线,算出它的曲线公式,然后下一个点可以输入它的X位置得到Y -- 以X方法或者Y方法为基准,拟合出以X为参数的曲线。
这里采用的是开源组件MathNet.Numerics,也可以利用别的的拟合曲线方案,目的就是先输出一条曲线公式。先安装其Nuget包MathNet.Numerics:
  1. [/code]引用using MathNet.Numerics;我们以X坐标为基准预测Y坐标值,已经写好函数:
  2. [code] 1     private static Point[] PredictPoints(Point[] pointArray, int degree, int predictCount)
  3. 2     {
  4. 3         Debug.WriteLine("输入:" + string.Join(",", pointArray.Select(i => $"({i.X},{i.Y})")));
  5. 4         var xList = pointArray.Select(i => i.X).ToArray();
  6. 5         var yList = pointArray.Select(i => i.Y).ToArray();
  7. 6         var lastX = xList[xList.Length - 1];
  8. 7         var lastX1 = xList[xList.Length - 2];
  9. 8         var lastPointLength = lastX - lastX1;
  10. 9         double[] parameters = Fit.Polynomial(xList, yList, degree);
  11. 10         var predictPoints = new Point[predictCount];
  12. 11         for (int i = 0; i < predictCount; i++)
  13. 12         {
  14. 13             var currentX = lastX + (i + 1) * lastPointLength;
  15. 14             var currentY = 0d;
  16. 15             for (int j = 0; j < degree + 1; j++)
  17. 16             {
  18. 17                 var parameterJ = parameters[j];
  19. 18                 for (int k = 0; k < j; k++)
  20. 19                 {
  21. 20                     parameterJ *= currentX;
  22. 21                 }
  23. 22
  24. 23                 currentY += parameterJ;
  25. 24             }
  26. 25
  27. 26             var newPoint = new Point(currentX, currentY);
  28. 27             predictPoints[i] = newPoint;
  29. 28         }
  30. 29         Debug.WriteLine("输出:" + string.Join(",", predictPoints.Select(i => $"({i.X},{i.Y})")));
  31. 30         return predictPoints;
  32. 31     }
复制代码
这里的double[] parameters = Fit.Polynomial(xList, yList, degree),表示通过X以及Y系列数据,以阶数degree(如二阶曲线)计算出当前多项式参数值parameters。
如果degree是2阶,可以计算得到y值:
y = parameters[0] + parameters[1]*x + parameters[2]*x^2
好,曲线公式有了,那下面就是塞x坐标得到y值,也就是point。
如果是Y轴向上递增的二队曲线,如下图,从左到右绿色点是已知点列表,黄色为预测的点。这里我们依次预测4个点:

这类场景,预测效果是比较正常的。
我们再看看抛物线的场景,沿X方向Y坐标值依次递减,即顺时针角度值增加,预测点如下:

从上图可以看出,Y方向值递减时预测效果是抛物线的另一半,Y轴值另一侧反向加快递减。但我们的期望肯定不是像这类抛物线,我们期望它能延着绿色轨迹向斜上方走。
为何它会快速弹返来呢?原因就是拟合的2阶曲线,公式如此。
这类方法,只能做到一维的预测,即碰到X、Y方向值不变或者值变革较少时,曲线变革就会像抛物线一样到达它的顶端后,就会快速弹返来。
以拟合成曲线公式来计算,会有肯定缺陷。这个我们也是能优化的,上方的函数里我们是以X轴为基准,得到的公式中X为变革量,下面换一下以Y轴为基准:
  1. 1     public static Point[] PredictPointsY(Point[] pointArray, int degree, int predictCount)
  2. 2     {
  3. 3         Debug.WriteLine("输入:" + string.Join(",", pointArray.Select(i => $"({i.X},{i.Y})")));
  4. 4         var xList = pointArray.Select(i => i.X).ToArray();
  5. 5         var yList = pointArray.Select(i => i.Y).ToArray();
  6. 6         var lastY = yList[yList.Length - 1];
  7. 7         var lastY1 = yList[yList.Length - 2];
  8. 8         var lastPointLength = lastY - lastY1;
  9. 9         double[] parameters = Fit.Polynomial(yList, xList, degree);
  10. 10         var predictPoints = new Point[predictCount];
  11. 11         for (int i = 0; i < predictCount; i++)
  12. 12         {
  13. 13             var currentY = lastY + (i + 1) * lastPointLength;
  14. 14             var currentX = 0d;
  15. 15             for (int j = 0; j < degree + 1; j++)
  16. 16             {
  17. 17                 var parameterJ = parameters[j];
  18. 18                 for (int k = 0; k < j; k++)
  19. 19                 {
  20. 20                     parameterJ *= currentY;
  21. 21                 }
  22. 22
  23. 23                 currentX += parameterJ;
  24. 24             }
  25. 25
  26. 26             var newPoint = new Point(currentX, currentY);
  27. 27             predictPoints[i] = newPoint;
  28. 28         }
  29. 29         Debug.WriteLine("输出:" + string.Join(",", predictPoints.Select(i => $"({i.X},{i.Y})")));
  30. 30         return predictPoints;
  31. 31     }
复制代码
这里的二阶拟合曲线就变成了:
x = parameters[0] + parameters[1]*y + parameters[2]*y^2
同样预测4个点,看下效果:

这个预测趋势就符合我们的期望了。
以是上方X方向以及Y方向分别为基准,拟合二队曲线,然后输出预测点,我们综合取一个比较适合的点即可。
如何取呢?可以看到我们期望的点是变革趋势变革小的一个。即以最后俩个数据点的角度A为基准,预测点与最后数据点的向量角度B1与B2,顺时针角度变革较小的点是我们期望输出的。

曲线拟合别的问题-预测失准

上面我们办理了坐标点场景,单方向拟合时输出点快速弹回的问题。在验证誊写时,我们还发现一类预测失准问题:

如上图,黄色点往上偏了(黄色点是直线,并且角度不符合原有趋势),真实期望我们是想要沿着原有角度减少的趋势,预测点为角度略偏下的方向:

这里预测失准的原因是,X方向值无法正常预测。因为按我们上面曲线拟合的方案,这类抛物线场景是以Y轴为基准,输入Y得到X方向值,但按曲线变革的方向输入一个最后俩点之间Y轴变革量,预测点的X值应该是接近无限大的,超出了曲线范围。
这里可以根据曲线点角度变革量来处理,看上图点与点之间角度是按顺时针依次增加的,以是预测出来的点也应该要继承顺时针增加角度。以是可以将输出的点按最后俩点Point(n)、Point(n-1)之间向量角度值,或者再增加最后三点之间角度的差值angleChange。
我利用的是增加角度变革量,如下图输出效果:

曲线拟合别的问题-预测位置超出

我们做的毕竟是预测,预测点肯定不能完美取代真实誊写触摸点。尤其是当我们按照设置下一个预测点太远,很大概率是会偏离原有曲线的
在上面PredictPoints预测函数内,利用了var lastPointLength = lastX - lastX1;来作为下一个预测点X方向的位置。但这着实是不符合实际情况的,因为你并不清楚下一个预测点也变革了 lastX - lastX1的X方向间隔,如果强行用此X变革量确定预测点,预测点偏离曲线的概率会很大。那如何办理呢?
我的办理方法是,通过上面PredictPoints计算出下一预测点的角度变革量changedAnge即可,然后再以lastPoint-lastPoint1之间的间隔作为半径围绕lastPoint进行旋转180+changedAnge至一个新的点。这个新点作为最后预测点

为何以lastPoint-lastPoint1之间的间隔作为半径?因为在快速誊写过程中,触摸框帧率是固定的,誊写速率不怎么变革的情况下,点与点之间的间隔很接近,以是我们只要预测出下一点的changedAnge就行了。
最后,我们按上面的方案验证下真实誊写预测的效果。输出誊写陈迹,我们换个颜色,蓝色为真实点、赤色为预测点。
预测1个点,赤色与真实誊写曲线重合:

预测2个点,赤色是第一个预测点,粉色是第二个预测点:

这里预测效果是在我的开发触摸屏设备(Dell P2418HT,1080p)上验证的,看上面效果粉色点偏移的较多。在这台触摸屏上,不建议预测2个点
需要留意的是,誊写预测与屏幕报点率(帧率)强相干。一样平常情况下,输出俩点之间间隔时间越长,俩点之间间距越大,预测点的误差也会变大,但相应的预测间隔变远了即誊写延迟会降低很大。
我这Dell触摸屏触摸移动时,输入均匀间隔33.3ms,一次输入包含2-3个点,点均匀间隔在16.6ms。俩点均匀间隔16.6ms,说明触摸框是60fps报点。另外,详细的触摸框报点率数据以及开启WM_POINTER消息提升应用层的触摸输入帧率可以看下:白板誊写延迟-触摸屏报点率 - 唐宋元明清2188 - 博客园 (cnblogs.com)
根据上面触摸报点数据,上面誊写预测方案预测1个点,在这台Dell触摸屏上誊写延迟可以降低16.67ms
出处:http://www.cnblogs.com/kybs0/本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则生存追究法律责任的权利。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4