基于Labview的图像获取.docx
《基于Labview的图像获取.docx》由会员分享,可在线阅读,更多相关《基于Labview的图像获取.docx(19页珍藏版)》请在冰点文库上搜索。
![基于Labview的图像获取.docx](https://file1.bingdoc.com/fileroot1/2023-6/4/4bd2e34a-755e-4113-a1c2-a52f1002c5b5/4bd2e34a-755e-4113-a1c2-a52f1002c5b51.gif)
基于Labview的图像获取
基于Labview的图像获取
本文详细陈述了基于Labview软件平台下,以常见的USB接口摄像头为图像获取设备,设计开发的图像实时获取并简单处理储存的虚拟仪器软件系统。
通过运用功能强大的视觉运动模块IMAQ,以图像化的程序语言完成图像获取与存储的编写。
完整的图像获取软件通过调用动态链接库(DLL),进而驱动USB接口摄像头进行所需的图像获取过程,同时将获取的的图像进行简单的处理并存储在相应的文件中。
另外,我们可以通过labview的前面板实时观察图像获取的情况并展示。
该图像获取软件系统克服了通用性差、开发时间长等缺点,具有可靠性强、灵活性高、开发门槛低、优秀的性价比等诸多优点。
关键词:
Labview平台;USB摄像头;IMAQ;图像获取
2.3LabVIEW中的程序结构介绍5
第三章图像获取程序中子VI的介绍8
第一章引言
Labview(LaboratoryVirtualInstrumentEngineeringWorkbench)是美国国家仪器公司(NationalInstrumentsCorporation)开发的实验室虚拟仪器集成环境。
相比较其它语言,labview使用图形化编辑语言G以框图流程形式编写程序,而非基于文本语言。
Labview的编程方式是通过数据流实现,连线表示数据流向,函数用编程图形来表示。
Labview直观的图形化环境和强大的图形化编程语言使程序开发更快捷,极大地提高了从事院校教学、科学研究和实验的效率,成为了科研与教学的重要助手。
图像是人类通过视觉来感知外界事物,在人脑意识中形成的客观存在,是人类获取外部信息的重要来源。
获取图像是各个技术领域一个重要的基础环节,USB类型接口的摄像头是一种相当便携、高速的图像获取设备,目前正被越来越广泛的运用。
另外一方面,图像获取系统程序存在开发周期长、开发门槛高等缺点。
因此,在现有资源的基础上,设计出一款低成本、简单通用的摄像头图像获取程序系统是十分必要的。
由NI公司推出的机器视觉开发软件——视觉开发模块包含了NIVisionBuilder和IMAQVision两个部分。
关于图像获取,其中的图像处理工具包(IMAQ)具有的强大的图像采集与处理能力,它将400多种函数集成到Labview的开发环境中并拥有交互式图像处理窗口。
在处理1维、2维、3维图像上IMQA有自己的优势。
基于IMAQ所设计的图像获取系统软件拥有高效、快捷、简便的特点。
同时labview图形化语言的设计使所有用户摆脱学习各种仪器低级编程协议的难点,由此来简化控制仪器的繁琐程度,克服图像获取系统开发时间长及高门槛等缺点。
图像获取所采用的是通用的USB摄像头作为工作设备,但是USB摄像头并非是NI设备。
利用labview平台开发USB摄像头设备的应用程序,我们采用labview中提供的动态链接库来实现软件与USB摄像头之间的通讯,由此来开发图像获取系统。
第二章图像获取程序基本函数介绍
2.1调用库函数节点函数(callliabraryfunctionnode)
在labview中开发非NI设备应用程序通过调用动态库连接来实现对设备的驱动。
实际上,在labview中有3种手段开发驱动设备的程序,它们分别是:
直接对设备的端口进行读写、通过代码接口节点调用由C语言编写的程序和通过调用库函数节点函数(CLF)来调用动态库连接函数(DLL)。
我们采用动态库链接机制,如此便可增强LabVIEW程序接口驱动能力、模块化设计和节省内存空间。
图2.1调用库函数节点函数
2.2IMAQVision中子VI基本介绍
本文介绍的图像获取程序使用的LabVIEW8.6版的IMAQUSB模块,该函数在LabVIEW8.6以后的版本中一杯imaqdx模块替代。
因此,我们将程序中使用到IMAQ函数模块的功能进行介绍。
2.2.1IMAQUSBGrabSetup.vi
初始化USB摄像头获取图像设置。
2.2.2IMAQCreate.vi
IMAQ图像需要首先在PC内存中创建图像缓冲区。
每个缓冲区有唯一的名称,在LabVIEW中通过指针来访问这些缓冲区。
因此LabVIEW中的图像数据类型实际是对存储图像的一个特定内存缓冲区的引用。
这种机制的原因在于:
相比较其他数据类型,图像数据类型通常会占据较大的内存空间。
有了一个预先分配好并且可以重复使用的内存空间,就可以避免对同一图像每一步处理之后都要重新分配内存。
该函数达到的目的为获取的图像创建内存缓冲区。
图2.2IMAQCreate.vi
2.2.3IMAQDispose.vi
删除图像数据并清空内存,为下一次图像获取数据腾出内存位置。
2.2.4IMAQUSBStop.vi
将USB摄像头停止工作,不再获取图像。
2.2.5IMAQUSBClose.vi
关闭USB摄像头,停止对摄像头的占用。
2.2.6IMAQUSBEnumerateCameras.vi
检测USB接口是否已经USB摄像头设备。
如若未连接USB摄像头设备,则该函数返回值为空;若已连接USB摄像头设备,则函数返回值为USB摄像头名称。
2.2.7IMQAUSBInit.vi
确定USB摄像头的接入。
当有USB摄像头接入时,则输出值为USB摄像头的文件路径。
2.2.8IMAQUSBPropertyPage.vi
对USB摄像头的功能参数进行设置,以确定USB摄像头设备将要使用的功能将是拍照或者是录像。
2.2.9IMAQAVIGetFilterNames.vi
找到电脑上安装的AVI兼容过滤器,每个过滤器都有不同的压缩质量、规模及时间。
2.2.10IMAQAVICreate.vi
创建一个新的AVI文件或替换一个旧的AVI文件
图2.3IMAQAVICreate.vi
2.2.11IMAQUSBGrabAcquire.vi
调用USB摄像头快速完成图像数据获取。
2.2.12IMAQAdd.vi
将两份图像数据ImageA和ImageB叠加在ImageDst,并将其输出。
图2.4IMAQAdd.vi
2.3LabVIEW中的程序结构介绍
2.3.1While循环
While循环在Labview中是一个大小可变的方框,执行框中的程序直到条件端子接收到的布尔值为F值。
While循环至少要运行一次且计数从0开始。
图2.5While循环
2.3.2For循环
和While循环一样For循环也是一个方框,For循环将制定框图中程序循环次数。
因此For循环中没有条件端子而有计数端子,用于制定循环次数。
2.3.3位移寄存器
移位寄存器可以将数据从一个循环周期传递到另外一个周期。
移位寄存器在流程图上用在循环边框上相应的一对端子来表示。
右边的端子中存储了一个周期完成后的数据,这些数据在这个周期完成之后将被转移到左边的端子,赋给下一个周期。
移位寄存器可以转移各种类型的数据--数值、布尔数、数组、字符串等等。
它会自动适应与它连接的第一个对象的数据类型。
2.3.4条件结构(Case结构)
Case结构含有两个或者更多的子程序(Case),执行那一个取决于与选择端子或者选择对象的外部接口相连接的某个整数、布尔数、字符串或者标识的值。
图2.6Case结构
2.3.5平铺式顺序结构
包括一个或多个顺序执行的子程序框图或帧。
平铺式顺序结构可确保子程序框图按一定顺序执行。
平铺式顺序结构的数据流不同于其它结构的数据流。
所有连线至帧的数据都可用时,平铺式顺序结构的帧按照从左至右的顺序执行。
每帧执行完毕后会将数据至传递至下一帧。
即帧的输入可能取决于另一个帧的输出。
图2.7平铺式顺序结构
2.3.6事件结构
事件结构包括一个或多个子程序框图或事件分支,结构执行时,仅有一个子程序框图或分支在执行。
事件结构可等待直至事件发生,并执行相应条件分支,处理该事件。
时间输出对应于使用的控制事件。
右键单击结构边框,可添加新的分支并配置要处理的事件。
连线事件结构边框左上角的“超时”接线端,指定事件结构等待事件发生的时间,以毫秒为单位。
默认值为–1,即永不超时。
事件数据节点位于每个事件分支结构的左边框内侧。
该节点用于识别事件发生时Labview返回的数据。
依据为各事件分支配置的事件,该节点可显示事件结构每个分支中不同的数据。
如配置单个分支处理多个事件,只有所有事件类型支持的数据才可用。
图2.8事件结构
第三章图像获取程序中子VI的介绍
在正式开发图像获取系统软件前,我们可以事先编写完整几个重要的子VI,以便在主VI中可以顺利调用。
另外子VI虽不似主VI般繁琐,但在整个图像获取系统中发挥必不可少的作用。
3.1InitCam.vi
利用调用“IMQAEnumerateCameras.vi”来检测USB接口是否已经USB摄像头设备。
如若未连接USB摄像头设备,则该函数返回值为空;若已连接USB摄像头设备,则函数返回值为USB摄像头名称。
在“IMQAEnumerateCameras.vi”子VI输出连接到“索引数组”函数上,“索引数组”的索引值为0,索引的返回值分别接入比较函数“等于?
”和索引隧道。
比较函数“等于?
”的下端接入空字符串,当程序未检测到USB摄像头接入时,由于子“IMQAEnumerateCameras.vi”返回值为空,通过索引数组改变为标量,则可以与空字符串通过比较函数“等于?
”相比较,此时比较函数返回值为T;当程序检测到USB摄像头已接入准备待续,则比较函数返回值为F。
随后我们将用到一个Case结构,比较函数“等于?
”的返回值接入条件子端。
当比较函数返回值为T,但按钮对话框显示“Error”,并且将T值经过索引隧道发送给名为“Error”的指示灯显示控件,另外将一个空文件经过索引隧道传递给名为“DirectShowCameraRefNum”的控件。
“DirectShowCameraRefNum”控件起到一个类似于指针的作用,利用这个控件可以在各个子VI间对其它控件进行属性操作;当比较函数返回值为F,我们将F值经过索引隧道同样发送给指示灯控件,同时将“索引数组”连接到索引隧道上的返回值连接到名为“IMQAUSBInit.vi”的子VI上,同时连入的还有常数值0。
之后“IMQAUSBInit.vi”路径输入连接另一个子VI“IMAQUSBPropertyPage.vi”的左端,同时连入的还有数值常量“ImageSettings”。
最后该VI的右端经过索引隧道同样连接到了“DirectShowCameraRefNum”上。
归纳“InitCam.vi”子VI的作用是:
检测是否已连接USB摄像头,若已连接则确定摄像头的名称位置,输出F值,并做好获取图像的拍摄准备;反之则发出错误指示,输出T值。
3.2InitAVI.vi
当前VI访问的文件路径经过拆分路径函数连入罗列文件夹函数,拆分路径函数在此处所起的作用是当输入为非法路径或者空文件,则返回值为空字符。
罗列函数文件夹函数将文件路径接入Case结构左端的索引隧道,并将文件夹名以字符串形式连接搜索一维数组函数的左端,在搜索一位数组函数接入元素字符串“SettingFiles”。
当未找到元素时,搜索一位数组函数元素索引为-1。
元素索引以常数值-1连接比较函数“等于?
”,比价函数的返回值接入Case结构的条件子端。
我们所用到的case结构,当比较函数返回值为T,即子VI当前访问的文件路径为空,case结构执行T页面程序。
文件路径经索引隧道连入创建路径函数,连入的名称或相对路径为“SettingFiles”字符串。
创建路径函数创建出的新路径连入创建文件夹函数,在新路径下创建新文件夹,其右端文件新路径连入Case结构右端的索引隧道;当比较函数返回值为F时,只需将文件路径和“SettingFiles”字符串连入创建路径函数,创建路径函数创建新路径连入Case结构右端的索引隧道。
利用Case结构我们将当前VI访问路径是否为空做出应对,并创建所需的新路径。
文件路径从Case结构的右端的索引隧道连入创建路径函数,同时连入“temp.avi”字符串。
创建路径函数引出的文件路径引入打开/创建/替换文件函数,该函数的操作是“replaceorcreate”,即替换旧文件或者创建新文件,函数的引用句柄输出连入关闭文件函数,关闭文件函数输出的文件路径连接到名为“AVIpath”的显示控件上。
利用“IMAQAVIGetFilterNamesVI”子VI来找到安装在电脑上的找到兼容的AVI压缩过滤器。
该子VI输出的过滤器名称连入索引数组函数,索引为2维数组。
索引数组函数输出元素连接子VI“IMAQAVICreateVI”,同时将关闭文件函数输出的文件路径引入子VI“IMAQAVICreateVI”,每秒最大帧数设置为30帧,接入常数值30即可。
然后将T值连入子VI“IMAQAVICreateVI”,将其设置应用于我们的图像获取。
子VI“IMAQAVICreateVI”创建的AVI文件引用连入“AviRefnum”。
整个“InitAVI.vi”检测了当前文件路径,并创建或替换了文件路径与文件。
另外,创建或替换了AVI文件并设定了图像获取的最大帧数。
图3.1InitAVI.vi
3.3SaveAVI.vi
首先我们放置一个“ExpressVI”显示文本对话框来确定文件路径,开始路径为当前VI访问路径,类型为“*.avi”,类型标签为“VideoFile”。
将文本对话框的取消端连入Case结构的条件端子。
当程序运行过程中我们取消了跳出的文本对话框,取消参数则输出T值,程序发生错误,我们将文本对话框的错误输出端经索引隧道连入Case结构中的“清除错误VI”来忽略这个错误;当我们选择了文件路径,取消参数输出F值,此时Case结构运行F值情况。
由输入的文件路径“Tempfilepath”作为源路径,已经文件对话框所选的输出路径作为目标路径,连接到Case结构中的复值函数中,并输入T值确认用源路径下的文件来替换所选路径中的文件。
我们所用的子VI“SaveAVI.vi”可以将所获取的图像保存到我们指定路径的文件夹里。
图3.2SaveAVI.vi
3.4ReadDataCam.vi
我们所需要的这个子VI“ReadDataCam.vi”通过一个简单移动的For循环语句来完成。
运用该子VI目的是为了快速获取每帧的图像,并对获取的图像我们所需要曝光时间的图像。
首先我们来确定For循环的循环次数,这取决我们所需要的曝光时间,故通过连入一个数值输入控件来决定循环次数。
我们将第一次使用位移寄存器来完成对循环的编写。
将图像数据“SummImageIn”引入循环左端的位移寄存器,另外同样的“DstImageIn”与“CamImageIn”分别连入循环左端的索引隧道。
我们把USB的路径“IMAQUSBSessionIn”和图像数据“CamImageIn”分别接入子VI“IMQAUSBGrabAcquire.vi”的左端。
通过使用子VI“IMQAUSBGrabAcquire.vi”来驱动摄像头快速采集图像数据,即完成了图像获取过程。
该VI的输出分别连接元素入队列函数、子VI“IMAQAddVI”的第二图像源以及位于循环外的图像数据输出“CamImageOut”。
元素入队列函数连接获取队列引用函数,目的是在主VI中能够顺利调用此VI中的图像数据。
接下来,将For循环左端的位移寄存器与“DstImageIn”连入子VI“IMQAUSBGrabAcquire.vi”的左端,用子VI“IMAQAddVI”达到将两张图像数据叠加成一张图像的作用,并将新的图像输出连入循环右端的位移寄存器中,然后输出到“DstImageOut”中。
在循环左端的位移寄存器需将图像数据输出到循环外的“SummImageOut”中。
就这样,我们通过For循环的位移寄存器和子VI“IMAQAddVI”将图像完成叠加,即获取我们所需的帧数。
如果我们仅需一帧的曝光时间,获取的图像“CamImage”会与空图像数据“SummImage”叠加并存储在“DstImage”中;如果需要两帧的曝光时间,则在一帧基础上将循环再运行一次,即把新获取的“CamImage”与位移寄存器中的“DstImage”叠加得到新的图像数据“DstImage”;以此类推,我们就完成了对图像获取与简单的处理。
图3.3ReadDataCam.vi
第四章图像获取主VI介绍
4.1检测USB摄像头的接入
通过While循环和Case结构来完成对是否已接入USB摄像头的检测。
4.1.1While循环语句
由子VI“InitCam.vi”引出的USB摄像头路径与逻辑输出分别进入While循环左端的索引隧道。
在While循环中有一个名为“Run”的布尔开关和一个“Exit”的停止按钮。
而While循环的停止条件将由子循环提供,我们在后面提及。
由于当USB摄像头接入时子VI“InitCam.vi”输出为F,为方便起见把“InitCam.vi”的逻辑输出连入非门,此时,当检测到USB摄像头时非门返回值为T。
然后,把非门的返回值连入下一个Case结构的条件子端。
4.1.2Case结构语句
当子VI“InitCam.vi”并未检测到USB摄像头接入时,非门返回值为F。
在F值情况下,Case结构中发送T常量值给While循环中条件端子,由条件端子T值是停止结束While循环。
另外,子VI“InitCam.vi”的文件路径输出与错误输出直接连接到整个VI最末端的“IMQAUSBClose.vi”上,关闭USB模块结束程序。
相反的,当检测到USB摄像头接入时,Case结构执行T值情况。
在T值情况框图中是一个事件结构。
4.2程序运行部分
图像获取过程程序会有三个不同的事件分支,我们使用事件结构对不同情况进行图形语言编写。
4.2.1事件结构的内容介绍
在我们选用的层叠式结构中共有三个子程序框图分别是“Run”、“超时”和“Exit”。
由于三个子程序的复杂情况各有不同,我们用倒叙从简单开始介绍。
当“Exit”的值发生改变,即在前面板中按下名为“Exit”的停止按钮,此时流程图与Case结构语句F值情况相同:
发送T常量值给While循环中条件端子,由条件端子T值是停止结束While循环。
“InitCam.vi”的文件路径输出与错误输出直接连接到整个VI最末端的“IMQAUSBClose.vi”上,关闭USB模块结束程序。
当发生“超时”情况时,则禁用前面板的输入控件包括:
“Run”、“Exit”、“Numberofframestoacquire”、“Expositiontimeperframe(1/30s)”。
并且发送F值给外围While循环的条件子端即While循环将继续循环运行。
图4.1“超时”
当“Run”的只发生改变,图像获取软件进入工作状态。
4.2.2图像获取过程介绍
在“Run”只改变的情况中,我们需要编写一个记录每帧图像获取的文件。
我们输入数值常数控件来作为所创文件名称,通过一个数值至十进制数字符串转换函数将数值转换为字符串,并与字符串“.txt”利用连接字符串函数得到文件名称。
接下来将我们选定的路径作为基路径,连入文件名称,用一个创建路径来创建我们所需的文件路径。
随后,就可以把创建的文件路径连入打开/替换/创建文件函数用来替换旧文件或者创建新文件。
该函数的操作是“replaceorcreate”,提示是字符串“0”,输出的引用句柄进入子While循环的索引隧道。
我们介绍的第二个While循环是图像获取系统的主体部分,大致可以分为三个部分。
最重要的一部分便是图像获取。
在前面介绍“ReadDataCam.vi”子VI中可知我们需要创建三个分别名为“Sunmm”、“Dst”、“Cam”的图像缓存位置,并将三张图像的图像类型设置为“RGB(U32)”。
然后,运用子VI“ReadDataCam.vi”,连入三份图像数据内存位置,加上输入我们所需的曝光时间“Expositiontimeperframe(1/30s)”便可得到获取的图像数据“DstImage”和每一帧获取的图像数据“CamImage”。
获取的“DstImage”图像数据连接入“IMAQAVIWriteFrameVI”,并连入与图像相关的时间数据(字符串),以及由子VI“InitAVI.vi”创建的AVI文件路径。
这样我们将获取的图像存入了相应的AVI文件中。
我们获得的图像数据进行存储于显示后,为了下一次运行必须将相应的内存位置清空。
所以我们将“ReadDataCam.vi”输出的三份图像数据,分别连入一个简单Case结构中的子VI“IMAQDisposeVI”来清空相应位置的内存。
在这里我们使用的Case结构:
当为T值,清空三份图像数据内存;当为F值,清空三份图像数据内存,并将“IMAQAVIWriteFrameVI”的文件输出路径连入“IMAQAVICloseVI”来关闭已写入图像的AVI文件。
Case结构的条件端子我们会在后面提到。
图4.2图像获取
在While循环中的另一部分是循环运行时间的记录与控制。
我们利用两个时间计数器函数,一个放置在循环左端外,另一个放置与While循环内。
将两个时间计数器通过减函数相减等到While循环的运行时间。
由于While循环可能不止运行一次,我们需要在循环开始时对两个时间计数器进行同步。
在循环内的时间计数器将计数值输入转换为双精度浮点数函数,输出的双精度浮点数与1000用除函数相除即将毫秒单位转化为秒单位,再将返回值连入名为“synctime(s)”的显示控件。
在While循环外部的计时器将通过一个局部变量对“synctime(s)”进行读取操作,从而完成时间同步。
我们将“synctime(s)”这个输出时间刻度用数值至数字字符串转换函数转换成字符串,再用搜索替换字符串用“.”来替换“,”。
然后,利用连接字符串函数把搜索替换好的字符串、空字符串和行结束常量连接起来,这样做的目的是将每个时间刻度数值分行。
最后,连接的字符串输入写入文本函数,文件路径来自在进入循环前就编写好的打开/替换/创建文件函数。
写入文本后,该函数引用句柄输出连接循环右端索引隧道,连接循环外的关闭文件函数。
在循环中的时间计数器输出的毫秒计时值还有另一个作用,连入数值至十进制数字符串转换函数转换为字符串型十进制数。
该十进制数连入所介绍上一部分中的“IMAQAVIWriteFrameVI”作为数值信息,与获取图像一起写入AVI文件。
图表4.3循环时间
该While循环第三部分是对循环运行次数与停止条件的编写。
把循环变量i连入显示控件“Numberofusedframes”作为使用过的帧数。
并将循环变量i与我们限定的帧数“Numberofframestoacquire”通过比较函数“大于等于?
”相比较,比较函数的返回值接入非门。
我们得到:
用过帧数小于限定帧数,非门输出T值;用过的帧数大于等于限定帧数是,非门输出F值。
该非门返回值一方面连入我们介绍第一个部分中Case结构的条件端子,即当用过的帧数大于等于限定帧数时,关闭AVI文件。
非门返回值另一方面连接一个与门,与门另一个输入端来自于另一个非门。
另一个非门输入连接在一个名为