【秣厉科技】LabVIEW工具包——OpenCV 教程(1):Mat 类的基本用法


前言

  1. 需要下载安装OpenCV工具包的朋友,请前往 此处
  2. 系统要求:Windows系统,LabVIEW>=2018,兼容32位和64位。

Mat类的基本用法

Mat即矩阵Matrix,是OpenCV最重要的核心类,通常作为图像数据的容器。
本文将介绍如何在LabVIEW下使用Mat类进行矩阵运算和图像处理。

1. Mat初始化(从 LV 到 Mat)

在LabVIEW中初始化一个Mat类,可以使用函数选板>>Addons>>Molitec>>OpenCV>>core>>Mat>>new.vi ,位置如下图。

在这里插入图片描述

放置一个new到程序框图中,可以发现它是一个多态VI,通过下拉列表查看到,一共有12种初始化模式。
在这里插入图片描述

Mat()创建一个空矩阵
Mat(mat,roi)创建子矩阵,指向另一个已知Mat的矩形ROI区域 ,适用于二维Mat。
Mat(mat,ranges)创建子矩阵,ranges代表每一维度的截取范围 ,适用于多维Mat。
Mat(rows,cols,type,scalar)通过行数、列数、类型、初始值来创建Mat,适用于二维Mat。
type包含数据类型+通道数,Mat中的每一个元素都等于scalar。下同
Mat(rows,cols,type,data,step=0)通过行数、列数、类型、数组来创建Mat,适用于二维Mat。
Mat将依次填入data数组的每一个值,不足时末尾补0。下同
目前限定数组内存连续,所以step永远等于0,直接忽略。下同
Mat(ndims,sizes,type,scalar)通过维数、尺寸、类型、初始值来创建Mat,适用于多维Mat。
sizes数组长度应等于ndims,代表每一维度的尺寸。下同
Mat(ndims,sizes,type,data,step=0)通过维数、尺寸、类型、数组来创建Mat,适用于多维Mat。
zeros(rows,cols,type)创建全0矩阵,参数为行数、列数、类型,适用于二维Mat。
zeros(ndims,sizes,type)创建全0矩阵,参数为维数、尺寸、类型,适用于多维Mat。
ones(rows,cols,type)创建全1矩阵,参数为行数、列数、类型,适用于二维Mat。
ones(ndims,sizes,type)创建全1矩阵,参数为维数、尺寸、类型,适用于多维Mat。
eye(rows,cols,type)创建单位矩阵(全1对角),参数为行数、列数、类型,只能是二维Mat。

例1-1:创建一个2行3列的1通道矩阵,元素类型为8U(8位无符号整数),初始值均为255。

  • 使用 Mat(rows,cols,type,scalar) 模式【左图】与 使用 Mat(ndims,sizes,type,scalar) 模式【右图】
    scalar 作为所有元素的公共初始值,其长度等于通道数。为了能涵盖所有数据类型,LabVIEW参数中的scalar采用最宽泛的double类型,但传入Mat对象时,将自动转换成type指定的数据类型。
    在这里插入图片描述

例1-2:创建一个100行150列3通道矩阵,元素类型为8U,初始值均为[0,128,255] 。

  • 使用 Mat(rows,cols,type,scalar) 模式【左图】与 使用 Mat(ndims,sizes,type,scalar) 模式【右图】
    因为是3通道Mat,所以scalar长度也为3,当表示颜色时,通道顺序为 [B, G, R]

在这里插入图片描述


例1-3:创建一个2行3列1通道矩阵,元素类型为32F(单精度浮点数float),初始值为{{1.1, 2.2, 3.3}, {4.4, 5.5, 6.6}}。

  • 使用 Mat(rows,cols,type,data,step=0) 模式【左图】与 使用 Mat(ndims,sizes,type,data,step=0) 模式【右图】
    data数组作为整体的初始值,依次填入Mat对象。填入顺序是:先通道,再满行,后满列
    同样,data也采用最宽泛的double类型,传入Mat对象时,将自动转换成type指定的数据类型。
    在这里插入图片描述

例1-4:创建一个2行3列3通道矩阵,元素类型8U,初始值{{[11,12,13], [21,22,23], [31,32,33]}, {[111,112,113], [121,122,123], [131,132,133]}}

  • 使用 Mat(rows,cols,type,data,step=0) 模式实现如下。
    此例Mat是3通道,data顺序正如上文提到:先通道,再满行,后满列。同一元素的3通道数据要彼此相邻,当作一个整体。
    当你不想手动输入data时,就可以如下图那样,通过LabVIEW “捆绑簇” 的方式将data单独列出,然后用其他算法产生数据,连接到data上作为变量输入。
    在这里插入图片描述

  • 使用 2D_to_Mat.vi 实现如下。
    由于二维矩阵最常用,所以Mat选板中额外提供一个VI,可以更加直观地将LabVIEW的2D数组,转化成二维Mat对象。2D数组的行,即是Mat的行;2D数组的列,即是Mat的列。多通道数据在2D数组中,同行相邻展开
    在这里插入图片描述

例1-5:创建一个三维1通道矩阵,尺寸4x3x2,元素类型32S(32位有符号整数),初始值:
{ { {1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24} } }

  • 使用 Mat(ndims,sizes,type,data,step=0) 模式实现如下。
    在多维矩阵的情况下,data的排列规律要更广义地理解:最低维度相邻排列,然后向次低纬度延申,再向更高维度延申,依此类推。这是因为矩阵遍历时,是从高纬度向低纬度,层层For循环剥开,最低维度在最后一层For循环。设元素的索引为(i,j,k),定义其“索引权重”为 (i * dim1 + j * dim2 + k),即:在读取它之前已经历的For循环次数。
    从而data的排列规律为:按照“索引权重”从低到高排列。(多通道时,依然是通道最先相邻排列)
    在这里插入图片描述

2. Mat可视化(从 Mat 到 LV)

将一个Mat对象的数据取出,并在LabVIEW中可视化地显示出来,可以使用相同选板下的 to_LV.vi ,位置如下图。

在这里插入图片描述

放置一个to_LV到程序框图中,可以发现它是一个多态VI,通过下拉列表查看到,一共有4种主模式,其中array模式又根据数据类型和维度的不同,细分为21种子模式。
在这里插入图片描述

raw读取原始buffer数据,返回数据类型为bytes数组,小端模式
(不必考虑Mat内存的不连续性,to_LV 已做处理,下同)
common根据Mat数据类型,对raw模式的buffer按照小端模式进行元素切割,排列成一维数组。
同样为了涵盖所有类型,该一维数组统一转化成double类型,相当于Mat初始化时的data数组。
image将Mat数据转化为LabVIEW的图像,仅适用于单通道和3通道,且数据类型为8U
array根据Mat的通道、维数、尺寸,对common模式返回的一维数组进行再切割,转化成LabVIEW中相应的多维数组。
目前每种数据类型下,只提供3种维度(2D、3D、4D),其他维度请选common模式,然后自写算法来切割。
多通道数据,依然是同行相邻展开(沿最低维度展开)

例2-1:把一个二维8U的Mat,读取成LabVIEW的2D数组

  • 如下图,上方是1通道,下方是3通道。注意data数组长度小于Mat容量时,末尾自动补零。
    最后调用release.vi 释放Mat的内存,该VI位于Mat选板最末。

在这里插入图片描述


例2-2:把一个未知数据类型的二维Mat,读取成LabVIEW的2D数组

  • 使用common模式读取,然后根据返回的 properties 确定Mat的通道和尺寸,对 double_1d 进行reshape。
    在这里插入图片描述

  • 上例中出现了Mat属性(properties)这个变量,是伴随 to_LV 一起返回的。除此之外,我们还可以单独获取Mat的某些属性,作为后续算法处理的前提条件。方法是使用“属性节点”,如下图。属性节点是LabVIEW自带的,位于Application Control选板。

在这里插入图片描述


例2-3:创建一张纯色图片,并用LabVIEW的2D图片控件显示。然后修改ROI区域,变成另一种颜色,再显示。

  • 注意,通过ROI创建的子矩阵,其内存直接指向父矩阵的某一块区域,而不是拷贝到新的内存空间(除非经过深拷贝,如clone)。所以当子矩阵被修改时,父矩阵也同样被修改。
  • 父矩阵和子矩阵必须全部release之后,内存才会真正被释放。只release其中一个,另一个依然在使用中。
  • setTo.vi 同样位于Mat选板,作用是将矩阵的元素统一重置为输入的scalar数值。
    在这里插入图片描述

3. 矩阵运算

3.1 操作符

  • 在Mat选板中,第一项是一个叫operator的子目录,里面包含多种运算符号,包括加、减、乘、除,逻辑与、或、非、异或,以及各种比较运算符。

在这里插入图片描述

  • 利用这些VI,可以实现矩阵的基础运算。其逻辑相当于在C++编程中,直接用运算符号,连接两个对象
  • 连接的两个对象,可以都是Mat,也可以一个是Mat,另一个是元素(scalar),通过多态VI来切换模式。

在这里插入图片描述


3.2 内置函数

Mat选板下的所有VI,其图标设计与配色风格一致,都属于Mat类的内置函数。
这些函数功能包括:

函数功能
create(对已有Mat)重新定义类型、尺寸,并分配存储空间
convertTo转换Mat数据类型,并可自定义线性变换
copyTo(浅)拷贝
clone(深)拷贝
col取源Mat的某一列,作为子矩阵输出(该子矩阵仍是二维,下同
colRange按照range范围,截取源Mat的某些列,作为子矩阵输出
row取源Mat的某一行,作为子矩阵输出
rowRange按照range范围,截取源Mat的某些行,作为子矩阵输出
diag创建对角矩阵,或获取矩阵对角向量
at按照坐标索引,读取或写入Mat中的某一元素
checkVector检查输入的Mat是否为向量(行数、列数,至少有一个等于1)
dot计算两个向量的内积(数量积)
cross计算两个向量的外积(向量积、叉积)
mul两个尺寸相同的Mat,对应元素相乘,得到同样尺寸的乘积矩阵。并非“矩阵乘法”
operator(*)顺便一提,上文提到的operator目录下的 * 符号,用于连接两个Mat时,才是真正的“矩阵乘法”
然而,下文中core目录下的multiply函数,其功能与内置函数mul一样,也不是“矩阵乘法”
adjustROI调整ROI区域(适用于子矩阵)
locateROI获取当前ROI位置(适用于子矩阵)
push_back在向量末尾追加元素,或在Mat的末尾追加另一个列数相同的Mat,作为拓展的行
pop_back删除向量末尾的n个元素,或删除Mat末尾的n个行
resize修改Mat行数。行数减少时元素被释放,行数增加时新元素被写入指定的初始值
reshape在数据不变的前提下,重新切割Mat的通道、尺寸
reserve保留一定数量的行空间,确保不会在resize等操作时被删除,下同
reserveBuffer保留一定数量的buffer空间
inv求逆矩阵
t求转置矩阵
setTo重置Mat元素为统一初始值

具体用法不一一列举了。下图是一个范例,从创建的Mat中取出2列,求内积。然后用其中一列作为对角元素,生成对角阵,再求该对角阵的逆矩阵。(更多用法,请查找工具包附带的范例:examples\Molitec\OpenCV\core\Mat…)

在这里插入图片描述


3.3 外部函数

由于Mat是OpenCV最重要的核心类型,所以几乎所有模块下的函数,都需要Mat类型的变量作为输入输出。
其中,对矩阵计算支持最多,数量最庞大的,要数 core 模块。其函数选板如下图(节选):

在这里插入图片描述

注意,虽然core选板下也有加、减、乘、除、逻辑、比较运算符,但与上文operator目录下的运算符用法不尽相同。最主要的区别是,core下的运算函数,多了一个Mask输入量,用以定义掩膜区域。另外,multiply函数的功能与Mat内置的mul一样,与 operator(*) 不一样。

下面是一个范例,计算一个输入Mat的平方、开方、自然底数幂、自然对数。(更多范例请查找:examples\Molitec\OpenCV\core…)

在这里插入图片描述


4. 图像容器

4.1 读取图片

在OpenCV中,对图像的处理,都是基于对矩阵的运算来实现的。因此Mat类经常作为承载图像的容器。
下图中展示的是:读取彩色图片文件到Mat对象,并转为灰度图。imread.vi 位于imgcodes模块,cvtColor.vi 位于imgproc模块。

在这里插入图片描述


4.2 读取视频流

从摄像头、视频文件、URL等读取一帧图像,也是存放在Mat对象中。
下图中展示的是:连续读取摄像头画面,并连续显示。VideoCapture类位于videoio模块。
作为容器的Mat只需初始化一次(空矩阵),进入read.vi后将自动分配空间。每次循环读取的图像,都会写入同一个Mat对象中,避免占用过多内存。

在这里插入图片描述


不要忘记,LabVIEW本身也很强大

通过上文的 “Mat初始化” 和 “Mat可视化” ,我们已经打通了LabVIEW的数组与OpenCV的Mat之间互相转化的渠道。既然如此,很多关于矩阵的计算,我们就不必拘泥于非要用OpenCV的接口来实现。毕竟LabVIEW在矩阵(数组)计算方面也足够强大。

比如:通过 to_LV.vi 从Mat中获取到数组之后,接下来完全用LabVIEW来完成对该数组的自定义处理。如果有需要,再将LabVIEW计算之后的结果,重新转化成Mat对象。

下图中展示的是:读取一个二维Mat到2D数组,然后用LabVIEW做QR分解,得到一个正交矩阵(Q)和一个上三角矩阵(R),最后再分别用Q、R 新建Mat对象。

在这里插入图片描述


总结

  1. 本系列博文作为LabVIEW工具包—OpenCV的教程,将以专栏的形式陆续发布和更新。
  2. 对工具包感兴趣的朋友,欢迎下载试用:秣厉科技 - LabVIEW工具包 - OpenCV
  3. 各位看官有什么想法、建议、吐槽、批评,或新奇的需求,也欢迎留言讨论。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

秣厉科技

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

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

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

打赏作者

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

抵扣说明:

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

余额充值