草庐IT

python实现汉字的拐点计算

wxplol 2024-04-21 原文

文章目录

一、线段曲率计算原理

一般的曲率计算方法,如玄长比例法、三次B样条表达、线性多边形逼近和局部对称等方法。今天主要介绍 弯曲值算法(Bending value) 算法。其表达式为:
b i k = m a x ( ∣ ( x i − k − x i ) + ( x i + k − x i ) ) ∣ , ∣ ( y i − k − y i ) + ( y i + k − y i ) ∣ ) b_{ik}=max(|(x_{i-k}-x_{i})+(x_{i+k}-x_{i}))|,|(y_{i-k}-y_{i})+(y_{i+k}-y_{i})|) bik=max((xikxi)+(xi+kxi)),(yikyi)+(yi+kyi))
计算Bending Value的示意图如下:

参考链接:

手写体汉字的书写质量评价

二、线段拐点提取流程

  • 计算线段上每一点的Bending Value,记作 B v ( i ) Bv(i) Bv(i),这里需要初始化阈值delta=1.1和搜索范围k=1

  • B v ( i ) > d e l t a Bv(i)>delta Bv(i)>delta,且对于所有 i − k < = j < = i + k i-k<=j<=i+k ik<=j<=i+k,均有 B v ( i ) > = B v ( j ) Bv(i)>=Bv(j) Bv(i)>=Bv(j),则该点符合局部曲率最大的性质,作为候选拐点。

  • 筛选条件一:判断候选拐点相邻三点 p i − 1 p_{i-1} pi1 p i p_{i} pi p i + 1 p_{i+1} pi+1是否位于同一条直线上,如果在则排除该点

  • 筛选条件二:计算自适应弯曲值(Bending Value)。算法流程如下:

    计算候选点的搜索范围k从1开始增加,用 b i k b_{ik} bik表示当前Bending Value。如果 b i k > = b i k + 1 b_{ik}>=b_{ik+1} bik>=bik+1,k增加1,否则停止增加。求得连续的Bending Value值后,需要再进行求平均值,作为最终该点的弯曲值,计算公式如下:
    b v i = 1 k i ∑ j = 1 k i b i j b_{v_{i}}=\frac{1}{k_{i}}\sum^{k_{i}}_{j=1}b_{ij} bvi=ki1j=1kibij
    根据计算的自适应拐点,还需要满足以下条件:

    • 条件一: b v i < t h e t a b_{v_{i}}<theta bvi<theta
    • 条件二: b v i < b v j b_{v_{i}}<b_{v_{j}} bvi<bvj,对于 j = i − 1 j=i-1 j=i1 j = i + 1 j=i+1 j=i+1
    • 条件三: b v i = b v i − 1 b_{v_{i}}=b_{v_{i-1}} bvi=bvi1,并且 k i < k i − 1 k_{i}<k_{i-1} ki<ki1
    • 条件四: b v i = b v i + 1 b_{v_{i}}=b_{v_{i+1}} bvi=bvi+1,并且 k i < = k i + 1 k_{i}<=k_{i+1} ki<=ki+1

    条件一表示Bending Value值应大于阈值,条件二、三、四表示拐点必须为局部最大值

三、python实现拐点的提取

3.1、曲线的点的平滑

当获取曲线的点整后点与点之间可能存在较大的间隔,需要我们对这些点进行一个采样来增加其连续性。一般采样方法有DDA、一次贝塞尔曲线拟合、二次贝塞尔曲线拟合或三次贝塞尔曲线离合。其中DDA和一次贝塞尔曲线拟合类似。都是通过两点之间直线的斜率来进行一个采样,当然使用更高阶的方法,采样曲线也更加平滑。

3.1.1、一次贝塞尔曲线拟合

如上图所示,对于平面上的两个点 P0 和 P1,假设另一点 B 匀速地从 P0 点运动到 P1 点,则有 B 点在 t 时刻的坐标公式:

将 B 点在各个时刻的坐标依次连接起来所形成的线,就是所谓的贝塞尔曲线。此公式表示的是一次贝塞尔曲线,也称为线性贝塞尔曲线。

python实现如下:

def getFirstBezierPointByT(start,end,t):
	'''
	根据一次贝尔曲线的的计算公式计算各个时刻的坐标值
	:param start:
	:param end:
	:param t:
	:return:
	'''
	x=(1-t)*start[0]+t*end[0]
	y=(1-t)*start[1]+t*end[1]

	return [x,y]

def calculateFirstBezierPoints(start,end):
	'''
	计算两点之间的塞尔曲线点集
	:param start:
	:param handle:
	:param end:
	:return:
	'''
	bx=start[0]
	by=start[1]
	ex=end[0]
	ey=end[1]

	beDis=math.sqrt(math.pow((bx-ex),2)+math.pow((by-ey),2))

	count=int(beDis//1)

	if count<2:
		return [start]

	step = 1.0 / count
	points=[]
	t = 0.0
	for i in range(count):
		points.append(getFirstBezierPointByT(start, end, t))
		t += step

	return points

def firstBezierCurveFitting(pts):
	'''
	一次贝塞尔曲线拟合
	:param pts:
	:return:
	'''
	new_pts = []
	for pt in pts:
		new_pt = []
		for i in range(0, (len(pt) - 1)):
			pp=calculateFirstBezierPoints(pt[i], pt[i + 1])
			new_pt += pp


		new_pt.append(pt[len(pt)-1])
		new_pts.append(new_pt)

	return new_pts

3.1.2、二次贝塞尔曲线拟合

同样地,对于平面上的三个点 P0、P1 和 P2 ,假设 P0P1 之间有个点 B1 匀速地从 P0 运动到 P1 ,P1P2 之间有个点 B2 匀速地从 P1 运动到 P2,则有:


假设另一点 B 匀速地从 B1 运动到 B2,则有 B 点的坐标公式:

将 B1 和 B2 的坐标公式代入上面的表达式,整理后得到 B 点的坐标公式:

B 点在各个时刻的坐标所连成的曲线即为二次贝塞尔曲线,其中 P0 和 P2 称为 数据点,P1 称为 控制点 。
具体python实现可以参考如下链接实现。

参考链接:

一种简单的贝塞尔拟合算法

3.2、拐点的计算

3.2.1、Bending value的计算

def computeBendingValue(p1,p2,p3):
	'''
	计算弯曲值
	:param p1:
	:param p2:
	:param p3:
	:return:
	'''
	return max(abs(p1[0]+p3[0]-2*p2[0]),abs(p1[1]+p3[1]-2*p2[1]))

3.2.2、判断三点是否在同一条直线上

def isSameGradient(p1,p2,p3):
	'''
	判断相邻三点是否在同一条直线上
	:param p1:
	:param p2:
	:param p3:
	:return:
	'''
	g1=(p1[1]-p2[1])/(p1[0]-p2[0]+0.00001)
	g2=(p2[1]-p3[1])/(p2[0]-p3[0]+0.00001)

	return g1==g2

3.2.3、计算拐点

计算拐点时,需要有些核心注意点:

  • 计算笔迹端点的Bending value,需要进行一些预处理,我的处理如下:

    #当位于左端点的时候
    if i-k<0:
        p1=pt[i]
    else:
        p1=pt[i-k]
    
    #当位于右端点的时候
    if i+k>=len(pt):
        p3=pt[i]
    else:
        p3=pt[i+k]
    
  • 如何计算自适应Bending value

    				k0=1
    				bv0=0
    				bv_list=[]
    				# 计算候选拐点所有的Bending Value曲率,直到最大值为止
    				while k0+i<len(pt) and i-k0>0:
    					bv0=computeBendingValue(pt[i-k0],pt[i],pt[i+k0])
    					if bv0>=bv:
    						bv_list.append(bv0)
    						bv=bv0
    						k0+=1
    
    					if bv0<bv:
    						break
    
    				k_list[i]=k0
    
    				# 计算所有曲率的平均值,作为最终的候选点的曲率
    				if len(bv_list)==0:
    					avg_bv=bv
    				else:
    					avg_bv=sum(bv_list)/len(bv_list)
    
  • 进一步筛选拐点的实现

    				# 条件一:bv<theta.theta=1.1
    				# 条件二:bvi<bvj and j=i-1 or j=i+1,表示
    				# 条件三:bvi==bvi-1 and ki<ki-1
    				# 条件四:bvi==bvi+1 and ki<=ki+1
    				# 条件一表示Bending Value应该大于theta;条件二~四表示Bending Value应该为局部最大值
    				if avg_bv<1.1 or \
    						(curvs[i] < curvs[i - 1] or curvs[i] < curvs[i + 1]) or \
    						(curvs[i]==curvs[i-1] and k_list[i]<k_list[i-1]) or \
    						(curvs[i]==curvs[i+1] and k_list[i]<=k_list[i+1]):
    					continue
    				else:
    					result.append(i)
    
  • 最后还需要进一步合并相邻的拐点

    	merge_result=[]
        i=0
    	tmp=[]
    	while i+1<len(result_1):
    		if result_1[i+1]-result_1[i]<5:
    			tmp.append(result_1[i])
    
    			if i+1==len(result_1)-1:
    				tmp.append(result_1[i+1])
    				merge_result.append(tmp)
    				tmp=[]
    
    		else:
    			tmp.append(result_1[i])
    			merge_result.append(tmp)
    			tmp=[]
    
    			if i + 1 == len(result_1) - 1:
    				merge_result.append([result_1[i+1]])
    
    		i=i+1
    
    	# 合并最终的结果
    	new_result=[]
    	for res in merge_result:
    		avg_res=sum(res)//len(res)
    		new_result.append(avg_res)
    

因为项目原因,不方便提供完整代码,但核心点已经给出,方便大家参考。最终结果如下:

有关python实现汉字的拐点计算的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby-on-rails - 使用一系列等级计算字母等级 - 2

    这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,

  3. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  4. Python 相当于 Perl/Ruby ||= - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。

  5. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  6. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  7. python - 如何读取 MIDI 文件、更改其乐器并将其写回? - 2

    我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的

  8. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  9. 「Python|Selenium|场景案例」如何定位iframe中的元素? - 2

    本文主要介绍在使用Selenium进行自动化测试或者任务时,对于使用了iframe的页面,如何定位iframe中的元素文章目录场景描述解决方案具体代码场景描述当我们在使用Selenium进行自动化测试的时候,可能会遇到一些界面或者窗体是使用HTML的iframe标签进行承载的。对于iframe中的标签,如果直接查找是无法找到的,会抛出没有找到元素的异常。比如近在咫尺的例子就是,CSDN的登录窗体就是使用的iframe,大家可以尝试通过F12开发者模式查看到的tag_name,class_name,id或者xpath来定位中的页面元素,会抛出NoSuchElementException异常。解决

  10. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

随机推荐