ICP

ICP 基本上有两种解法

1.四元法


image.png
function ret = normal_gravity( data )
% 数据在重心上正则化

    [m, n] = size(data);
    data_mean = mean(data);%坐标在x,y,z上的平均值
    ret = data - ones(m, 1) * data_mean;

end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [data_g,  data_p] = icp_process_xgtu(data_g,  data_p)

[ data_g, data_p, error, data_pp, R ] = icp_process(data_g, data_p);
log_info(strcat('点云之间当前差值:', num2str(error)));
log_info('当前变换矩阵:');
disp(R);

cnt = 1;
last_error = 0;
last_R = R;
% 进行迭代处理点云
while abs(error - last_error) > 0.01
    cnt = cnt + 1;
    last_error = error;
    last_R = R;
    [data_g, data_p, error, data_pp, R] = icp_process(data_g, data_p);
    R = last_R * R;
    log_info(strcat('当前循环次数', num2str(cnt), '点云之间的差值', num2str(error)));
    log_info('当前变换矩阵:');
    disp(R);
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [ data_g, data_p, err, data_pp, R ] = icp_process( data_g, data_p )

    [k1, n] = size(data_g);
    [k2, m] = size(data_p);
    
    data_p1 = zeros(k2, 3);     % 用来存储两个点集之间的临时误差
    data_pp = zeros(k1, 3);     % 用来存储data_g在data_p中对应的最小点
    distance = zeros(k1, 1);    % data_g(i)与data_p中的距离
    error = zeros(k1, 1);       % 用来存储data_g的临时最小误差
    
    % 坐标数据重心正则化
    data_g = normal_gravity(data_g);
    data_p = normal_gravity(data_p);
    
    % 求对应的最近点
    for i = 1:k1
        data_p1(:, 1) = data_p(:, 1) - data_g(i, 1);    % data_p所有点的x坐标都减去data_g中一个点的x轴坐标
        data_p1(:, 2) = data_p(:, 2) - data_g(i, 2);    % data_p所有点的y坐标都减去data_g中一个点的y轴坐标
        data_p1(:, 3) = data_p(:, 3) - data_g(i, 3);    % data_p所有点的z坐标都减去data_g中一个点的z轴坐标
        distance = data_p1(:, 1).^2 + data_p1(:, 2).^2 + data_p1(:, 3).^2;  % data_p与data_g(i)点的距离
        [min_dis, min_index] = min(distance);   % data_p与data_g(i)点的距离最小的点
        data_pp(i, :) = data_p(min_index, :);   % data_pp中保存data_g的对应最小点
        error(i) = min_dis;     % 存储对应的误差
    end

    % 四元数法求解
    V = (data_g' * data_pp) ./ k1;
    
    % 对称矩阵Q用于求解四元
    matrix_Q = [V(1,1)+V(2,2)+V(3,3),   V(2,3)-V(3,2),          V(3,1)-V(1,3),          V(1,2)-V(2,1);  
                V(2,3)-V(3,2),          V(1,1)-V(2,2)-V(3,3),   V(1,2)+V(2,1),          V(1,3)+V(3,1);  
                V(3,1)-V(1,3),          V(1,2)+V(2,1),          V(2,2)-V(1,1)-V(3,3),   V(2,3)+V(3,2);  
                V(1,2)-V(2,1),          V(1,3)+V(3,1),          V(2,3)+V(3,2),          V(3,3)-V(1,1)-V(2,2)];
    
    [V2, D2] = eig(matrix_Q);       % 返回特征的对角矩阵D2和矩阵v2
    lambdas = [D2(1, 1), D2(2, 2), D2(3, 3), D2(4, 4)]; % 特征值
    [lambda, ind] = max(lambdas);   % 最大的特征值以及索引
    Q = V2(:, ind); % Q所对应的值便是四元
    
    % 变换矩阵
    R=[Q(1,1)^2+Q(2,1)^2-Q(3,1)^2-Q(4,1)^2,     2*(Q(2,1)*Q(3,1)-Q(1,1)*Q(4,1)),        2*(Q(2,1)*Q(4,1)+Q(1,1)*Q(3,1));  
       2*(Q(2,1)*Q(3,1)+Q(1,1)*Q(4,1)),         Q(1,1)^2-Q(2,1)^2+Q(3,1)^2-Q(4,1)^2,    2*(Q(3,1)*Q(4,1)-Q(1,1)*Q(2,1));  
       2*(Q(2,1)*Q(4,1)-Q(1,1)*Q(3,1)),         2*(Q(3,1)*Q(4,1)+Q(1,1)*Q(2,1)),        Q(1,1)^2-Q(2,1)^2-Q(3,1)^2-Q(4,1)^2;  
    ];
    
    % 更新data_p,其实这应该也是对应论文中平移矩阵的过程,至于是否等价我觉得应该是不等价的,在不同情况下效果应该不同
    data_p = data_p * R;
    data_pp = data_pp * R;
    data_p = normal_gravity(data_p);
    data_pp = normal_gravity(data_pp);
    err = mean(error);
    
end

% 参考文献:A method for registration of 3-D shapes
% 代码来源:[https://github.com/XgTu/2DASL](https://github.com/XgTu/2DASL),我只是添加了下注释啦

2.SVD
参考博客1:https://zhuanlan.zhihu.com/p/35893884
参考博客2:https://blog.csdn.net/jsgaobiao/article/details/78873718

import numpy as np
import random
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# https://zhuanlan.zhihu.com/p/35893884

def nearest_point(P, Q):
    P = np.array(P)
    Q = np.array(Q)
    dis = np.zeros(P.shape[0])
    index = np.zeros(Q.shape[0], dtype = np.int)

    for i in range(P.shape[0]):
        minDis = np.inf
        for j in range(Q.shape[0]):
            tmp = np.linalg.norm(P[i] - Q[j], ord = 2)
            if minDis > tmp:
                minDis = tmp
                index[i] = j
        dis[i] = minDis
    return dis, index

def find_optimal_transform(P, Q):
    meanP = np.mean(P, axis = 0)
    meanQ = np.mean(Q, axis = 0)
    P_ = P - meanP
    Q_ = Q - meanQ

    W = np.dot(Q_.T, P_)
    U, S, VT = np.linalg.svd(W)
    R = np.dot(U, VT)
    if np.linalg.det(R) < 0:
       R[2, :] *= -1

    T = meanQ.T - np.dot(R, meanP.T)
    return R, T

def icp(src, dst, maxIteration=50, tolerance=0.001, controlPoints=100):
    A = np.array(src)
    B = np.array(dst)
    lastErr = 0
    if (A.shape[0] != B.shape[0]):
        length = min(A.shape[0], B.shape[0])
        length = min(length, controlPoints)
        sampleA = random.sample(range(A.shape[0]), length)
        sampleB = random.sample(range(B.shape[0]), length)
        P = np.array([A[i] for i in sampleA])
        Q = np.array([B[i] for i in sampleB])
    else:
        length = A.shape[0]
        if (length > controlPoints):
            sampleA = random.sample(range(A.shape[0]), length)
            sampleB = random.sample(range(B.shape[0]), length)
            P = np.array([A[i] for i in sampleA])
            Q = np.array([B[i] for i in sampleB])
        else :
            P = A
            Q = B

    for i in range(maxIteration):
        print("Iteration : " + str(i) + " with Err : " + str(lastErr))
        dis, index = nearest_point(P, Q)
        R, T = find_optimal_transform(P, Q[index,:])
        A = np.dot(R, A.T).T + np.array([T for j in range(A.shape[0])])
        P = np.dot(R, P.T).T + np.array([T for j in range(P.shape[0])])

        meanErr = np.sum(dis) / dis.shape[0]
        if abs(lastErr - meanErr) < tolerance:
            break
        lastErr = meanErr

        # visualization
        # ax = plt.subplot(1, 1, 1, projection='3d')
        # ax.scatter(P[:, 0], P[:, 1], P[:, 2], c='r')
        # ax.scatter(Q[:, 0], Q[:, 1], Q[:, 2], c='g')
        # plt.show(block = False)

    R, T = find_optimal_transform(A, np.array(src))
    return R, T, A

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容