点击上方“AI搞事情”关注我们



> 本文转载自:

https://www.cnblogs.com/zl03jsj/p/8048102.html


上一篇文章介绍了目前大多数人在拟合手写笔迹的时候使用的算法, 这篇文章介绍一种自己独创的算法.

这种算法具有以下优点:
1) 使用二次贝塞尔曲线拟合, 计算量大概比3次贝塞尔曲线少三分之一.
2) 不必等到用户输入了下一个点之后, 才能绘制当前两个点之间的曲线, 这种算法可以先绘当前需要拟合的线段的一部分, 能够非常及时的把用户的输入反馈给用户, 用户体验立刻提高了2个档次.
3) 不用计算控制点, 处理起来更加简单, 计算量也再次减少, 用户绘制体验得到进一步提高.
4) 笔迹拟合更加接近真实手写的笔迹.

有以下缺点:

我真尼玛没发现有缺点, 我真的不能欺骗大家, 它明明没有缺点, 我非要找一个缺点出来吗!!!?,作为一个程序员, 我不能说谎啊!!!!!O(∩_∩)O哈哈~

这么厉害的算法, 大家是不是已经迫不及待了. 下面就来给大家分享这个算法的思路, 先看下面的图解:

可能大家只看图就已经知道应该怎么做了. 现在按照图中的标注, 假设:ABCDEFG为原笔迹点. 

1) 当用户通过点击鼠标或者点击手机屏幕手势, 输入点A时, 我们在A的位置画下一个小圆点

2) 首先需要设立一个系数k,取值为(0, 0.5]之间的小数. 当用于通过移动, 输入了第二个点B时, 我们在线段AB上找到一个点A', 使得 |A'B| / |AB| = k, 并绘制线段AA', 将其作为手写笔迹的一部分.

3) 当用户再次移动鼠标, 得到得到第三个点C时, 我们在BC上, 找到两个点, B' 和 B'', 满足 |BB'| / |BC| = |B''C| / |BC| = k, 然后将前面的 A' 和 B' 作为两个端点,

  点B作为控制点, 绘制A'BB' 描述的二次贝塞尔曲线. 作为手写笔迹的一部分.

4) 连接B'B''的直线线段, 作为时候写笔迹的一部分.

5) 当用于输入点D,E,F.......时, 回到第2步, 循环执行2,3,4.

6) 当用于输入最后一个点G时, 执行2, 3步, 然后直接连接F'G, 结束绘制.

为什么要把第4步单独分离出来呢, 因为当k取值为0.5的时候, B'B'', C'C''.....F'F'' 直接重合为同一个点, 就可以直接省略弟4步.(实践证明, k值取0.5, 不但速度快, 效果还非常好!!!!)

这个算法, 初看起来, 有一些问题, 整个曲线没有经过作为原笔迹点的BCDEF, 是不是效果不理想呢???..再细想一下:

使用点ABC来举例, 虽然没有经过点B, AA'和B'B两条线段的轨迹是完全和原笔迹的连线重合的, 即使阈值取0.5的情况, 也有两个点(A', B')和原笔迹连线重合'

所以, 我们虽然放弃了一棵树,得到了一片森林;放弃一个点, 重合了无数个点, 我们还可以通过阈值k来控制曲线的拟合程度, k越小, 转角的地方越锐利; k越大, 拟合越平滑.

同样,为了大家学习方便, 我在前面一篇文章的基础上稍作修改, 把这种算法用Python实现出来, 提供大家参考和理解:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import numpy as np
from scipy.special import comb, perm
import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['SimHei']
# plt.rcParams['font.sans-serif'] = ['STXIHEI']
plt.rcParams['axes.unicode_minus'] = Falseclass Handwriting:def __init__(self, line):self.line = lineself.index_02 = None  # 保存拖动的这个点的索引self.press = None  # 状态标识,1为按下,None为没按下self.pick = None  # 状态标识,1为选中点并按下,None为没选中self.motion = None  # 状态标识,1为进入拖动,None为不拖动self.xs = list()  # 保存点的x坐标self.ys = list()  # 保存点的y坐标self.cidpress = line.figure.canvas.mpl_connect('button_press_event', self.on_press)  # 鼠标按下事件self.cidrelease = line.figure.canvas.mpl_connect('button_release_event', self.on_release)  # 鼠标放开事件self.cidmotion = line.figure.canvas.mpl_connect('motion_notify_event', self.on_motion)  # 鼠标拖动事件self.cidpick = line.figure.canvas.mpl_connect('pick_event', self.on_picker)  # 鼠标选中事件self.ctl_point_1 = Nonedef on_press(self, event):  # 鼠标按下调用if event.inaxes != self.line.axes: returnself.press = 1def on_motion(self, event):  # 鼠标拖动调用if event.inaxes != self.line.axes: returnif self.press is None: returnif self.pick is None: returnif self.motion is None:  # 整个if获取鼠标选中的点是哪个点self.motion = 1x = self.xsxdata = event.xdataydata = event.ydataindex_01 = 0for i in x:if abs(i - xdata) < 0.02:  # 0.02 为点的半径if abs(self.ys[index_01] - ydata) < 0.02: breakindex_01 = index_01 + 1self.index_02 = index_01if self.index_02 is None: returnself.xs[self.index_02] = event.xdata  # 鼠标的坐标覆盖选中的点的坐标self.ys[self.index_02] = event.ydataself.draw_01()def on_release(self, event):  # 鼠标按下调用if event.inaxes != self.line.axes: returnif self.pick is None:  # 如果不是选中点,那就添加点self.xs.append(event.xdata)self.ys.append(event.ydata)if self.pick == 1 and self.motion != 1:  # 如果是选中点,但不是拖动点,那就降阶x = self.xsxdata = event.xdataydata = event.ydataindex_01 = 0for i in x:if abs(i - xdata) < 0.02:if abs(self.ys[index_01] - ydata) < 0.02: breakindex_01 = index_01 + 1self.xs.pop(index_01)self.ys.pop(index_01)self.draw_01()self.pick = None  # 所有状态恢复,鼠标按下到稀放为一个周期self.motion = Noneself.press = Noneself.index_02 = Nonedef on_picker(self, event):  # 选中调用self.pick = 1def draw_01(self):  # 绘图self.line.clear()  # 不清除的话会保留原有的图self.line.set_title('Bezier曲线拟合手写笔迹')self.line.axis([0, 1, 0, 1])  # x和y范围0到1# self.bezier(self.xs, self.ys)  # Bezier曲线self.all_curve(self.xs, self.ys)self.line.scatter(self.xs, self.ys, color='b', s=20, marker="o", picker=5)  # 画点# self.line.plot(self.xs, self.ys, color='black', lw=0.5)  # 画线self.line.figure.canvas.draw()  # 重构子图# def list_minus(self, a, b):#     list(map(lambda x, y: x - y, middle, begin))def controls(self, k, begin, end):if k <= 0 or k >= 1: returnfirst_middle = begin + k * (end - begin)second_middle = begin + (1 - k) * (end - begin)return first_middle, second_middledef all_curve(self, xs, ys):le = len(xs)if le < 2: returnself.ctl_point_1 = Nonebegin = [xs[0], ys[0]]end = [xs[1], ys[1]]self.one_curve(begin, end)for i in range(2, le):begin = endend = [xs[i], ys[i]]self.one_curve(begin, end)end = [xs[le - 1], ys[le - 1]]x = [self.ctl_point_1[0], end[0]]y = [self.ctl_point_1[1], end[1]]#linestyle='dashed',self.line.plot(x, y,  color='yellowgreen', marker='o', lw=3)def one_curve(self, begin, end):ctl_point1 = self.ctl_point_1begin = np.array(begin)end = np.array(end)ctl_point2, self.ctl_point_1 = self.controls(0.4, begin, end)color = 'red';if ctl_point1 is None :xs = [begin[0], self.ctl_point_1[0]]ys = [begin[1], self.ctl_point_1[1]]self.line.plot(xs, ys, color=color, marker='o', linewidth='3')else :xs = [ctl_point1[0], begin[0], ctl_point2[0]]ys = [ctl_point1[1], begin[1], ctl_point2[1]]self.bezier(xs, ys)xs = [ctl_point2[0], self.ctl_point_1[0]]ys = [ctl_point2[1], self.ctl_point_1[1]]self.line.plot(xs, ys, color=color, marker='o', linewidth='3')def bezier(self, *args):  # Bezier曲线公式转换,获取x和yt = np.linspace(0, 1)  # t 范围0到1le = len(args[0]) - 1self.line.plot(args[0], args[1], marker='o', linestyle='dashed', color='limegreen', lw=1)le_1 = 0b_x, b_y = 0, 0for x in args[0]:b_x = b_x + x * (t ** le_1) * ((1 - t) ** le) * comb(len(args[0]) - 1, le_1)  # comb 组合,perm 排列le = le - 1le_1 = le_1 + 1le = len(args[0]) - 1le_1 = 0for y in args[1]:b_y = b_y + y * (t ** le_1) * ((1 - t) ** le) * comb(len(args[0]) - 1, le_1)le = le - 1le_1 = le_1 + 1color = "mediumseagreen"if len(args) > 2: color = args[2]self.line.plot(b_x, b_y, color=color, linewidth='3')fig = plt.figure(2, figsize=(12, 6))
ax = fig.add_subplot(111)  # 一行一列第一个子图
ax.set_title('手写笔迹贝赛尔曲线, 计算控制点图解')handwriting = Handwriting(ax)
plt.xlabel('X')
plt.ylabel('Y')# begin = np.array([20, 6])
# middle = np.array([30, 40])
# end = np.array([35, 4])
# handwriting.one_curve(begin, middle, end)
# myBezier.controls(0.2, begin, middle, end)
plt.show()

下一篇文章,不出意外应该是这个手写笔迹系列的最后一篇文章.

我将把我实现笔锋效果的具体原理和细节, 还有用C++对算法的具体实现, 以及可以直接运行查看效果的Demo一起分享给大家.

长按二维码关注我们

有趣的灵魂在等你

原笔迹手写实现平滑和笔锋效果之:笔迹的平滑(二)相关推荐

  1. python模拟手写笔迹_原笔迹手写实现平滑和笔锋效果之:笔迹的平滑(一)

    之前研究过一种用于 模拟真实 手写笔迹签名 的算法,  要求能够保持原笔迹平滑,并有笔锋的效果. 在网上看了一些资料, 资料很多, 能够达到用于正式产品中的效果的一个都没有找到. 但是即使按照这篇文章 ...

  2. 图形图像处理 - 手写 QQ 说说图片处理效果

    OpenCv 的基础学习目前先告一段落了,后面我们要开始手写一些常用的效果,且都是基于 Android 平台的.希望我们有一定的 C++ 和 JNI 基础,如果我们对这块知识有所欠缺,大家不妨看看这个 ...

  3. 手写原笔迹输入_原笔迹手写软件 - 随意写 V1.1

    双指模式只在双框和全屏模式下支持,如果用双指切换到其它模式就不能再用双指切换回来,如果切换到浏览模式可以用手机摇动切换回来,因为浏览模式的双指已被定义为放大缩小了,文字模式只要点一下非键盘位置就会切换 ...

  4. 基于Visual Studio2012实现Windows8的metro界面笔迹手写识别文档

    手写识别,是指将在手写设备上书写时产生的有序轨迹信息化转化为汉字内码的过程,实际上是手写轨迹的坐标序列到汉字的内码的一个映射过程,是人机交互最自然.最方便的手段之一. 随着智能手机.掌上电脑等移动信息 ...

  5. vue 手写图片左右跑马灯 效果

    效果图与代码贴上: 1.html部分 <template><div class="box"><Icon class="icons1 icon ...

  6. Java电子书平滑翻页效果_(转载)Android 平滑和立体翻页效果1

    Android 平台提供了一套完整的动画框架,使得开发者可以用它来开发各种动画效果,本文将向读者阐述 Android 的动画框架是如何实现的.任何一个框架都有其优势和局限性,只有明白了其实现原理,开发 ...

  7. php 手写签批 手机办公_好签小程序手写签名组件/在线手写签批系统

    支持多种文档格式 好签原笔迹手写技术可以在任何版式文档格式上进行手写与笔迹的展示,与文档类型无关,它是一款真正意义的跨文档格式的手写批示引擎,在常用的版式文件上都可以进行手写批示,如PDF.JGP. ...

  8. pcie ep 应该支持哪种interrupt_7寸国产笔记本评测,酷睿处理器+8G+256G,还支持手写笔...

    在大多数的人的传统印象里,可能都会认为便携式笔记本应该搭载12.5英寸屏幕,然后把重量控制在2公斤以内就行了,而实际上,我认为的便携式笔记本则应该拥有更小的屏幕,如果能够像智能手机一样装进衣服口袋,那 ...

  9. 微信小程序 手写签名_你竟然还不知道在微信上就可以手写签名、签文件了~

    原标题:你竟然还不知道在微信上就可以手写签名.签文件了~ 你是否遇到以下问题: 正在休假却收到公司的夺命连环call,说有重要文件需要你签字确认? 正在上班,却有孩子学校.小区.甚至居委会各种需要通知 ...

  10. android 手写 流畅,提高Android应用手写流畅度(基础篇)

    在使用android类的手写应用时,整体上都有这样一个印象:android的手写不流畅.不自然,和苹果应用比起来相差太远.本文结合作者亲身经历,介绍一下有效提高手写流畅度的几种方法: 1.未做任何处理 ...

最新文章

  1. 使用c语言读写netcdf文件,[原][c][netcdf]读取函数
  2. 【转载】SAP集成会计凭证生成的原理
  3. Linux 系统的硬链接和软链接详解
  4. 《复联4》| 生活需要漫威这块糖
  5. Spring5 新特性
  6. MAC电脑常用快捷键
  7. 查看java堆的详细信息
  8. 杭州电子科技大学考研经验贴
  9. java将图片压缩到指定大小以内并保存图片
  10. Linux下sz下载文件超过4G办法
  11. 积木拼图游戏-儿童游戏免费拼图3-6岁
  12. 熊猫烧香作者李俊的个人简历
  13. [零基础学python]集成开发环境(IDE)
  14. 研一(研究生)看论文文献必须要知道的几个网站
  15. UVA - 12627 Erratic Expansion(分治)
  16. mysql数据库—PXC集群
  17. Android 向系统日历添加日程
  18. Uboot1之什么是uboot?
  19. 单招考试计算机ip不会看,单招考试“花样”多 不同维度测技能
  20. 机器学习:基于朴素贝叶斯对花瓣花萼的宽度和长度分类预测

热门文章

  1. typecho评论不显示用户头像怎么办?怎么改为QQ头像显示?
  2. n维向量,n维向量空间,n维向量空间的维数
  3. 海滩xxxx_人工智能处理海洋垃圾:借助数据清理世界各地的海滩
  4. 中国版Second Life前途难料
  5. python打印什么意思,python语句:print(*[1,2,3]),是什么意思?
  6. 一瑞士法郎是多少人民币
  7. 请选择正确html,请选择可以使单元格中的内容进行左对齐的正确HTML标记(      )。...
  8. python word表格操作_Python|处理word的基本操作
  9. Null和Underfined
  10. 三进制与八进制相互转换