栅格数据矢量化(附有完整代码)

简介: 栅格数据矢量化(附有完整代码)

  栅格一矢量数据转换是数据转换的一种方法,即矢量与栅格两种数据形式之间的转换技术。空间数据表示的两种方法各有优缺点和适用场合,因此需要根据使用目的进行栅格一矢量数据的转换。


矢量数据向栅格数据的转换一般比较方便;对于点、线目标,由其所在的栅格行、列数表示,对于面状目标,则需判定落人该面积内的像元.通常栅格(像元)尺寸均大于原来坐标表示的分辨率,所以若将栅格化数据再反转回去,则不可能达到原来矢量数据的精度.将矢量数据转化为栅格数据,主要用于空间分析、多边形叠置等。


       栅格数据矢量化较为复杂,如果由一幅扫描的数字化地图来建立矢量数据库,则需要经过数字图象处理,如边缘增强、细化、二值化、特征提取及模式识别才能获得矢量数据。人们通常将多色地图分色后逐个元素(如等高线地貌、水系、道路网、地物、符号与注记等)加以识别和提取。如果将数字影像矢量化,则需要事先做好重采样、图象处理、影像匹配和影像理解等过程,才能将影像上的语义和非语义信息提取出来,并形成矢量形式的数据。


说明:栅格数据矢量化流程参考:GIS中栅格数据转换矢量数据算法 | 麻辣GIS


一、栅格数据矢量化流程


1、二值化


由于扫描后的图像是以不同灰度级存储的,为了进行栅格数据矢量化的转换,需压缩为两级(0和1),称为二值化。


f57709f5e41ca1b6af1fb0c530ef7d86.jpg


2、二值图像预处理


       对于扫描输入的图幅,由于原稿不干净等原因,总是会出现一些飞白、污点、线划边缘凹凸不平等。

2100b77e43a66a719c8021149908f7af.jpg


3、细化骨架化


       所谓细化就是将二值图像象元阵列逐步剥除轮廓边缘的点,使之成为线划宽度只有一个象元的骨架图形。细化后的图形骨架既保留了原图形的绝大部分特征,又便于下一步的跟踪处理。


细化的基本过程是:

  1. 确定需细化的象元集合;
  2. 移去不是骨架的象元;
  3. 重复,直到仅剩骨架象元;

e220482387164351e1ca6b72d739722b.jpg


如果是对扫描后的地图图像进行细化处理,应符合下列基本要求:

  1. 保持原线划的连续性;
  2. 线宽只为一个象元;
  3. 细划后的骨架应是原线划的中心线;
  4. 保持图形的原有特征;


ab3ebbec30cd23b208e1d7857c681b6a.jpg


4、追踪

       细化后的二值图像形成了骨架图,追踪就是把骨架转换为矢量图形的坐标序列。其基本步骤为:


从左向右,从上向下搜索线划起始点,并记下坐标;

朝该点的8个方向追踪点,若没有,则本条线的追踪结束,转(1)进行下条线的追踪;否则记下坐标;

把搜索点移到新取的点上,转(2);


注意的是,已追踪点应作标记,防止重复追踪。


2d5462ec09334b69569d2caab5968c13.jpg


5、拓扑化


       为了进拓扑化,需找出线的端点和结点,以及孤立点。


  1. 孤立点:8邻城中没有为1的象元。如图(1);
  2. 端点:8邻城中只有一个为1的象元。如图(2);
  3. 结点:8邻城中有三个或三个以上为1的象元。如图(3);


499963d53e32462661e680728e7eb2ee.jpg


二、栅格数据矢量化实验


1、栅格数据矢量化代码


说明:由于代码有点多,如果全部贴上去的话会造成博客有些卡顿(上一篇博客就是放太多代码造成打开时卡顿),所以下面三个源文件都是只放了部分代码,如果需要完整工程,见底部链接!!!


(1)myVector.h

//命名空间只在头文件中包含即可,否则易出错
using namespace std;
using namespace cv;
class myVector
{
public:
  myVector();
  ~myVector();
  //定义结构体,和类的作用类似
  struct result
  {
    QVector<QVector<Point2d> > roads; //Point2d二维数据点类型,用于存放坐标
    QVector<double> width;
    QString ImageFilePath;
    int rows;
    int cols;
  };
public:
  QVector<result> roadInformation;  //保存矢量化后的数据结果,包括新旧影像
  //************************** 道路细化 **************************//
  //对输入图像道路进行细化//
  void imageThin(Mat& src, Mat& dst);
  //道路细化查表法//
  Mat img_bone(Mat& mat);
  //查表法//
  Mat lookUpTable(Mat& mat, int lut[]);
  //************************** 道路矢量化 **************************//
  //利用栅格道路数据创建矢量化道路数据,切割大小为deltaSize
  void  createVectorFromFile(QString inputFileName, int deltaSize);
  //利用栅格道路数据创建矢量化道路数据,不切割//
  void createVectorFromFile(QString inputFileName, QString outputFilePath);
  //道路矢量化//
  void createVector(Mat& binSrc, Mat& mix_src, Mat& endPoint, Mat& nodePoint, int index, bool cut, int startX, int startY);
  //简化矢量道路数据//
  void simplifyVector(int index);
  //简化矢量道路数据//
  QVector<Point2d> simplifyVector(QVector<Point2d> vector, int threshold);
  //输出矢量道路数据//
  void outputBinaryResult(int index, QString outputFilePath, QString str);


(2)myVector.cpp

//************************** 道路矢量化 **************************//
//利用栅格道路数据创建矢量化道路数据,不切割//
void myVector::createVectorFromFile(QString inputFileName, QString outputFilePath)
{
  clock_t start, finish;    //clock_t 是一个长整型数据类型,是一种用于保存时间的数据类型
  start = clock();      //返回值是该程序从启动到clock()函数调用占用CPU的时间,创建矢量化道路的起始时间
  double tim;
  Mat src_img, dst_img, imgbone, src_imgBool, imgbone_bool, imgnode_bool, img_endpoint_bool;
  //判断导入的数据是否存在,参数 0 是指imread按单通道的方式读入图像,即灰白图像
  if ((src_img = imread(inputFileName.toStdString(), 0)).empty())
  {
    cout << "load image error!";
    return;
  }
  result Result;              //result是自定义的类
  Result.ImageFilePath = inputFileName;
  Result.rows = src_img.rows;
  Result.cols = src_img.cols;
  roadInformation.append(Result);     //添加当前影像数据
  /******************** 道路细化 ********************/
  qDebug() << "开始细化..." << endl;
  double thin_start = (double)getTickCount();
  //高斯滤波是一种线性滤波器,能够有效的抑制噪声、平滑图像
  GaussianBlur(src_img, src_img, Size(7, 7), 0, 0);
  //输入图像细化
  imageThin(src_img, src_imgBool);
  threshold(src_imgBool, src_imgBool, 0, 255, THRESH_OTSU);
  imgbone = img_bone(src_imgBool);
  double thin_end = ((double)getTickCount() - thin_start) / getTickFrequency();
  qDebug() << "细化道路数据使用时间:" << thin_end << "s" << endl;
  qDebug() << "细化结束!" << endl;
  //去掉噪,例如过滤很小或很大像素值的图像点
  //threshold(src_img, src_imgBool, 0, 255, THRESH_OTSU);
  //Mat imgbone = img_bone(src_imgBool);
  imwrite(".\\image_save\\roadThin.png", imgbone * 255);
  /******************** 道路矢量化 ********************/
  qDebug() << "开始矢量化..." << endl;
  double change_detection_start = (double)getTickCount();
  threshold(imgbone, imgbone_bool, 0, 255, THRESH_BINARY);
  Mat nodePoint = road_node(imgbone);         //获取弧段连接点
  Mat endPoint = road_endpoint(imgbone);        //获取弧段端点
  Mat mix_node = LineBreak(imgbone_bool, nodePoint);  //对弧段进行打断
  //矢量化
  createVector(src_imgBool, mix_node, endPoint, nodePoint, roadInformation.size() - 1, false, 0, 0);
  double vector_end = ((double)getTickCount() - change_detection_start) / getTickFrequency();
  qDebug() << "矢量化道路数据所用时间:" << vector_end << endl;
}


(3)main.cpp

#include<iostream>
#include"myVector.h"
// 注:main.cpp 文件中不能再添加命名空间,否则会调用自定义成员函数时找不到标识符
int main()
{
  myVector roadThinAndVector;
  roadThinAndVector.createVectorFromFile("..\\testImage\\labels.png", "roadThin.png");  //旧时相影像栅格数据矢量化
  //简化矢量道路数据,也即一些预处理,如:平滑等
  roadThinAndVector.simplifyVector(0);    //旧影像
  //输出矢量道路数据简化后的道路数据
  roadThinAndVector.outputBinaryResult(0, "roadVector.png", ".\\old_vector_result.txt");
  qDebug() << "矢量化结束!" << endl;
  system("pause");
  return 0;
}


2、栅格数据矢量化结果


(1)输入二值图

3423f370b55844068aa5ae119c4a2076.png


(2) 细化、矢量化结果


73a9d3d9f6c04aab88b06b7899924d56.png

107b76738dd54816a38ec6b6b5f5b625.png

说明:左图是细化结果,右图是矢量化结果;


(3)配置说明


vs2019 + opencv4.4.0 + Qt5.12.5 + gdal2.3.2 + Debug x64


vs2019、opencv4.4.0 和 Qt5.12.5 的配置过程比较简单这里就不过多阐述了;


gdal2.3.2 的配置流程见:https://blog.csdn.net/weixin_47156401/article/details/120648970?spm=1001.2014.3001.5502


上述环境配置完成后使用该工程的过程中,若出现以下问题:


如果出现无法打开 Qt 源文件,解决方案见:https://blog.csdn.net/weixin_47156401/article/details/120626400?spm=1001.2014.3001.5502


说明:上述环境配置、问题解决方案都是通过亲测验证,真实有效!!!


代码工程文件中包含二值图细化、矢量化和矢量化简化等工程,且代码注释非常详细,由上述给出的部分代码也可以看出,在使用的过程中如有疑问,可留言!

工程链接:栅格数据矢量化代码实现-C/C++文档类资源-CSDN下载

相关文章
|
数据可视化 定位技术 Sentinel
如何用Google Earth Engine快速、大量下载遥感影像数据?
【2月更文挑战第9天】本文介绍在谷歌地球引擎(Google Earth Engine,GEE)中,批量下载指定时间范围、空间范围的遥感影像数据(包括Landsat、Sentinel等)的方法~
5916 1
如何用Google Earth Engine快速、大量下载遥感影像数据?
|
人工智能 算法 数据可视化
路径规划最全综述+代码+可视化绘图(Dijkstra算法+A*算法+RRT算法等)-2
路径规划最全综述+代码+可视化绘图(Dijkstra算法+A*算法+RRT算法等)-2
|
数据可视化 前端开发 关系型数据库
基于Mybatis-Plus实现Geometry字段在PostGis空间数据库中的使用
本文讲解在mybatis-plus中操作geometry空间字段,同时实现查询和插入操作​。通过geojson,结合前端可视化组件即可完成​矢量数据的空间可视化。
3702 0
基于Mybatis-Plus实现Geometry字段在PostGis空间数据库中的使用
MyBatisPlus+PostGIS实现Geometry数据的通用读写
MyBatisPlus+PostGIS实现Geometry数据的通用读写
1367 0
|
移动开发 Java API
大疆无人机对接
本文介绍了大疆无人机对接第三方云平台的方案,包括设备对接和CloudAPI对接两种方式,重点讨论了CloudAPI对接。CloudAPI对接方案通过DJI Pilot 2或大疆机场将无人机与第三方云平台连接,实现低门槛接入,无需重复开发APP。方案优势在于让开发者更专注于业务开发,而非无人机功能适配。文章详细阐述了对接流程,包括环境准备、申请APPKey、对接流程、直播功能及获取无人机实时数据等内容,并提供了丰富的接口说明和技术支持资源。
10900 4
大疆无人机对接
|
计算机视觉
Opencv学习笔记(三):图像二值化函数cv2.threshold函数详解
这篇文章详细介绍了OpenCV库中的图像二值化函数`cv2.threshold`,包括二值化的概念、常见的阈值类型、函数的参数说明以及通过代码实例展示了如何应用该函数进行图像二值化处理,并展示了运行结果。
4419 0
Opencv学习笔记(三):图像二值化函数cv2.threshold函数详解
|
定位技术
ArcGIS:如何对栅格图像进行地理配准和定义投影?
ArcGIS:如何对栅格图像进行地理配准和定义投影?
6772 1
|
算法 数据可视化 定位技术
QGIS+Conda+jupyter玩转Python GIS
QGIS+Conda+jupyter玩转Python GIS
711 1
|
前端开发 开发者 UED
前端只是切图仔?来学学给开发人看的UI设计
该文章针对前端开发者介绍了UI设计的基本原则与实践技巧,覆盖了布局、色彩理论、字体选择等方面的知识,并提供了设计工具和资源推荐,帮助开发者提升产品的视觉与交互体验。