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

这篇具有很好参考价值的文章主要介绍了python实现两函数通过缩放,平移和旋转进行完美拟合。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Curve _fitting

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

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

这是初步将两组数据画到图里的情况,和背景需求是一致的。其实从肉眼看过去左图逆时针旋转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库中的一个方法,它被用来计算数组中元素的标准差。标准差是一种衡量数据分散程度的指标,值越大表明数据越分散。将数据点除以该数值可以统一到一个离散的程度。经过处理之后得到了以下图样:

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

接下来就要引入一个比较热的判断距离的算法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 为端点的线段对齐,然后返回这个旋转的角度。获取的角度就是一个比较贴合的角度,但是不是最精准的。

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

此时旋转的角度确实如我们所想,度数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次获得最后的结果。

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

这样一看,两条线就非常的贴合了,角度也调整成了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)

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

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

全部代码可以联系本菜鸡!纯原创!!!文章来源地址https://www.toymoban.com/news/detail-550136.html

到了这里,关于python实现两函数通过缩放,平移和旋转进行完美拟合的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Python实现图像的平移、镜像、旋转(不调用CV自身函数)

    老师布置的作业。。。。。 图像的平移在几何变换中算是最简单的变换之一,话不多说,直奔主题 由图可知,在opencv中图像的原点一般为左上角,设初始坐标为 ( x 0 , x 0 ) (x_{0}, x_{0}) ( x 0 ​ , x 0 ​ ) 的经过平移 ( △ x , △ y ) (bigtriangleup x, bigtriangleup y) ( △ x , △ y ) 后,坐

    2024年02月06日
    浏览(42)
  • Android中矩阵Matrix实现平移,旋转,缩放和翻转的用法详细介绍

    一,矩阵Matrix的数学原理 矩阵的数学原理涉及到矩阵的运算和变换,是高等代数学中的重要概念。在图形变换中,矩阵起到关键作用,通过矩阵的变换可以改变图形的位置、形状和大小。矩阵的运算是数值分析领域的重要问题,对矩阵进行分解和简化可以简化计算过程。对于

    2024年01月22日
    浏览(53)
  • GLSL——旋转、平移和缩放

    hello 兄弟们,好久不见撒,我又回来啦!,今天主要讲解如何在顶点着色器中进行旋转、平移和缩放,涉及到矩阵和向量方面的知识哦,忘记的可以翻一下高中数学啦,在讲之前,先回顾一下矩阵和向量点积的知识,矩阵点乘向量,等于矩阵的每一行分别和向量相乘的和,如

    2024年02月16日
    浏览(42)
  • Matlab图像的平移,旋转,缩放,裁剪

    %%------------------------Matlab图像的平移,旋转,缩放,裁剪------------------------------- %-------------------头文件----------------------------- clc ; %清屏幕 clear ; %删除所有的变量 close all ; %将所有打开的图片关掉 %--------------------图像平移 imtranslate-------------------------- A = imread(\\\'1.jpg\\\') ; subplot(

    2024年02月04日
    浏览(43)
  • 【OpenCV】图像变换(缩放、平移、旋转、仿射)

    图像变换是指通过对图像进行缩放、平移、旋转、仿射、透视等变换来改变图像的形状和大小。在本篇博客中,我们将详细介绍OpenCV中的图像变换函数,并提供示例代码以帮助读者更好地理解这些函数的使用方法。 缩放变换是指通过改变图像的大小来改变图像的形状。在Op

    2024年02月07日
    浏览(59)
  • 二维坐标基本变换(平移、旋转、缩放、镜像、阵列)

    诸如图像、模型等的基本变换,实际上都是点坐标的变换,通过矩阵,可以非常方便的达到这个目的。在下文仅介绍二维坐标变换原理。 首先,定义点类如下: 注意,为了形式统一,变换矩阵应统一为3*3阶,同理,对于三维坐标变换矩阵应是4*4阶。关于矩阵的表示,实际上

    2024年02月04日
    浏览(77)
  • webgl-矩阵、旋转、平移、缩放 glMatrix组件

    引入新组建glMatrix glMatrix.js /*! @fileoverview gl-matrix - High performance matrix and vector operations @author Brandon Jones @author Colin MacKenzie IV @version 3.4.3 Copyright (c) 2015-2021, Brandon Jones, Colin MacKenzie IV. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation f

    2024年02月10日
    浏览(46)
  • cesium 3DTileset的平移、旋转、缩放

    加载模型 平移和修改高度 方法一:

    2024年02月12日
    浏览(39)
  • 【Unity3D】缩放、平移、旋转场景

    1 前言         场景缩放、平移、旋转有两种实现方案,一种是对场景中所有物体进行同步变换,另一种方案是对相机的位置和姿态进行变换。         对于方案一,如果所有物体都在同一个根对象下(其子对象或孙子对象),那么只需要对根对象施加变换就可以实现场景变

    2023年04月22日
    浏览(55)
  • Unity 3d角色展示脚本(旋转 平移 缩放)展示界面

    不考虑性能 很简陋的一个功能,主要是用于角色渲染的观察用,比simplecontroller要好用一点

    2024年02月11日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包