二维svg图近似匹配

在此感谢所有愿意将自己的学习成果进行分享的博主,让我可以有中文资料参考(英语渣的痛谁懂…

二维ICP算法推导

py的svgpathtools的库可以实现读取svg图的关键点
https://www.cnpython.com/qa/342993

ICP算法的作用

通过旋转和平移,将两个对应点的距离尽可能的缩小。

先用图解释一下二维ICP工作原理

计算欧式距离最近的两点被认为是对应点,那么我们就将两个点云中的任意两点进行匹配,让匹配后的对应点之间的距离累加最小。

1.我们初始载入图片,获取图的关键点(初始化点云)。
2.然后先将重心重合(平移),每个点以矩阵坐标上相等的点作为暂时的对应点。
3.进行旋转,使两个点云之间的距离平方和最小。
4.判断是否满足需求,不满足重复2,3步骤(迭代获取有意义的对应点).在这里插入图片描述
(我这图画的真丑…但是不碍事,能看就行)

ICP实现过程(匹配对应点)

输入

我们解析两张svg图片,得到svg图的所有关键点,为输入数据——两个点云(点集)。

初始化对应点

在这里插入图片描述
这里的ai和bj是视觉上(真正有意义)的对应点,需要迭代匹配才能得到。
{ai,bj}=arg min{sqrt( (aix-bjx)2 +(aiy-bjy)2 ) },这是视觉上的对应点的定义,取距离最短的两点作为对应点。

迭代求旋转矩阵中用到的ai,bi是点云归一化后的对应点,不一定是视觉上的对应点
平移之后通过旋转确定点的匹配。
比如这张图,此时的ai,bi只是因为矩阵坐标上一致所以被认为是对应点。
在这里插入图片描述

ICP算法最重要的就是找到对应的旋转和平移矩阵,使得变换后的两个点云近似匹配(对应点距离平方最小)

求旋转,平移矩阵

在对点云归一化的过程中,两个点云的重心已经重叠。已经实现部分的平移,所以我们接下来重心放在旋转上,得到旋转矩阵后就可以得到实际的平移矩阵 R*t
在这里插入图片描述
t是点云归一化的矩阵——对于矩阵的每一个点-=点云重点。

在这里插入图片描述
在这里插入图片描述

关于迭代

在这里插入图片描述
第三点匹配失败是因为变化不大,没有迭代的必要了。
如果没有达到迭代终止的三种情况就继续迭代。

ICP代码

代码借鉴:这篇博客是基于三维图像进行的ICP算法的实现,我稍微改了一些东西
Eigen库的导入和使用参考1
Eigen库的导入和使用参考2
这段代码只是第一次旋转平移,初次尝试匹配,并加上迭代的功能。

#include <Eigen/Dense>
#include <iostream>
#include <string.h>
#include <fstream>
#include <vector>
#include<cmath>
using namespace std;
struct Point
{
	double x, y;
};

//功能:读取文件txt、asc->MatrixXd
void CreateCloudMatrix(const string& file_path, Eigen::MatrixXd& inPutPointCloud)
{
	ifstream file;
	file.open(file_path.c_str(), ios::in);
	if (!file.is_open()) {
		cout << "文件打开失败" << endl;
		exit(0);
	}

	//	ifstream file();//c_str():生成一个const char*指针,指向以空字符终止的数组。
	string line;
	Point point;
	vector<Point> cloud;

	while (getline(file, line))           //用到x,y,z
	{
		stringstream ss(line);
		ss >> point.x;
		ss >> point.y;
		cloud.push_back(point);
	}
	Eigen::MatrixXd pointCloud = Eigen::MatrixXd::Zero(cloud.size(), 2);//size行,两列
	for (int i = 0; i < cloud.size(); i++)
	{
		pointCloud(i, 0) = cloud[i].x;
		pointCloud(i, 1) = cloud[i].y;
	}
	inPutPointCloud = pointCloud;



	file.close();

}

//功能:SVD计算点集配准
//介绍:输入两个对应点云,输出旋转矩阵、平移矩阵。
void PointCloudRegistrationSVD(Eigen::MatrixXd &inputPointCloud1, Eigen::MatrixXd &inputPointCloud2, Eigen::Matrix2d& rotationMat, Eigen::Vector2d& translationMat)
{
	// MatrixXd 表示的是动态数组,初始化的时候指定数组的行数和列数
	//每列求均值->1X2
	Eigen::RowVector2d meanVector1 = inputPointCloud1.colwise().mean();
	Eigen::RowVector2d meanVector2 = inputPointCloud2.colwise().mean();


	//平移后的矩阵
	//减去质心坐标,每行坐标减去上面的那个1X2矩阵
	inputPointCloud1.rowwise() -= meanVector1;
	inputPointCloud2.rowwise() -= meanVector2;

	cout << "平移后的:" << endl;
	cout << "矩阵1:" << endl;
	for (int i = 0; i < inputPointCloud1.size() / 2; i++) {
		cout << inputPointCloud1(i, 0) << " " << inputPointCloud1(i, 1) << endl;
	}
	cout << endl;
	cout << "矩阵2:" << endl;
	for (int i = 0; i < inputPointCloud2.size() / 2; i++) {
		cout << inputPointCloud2(i, 0) << " " << inputPointCloud2(i, 1) << endl;
	}
	cout << endl;

	double sum1 = 0, sum2 = 0;
	for (int i = 0; i < inputPointCloud1.size() / 2; i++) {
	
		sum1 += inputPointCloud1(i, 0) * inputPointCloud2(i, 1) - inputPointCloud1(i, 1) * inputPointCloud2(i, 0);
		sum2 += inputPointCloud1(i, 0) * inputPointCloud2(i, 0) + inputPointCloud1(i, 1) * inputPointCloud2(i, 1);
	}


	double Atan  = atan(sum1 / sum2);
	

	//点云1的所有坐标-=点云2的所有坐标*旋转矩阵(平移后的点云)
	rotationMat(0, 0) = cos(Atan);
	rotationMat(0, 1) = sin(Atan);
	rotationMat(1, 0) = -sin(Atan);
	rotationMat(1, 1) = cos(Atan);



	translationMat = meanVector2 - meanVector1 * rotationMat;

}

void test()
{
	Eigen::MatrixXd inPutPointCloud1;
	Eigen::MatrixXd inPutPointCloud2;

	Eigen::Matrix2d rotationMat;
	//旋转矩阵
	Eigen::Vector2d translationMat;
	//平移矩阵

	CreateCloudMatrix("C:\\Users\\mio\\Desktop\\data1.txt", inPutPointCloud1);
	CreateCloudMatrix("C:\\Users\\mio\\Desktop\\data2.txt", inPutPointCloud2);
	//初始化点云

	PointCloudRegistrationSVD(inPutPointCloud1, inPutPointCloud2, rotationMat, translationMat);

	cout << "旋转矩阵:" << endl;
	cout << rotationMat(0, 0) << " , " << rotationMat(0, 1) << endl;
	cout << rotationMat(1, 0) << " , " << rotationMat(1, 1) << endl;

	cout << endl << "矩阵1经过旋转矩阵后的变化:" << endl;
	for (int i = 0; i < inPutPointCloud1.size() / 2; i++) {
		
		cout << inPutPointCloud1(i, 0) * rotationMat(0, 0) + inPutPointCloud1(i, 1) * rotationMat(1, 0) << " "
			<< inPutPointCloud1(i, 0) * rotationMat(0, 1) + inPutPointCloud1(i, 1) * rotationMat(1, 1) << endl;

	}

}

int main()
{
	test();
}





对应点匹配成功后就可以确定两个svg图形是否满足相似性。

测试结果

点按同一个时针方向导入

在这里插入图片描述

手动演示一下匹配过程

1.初始的时候
在这里插入图片描述
数据给出来

data1:
5 -1
4 5
5 6
6 5
7 5
data2:
0 -4
-6 -5
-7 -4
-6 -3
-6 -2

2.重心归一:平移
在这里插入图片描述
3.旋转,匹配成功
在这里插入图片描述

尝试一下点随机分布

也匹配的上,但是没有同一时针输入准确
test 1

data1:
5 -1
7 5
4 5
5 6
6 5
data2:
0 -4
-6 -5
-7 -4
-6 -3
-6 -2

在这里插入图片描述

手动演示

直接看旋转之后的
在这里插入图片描述
test 2

data1:
5 -1
7 5
4 5
5 6
6 5
data2:
-6 -3
-6 -2
0 -4
-6 -5
-7 -4

在这里插入图片描述

是否满足相似性

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值