首页 > 编程语言 >python实现两函数通过缩放,平移和旋转进行完美拟合

python实现两函数通过缩放,平移和旋转进行完美拟合

时间:2023-07-12 16:13:13浏览次数:52  
标签:distance angle 缩放 python 拟合 np theta DTW best

Curve _fitting

前几天在工作的时候接到了一个需求,希望将不同坐标系,不同角度的两条不规则曲线,并且组成该曲线的点集数量不一致,需求是希望那个可以通过算法的平移和旋转搞到一个概念里最贴合,拟合态进行比较。

image-20230712151728578

这是初步将两组数据画到图里的情况,和背景需求是一致的。其实从肉眼看过去左图逆时针旋转120度可以得到一个大致差不多的图。

但这里存在了两个问题:

  1. 就算搞到了同一个坐标系,一个基准点选取在哪里,图像绕着这个点旋转才可以得到最拟合点样子
  2. 找到基准点,判断最拟合的标准是什么,怎么算距离

首先我们将两图换到一个相同坐标系下

def Convert_to_the_same_scale(xs1, ys1, xs2, ys2):
    xs1 = np.array(xs1)
    ys1 = np.array(ys1)

    xs2 = np.array(xs2)
    ys2 = np.array(ys2)

    # 减去平均值,使得数据中心化
    xs1 -= np.mean(xs1)  # 算一个平均值,最后回到0,0坐标系下
    ys1 -= np.mean(ys1)

    xs2 -= np.mean(xs2)
    ys2 -= np.mean(ys2)

    # 除以标准差,使得数据标准化
    xs1 /= np.std(xs1)
    ys1 /= np.std(ys1)

    xs2 /= np.std(xs2)
    ys2 /= np.std(ys2)


    return xs1.tolist(), ys1.tolist(), xs2.tolist(), ys2.tolist()

我们运用numpy中的函数进行了数据中心化和标准化的处理。np.std()函数是Numpy库中的一个方法,它被用来计算数组中元素的标准差。标准差是一种衡量数据分散程度的指标,值越大表明数据越分散。将数据点除以该数值可以统一到一个离散的程度。经过处理之后得到了以下图样:

image-20230712153034757

接下来就要引入一个比较热的判断距离的算法DTW,DTW是指动态时间扭曲(Dynamic Time Warping)算法,这是一种用于测量两个可能不等长的序列之间相似度的算法。该算法可以找到序列之间的对齐方式,使得对齐后的总体误差最小。

在信号处理、识别和数据挖掘领域,DTW广泛应用于各种任务,如语音识别和手写数字识别。它的主要优点是能够处理时间序列的“弹性”对齐问题,即即使在时间尺度上存在变形,也能够匹配和识别模式。

DTW 算法的基本步骤如下:

  1. 初始化:创建一个二维矩阵,其中行数和列数分别等于两个输入序列的长度。将第一个元素设为 0,其余元素设为无穷大。
  2. 递归填充:从左上角开始,计算当前位置的距离(通常是欧氏距离),并将其与左方、上方、左上方三个元素的最小值相加,结果存储在当前位置。
  3. 寻找路径:从右下角开始,向左上角回溯,寻找最小累计距离的路径。这就是两个序列之间的最佳对齐路径。
  4. 输出距离:返回最后一个元素的值,即为两个序列之间的 DTW 距离。

值得注意的是,尽管 DTW 可以很好地处理变化的速度和非线性变形,但是它对输入序列的噪声和异常值敏感,并且计算成本相当高。

我这两个图像都是由几千个点组成的,所以如果让DTW算法去自己360度无死角找best angle和最拟合的点的话,计算量就太大了。

所以经过一些计算,我可以先拿到两组点的端点,将起点对齐之后,再去将一条线以另一条线为准进行旋转,此时得到的角度可以被视作是一个范围角度。

def get_degree(a,b,c,d):
    def get_vector_from_points(p1, p2):
        return np.array([p2[0] - p1[0], p2[1] - p1[1]])

    def dot_product(v1, v2):
        return np.dot(v1, v2)

    def cross_product(v1, v2):
        return v1[0]*v2[1] - v1[1]*v2[0]

    def length_of_vector(v):
        return np.linalg.norm(v)

    def translate_point(p, t):
        return [p[0]+t[0], p[1]+t[1]]

    def rotate_point(p, angle):
        px, py = p
        cos_theta = np.cos(angle)
        sin_theta = np.sin(angle)
        
        qx = cos_theta * px - sin_theta * py
        qy = sin_theta * px + cos_theta * py
        
        return [qx, qy]

    def align_lines(A, B, C, D):
        # Step 1: Translate
        T = get_vector_from_points(C, A)
        C_translated = translate_point(C, T)
        D_translated = translate_point(D, T)

        # Step 2: Rotate
        vAB = get_vector_from_points(A, B)
        vCD = get_vector_from_points(C_translated, D_translated)
        cos_theta = dot_product(vAB, vCD) / (length_of_vector(vAB)*length_of_vector(vCD))
        theta = np.arccos(cos_theta)
        direction = np.sign(cross_product(vAB, vCD)) 

        C_final = rotate_point(C_translated, direction*theta)
        D_final = rotate_point(D_translated, direction*theta)

        return np.degrees(direction*theta)
    degree = align_lines(a,b,c,d)
    return degree

rotate_bosch = [[BOSCH_xs[0],BOSCH_ys[0]],[BOSCH_xs[-1],BOSCH_ys[-1]]]
rotate_ego = [[EGO_xs[0],EGO_ys[0]],[EGO_xs[-1],EGO_ys[-1]]]
theta = (get_degree(rotate_ego[0],rotate_ego[1],rotate_bosch[0],rotate_bosch[1]))

tran_x = EGO_xs[0] - BOSCH_xs[0]
tran_y = EGO_ys[0] - BOSCH_ys[0]

这段代码定义了一个名为 get_degree 的函数,其目的是计算两条线之间的夹角。这里的参数 a,b,c,d 分别代表两条线上的四个点,其中 ab 在一条线上,cd 在另一条线上。

下面是该函数的详细步骤:

  1. 定义辅助函数:这些函数用于执行向量运算、平移和旋转点等操作。
  2. 定义主要流程:在 align_lines 函数中,首先通过向量差得到平移量 T,将 cd 两点进行平移,使得平移后的 c 点与 a 点重合;接着计算 ABCD 两向量的夹角 theta,并确定旋转方向 direction;最后根据 direction*theta 对平移后的 cd 进行旋转。该函数返回的是 theta 的度数形式。
  3. 调用主要流程:在 get_degree 函数中,调用 align_lines 函数并返回得到的角度。

简单来说,这段代码就是把以 cd 为端点的线段通过平移和旋转,让它和以 ab 为端点的线段对齐,然后返回这个旋转的角度。获取的角度就是一个比较贴合的角度,但是不是最精准的。

image-20230712154046616

此时旋转的角度确实如我们所想,度数120度左右,效果大概是这样,看起来还不错。那这样的基础上我们再去编写DTW算法就速度比较快了。Python并没有自带的DTW(Dynamic Time Warping)算法,但是第三方的库,例如fastdtwdtaidistance提供了这个算法的实现。

使用fastdtw:

from scipy.spatial.distance import euclidean
from fastdtw import fastdtw

x = np.array([1, 2, 3, 4, 5], dtype=float)
y = np.array([2, 3, 4, 5, 6], dtype=float)

distance, path = fastdtw(x, y, dist=euclidean)

print("DTW distance: ", distance)
print("DTW path: ", path)

在上述代码中,fastdtw函数接收两个序列,xy,以及一个用于计算两点之间距离的函数。这里采用欧氏距离进行计算。

使用dtaidistance:

from dtaidistance import dtw

x = np.array([1, 2, 3, 4, 5], dtype=float)
y = np.array([2, 3, 4, 5, 6], dtype=float)

distance = dtw.distance(x, y)

print("DTW distance: ", distance)

在这段代码中,dtw.distance函数接收两个序列,并返回它们之间的DTW距离。

# 对第二条曲线进行旋转,并计算与第一条曲线的DTW距离
def compute_dtw_distance(points1, points2, angle, center_point):
    rotated_points = rotate_points(points2, angle, center_point)
    distance, _ = fastdtw(points1, rotated_points, dist=euclidean)
    return distance

# 寻找最佳拟合角度
def find_best_fit(points1, points2, center_point=(0, 0), theta = 0):
    """Finds the rotation angle that gives the best fit between two sets of points."""
    angles=np.arange(theta-10, theta+10, 0.3)
    min_distance = float('inf')
    best_angle = None

    for angle in angles:
        distance = compute_dtw_distance(points1, points2, angle, center_point)

        if distance < min_distance:
            min_distance = distance
            best_angle = angle

    return best_angle, min_distance

代码里面我们为了减轻计算量,角度区间为之前得到的theta以及 正负10度,每隔0.3度进行一次计算。然后我们再根据得到的best_angle旋转1次获得最后的结果。

image-20230712154950575

这样一看,两条线就非常的贴合了,角度也调整成了115.5度。但是老板突然跟我说有没有可能这不是最贴合的情况(我#¥#@#@!#@$$#$@),要我把初始点不对齐也算一下,我想了一下,也做了几组测试,我选取了起点周围情况0.4*0.4面积内的格点,一般情况也不会在这个范围之外了。进行一个循环的计算,保留做小的距离和最佳的角度

tran_x = EGO_xs[0] - BOSCH_xs[0]
tran_y = EGO_ys[0] - BOSCH_ys[0]
trans_x = np.linspace(tran_x-0.2, tran_x + 0.2, 5)
trans_y = np.linspace(tran_y-0.2, tran_y + 0.2, 5)

min_distance = float('inf')
best_angle = 0.0
best_bosch_x = []
best_bosch_y = []
start_time = time.time()
for i in trans_x:
    for j in trans_y:
        tmpx,tmpy = copy.deepcopy(BOSCH_xs), copy.deepcopy(BOSCH_ys)
        tmpx,tmpy = translate_points(tmpx,tmpy, i, j)

        ego = np.column_stack((EGO_xs, EGO_ys))
        bosch = np.column_stack((tmpx,tmpy))
        angle, distance = find_best_fit(ego,bosch,center_point=(tmpx[0],tmpy[0]),theta= theta)
        if distance < min_distance:
            min_distance = distance
            best_angle = angle
            best_bosch_x = tmpx
            best_bosch_y = tmpy
        print("此时Bosch的起点在{},{}, 平移的长度为{},{}".format(tmpx[0],tmpy[0],i,j))
        print(BOSCH_xs[0], BOSCH_ys[0])
        print("起点在{},{} 最佳角度为{}  误差距离为{}".format(tmpx[0],tmpy[0],angle,distance))
BOSCH_xs, BOSCH_ys = rotate_points_after_DTW(best_bosch_x,best_bosch_y, best_angle)

image-20230712155402466

别说,你还真别说,上图所示的才是最拟合状态,角度也有微小的变化。这是根据DTW算法得出的结果,但是我个人觉得起点对齐的时候更拟合一点。~~~

全部代码可以联系本菜鸡!纯原创!!!

标签:distance,angle,缩放,python,拟合,np,theta,DTW,best
From: https://www.cnblogs.com/ivanlee717/p/17547754.html

相关文章

  • python pip安装使用
    安装了python,没安装pip,在pycharm中执行pip命令会报错:py:无法将“py”项识别为cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。首先需要安装pip下载pip并解压到本地:https://pypi.org/project/pip/#files,window我下载......
  • Python 量化投资(一):滑动均值、布林带、MACD、RSI、KDJ、OBV
    滑动均值和标准差为了更好利用向量化来加速,滑动窗口使用np.lib.stride_tricks.sliding_window_view(x,win)提取,它会返回所有x[i]开头并且长度为win的数组的数组。defrolling(x,win):r=np.lib.stride_tricks.sliding_window_view(x,win)pad=np.zeros([len(x)-......
  • python魔术方法之__new__
    一、基本用法#从一个类建立一个对象#__new__从class建立一个object过程#__init__有了object初始化过程classLanguage:def__new__(cls,*args,**kwargs):print("__new__")returnsuper().__new__(cls)def__init__(self):print(......
  • 【Python】对密码文本进行加密, 并判断 hashlib
    importhashlibdefencrypt_password(password,salt):#创建一个sha256的哈希对象sha256_hash=hashlib.sha256()#将盐值和密码组合起来并进行哈希hashed_password=salt.encode('utf-8')+password.encode('utf-8')sha256_hash.update(hashed_......
  • 5python学习笔记
    1.python特点​Python具有代码简单、学习难度低、语法清楚、功能库丰富等优势,同样功能的代码,Python代码数量只有C或Java的1/5,甚至1/10。例:打印HelloWorld,C语言需要6行,Java需要5行,Python只需要1行。2.python相关概念第三方库:需要自行安装的库python解释器:将源代码翻译......
  • python解析xml
    主要是查询标签:importxml.dom.minidoms='''xml字符串''''''这里做一些解释:<?xmlversion="1.0"encoding="UTF-8"?><soapenv:Envelopexmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/......
  • Python异步编程
    协程不是计算机提供,程序员人为创造也称为微线程,是一种上下文切换技术(通过一个线程实现代码块互相切换执行)普通代码的执行流程自上而下顺序执行deffun1():print(1)#...print(2)deffun2():print(3)#...print(4)fun1()fun2()-结......
  • 用Python编写网页自动答题工具,满分轻松到手,你就是全班最靓的仔!
    最近自动答题的外包很多,来给大家分享一下如何用Python来实现自动答题。好了话不多说,我们开始操作。首先你需要准备这些环境使用Python3.8解释器Pycharm编辑器 模块使用importrequests--->数据请求模块pipinstallrequestsimportrefromsele......
  • python安装教程
    1.下载安装python解释器:地址:https://www.python.org/downloads/release/python-372/(选择此项) 2.pycharm安装教程:安装地址:https://www.jetbrains.com/pycharm/download/download-thanks.html?platform=windows&code=PCC ......
  • python3。1
          print('您拥有三次机会输入正确的账号和密码')print('三次输入错误,账号将被锁定')i=3whilei>0:user_name=input('请输入您的账号:')psw=input('请输入密码:')ifuser_name=='zy'andpsw=='666666':......