首页 > 其他分享 >(slam工具)4 3D点集配准相似变换sRt计算

(slam工具)4 3D点集配准相似变换sRt计算

时间:2024-06-17 22:36:00浏览次数:19  
标签:src 配准 inx dst list 点集 points sRt np

 

 

https://github.com/Dongvdong/v1_1_slam_tool

 

 

import random
import math

import numpy as np
import os







def API_pose_estimation_3dTo3d_ransac(points_src, points_dst): #NED -> slam
    p = np.array(points_src, dtype=float)
    q = np.array(points_dst, dtype=float)
    print("匹配点数: ", len(points_src), " ", len(points_dst))
    # 1.计算s并去质心
    mean_p = np.mean(p, axis=0)
    mean_q = np.mean(q, axis=0)

    p_norm = p - mean_p
    q_norm = q - mean_q

    # 计算距离比
    iter_num = 0
    _s = 0
    inliner_num = 0
    ransac_time=2000 # ransac随机抽取验证次数
    while iter_num < ransac_time:
        # 随机挑选两个元素
        _list = []
        # print("len(points_src): ",len(points_src))
        # if len(points_src) < 2:
        #     break
        inx_1, inx_2 = random.sample(range(len(points_src)), 2)
        #print("inx_1: ",inx_1,"inx_2: ",inx_2)
        # print("inx_2: ",inx_2)
        p_r = np.array([points_src[inx_1], points_src[inx_2]], dtype=float)
        q_r = np.array([points_dst[inx_1], points_dst[inx_2]], dtype=float)
        _list.append(inx_1)
        _list.append(inx_2)
        # 计算s

        p_norm_r = p_r - mean_p
        q_norm_r = q_r - mean_q

        # 所有点的xyz平方求和
        d1_list = []
        d2_list = []
        for i in range(len(q_norm_r)):
            d1 = q_norm_r[i]
            d2 = p_norm_r[i]
            dist1 = math.sqrt(np.sum(d1**2))
            dist2 = math.sqrt(np.sum(d2**2))
            d1_list.append(dist1)
            d2_list.append(dist2)
        s_r = np.sum(d1_list) / np.sum(d2_list)

        # 计算其他点s的误差值
        inliner_p = [points_src[inx_1], points_src[inx_2]]
        inliner_q = [points_dst[inx_1], points_dst[inx_2]]

        for inx in range(len(points_src)):
            # 计算点不参与验证
            if inx == inx_1 or inx == inx_2:
                continue
            p_src = np.array(points_src[inx])
            q_dst = np.array(points_dst[inx])
            # 分别计算到质心距离
            p_src_norm = p_src - mean_p
            q_dst_norm = q_dst - mean_q
            p_src_norm_dist = math.sqrt(np.sum(p_src_norm**2))
            q_dst_norm_dist = math.sqrt(np.sum(q_dst_norm**2))
            # 计算误差
            cal_dist = p_src_norm_dist * s_r
            error = cal_dist - q_dst_norm_dist
            if abs(error) < 3:
                inliner_p.append(points_src[inx])
                inliner_q.append(points_dst[inx])
                _list.append(inx)

        # 利用所有内点计算新的s
        p_r = np.array(inliner_p)
        q_r = np.array(inliner_q)
        p_norm_f = p_r - mean_p
        q_norm_f = q_r - mean_q

        d1_list = []
        d2_list = []
        for i in range(len(q_norm_f)):
            d1 = q_norm_f[i]
            d2 = p_norm_f[i]
            dist1 = math.sqrt(np.sum(d1**2))
            dist2 = math.sqrt(np.sum(d2**2))
            d1_list.append(dist1)
            d2_list.append(dist2)

        s_final = np.sum(d1_list) / np.sum(d2_list)
        # 记录内点数最高的模型
        if inliner_num < len(inliner_p):
            _s = s_final
            inliner_num = len(inliner_p)
            inx_list = _list
        iter_num += 1

    s = _s

    # 2.用s缩放src到dst尺度下
    p = s * p
    mean_p = np.mean(p, axis=0)
    p_norm = p - mean_p

    # 2.计算q1*q2^T(注意顺序:q2->q1,x是dst,y是src)
    N = len(p)

    W = np.zeros((3, 3))
    for i in range(N):
        x = q_norm[i, :]     # 每一行数据
        x = x.reshape(3, 1)  # 3行1列格式 一维数组借助reshape转置
        y = p_norm[i, :]
        y = y.reshape(1, 3)
        W += np.matmul(x, y)

    # 3.SVD分解W
    # python 线性代数库中svd求出的V与C++ Eigen库中求的V是转置关系
    U, sigma, VT = np.linalg.svd(W, full_matrices=True)
    # 旋转矩阵R
    R = np.matmul(U, VT)    # 这里无需再对V转置
    # 在寻找旋转矩阵时,有一种特殊情况需要注意。有时SVD会返回一个“反射”矩阵,这在数值上是正确的,但在现实生活中实际上是无意义的。
    # 通过检查R的行列式(来自上面的SVD)并查看它是否为负(-1)来解决。如果是,则V的第三列乘以-1。
    # 验证R行列式是否为负数   参考链接:https://blog.csdn.net/sinat_29886521/article/details/77506426
    if np.linalg.det(R) < 0:
        det = np.linalg.det(np.matmul(U, VT))
        mat = np.array([[1, 0, 0], [0, 1, 0], [0, 0, det]])
        R = np.matmul(U, VT, mat)
    # 平移向量
    T = mean_q - np.matmul(R, mean_p)   # dst - src
    T = T.reshape(3, 1)
    sR = s*R
    RT_34 = np.c_[sR, T]

    # 4.计算误差值
    p = np.array(points_src)
    error_sum = 0
    inx_list2 = []
    error_ENU = []
    for i in range(N):
        src = p[i, :]
        dst = q[i, :]
        src = src.reshape(3, 1)
        dst = dst.reshape(3, 1)
        test_dst = np.matmul(sR, src) + T

        error_Mat = test_dst - dst
        error_Mat2 = error_Mat**2
        error = math.sqrt(np.sum(error_Mat2))
        error_ENU.append(error)
        if error < 3:
            inx_list2.append(i)
        error_sum += error

    print("mean error:", error_sum/N)
    print("max error:", max(error_ENU))
    print("RT_34:\n", RT_34)
    print("缩放系数s:\n", s)
    print("旋转矩阵R:\n", R)
    print("通尺度下的T:\n", T)
    return RT_34, sR, T


# 根据 srt 将单个目标点云变换到指定坐标系下
def API_src3D_sRt_dis3D_one(points_src,SR,T):
           
    points_src_ = [[points_src[0]], [points_src[1]], [points_src[2]]]
    points_dis_ = np.matmul(SR,points_src_) + T
    #points_dis_ = SR @ points_src_ + T
    points_dis_t=[points_dis_[0][0],points_dis_[1][0],points_dis_[2][0]]

    return points_dis_t

# 将1组3d点 根据 srt变换到另一个坐标系下
def API_src3D_sRt_dis3D_list(points_src,points_dst,SR,T):


    points_dis_t_list=[]
    error_sum=0 # 误差计算

    for p_i in range(0,len(points_src)):
        # 根据srt计算便函后的x y z平移点
        points_dis_t=API_src3D_sRt_dis3D_one(points_src[p_i],SR,T)   
        print("原始点",points_src[p_i],"变换后的点",points_dis_t,"真值",points_dst[p_i])
        points_dis_t_list.append(points_dis_t)
        
        # =========整体转换后的计算误差===============
        points_dis_t = np.array(points_dis_t)
        points_dst[p_i] = np.array(points_dst[p_i])
        error_Mat = points_dis_t - points_dst[p_i]
        error_Mat2 = error_Mat**2
        error = np.sum(error_Mat2)
        error_sum += error
      
    error_sum=math.sqrt(error_sum)/len(points_src)
    print("平均误差:",error_sum)
    return points_dis_t_list
    

'''
if __name__ == "__main__":
    
    
    # points_src=[[1,1,1],[2,2,2],[3,3,3]]
    # points_dst=[[11,11,11],[21,21,21],[31,31,31]]
    # RT_34, SR, T = API_pose_estimation_3dTo3d_ransac(points_src, points_dst) # 
    # points_dis_t_list=API_src3D_sRt_dis3D_list(points_src,points_dst,SR, T)
    # print("变换后的列表",points_dis_t_list)

'''

'''
    # # 4 数据转化 为3D-3D计算相似变换准备  colmap enu 变换到 gnss enu坐标系上
    # #ENU_List  :名字 e n u 转化为:  e n u
    # 4-1 读取gnss enu
    # 取出前400个数据计算
    ENU_GNSS_List_4= API_read2txt("data/test/2ENU_from_GNSS.txt")
    ENU_GNSS_List_4_400=[]
    for i in range(160 , len(ENU_GNSS_List_4)):
        ENU_GNSS_List_4_400.append(ENU_GNSS_List_4[i]) 
    ENU_GNSS_List_3_400=API_data0123_to_data123(ENU_GNSS_List_4_400) # 去掉第一列名字
    
    # 4-2 读取colmap enu
    ENU_colmap_list_4= API_read2txt("data/test/colmap_images_t.txt")
    ENU_colmap_list_3=API_data0123_to_data123(ENU_colmap_list_4) # 去掉第一列名字

    # 4-4 计算变换关系 points_src 到 points_dst
    points_src=ENU_colmap_list_3
    points_dst=ENU_GNSS_List_3_400 #ENU_GNSS_List_3_400
    RT_34, SR, T = API_pose_estimation_3dTo3d_ransac(points_src, points_dst) # 
   
    colmapenu_in_gnssenu_3=API_src3D_sRt_dis3D_list(points_src,points_dst,SR, T)
    
    # 4-5 保存计算结果 colmap enu变换到gnss enu坐标系下的新坐标
    colmapenu_in_gnssenu_4=[]
    for i in range(0,len(ENU_GNSS_List_4_400)):
        name=ENU_GNSS_List_4[i][0]
        #保存数据 名字 e n u
        li=[name,colmapenu_in_gnssenu_3[i][0],colmapenu_in_gnssenu_3[i][1],colmapenu_in_gnssenu_3[i][2]]
        colmapenu_in_gnssenu_4.append(li)
    # 保存数据
    colmapeEnu_from_GnssEnu_txt_name="data/test/3colmapeEnu_from_GnssEnu.txt"
    API_Save2txt(colmapeEnu_from_GnssEnu_txt_name,colmapenu_in_gnssenu_4)
'''

  

标签:src,配准,inx,dst,list,点集,points,sRt,np
From: https://www.cnblogs.com/gooutlook/p/18253353

相关文章

  • 【图像准配】用于多模态图像配准的 CCRE(Matlab实现)
     ......
  • QGIS配准工具的变换算法(翻译自QGIS官方文档)
    QGIS配准工具的变换算法配准工具中有多种变换算法可用,具体取决于输入数据的类型和质量、您愿意在最终结果中引入的几何变形的性质和数量,以及地面控制点(GCP)的数量。目前,可以使用以下变换类型:线性算法用于创建坐标定位文件,与其他算法不同,它实际上不会变换栅格像素。它......
  • 字符串分割处理srttok的用法
    字符串处理srttok的用法strtok函数是C语言中用于分割字符串的一个非常有用的工具。以下是关于strtok函数用法的详细说明:一、函数原型c复制代码char*strtok(char*str,constchar*delim);二、参数说明str:要分割的字符串。首次调用时,它应指向要分割的原始字符串。在后续......
  • §1. 平面点集与多元函数
    掌握平面点集中的相关概念(邻域、内点、外点、界点、聚点、孤立点、开集、闭集、区域、有界点集),能够判断开集,闭集、有界集、区域、及它们的聚点、界点等,以及上的完备性定理(柯西准则、闭域套定理及推论、聚点原理、有界覆盖原理)。掌握二元(多元)函数的概念。难点:1.内点、外点、界点......
  • 操作系统之CPU调度算法——FCFS、SJF和SRTF
    目录前言 FCFS先来先服务调度算法定义与步骤 举例SJF短作业优先调度算法定义与步骤举例SRTF最短剩余时间优先调度算法定义与步骤举例结束语​​​​​​​前言 今天是坚持写博客的第12天,为不断坚持的自己和大家点赞。最近经历了一场时长半小时的答辩,还是需......
  • ENVI自动地理配准:GCP地面控制点的自动产生
      本文介绍基于ENVI软件,利用“ImageRegistrationWorkflow”工具实现栅格遥感影像自动寻找地面控制点从而实现地理配准的方法。  在ENVI手动地理配准栅格图像的方法这篇文章中,我们介绍了在ENVIClassic5.3(64-bit)软件中通过“SelectGCPs:ImagetoImage”工具手动指定......
  • 自动化部署elasticsearch三节点集群
    什么是Elasticsearch?Elasticsearch是一个开源的分布式搜索和分析引擎,构建在ApacheLucene的基础上。它提供了一个分布式多租户的全文搜索引擎,具有实时分析功能。Elasticsearch最初是用于构建全文搜索引擎,但它的功能已经扩展到包括日志分析、应用程序性能监控、地理信息系统等......
  • ModbusRTU从站扫描工具 python实现
    扫描指定串口下,有哪些modbusRTU服务端[1-247]frompymodbus.clientimportModbusSerialClientasModbusClientfrompymodbus.exceptionsimportModbusIOException,ConnectionException,NoSuchSlaveExceptionimporttimedefread_holding_registers(client,slave_addres......
  • ModbusTcp和ModbusRtu全面理解
    一、何为Modbus通信协议1.1Modbus基本介绍Modbus是一种通信协议,是Modicon公司(现在的施耐德电气SchneiderElectric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。Modbus已经成为工业领域通信协议的业界标准(Defacto),并且现在是工业电子设备之间常用的连接方式M......
  • 用python写一个 将指定目录下以及其下所有子目录下的srt文件复制一份并重命名带上文件
    代码:importosimportshutildefcopy_and_rename_files(src_directory,target_directory):#确保目标目录存在ifnotos.path.exists(target_directory):os.makedirs(target_directory)#遍历指定目录及其所有子目录forroot,dirs,file......