白板软件书写速度是其最核心的功能,注册StylusPlugin从触摸线程拿触摸点数据并在另一UI线程绘制渲染是比较稳妥的方案,具体的可以查看小伙伴德熙的2019-1-28-WPF-高性能笔 - lindexi - 博客园 (cnblogs.com)

上面StylusPlugin方案能提升在大屏目前如富创通、华欣触摸框的主要产品版本上,1帧16ms左右的书写性能。除了这个跳过一些流程来减少延时,我们还能继续优化书写性能么?答案肯定是可以的

本文我们介绍下书写加速的一类实现方向,通过预测下一个甚至N个点,提前绘制笔迹来降低书写延迟。

曲线拟合预测

书写预测,这里介绍下曲线拟合的方案:

取N个点拟合成一条曲线,算出它的曲线公式,然后下一个点可以输入它的X位置得到Y -- 以X方法或者Y方法为基准,拟合出以X为参数的曲线。

我们来试试,这里采用的是开源组件MathNet.Numerics,先安装其Nuget包MathNet.Numerics:

<PackageReference Include="MathNet.Numerics" Version="5.0.0" />

引用using MathNet.Numerics;我们以X坐标为基准预测Y坐标值,已经写好函数:

 1     private static Point[] PredictPointsNormal(Point[] pointArray, int degree, int predictCount)
2 {
3 Debug.WriteLine("输入:" + string.Join(",", pointArray.Select(i => $"({i.X},{i.Y})")));
4 var xList = pointArray.Select(i => i.X).ToArray();
5 var yList = pointArray.Select(i => i.Y).ToArray();
6 var lastX = xList[xList.Length - 1];
7 var lastX1 = xList[xList.Length - 2];
8 var lastPointLength = lastX - lastX1;
9 double[] parameters = Fit.Polynomial(xList, yList, degree);
10 var predictPoints = new Point[predictCount];
11 for (int i = 0; i < predictCount; i++)
12 {
13 var currentX = lastX + (i + 1) * lastPointLength;
14 var currentY = 0d;
15 for (int j = 0; j < degree + 1; j++)
16 {
17 var parameterJ = parameters[j];
18 for (int k = 0; k < j; k++)
19 {
20 parameterJ *= currentX;
21 }
22
23 currentY += parameterJ;
24 }
25
26 var newPoint = new Point(currentX, currentY);
27 predictPoints[i] = newPoint;
28 }
29 Debug.WriteLine("输出:" + string.Join(",", predictPoints.Select(i => $"({i.X},{i.Y})")));
30 return predictPoints;
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

如果是Y轴向上递增的二队曲线,如下图,从左到右绿色点是已知点列表,黄色为预测的点。这里我们依次预测4个点:

这类场景,预测结果是比较正常的。

我们再看看抛物线的场景,沿X方向Y坐标值依次递减,即顺时针角度值增加,预测点如下:

从上图可以看出,Y方向值递减时预测结果是抛物线的另一半,Y轴值另一侧反向加速递减。但我们的期望肯定不是像这类抛物线,我们期望它能延着绿色轨迹向斜上方走。

为何它会快速弹回来呢?原因就是拟合的2阶曲线,公式如此。

这类方法,只能做到一维的预测,即遇到X、Y方向值不变或者值变化较少时,曲线变化就会像抛物线一样到达它的顶端后,就会快速弹回来。

以拟合成曲线公式来计算,会有一定缺陷。这个我们也是能优化的,上方的函数里我们是以X轴为基准,得到的公式中X为变化量,下面换一下以Y轴为基准:

 1     public static Point[] PredictPointsNormalY(Point[] pointArray, int degree, int predictCount)
2 {
3 Debug.WriteLine("输入:" + string.Join(",", pointArray.Select(i => $"({i.X},{i.Y})")));
4 var xList = pointArray.Select(i => i.X).ToArray();
5 var yList = pointArray.Select(i => i.Y).ToArray();
6 var lastY = yList[yList.Length - 1];
7 var lastY1 = yList[yList.Length - 2];
8 var lastPointLength = lastY - lastY1;
9 double[] parameters = Fit.Polynomial(yList, xList, degree);
10 var predictPoints = new Point[predictCount];
11 for (int i = 0; i < predictCount; i++)
12 {
13 var currentY = lastY + (i + 1) * lastPointLength;
14 var currentX = 0d;
15 for (int j = 0; j < degree + 1; j++)
16 {
17 var parameterJ = parameters[j];
18 for (int k = 0; k < j; k++)
19 {
20 parameterJ *= currentY;
21 }
22
23 currentX += parameterJ;
24 }
25
26 var newPoint = new Point(currentX, currentY);
27 predictPoints[i] = newPoint;
28 }
29 Debug.WriteLine("输出:" + string.Join(",", predictPoints.Select(i => $"({i.X},{i.Y})")));
30 return predictPoints;
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。

我使用的是增加角度变化量,如下图输出效果:

最后,我们按上面的方案验证下真实书写预测的效果。输出书写痕迹,我们换个颜色,蓝色为真实点、红色为预测点。

预测1个点,红色与真实书写曲线重合:

预测2个点,红色是第一个预测点,粉色是第二个预测点。粉色点偏移的较多:

这里预测效果是在我的开发触摸屏设备(Dell P2418HT,1080p)上验证的。

需要注意的是,书写预测与屏幕报点率(帧率)强相关。一般情况下,输出俩点之间间隔时间越长,俩点之间间距越大,预测点的误差也会变大,但相应的预测距离变远了即书写延迟会降低很大。

我这10Dell触摸屏触摸移动时,输入平均间隔33.3ms,一次输入包含2-3个点,点平均间隔在16.6ms。输入平均间隔33.3ms,说明是触摸框60fps报点。另外,详细的触摸框报点率数据以及开启WM_POINTER消息提升应用层的触摸输入帧率可以看下:白板书写延迟-触摸屏报点率 - 唐宋元明清2188 - 博客园 (cnblogs.com)

根据上面触摸报点数据,上面书写预测方案预测1个点,在这台Dell触摸屏上书写延迟可以降低16ms左右。

.NET 白板书写预测-曲线拟合的更多相关文章

  1. [C#] 使用 Excel 和 Math.Net 进行曲线拟合和数据预测

    以前在工作中遇到了一个数据错误的问题,顺便写下 用 Math.Net 解决的思路. 1. 错误的数据 上图是同一组探测器在同一天采集到的 19 次数据,总体来说重复性不错,但很明显最后 8 个探测器出 ...

  2. 小而美的GIF生成神器ScreenToGif

    起因 在写计算机图形学博客时,需要讲解一个算法,课本上抽象的语言未免让人读着头大,还在老师给的PPT中有代码的演示,我就想将演示做出GIF动图帮助读者理解算法,其实之前浪迹博客园的时候就发现有许多博主 ...

  3. 研发团队管理:IT研发中项目和产品原来区别那么大,项目级的项目是项目,产品级的项目是产品!!!

    前言   从事IT行业多年,一路从小杂兵成长为大团队Leader,对于研发整个体系比较清楚,其实大多人都经历过但是都忽略了的研发成本管控的一个关键的点就是研发过程中项目级和产品级的区别.   市场基本 ...

  4. C# 自定义并动态切换光标

    系统有很多光标类型 :Cursors 类 (System.Windows.Input) | Microsoft Docs 本章介绍如何自定义光标.并动态切换光标类型. 动态切换光标类型 以白板书写为例 ...

  5. 高质量JavaScript代码书写基本要点

    翻译-高质量JavaScript代码书写基本要点 by zhangxinxu from http://www.zhangxinxu.com本文地址:http://www.zhangxinxu.com/ ...

  6. [转] 翻译-高质量JavaScript代码书写基本要点 ---张鑫旭

    by zhangxinxu from http://www.zhangxinxu.com本文地址:http://www.zhangxinxu.com/wordpress/?p=1173 原文作者:St ...

  7. 高质量JavaScript代码书写基本要点学习

    高质量JavaScript代码书写基本要点学习 可维护的代码意味着: •可读的 •一致的 •可预测的 •看上去就像是同一个人写的 •已记录   最小全局变量(Minimizing Globals)   ...

  8. 书写规范的javaScript

    书写可维护的代码 代码的维护,修改以及扩展都是需要时间和人力成本的,所以为了减少浪费不必要的成本,从一开始就要书写可维护的代码,这样给自己也给项目其他人提供便利. 书写可维护的代码意味着你的代码是: ...

  9. 关于java实现自定义曲线拟合的研究

    项目需要拟合曲线,使用java实现.采用了apache-commons-math3实现自定义的曲线. 作为apache开源的搞数学计算的超强的库,一直不受市场重视.为啥呢?经过研究,使用java这个强 ...

  10. 语法设计——基于LL(1)文法的预测分析表法

    实验二.语法设计--基于LL(1)文法的预测分析表法 一.实验目的 通过实验教学,加深学生对所学的关于编译的理论知识的理解,增强学生对所学知识的综合应用能力,并通过实践达到对所学的知识进行验证.通过对 ...

随机推荐

  1. 【Mybatis + Spring】 Mybatis - Spring 结合

    环境搭建 EvBuild 软件环境准备 - MySQL 5.0 + - IDEA 2018 + - JDK1.8 + 依赖包相关 - Junit单元测试 - JDBC驱动 - Mybatis 组件 - ...

  2. 【SpringBoot】15 数据访问P3 整合Mybatis

    重新建立一个SpringBoot工程 选择依赖组件 然后删除不需要的Maven&Git文件 还是先查看我们的POM文件 整合Mybatis的组件多了这一个,默认的版本是3.5.4 然后再看看整 ...

  3. .Net内存管理释放的两种方式

    在.Net中,资源回收主要是指内存管理和非托管资源的释放.分别提供了两种主要的方式进行处理: 垃圾回收(GC) 确认性资源释放(DRD) 官网相关文档的链接:https://learn.microso ...

  4. tensorflow 数据集对象(tf.data)的使用( tf.data.Dataset 、tf.data.TextLineDataset 、 tf.data.TFRecordDataset ) 示例

    tensorflow   使用数据集(tf.data)的方法对数据集进行操纵. 1.    对   数组(内存向量)  进行操纵 : import tensorflow as tf input_dat ...

  5. 一个好主板对CPU超频的现实意义————一次超频经历 (z390ws华硕工作站主板+i7-9700k CPU ,Ubuntu18.04.5系统,8核心超频 5.2Ghz以上,单核心满负荷运转可以稳定运行10多分钟后才重启)

    本人于今年2020年1月份在某宝上购买了一款workstation主板,也就是工作站主板,传说中的华硕Z390WS主板(购入价格为3900元),由于当时手里有些小钱,又弄了一个大蝴蝶1350w的电源( ...

  6. tensorflow1.x——如何在C++多线程中调用同一个session会话

    相关内容: tensorflow1.x--如何在python多线程中调用同一个session会话 ================================================= 从 ...

  7. 什么是MMU

    一.MMU的定义   MMU是Memory Management Unit的缩写,中文名是内存管理单元,有时也称作分页内存管理单元(Paged Memory Management Unit,缩写为PM ...

  8. 在Debian上安装freeswitch

    在Debian上安装freeswitch 说明: 首次发表日期:2024-08-12 参考文档: https://medium.com/@jogikrunal9477/ultimate-guide-t ...

  9. 神经网络之卷积篇:详解三维卷积(Convolutions over volumes)

    详解三维卷积 从一个例子开始,假如说不仅想检测灰度图像的特征,也想检测RGB彩色图像的特征.彩色图像如果是6×6×3,这里的3指的是三个颜色通道,可以把它想象成三个6×6图像的堆叠.为了检测图像的边缘 ...

  10. 【CMake系列】11-CMake Pack

    cmake pack 用于将我们的写好的项目 打包,发送给使用方:打包后产生的内容有 源代码包 二进制包 平台原生的二进制安装 Debian -> .deb red hat -> .rpm ...