利用半自动化从点云中提取规则多边形

高精度地图一个重要的矢量数据就是交通标志,比如地面的字符、箭头,路边的限速牌等。在我理解看来,交通标志与车道线是同等重要的,因为它是无人车初始定位的重要匹配依据。写到这就扯远了,我今天要讨论的是,如何用交互的方式从点云中提取规则多边形,这篇文章以地面字符箭头为例,也就是矩形的提取。首先,算法输入是已经分类好的初始点云,如下图所示:

这里所说的半自动化是需要用户在点云上点一下,以生成一个种子点,作为区域增长的起点。步骤如下:

1.捕捉点云,生成一个种子点Seed

2.查找Seed周围的临近点(这里会用kdtree加快速度),然后以临界点作为种子点再次查找,一直找到没有新的点加入为止

3.经过步骤2得到一个聚类簇的集合,因其是三维点,不利于计算轮廓(字符箭头显然是二维面)。需要求得其法线方向(参见我另一篇博客:利用PCA求点云的法线),然后根据法线方向将其投影到一个二维面内

4.经过第三步,三维点云被投影到二维面,这时的轮廓就很好计算了(可采用geos中的getEnvelope方法,这一步会跟第5步同时做)

5.计算出轮廓是不规则多边形的,要进行规则化,这里规则化为矩形,当然也可以规则化为其他图形.将不规则多边形规则化为矩形,有一个重要方法是,选择多边形的每一条边作为x轴,并选该边的一个端点作为原点建立直角坐标系,再求其包围盒.所有包围盒里最小的那个就是所要求得的矩形轮廓.

6.求得了矩形轮廓,下一步就是参照第四和第五步的变换矩阵,再逆变换回去,矩形轮廓就出来了,如下图所示:


代码如下:

class ExtractSymbol
	{
	public:
		ExtractSymbol(const CloudComparePointType& seed, const pcl::PointCloud<CloudComparePointType>::Ptr& cloud)
			: _cloud(cloud), octree(5.0), _seed(seed.x, seed.y, seed.z)
		{
			octree.setInputCloud(cloud);
			octree.addPointsFromInputCloud();
			geom = createPoint(osg::Vec2d(seed.x, seed.y));
			search(seed);
		}

		geos::geom::Geometry* createCube(const osg::Vec2d& v1, const osg::Vec2d& v2, const osg::Vec2d& v3, const osg::Vec2d& v4)
		{
			CoordinateSequence *pts = new CoordinateArraySequence();
			pts->add(Coordinate(v1.x(), v1.y()));
			pts->add(Coordinate(v2.x(), v2.y()));
			pts->add(Coordinate(v3.x(), v3.y()));
			pts->add(Coordinate(v4.x(), v4.y()));
			pts->add(Coordinate(v1.x(), v1.y()));

			GeometryFactory *fact = new GeometryFactory();
			geos::geom::Polygon* poly = fact->createPolygon(fact->createLinearRing(pts), NULL);
			return poly;
		}

		geos::geom::Geometry* createPoint(const osg::Vec2d& v)
		{
			GeometryFactory *fact = new GeometryFactory();
			geos::geom::Point* point = fact->createPoint(Coordinate(v.x(), v.y()));
			return point;
		}

		void search(const CloudComparePointType& seed)
		{
			std::vector<int> pointIdxRadiusSearch;
			std::vector<float> pointRadiusSquaredDistance;

			bool needSearch = false;
			if(octree.radiusSearch(seed, 0.5, pointIdxRadiusSearch, pointRadiusSquaredDistance) > 0)
			{
				for (size_t i = 0; i < pointIdxRadiusSearch.size(); ++i)
				{
					CloudComparePointType p = _cloud->points[pointIdxRadiusSearch[i]];
					geos::geom::Geometry* pt = createPoint(osg::Vec2d(p.x, p.y));
					if (!geom->covers(pt))
					{
						geom = geom->Union(pt)->convexHull();
						needSearch = true;
					}
				}
			}

			if (!needSearch) return;

			const std::vector<Coordinate>* coordinates = geom->getCoordinates()->toVector();
			for (std::vector<Coordinate>::const_iterator itr = coordinates->begin(); itr != coordinates->end(); itr++)
			{
				CloudComparePointType p;
				p.x = itr->x;
				p.y = itr->y;
				p.z = seed.z;
				search(p);
			}
		}

		bool getBound(ville3d::Vertex& center, ville3d::Vector& heading, double& length, double& width)
		{
			const std::vector<Coordinate>* coordinates = geom->getCoordinates()->toVector();

			osg::ref_ptr<osg::Vec3dArray> res_envelop = getMinEnvelop(coordinates);

			if (res_envelop.valid())
			{
				getCenteroid(res_envelop, center);
				getRectangle(res_envelop, heading, length, width);
				return true;
			}
			return false;
		}

		void getCenteroid(osg::Vec3dArray* rectangle, ville3d::Vertex& center)
		{
			osg::Vec3d centeroid;
			for (osg::Vec3dArray::iterator itr = rectangle->begin(); itr != rectangle->end(); itr++)
			{
				centeroid += *itr;
			}
			centeroid = centeroid / rectangle->size();
			center = ville3d::Vertex(centeroid.x(), centeroid.y(), _seed.z());
		}


		void getRectangle(osg::Vec3dArray* rectangle, ville3d::Vector& heading, double& length, double& width)
		{
			osg::Vec3d c0 = rectangle->at(0);
			osg::Vec3d c1 = rectangle->at(1);
			osg::Vec3d c2 = rectangle->at(2);

			double length0 = (c2 - c1).length();
			double length1 = (c1 - c0).length();

			if (length0 > length1)
			{
				length  = length0;
				width   = length1;
				heading = ville3d::Vector(c2.x() - c1.x(), c2.y() - c1.y(), 0);
			}
			else
			{
				length = length1;
				width = length0;
				heading = ville3d::Vector(c1.x() - c0.x(), c1.y() - c0.y(), 0);
			}
		}

		osg::Vec3dArray* getMinEnvelop(const std::vector<Coordinate>*& coordinates)
		{
			if (coordinates->size() < 2) return NULL;

			osg::Matrixd res_matrix;
			double min_area = DBL_MAX;
			geos::geom::Geometry* res_envelop = NULL;
			osg::ref_ptr<osg::Vec3dArray> result = new osg::Vec3dArray;

			for (int i = 0; i < coordinates->size() - 1; i++)
			{
				Coordinate c1 = coordinates->at(i);
				Coordinate c2 = coordinates->at(i + 1);

				osg::Vec3d u = osg::Vec3d(c2.x - c1.x, c2.y - c1.y, 0);
				u.normalize();
				osg::Vec3d n = osg::Z_AXIS;
				osg::Vec3d v = n ^ u;

				osg::Vec3d  dd;
				dd.x() = osg::Vec3d(c1.x, c1.y, 0.0) * osg::Vec3d(u.x(), u.y(), u.z());
				dd.y() = osg::Vec3d(c1.x, c1.y, 0.0) * osg::Vec3d(v.x(), v.y(), v.z());
				dd.z() = osg::Vec3d(c1.x, c1.y, 0.0) * osg::Vec3d(n.x(), n.y(), n.z());

				double m[4][4];
				m[0][0] = u.x(); m[0][1] = u.y(); m[0][2] = u.z(); m[0][3] = -dd.x();
				m[1][0] = v.x(); m[1][1] = v.y(); m[1][2] = v.z(); m[1][3] = -dd.y();
				m[2][0] = n.x(); m[2][1] = n.y(); m[2][2] = n.z(); m[2][3] = -dd.z();
				m[3][0] = 0.0;	 m[3][1] = 0.0;   m[3][2] = 0.0;   m[3][3] = 1.0;

				osg::Matrixd matrix = osg::Matrixd(m[0][0], m[0][1], m[0][2], m[0][3],
					m[1][0], m[1][1], m[1][2], m[1][3],
					m[2][0], m[2][1], m[2][2], m[2][3],
					m[3][0], m[3][1], m[3][2], m[3][3]);

				geos::geom::Geometry* pointset;
				for (std::vector<Coordinate>::const_iterator itr = coordinates->begin(); itr != coordinates->end(); itr++)
				{
					osg::Vec3d v = matrix.postMult(osg::Vec3d(itr->x, itr->y, 0.0));
					if (itr == coordinates->begin())
					{
						pointset = createPoint(osg::Vec2d(v.x(), v.y()));
					}
					else
					{
						pointset = pointset->Union(createPoint(osg::Vec2d(v.x(), v.y())));
					}
				}

				geos::geom::Geometry* envelop = pointset->getEnvelope();
				double area = envelop->getArea();

				if (area < min_area)
				{
					min_area = area;
					res_matrix = matrix;
					res_envelop = envelop;
				}
			}	

			const std::vector<Coordinate>* envelopcoords = res_envelop->getCoordinates()->toVector();
			assert(envelopcoords->size() == 5);
			for (std::vector<Coordinate>::size_type i = 0; i < 4; i++)
			{
				Coordinate c = envelopcoords->at(i);
				osg::Vec3d v = osg::Matrix::inverse(res_matrix).postMult(osg::Vec3d(c.x, c.y, 0.0));
				result->push_back(v);
			}
			return result.release();
		}

	private:
		pcl::PointCloud<CloudComparePointType>::Ptr _cloud;
		pcl::octree::OctreePointCloudSearch<CloudComparePointType, pcl::octree::OctreeContainerPointIndices, pcl::octree::OctreeContainerPointIndices> octree;
		osg::Vec3d _seed;
		geos::geom::Geometry* geom;
	};
当然,这里是采用半自动化方法.其实,要是点云分类的足够好,比如某一类点云统统都要规则化为矩形,那将该算法稍改一下,利用聚类算法,比如DBSCAN等改造一下,就可以全自动进行了.


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神气爱哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值