梁博 曾齐红 刘远刚 邓帆 邵燕林
摘要:针对露头影像裂缝特征识别与矢量化问题,提出基于c++与MATLAB混合编程的裂缝识别及矢量化方法。调用MATLAB中的图像裂缝识别函数快速提取露头裂缝,同时采用一种改进的八邻域追踪算法实现裂缝线状要素的矢量化。实验结果表明,该方法将c++与MATLAB二者优势互补,兼顾了程序的开发效率与执行效率;改进的八邻域追踪算法,在追踪过程中自动简化线要素,既压缩了裂缝矢量数据的存储空间,也提高了八邻域追踪算法的效率。
关键词:混合编程;MATLAB;C++;八邻域追踪;矢栅转换
中图分类号:P208 文献标识码:A 文章编号:1006-8228(2020)07-72-04
0引言
露头作为探索地下矿物油藏、反映地质运动的有利凭证,在地质研究中起到关键性作用,对露头表面裂缝线状要素进行自动识别与矢量化对露头数字模型建立和露头表面裂缝特征分析具有重要意义。MATLAB凭借其强大的数值计算能力被广泛應用于数字图像处理、数值分析、算法开发等众多领域,将MATLAB应用于露头表面裂缝识别,可大大提高开发效率。其作为一门解释性编程语言,MATLAB也存在诸多短板,例如代码执行效率低、源代码保密性较差、界面开发能力较差等。而c++作为一门面向对象的程序设计语言,执行效率较高,程序开发较为灵活且易于维护,但与MATLAB相比在图像处理算法实现上较为复杂,若算法涉及大量的工程计算,仅仅依靠c++进行算法开发显然要繁琐许多,不仅降低了开发效率,而且增加了出错的风险。实际应用中,若能将c++与MATLAB二者优势互补,将大大提高编程效率,同时也保证了程序的执行效率与灵活性。
针对以上问题,笔者提出将c++与MATLAB混合编程应用于露头表面线状要素识别及矢量化方法的研究。在前期研究工作中,已经利用MATLAB开发了露头表面裂缝线状要素识别与提取算法,本文将具体介绍如何利用c++调用MATLAB所实现的算法,提取露头图像中的裂缝特征,并利用c++实现一种改进的线状要素矢量化方法完成裂缝特征的矢量化。
1C++与MATLAB混合编程实现露头裂缝识别
本文采用的露头裂缝识别算法,是一个在MATLAB中封装好的函数——Beamlet函数。研究中,为了实现露头裂缝的识别,采用c++与MATLAB混合编程的方式,将MATLAB中的Beamlet函数发布成C++函数库,在VS2017集成开发环境中,以及OpenCV跨平台视觉库的支持下,通过c++调Beamlet函数。
1.1 MATLAB函数编译成C++库文件
(1)开发环境
本文所有试验均在windows 10操作系统下完成。c++开发环境为Microsoft Visual Smdio 2017,Matlab编程环境为Matlab 2016b,程序中采用的OpenCV版本为3.2.0。
(2)生成C++函数库文件
Matlab编译器使用mcc命令能将一个M文件编译成c++能够调用的头文件及其函数库。因此,采用mcc命令将Matlab中的图像裂缝识别函数(Beamlet函数)编译成供C++调用的函数库。具体过程包括:①首先在Matlab中准备裂缝识别程序,并命名为Beamlet.m,该函数的功能是对露头表面图像进行裂缝线状要素识别,识别结果以栅格数据的形式输出;②打开MATLAB软件的命令窗口,输入mcc命令:mex-setup,在出现的相应界面中点击选项:mex-setupC++;③输入编译命令:mcc-W cpplib:Beamlet-Tlink:lib add,其中“Beamlet”表示想要生成库文件的名称,点击确定,开始编译;④提示编译成功,即成功将M文件编译成c++函数库,其中Beamlet.h、Beamlet.1ib和Beamlet.dll可供c++程序访问。
1.2调用Beamlet算法识别裂缝
(1)配置C++项目
在VS2017中配置vc++项目,为调用图像裂缝线状要素识别函数做准备。具体过程如下:①在VS2017中创建c++项目;②进入项目的属性页,配置vc++目录中的包含目录与库目录,包含目录:[Matlab安装目录]kR2016a\externkinclude;库目录:[Matlab安装目录]kR2016a\extern\lib\win64kmicrosoft;③向项目中添Beamlet.h、Beamlet.1ib、Beamlet.dll这3个文件;④在项目中加载Beamlet.1ib文件,即在项目中需要调用Beamlet算法的文件头部添加命令:#pragma comment(lib,”Beamlet.lib”),链接Beamlet.1ib库文件;⑤添加c+斗头文件,#include,运行测试,完成配置。
(2)调用Beamlet函数
下面重点说明在vc++项目中用c++语句调用MATLAB函数的具体方法,图1为c++调用Beamlet函数方法流程图。
流程图中算法具体步骤如下:
步骤1c++读取图像。采用OpenCV中Mat类读取图像,存入矩阵。
string path=”E:\\data\\1.jpg”;
cv::Mat image=imread(path,1):
//实例化Mat对象image,读取图像存入矩阵image
步骤2设置函数两个输入参数a,b。double a[1]:double b[1]
//用double类型数组a、b存放两个参数
步骤3参数c++类型转化为MATLAB类型。MATLAB函数均是以矩阵的形式进行参数的输入,输出,因此,在调用函数时,需将参数转换为mwArray类型进行传输。
步骤9采用cv::Mat类对象存储Beamlet函数的处理结果。
步骤1 0函数处理结果图像显示。
结果如图2所示。
2改进的八邻域追踪矢量化算法
如图2所示,调用Beamlet算法识别的露头裂缝为一张细化的二值图像,图中裂缝被识别为宽度为一个像素的栅格线。在栅格数据中采用紧密相连的栅格像元来表示线状要素,而在矢量数据中只需记录曲线折点的相应坐标便可以精确的表示线状要素。为了精确表达裂缝特征,需要将栅格图像转为矢量图形。在栅格转矢量的过程中,若完全保留每个栅格像元的坐标,则会造成大量的冗余点,这是算法需要避免的。本文借助C++中的双向队列deque来存储矢量曲线的坐标点,采用一种改进的八邻域追踪算法实现裂缝栅格图像的矢量化,同时尽量压缩矢量图形上的定点数。
2.1八邻域追踪算法基本思路
当进行线状要素追踪时,与当前像素点相邻的像素点则称作该点的8个邻接像素,如图3所示,设定种子点P1左上角点为邻域点1,按顺时针方向将八邻域点分别标号为①、②、…、。八邻域追踪算法基本思路如下:①在二值图像中找到一个裂缝点(图2中白色像素点)作为追踪起点;②按照八方向搜索机制检索相邻裂缝点,当找到相邻裂缝点时,记录下来并将起点从影像中删除(赋值为背景颜色),然后以该邻接点为新的起点继续搜索;③若起点的八方向均未搜索到裂缝点,则终止本次搜索,并转入①,开始下一条线的追踪。
2.2线状要素矢量化实现
本文采用OpenCV中的Mat类对象存储Beamlet函数处理结果,即读取线状要素栅格图像并存入矩阵。设a(i,j)为当前栅格像素(i,j)的灰度值,像素点为白色(a(i,j)=255))或黑色(a(i,j)=0),当前像素(i,j)的3×3邻域矩阵如图3所示。图中点Pl为追踪起点,其像素值a(i,J)=255,该点的①号邻域点P2的值a(i-1,j-1)=255,说明P2是追蹤线上的下一个点。
在八邻域追踪算法的基础上,本文提出了一种改进,改进算法流程见图4。主要步骤包括:①确定栅格影像追踪起点;②从起点开始向八方向搜索相邻点,搜索成功时,将该点赋值为背景色,记录检索方向并保存;③判断此方向与上一点处所保存的方向是否一致,若一致,且此点不为该条线的端点,则舍弃该点继续追踪下一点,否则保留该点,而后将其作为新起点;④若新起点的八方向均未检索成功,表示一条线的搜索已完成,将此线对象保存;⑤搜索另一线对象,直到全部遍历完成。
该算法具有两个优点:①每次成功搜索到新的邻接点时,记录方向,下一次搜索时会率先搜索此方向,这符合普遍规律下线条的延伸状态,大大减少检索的次数,提高效率;②只存储同一条线段的首尾两点,在保留曲线的特征点(折点)的同时实现矢量数据压缩。
最后调用本文实现的矢量化算法将Matlab模块提取的裂缝图像矢量化,并输出为Shapefile格式。如图5所示,为矢量化的裂缝图形与对应的露头图像叠加显示的效果。
3结束语
本文提出基于c++与Matlab混合编程的露头表面裂缝识别与矢量化方法,给出了实验方案的总体设计、实验思路详细分析、算法设计及具体实现,提供了类似问题完整解决方案。实验效果表明:借助c++与Matlab混合编程成功调用Matlab函数,参数的输入与输出主要依赖C++程序实现,后台图像数据的处理与运算主要由Matlab模块函数实现,充分结合了c++与Matlab各自的优点,兼顾了程序的开发效率与执行效率;通过一种改进的八邻域追踪算法对栅格数据进行矢量化,减少在追踪过程中的邻近点搜索次数,提高算法效率同时减少了线状图形中的冗余顶点,实现了结果数据的压缩。