python实现汉字的拐点计算

本文详细介绍了如何利用Python计算线段曲率,通过BendingValue算法提取拐点,涉及一次和二次贝塞尔曲线拟合,以及自适应拐点筛选条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、线段曲率计算原理

一般的曲率计算方法,如玄长比例法、三次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 时刻的坐标公式:

img

将 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,则有:

img
img

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

img

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

img

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)
    

因为项目原因,不方便提供完整代码,但核心点已经给出,方便大家参考。最终结果如下:
在这里插入图片描述

<think>好的,我现在需要帮助用户了解Python中常用的数据聚类方法,特别是KMeans、DBSCAN和层次聚类。用户已经提到了这三个方法,并且引用了相关资料,我需要结合这些资料和之前的回答来组织内容。 首先,我得确认用户的需是什么。他们可能是在学习聚类算法,或者需要在实际项目中使用这些方法,所以需要原理、实现步骤、应用场景以及优缺点。根据用户提供的引用,引用[1]和[2]提到了DBSCAN和KMeans的代码实例,引用[3]详细解释了KMeans的算法流程和初始化方法,引用[4]提到了手写算法的实现细节。这些资料可以帮助我补充代码示例和原理解释。 接下来,我需要回顾之前自己的回答,确保内容一致但不过于重复。之前的回答已经详细对比了三种方法的原理、参数调优、案例和常见问题。现在需要以更简洁的方式呈现,可能分为方法介绍、代码示例、应用场景和选择建议几个部分。 要注意用户可能对数学公式和代码实现感兴趣,所以需要包含必要的公式,比如KMeans的目标函数,DBSCAN的核心概念,以及层次聚类的合并策略。同时,根据用户提供的引用,添加具体的代码示例,如DBSCAN的Python实现和KMeans的初始化方法。 用户引用的资料中提到KMeans的初始化方法,如Forgy和随机划分,这需要在KMeans部分详细说明。同时,DBSCAN的地理应用案例和层次聚类的树状图分析也是重点。需要确保这些内容都被涵盖。 另外,用户可能对如何选择算法感兴趣,所以需要总结一个选择指南,比如数据形状、是否需要处理噪声、是否需要层次结构等。结合引用中的信息,比如DBSCAN适合非球形数据和噪声处理,KMeans适合球形数据,层次聚类适合需要层次分析的场景。 还要注意使用正确的中文术语,避免翻译错误,比如“质心”、“密度可达”、“树状图”等。同时,按照用户的要,行内公式用$...$,独立公式用$$...$$,并检查LaTeX语法是否正确。 最后,生成相关问题,确保问题覆盖不同方面,如算法比较、参数调优、应用场景等。这有助于用户进一步探索。 现在需要组织内容结构: 1. 方法概述:分别介绍三种方法的基本原理和特点。 2. 代码示例:使用用户提供的引用中的代码,补充说明。 3. 应用场景:结合行业案例,如电商、交通、生物等。 4. 算法选择指南:根据数据类型和需推荐方法。 5. 常见问题:如初始化、参数调优、处理噪声等。 在写作过程中,需要引用用户提供的资料,比如引用[1]中的DBSCAN代码,引用[3]中的KMeans初始化方法,引用[2]中的密度聚类效果对比。同时,确保回答结构清晰,逐步引导用户理解每个方法的核心概念和实现步骤。 需要特别注意避免直接复制之前的回答内容,而是整合引用资料和之前的知识点,形成新的、更贴近用户当前问题的回答。例如,用户可能更关注实际应用,因此加强案例部分的说明,如使用KMeans进行客户分群,DBSCAN处理地理数据等。 最后,检查是否符合所有格式要,如引用标识、代码块使用正确语法、中英文标点正确使用等。确保回答真实可靠,参考了提供的引用资料,并且逻辑连贯,易于理解。</think>### Python数据聚类常用方法解析 #### 一、核心方法原理与实现 ##### 1. **K-Means聚类** - **数学原理**:通过迭代最小化目标函数 $$J = \sum_{i=1}^k \sum_{x \in C_i} \|x - \mu_i\|^2$$ 其中$\mu_i$为簇质心,算法流程包括: 1. 随机选择初始质心(常用Forgy方法或随机划分[^3]) 2. 将样本分配到最近质心 3. 更新质心为簇内均值 4. 重复直到收敛[^1][^3] - **代码示例**: ```python from sklearn.cluster import KMeans import numpy as np X = np.array([[1, 2], [1, 4], [5, 8], [8, 8]]) kmeans = KMeans(n_clusters=2, n_init=20).fit(X) # 增加初始化次数避免局部最优 print(kmeans.labels_) # 输出聚类标签 ``` - **特点**: - 适合球形数据分布 - 需预先指定簇数$k$ - 对初始质心敏感,可能陷入局部最优[^1] ##### 2. **DBSCAN密度聚类** - **核心概念**: - **$\epsilon$-邻域**:半径$\epsilon$内的样本集合 - **核心点**:邻域内样本数≥`min_samples`的点 - **密度可达**:通过核心点链连接的样本集合[^2][^4] - **代码示例**(含噪声识别): ```python from sklearn.cluster import DBSCAN X = np.array([[1, 2], [2, 2], [25, 80], [8, 7]]) dbscan = DBSCAN(eps=3, min_samples=2).fit(X) print(dbscan.labels_) # 输出标签,-1表示噪声点 ``` - **特点**: - 自动识别任意形状簇 - 可检测噪声点 - 适合地理数据(需用Haversine距离[^1]) - 对参数$\epsilon$敏感,需通过k-距离图选择[^2] ##### 3. **层次聚类** - **合并策略**: | 方法 | 计算方式 | 适用场景 | |------------|------------------------------|------------------| | 单链接 | 最小簇间距(易形成链状簇) | 细长结构数据 | | 全链接 | 最大簇间距(生成紧凑簇) | 噪声较少数据 | | Ward方法 | 最小化合并后的方差增量 | 球形簇优化[^1] | - **代码示例**(树状图可视化): ```python from scipy.cluster.hierarchy import dendrogram, linkage import matplotlib.pyplot as plt Z = linkage(X, method='ward') plt.figure(figsize=(10, 5)) dendrogram(Z) plt.axhline(y=4, color='r', linestyle='--') # 根据高度切割簇 plt.show() ``` #### 二、应用场景对比 | 算法 | 典型场景 | 优势 | |--------------|---------------------------------------------|-------------------------------| | **K-Means** | 客户分群(RFM模型)、图像压缩 | 计算效率高,适合大数据量 | | **DBSCAN** | 交通热点识别、异常检测(如信用卡欺诈[^4]) | 处理噪声,发现任意形状簇 | | **层次聚类** | 生物基因分类、文档主题聚类 | 可视化层次关系,无需预设簇数 | #### 三、算法选择指南 1. **数据形状** - 球形簇 → K-Means - 不规则形状 → DBSCAN - 需要层次分析 → 层次聚类[^2] 2. **参数要** - 已知簇数 → K-Means - 未知簇数但能确定密度参数 → DBSCAN - 需要探索层次结构 → 层次聚类 3. **噪声处理** - 含噪声数据优先选择DBSCAN #### 四、常见问题解决方案 1. **K-Means初始化优化** ```python KMeans(init='k-means++') # 改进初始化方法,减少迭代次数 ``` 2. **DBSCAN参数选择** - 绘制k-距离图(k=min_samples-1),选择拐点作为$\epsilon$[^2] ```python from sklearn.neighbors import NearestNeighbors nn = NearestNeighbors(n_neighbors=5).fit(X) distances, _ = nn.kneighbors(X) plt.plot(np.sort(distances[:,4])) # 观察拐点 ``` 3. **层次聚类计算优化** ```python from sklearn.cluster import AgglomerativeClustering model = AgglomerativeClustering(n_clusters=3, linkage='ward') # 预设簇数加速计算 ``` ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值