版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。
EmguCV学习笔记目录 Vb.net
EmguCV学习笔记目录 C#
笔者的博客网址:VB.Net-CSDN博客
教程相关说明以及如何获得pdf教程和代码(博客上的教程内容会和pdf教程一致,教程中也会包含所有代码),请移步:EmguCV学习笔记
2.3 Mat类
Mat类也是OpenCV中的矩阵类,EmguCV对其进行了封装。Mat类提供了图像数据读取、保存方法,提供矩阵运算方法,如矩阵加减、乘法、转置等,如等。Mat类是EmguCV中处理图像的最常用和最重要的类,需要好好掌握。
Mat类常用属性:
- Rows和Cols:分别表示矩阵的行数和列数。可以通过这两个属性来获取矩阵的大小。
- Step:表示矩阵每一行的字节数。这个属性可以用于计算矩阵中某个元素的地址。
- Data:表示矩阵数据的指针。这个指针指向矩阵数据的第一个元素,可以通过它来访问矩阵中的每个元素。
- Depth:这是一个DepthType枚举,表示矩阵中每个元素的深度,即数据类型的位数。例如,CV_8U表示每个元素是8位无符号整数。
DepthType枚举元素主要有:
元素名称 | 元素值 | 占用位数 | 说明 |
Cv8U | 0 | 8位 | 无符号整数 Byte |
Cv8S | 1 | 8位 | 符号整数 SByte |
Cv16U | 2 | 16位 | 无符号整数 UInt16 |
Cv16S | 3 | 16位 | 符号整数 Int16 |
Cv32S | 4 | 32位 | 无符号整数 Int32 |
Cv32F | 5 | 32位 | 浮点数 Single |
Cv64F | 6 | 64位 | 浮点数 Double |
注意:在OpenCV中使用CV_8UC1这样的类型,表示8位无符号单通道矩阵。C后面的数字表示通道数。
- NumberOfChannels:表示矩阵中每个元素的通道数。例如,3通道的CV_8U通常表示每个元素有3个通道,分别表示红、绿、蓝三个颜色通道。
- IsContinuous:表示矩阵是否是连续的。如果矩阵的所有元素在内存中是连续存储的,那么这个属性的值为true,否则为false。
- Size:表示矩阵的大小,即行数和列数。这个属性返回一个Size类型的对象,可以通过它来获取矩阵的行数和列数。
- Width和Height:分别表示矩阵的宽度和高度。可以通过这两个属性来获取矩阵的大小。
- ElementSize:表示矩阵中每个元素的大小,即字节数。这个属性等价于Depth属性。
不同通道数、不同深度的Mat对应的数据结构如下面的图示:
图2-27 Rows=4、Cols=3、CV8U、单通道Mat数据的结构
图2-28 Rows=4、Cols=3、CV8U、三通道Mat数据的结构
图2-29 Rows=4、Cols=3、CV32S、三通道Mat的数据结构
2.3.1 构造函数
1、使用行数、列数、深度、通道数初始化Mat
【代码位置:frmChapter2_2_1】Button1_Click、showMatInfo
'构造函数1:使用行数、列数、深度、通道数初始化Mat
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'指定行数4、列数3,无符号整数 Byte,通道数1
Dim m8U1 As New Mat(4, 3, CvEnum.DepthType.Cv8U, 1)
'将值设置为255
m8U1.SetTo(New MCvScalar(255))
'输出Mat信息
showMatInfo(m8U1, "m8U1")
TextBox1.Text &= "============" & vbCrLf
'指定行数4、列数3,无符号整数 Byte,通道数3
Dim m8U3 As New Mat(4, 3, CvEnum.DepthType.Cv8U, 3)
m8U3.SetTo(New MCvScalar(200, 100, 0))
showMatInfo(m8U3, "m8U3")
TextBox1.Text &= "============" & vbCrLf
'指定行数4、列数3,无符号整数 Int32,通道数3
Dim m32S3 As New Mat(4, 3, CvEnum.DepthType.Cv32S, 3)
m32S3.SetTo(New MCvScalar(200, 100, 0))
showMatInfo(m32S3, "m32S3")
End Sub
'此方法输出Mat的信息
Private Sub showMatInfo(ByVal m As Mat, ByVal strTypeName As String)
TextBox1.Text &= "strTypeName:" & strTypeName & vbCrLf
TextBox1.Text &= "Cols:" & m.Cols & vbCrLf
TextBox1.Text &= "Rows:" & m.Rows & vbCrLf
TextBox1.Text &= "Depth:" & m.Depth & vbCrLf
TextBox1.Text &= "ElementSize:" & m.ElementSize & vbCrLf
TextBox1.Text &= "Height:" & m.Height & vbCrLf
TextBox1.Text &= "Width:" & m.Width & vbCrLf
TextBox1.Text &= "NumberOfChannels:" & m.NumberOfChannels & vbCrLf
TextBox1.Text &= "Step:" & m.Step & vbCrLf
TextBox1.Text &= "Width*Height:" & m.Size.Width & "*" & m.Size.Height & vbCrLf
TextBox1.Text &= "IsContinuous:" & m.IsContinuous & vbCrLf
End Sub
显示结果如下:
图2-30 显示Mat信息
2、使用行数、列数、深度、通道数、指向数据的指针、步长初始化Mat
【代码位置:frmChapter2_2_1】Button2_Click、outputMatdata8U1C
'构造函数2:使用行数、列数、深度、通道数、指向数据的指针、步长 初始化Mat
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
'包含数据的数组
Dim buffer() As Byte = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
'定义指针并分配内存空间
Dim matptr As IntPtr = Marshal.AllocHGlobal(buffer.Length)
'将数组拷贝到指针对应的内存空间
Marshal.Copy(buffer, 0, matptr, buffer.Length)
'指定行数4、列数3,无符号整数 Byte,通道数1,数据,步长3
Dim m As New Mat(4, 3, CvEnum.DepthType.Cv8U, 1, matptr, 3)
'显示Mat中元素的值
Call outputMatdata8U1C(m)
End Sub
'输出矩阵中元素的值
'为简化说明,在此方法中只考虑Cv8U单通道且数据连续的Mat
Private Sub outputMatdata8U1C(ByVal m As Mat)
Dim colcount As Integer = m.Cols
Dim rowcount As Integer = m.Rows
'获得数组大小
Dim bufferSize As Integer = m.Step * m.Rows
'定义数组
Dim buffer() As Byte
ReDim buffer(bufferSize 1)
'将Mat的数据指针指向的内存拷贝到数组
System.Runtime.InteropServices.Marshal.Copy(m.DataPointer, buffer, 0, bufferSize)
'循环输出每个元素的值
For row As Integer = 0 To rowcount 1
For col As Integer = 0 To colcount 1
TextBox1.Text &= buffer(row * colcount + col) & " "
Next
TextBox1.Text &= vbCrLf
Next
End Sub
显示结果如下:
图2-31 输出Mat元素的值
3、使用图片、色彩模式初始化Mat
其中参数ImreadModes(图像读取模式)枚举可以使用来指定不同的图像读取模式,以读取不同类型的图像。ImreadModes包含以下成员:
- Unchanged:不改变深度和通道数,读取原始图像。
- GrayScale:将图像转换为单通道灰度图像。
- Color:将图像转换为三通道彩色图像。
- AnyDepth:读取图像时不限制深度。
- AnyColor:读取图像时不限制通道数。
- LoadGdal:使用GDAL库读取图像。
- ReducedGrayscale2:将图像转换为8位单通道灰度图像。
- ReducedColor2:将图像转换为8位三通道彩色图像。
- ReducedGrayscale4:将图像转换为4位单通道灰度图像。
- ReducedColor4:将图像转换为4位三通道彩色图像。
- ReducedGrayscale8:将图像转换为1位单通道灰度图像。
- ReducedColor8:将图像转换为1位三通道彩色图像。
【代码位置:frmChapter2_2_1】Button3_Click
'构造函数3:使用图片、图像读取模式初始化Mat
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
'使用三通道彩色图像模式将数据载入Mat
Dim m1 As New Mat("C:\learnEmgucv\lena.jpg", CvEnum.ImreadModes.Color)
Call showMatInfo(m1, "彩色图片")
'使用灰度图像模式将数据载入Mat
Dim m2 As New Mat("C:\learnEmgucv\lena.jpg", CvEnum.ImreadModes.Grayscale)
Call showMatInfo(m2, "灰度图片")
End Sub
显示结果如下:
图2-32 使用图片初始化Mat
4、无参数的构造函数
下面的代码使用Mat无参数的构造函数,同时通过Matrix给Mat赋值,这也是本教程代码中常用的方式。
【代码位置:frmChapter2_2_1】Button4_Click
'构造函数4:无任何参数,同时此代码中还使用Matrix赋值给Mat
Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
Dim m As New Mat()
Dim inputBytes(,) As Byte = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}
Dim matr As New Matrix(Of Byte)(inputBytes)
'使用Matrix.Mat属性给Mat赋值
m = matr.Mat
showMatInfo(m, "MatrixToMat")
End Sub
显示结果如下:
图2-33 使用Matrix给Mat赋值
2.3.2 给Mat元素赋值
在【3.2.1 构造函数】中其实已经讲述了如何给Mat元素赋值的方法。这里另外还有一个方法,就是将数组拷贝到Mat数据指针处。
【代码位置:frmChapter2_2_1】Button5_Click
'将值拷贝到mat
Private Sub Button5_Click(sender As Object, e As EventArgs) Handles Button5.Click
'这里特地少了1个值,方便查看效果
Dim buffer() As Byte = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
Dim m As New Mat(4, 3, CvEnum.DepthType.Cv8U, 1)
'输出mat元素的值,可以看到值是随机的
Call outputMatdata8U1C(m)
TextBox1.Text &= "===============" & vbCrLf
Dim matptr As IntPtr = m.DataPointer
'将数组拷贝到指针处
Marshal.Copy(buffer, 0, matptr, buffer.Length)
'由于差1个值,所以,Mat中最后一个元素的值仍然是之前的
Call outputMatdata8U1C(m)
End Sub
显示结果如下:
图2-34 使用数组给Mat赋值
2.3.3 读取Mat元素的值
1、读取Cv8U单通道且数据连续的Mat的元素的值,在【3.2.1 构造函数】第二部分代码【outputMatdata8U1C】中已经实现。这里不在累述。
2、读取Cv16U三通道且数据连续的Mat的元素的值,
【代码位置:frmChapter2_2_1】Button6_Click
'获得Cv16u(即short或Int16)、3通道且连续的Mat元素的值
Private Sub Button6_Click(sender As Object, e As EventArgs) Handles Button6.Click
Dim m As New Mat(4, 3, CvEnum.DepthType.Cv16U, 3)
'将所有元素设置为(200, 100, 0)
m.SetTo(New MCvScalar(200, 100, 0))
'计算数组所占用的内存的大小
Dim bufferlen As Integer = m.Step * m.Rows / 2
'因为用的16U,也就是2个字节,对应Short类型
Dim buffer() As Short
ReDim buffer(bufferlen 1)
'将Mat的数据指针指向的内存拷贝到数组
System.Runtime.InteropServices.Marshal.Copy(m.DataPointer, buffer, 0, bufferlen)
'简单输出,未考虑行列
For Each b As Short In buffer
TextBox1.Text &= b & vbCrLf
Next
End Sub
显示结果如下:
图2-35 输出Cv16U三通道Mat元素的值
3、单通道Mat数据拷贝到数组
以下代码中使用到了Mat的CopyTo方法。
【代码位置:frmChapter2_2_1】Button7_Click
'单通道mat数据拷贝到数组
Private Sub Button7_Click(sender As Object, e As EventArgs) Handles Button7.Click
Dim m As New Mat()
Dim matr As New Matrix(Of Byte)(New Byte(,) {{0, 220, 0}, {30, 140, 199}, {80, 220, 7}, {65, 42, 190}})
m = matr.Mat
Dim b() As Byte
ReDim b(m.Cols * m.Rows 1)
'使用CopyTo方法将Mat中的元素的值拷贝到数组
m.CopyTo(b)
'输出数组中的值
For i As Integer = 0 To b.Length 1
TextBox1.Text &= b(i) & vbCrLf
Next
End Sub
显示结果如下:
图2-36 单通道Mat元素的值输出到数组
4、三通道Mat数据拷贝到数组
【代码位置:frmChapter2_2_1】Button8_Click
'三通道mat数据拷贝到数组
Private Sub Button8_Click(sender As Object, e As EventArgs) Handles Button8.Click
Dim m As New Mat(4, 3, CvEnum.DepthType.Cv8U, 3)
m.SetTo(New MCvScalar(200, 100, 0))
Dim b() As Byte
ReDim b(m.Cols * m.Rows * m.NumberOfChannels 1)
'使用CopyTo方法将Mat中的元素的值拷贝到数组
m.CopyTo(b)
'输出数组中的值
For i As Integer = 0 To b.Length 1
TextBox1.Text &= b(i) & vbCrLf
Next
End Sub
显示结果如下:
图2-37 三通道Mat元素的值输出到数组
2.3.4 修改Mat元素的值
【代码位置:frmChapter2_2_1】Button9_Click
'修改Mat中的数值
Private Sub Button9_Click(sender As Object, e As EventArgs) Handles Button9.Click
Dim m As New Mat(4, 3, CvEnum.DepthType.Cv8U, 1)
m.SetTo(New MCvScalar(255))
TextBox1.Text &= "修改前的矩阵:" & vbCrLf
Call outputMatdata8U1C(m)
'需要修改的行列元素的值
Dim row As Integer
Dim col As Integer
'修改行序号0,列序号0位置元素(如果将二维数组拉通排列,序号0的元素)的值
row = 0
col = 0
Dim ptr1 As IntPtr = m.DataPointer
ptr1 += row * 3 + col
Dim newvalue(0) As Byte
newvalue(0) = 100
System.Runtime.InteropServices.Marshal.Copy(newvalue, 0, ptr1, 1)
'修改行序号1,列序号1位置元素(如果将二维数组拉通排列,序号4的元素)的值
row = 1
col = 1
Dim ptr2 As IntPtr = m.DataPointer
ptr2 += row * 3 + col
newvalue(0) = 200
System.Runtime.InteropServices.Marshal.Copy(newvalue, 0, ptr2, 1)
TextBox1.Text &= vbCrLf & "修改后的矩阵:" & vbCrLf
Call outputMatdata8U1C(m)
End Sub
显示结果如下,可以看到(0,0)和(1,1)位置的元素已经被修改:
图2-38 修改Mat元素的值
2.3.5 Mat的克隆
使用Mat的Clone方法来实现对一个Mat的完全复制。
以下代码测试,说明修改克隆后的Mat对源Mat不产生影响。
【代码位置:frmChapter2_2_1】Button10_Click
'clone,不会影响源Mat
Private Sub Button10_Click(sender As Object, e As EventArgs) Handles Button10.Click
Dim m1 As New Mat()
Dim matr1 As New Matrix(Of Byte)(New Byte(,) {{0, 0, 0}, {1, 1, 1}, {2, 2, 2}, {3, 3, 3}})
m1 = matr1.Mat
Call outputMatdata8U1C(m1)
TextBox1.Text &= "===============" & vbCrLf
Dim m2 As New Mat
m2 = m1.Clone
Call outputMatdata8U1C(m2)
TextBox1.Text &= "===============" & vbCrLf
'需要修改的行列元素的值
Dim row As Integer
Dim col As Integer
'修改行序号0,列序号0位置元素(如果将二维数组拉通排列,序号0的元素)的值
row = 0
col = 0
Dim ptr1 As IntPtr = m2.DataPointer
ptr1 += row * 3 + col
Dim newvalue(0) As Byte
newvalue(0) = 100
System.Runtime.InteropServices.Marshal.Copy(newvalue, 0, ptr1, 1)
Call outputMatdata8U1C(m2)
TextBox1.Text &= "===============" & vbCrLf
Call outputMatdata8U1C(m1)
End Sub
显示结果如下,可以看到(0,0)和(1,1)位置的元素已经被修改:
图2-39 克隆Mat修改后不会影响源Mat
作为对比,请将上述代码中的
m2 = m1.Clone
修改为
m2 = m1
【代码位置:frmChapter2_2_1】Button11_Click
'使用=赋值,会影响源Mat
Private Sub Button11_Click(sender As Object, e As EventArgs) Handles Button11.Click
……
'修改Button10_Click中此处的代码:
m2 = m1
……
End Sub
显示结果如下,可以看到(0,0)和(1,1)位置的元素已经被修改:
图2-40 赋值并修改后会影响源Mat
2.3.6 获得子Mat
以下代码使用Mat的构造函数方法之一,源Mat和矩形(Rectangle)作为参数,生成一个子Mat。测试修改子Mat中元素的值,源Mat也会对应改变。
【代码位置:frmChapter2_2_1】Button12_Click、outputMatdata8U1CAfter
outputMatdata8U1Cafter作用是输出数据不连续的Mat的值。
'不连续的子矩阵
Private Sub Button12_Click(sender As Object, e As EventArgs) Handles Button12.Click
Dim m1 As New Mat()
Dim matr1 As New Matrix(Of Byte)(New Byte(,) {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}, {9, 10, 11}})
m1 = matr1.Mat
Call outputMatdata8U1C(m1)
TextBox1.Text &= "===============" & vbCrLf
'm2实际和m1指向的内存空间位置相同,m2修改会影响m1
'原始视图 DataPointer,与m1相比较,指针位置多了4
'IsContinuous属性(数据是否连续)为False
'实际值查看在:原始视图-非公共成员-bytes
Dim m2 As New Mat(m1, New Rectangle(1, 1, 2, 2))
TextBox1.Text &= "输出连续数据" & vbCrLf
Call outputMatdata8U1C(m2)
TextBox1.Text &= "===============" & vbCrLf
TextBox1.Text &= "输出不连续数据" & vbCrLf
Call outputMatdata8U1CAfter(m2)
TextBox1.Text &= "===============" & vbCrLf
'需要修改的行列元素的值
Dim row As Integer
Dim col As Integer
'修改行序号0,列序号0位置元素(如果将二维数组拉通排列,序号0的元素)的值
row = 0
col = 0
Dim ptr1 As IntPtr = m2.DataPointer
ptr1 += row * 3 + col
Dim newvalue(0) As Byte
newvalue(0) = 100
System.Runtime.InteropServices.Marshal.Copy(newvalue, 0, ptr1, 1)
Call outputMatdata8U1CAfter(m2)
TextBox1.Text &= "===============" & vbCrLf
'再次输出m1,可以看到修改m2中元素的之后,m1对应元素的值也变化了
Call outputMatdata8U1CAfter(m1)
End Sub
'对outputMatdata8U1C方法进行修改,修改后的方法考虑数据不连续的情况
'为简化说明,这里仍然使用8U单通道Mat
Private Sub outputMatdata8U1CAfter(ByVal m As Mat)
Dim colcount As Integer = m.Cols
Dim rowcount As Integer = m.Rows
Dim matstep As Integer = m.Step
'每次读取数据时候的指针位置
Dim pos As IntPtr = IntPtr.Zero
For i As Integer = 0 To rowcount 1
'检查指针位置,
'如果是第一次循环,即pos初始为0,那么指针位置为mat的指针位置
'否则,应该将指针位置增加step
If pos = IntPtr.Zero Then
pos = m.DataPointer
Else
pos += matstep
End If
Dim bufferSize As Integer = colcount
Dim buffer() As Byte
ReDim buffer(bufferSize 1)
'每次循环从新的指针位置拷贝数据,拷贝的数量为mat的列数。
System.Runtime.InteropServices.Marshal.Copy(pos, buffer, 0, bufferSize)
For col As Integer = 0 To colcount 1
TextBox1.Text &= buffer(col) & " "
Next
TextBox1.Text &= vbCrLf
Next
End Sub
显示结果如下:
图2-41 生成子矩阵并修改后与原矩阵对比
2.3.7 修改Mat的深度
以下代码使用Mat的ConvertTo方法将Mat的深度从Cv8U修改到Cv16U。
【代码位置:frmChapter2_2_1】Button13_Click
'更改mat的DepthType
Private Sub Button13_Click(sender As Object, e As EventArgs) Handles Button13.Click
Dim m As New Mat(4, 3, CvEnum.DepthType.Cv8U, 3)
m.SetTo(New MCvScalar(200, 100, 0))
'输出Mat信息
showMatInfo(m, "m8U3")
TextBox1.Text &= "============" & vbCrLf
Dim mOut As New Mat
m.ConvertTo(m, CvEnum.DepthType.Cv16U)
'输出Mat信息
showMatInfo(m, "m16U3")
TextBox1.Text &= "============" & vbCrLf
End Sub
显示结果如下:
图2-42 修改深度后的矩阵与原矩阵对比
2.3.8 最大值和最小值
Mat使用MinMax方法来获得最大值和最小值及对应坐标。使用方式和Matrix相同。
【代码位置:frmChapter2_2_1】Button14_Click
'最大最小值以及对应坐标位置
Private Sub Button14_Click(sender As Object, e As EventArgs) Handles Button14.Click
Dim m As New Mat()
Dim matr As New Matrix(Of Byte)(New Byte(,) {{0, 220, 0}, {30, 140, 199}, {80, 220, 7}, {65, 42, 190}})
m = matr.Mat
Dim maxvalues() As Double
Dim minvalues() As Double
Dim maxposes() As Point
Dim minposes() As Point
'只能查找每个通道的最大值和最小值,而不是查找每个通道的所有最大值和最小值
'最大值和最小值的位置,也只是返回最先的那个。
m.MinMax(minvalues, maxvalues, minposes, maxposes)
TextBox1.Text &= "最小值:" & minvalues(0) & " 位置:" & minposes(0).X & "," & minposes(0).Y & vbCrLf
TextBox1.Text &= "最大值:" & maxvalues(0) & " 位置:" & maxposes(0).X & "," & maxposes(0).Y
End Sub
显示结果如下:
图2-43 获得Mat元素最大最小值
2.3.9 Mat的运算
2.3.9.1 加法
【代码位置:frmChapter2_2_1】Button15_Click
'加法
Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click
Dim m1 As New Mat()
Dim matr1 As New Matrix(Of Byte)(New Byte(,) {{0, 220, 0}, {30, 140, 225}, {80, 200, 7}, {65, 42, 190}})
m1 = matr1.Mat
Dim m2 As New Mat()
Dim matr2 As New Matrix(Of Byte)(New Byte(,) {{50, 40, 220}, {130, 16, 80}, {95, 221, 229}, {17, 3, 22}})
m2 = matr2.Mat
Dim mout As New Mat
'方法1:
mout = m1 + m2
'方法2:
'CvInvoke.Add(m1, m2, mout)
Call outputMatdata8U1C(mout)
End Sub
显示结果如下:
图2-44 Mat的加法
2.3.9.2 减法
【代码位置:frmChapter2_2_1】Button16_Click
'减法
Private Sub Button16_Click(sender As Object, e As EventArgs) Handles Button16.Click
Dim m1 As New Mat()
Dim matr1 As New Matrix(Of Byte)(New Byte(,) {{0, 220, 0}, {30, 140, 225}, {80, 200, 7}, {65, 42, 190}})
m1 = matr1.Mat
Dim m2 As New Mat()
Dim matr2 As New Matrix(Of Byte)(New Byte(,) {{50, 40, 220}, {130, 16, 80}, {95, 221, 229}, {17, 3, 22}})
m2 = matr2.Mat
Dim mout As New Mat
'方法1:直接相减,对于小于0的值设置为0
mout = m1 m2
'方法2:使用 CvInvoke.AbsDiff,与直接相减有所不同,小于0的值将取绝对值
'CvInvoke.AbsDiff(m1, m2, mout)
Call outputMatdata8U1C(mout)
End Sub
使用上述方法,应注意直接相减和使用AbsDiff函数的区别。
显示结果如下:
图2-45 Mat的减法
2.3.9.3 乘法
【代码位置:frmChapter2_2_1】Button17_Click
'乘法
Private Sub Button17_Click(sender As Object, e As EventArgs) Handles Button17.Click
Dim m1 As New Mat()
Dim matr1 As New Matrix(Of Byte)(New Byte(,) {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}, {9, 10, 11}})
m1 = matr1.Mat
'矩阵乘以一个数值
Dim mout1 As New Mat
mout1 = m1 * 2
Call outputMatdata8U1C(mout1)
TextBox1.Text &= "===============" & vbCrLf
'两个矩阵相乘,大小必须相同
Dim m2 As New Mat()
Dim matr2 As New Matrix(Of Byte)(New Byte(,) {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}, {9, 10, 11}})
m2 = matr2.Mat
'对应元素相乘,不同于一般的矩阵相乘
Dim mout2 As New Mat
CvInvoke.Multiply(m1, m2, mout2)
Call outputMatdata8U1C(mout2)
End Sub
显示结果如下:
图2-45 Mat的乘法
2.3.9.4 除法
【代码位置:frmChapter2_2_1】Button18_Click
'除法
Private Sub Button18_Click(sender As Object, e As EventArgs) Handles Button18.Click
Dim m1 As New Mat()
Dim matr1 As New Matrix(Of Byte)(New Byte(,) {{0, 21, 112}, {21, 36, 55}, {24, 77, 24}, {90, 210, 121}})
m1 = matr1.Mat
'矩阵除以一个数值
Dim mout1 As New Mat
mout1 = m1 / 2
Call outputMatdata8U1C(mout1)
TextBox1.Text &= "===============" & vbCrLf
'两个矩阵相除,大小必须相同
Dim m2 As New Mat()
Dim matr2 As New Matrix(Of Byte)(New Byte(,) {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}, {9, 10, 11}})
m2 = matr2.Mat
'对应元素相除,不同于一般的矩阵相除
Dim mout2 As New Mat(m1.Size, DepthType.Cv8U, 1)
CvInvoke.Divide(m1, m2, mout2)
Call outputMatdata8U1C(mout2)
End Sub
显示结果如下:
图2-46 Mat的除法
注意:如果相除的矩阵元素类型是整型,不论被除数还是除数为0,都将会返回0。如果相除的矩阵元素类型是浮点型,被除数不为0,除数为0,那么将返回∞(无穷大);被除数为0,且除数为0,那么将返回NaN。下面的代码演示了使用0作为被除数时的输出结果,其中outputMatdata64F1C函数输出64位浮点值类型Mat的元素的值。
【代码位置:frmChapter2_2_1】Button19_Click、outputMatdata64F1C
'除法
Private Sub Button19_Click(sender As Object, e As EventArgs) Handles Button19.Click
Dim m1 As New Mat()
Dim matr1 As New Matrix(Of Double)(New Double(,) {{9, 0, 0}, {22, 33, 55}, {24, 23, 24}, {90, 87, 121}})
m1 = matr1.Mat
'两个矩阵相除,大小必须相同
Dim m2 As New Mat()
Dim matr2 As New Matrix(Of Double)(New Double(,) {{0, 0, 2}, {3, 4, 5}, {6, 7, 8}, {9, 10, 11}})
m2 = matr2.Mat
'对应元素相除,不同于一般的矩阵相除
Dim mout As New Mat(m1.Size, DepthType.Cv64F, 1)
CvInvoke.Divide(m1, m2, mout)
Call outputMatdata64F1C(mout)
End Sub
'输出64位浮点值类型Mat的元素的值
Private Sub outputMatdata64F1C(ByVal m As Mat)
Dim colcount As Integer = m.Cols
Dim rowcount As Integer = m.Rows
'获得数组大小
Dim bufferSize As Integer = m.Step * m.Rows
'定义数组
Dim buffer() As Byte
ReDim buffer(bufferSize 1)
'将Mat的数据指针指向的内存拷贝到数组
System.Runtime.InteropServices.Marshal.Copy(m.DataPointer, buffer, 0, bufferSize)
'注意Double类型占用8个字节
Dim bteOutDouble(7) As Byte
For i As Integer = 0 To buffer.Length 1 Step 8
For j As Integer = 0 To 7
bteOutDouble(j) = buffer(i + j)
Next
'这里使用了BitConverter.ToDouble将字节数组转Double
Dim outDouble As Double = BitConverter.ToDouble(bteOutDouble, 0)
TextBox1.Text &= outDouble.ToString & vbCrLf
Next
End Sub
显示结果如下:
图2-48 Mat浮点类型的除法
可以看到输出的前三个值分别为:∞、NaN、0
2.3.9.5 And运算
【代码位置:frmChapter2_2_1】Button20_Click
'And运算
Private Sub Button20_Click(sender As Object, e As EventArgs) Handles Button20.Click
Dim m1 As New Mat()
Dim matr1 As New Matrix(Of Byte)(New Byte(,) {{0, 0, 0}, {1, 1, 1}, {2, 2, 2}, {3, 3, 3}})
m1 = matr1.Mat
Dim m2 As New Mat()
Dim matr2 As New Matrix(Of Byte)(New Byte(,) {{1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}})
m2 = matr2.Mat
Dim mout As New Mat
'方法1:
mout = m1 And m2
'方法2:
'CvInvoke.BitwiseAnd(m1, m2, mout)
Call outputMatdata8U1C(mout)
End Sub
显示结果如下:
图2-48 Mat的And运算
2.3.9.6 Or运算
【代码位置:frmChapter2_2_1】Button21_Click
'Or运算
Private Sub Button21_Click(sender As Object, e As EventArgs) Handles Button21.Click
Dim m1 As New Mat()
Dim matr1 As New Matrix(Of Byte)(New Byte(,) {{0, 0, 0}, {1, 1, 1}, {2, 2, 2}, {3, 3, 3}})
m1 = matr1.Mat
Dim m2 As New Mat()
Dim matr2 As New Matrix(Of Byte)(New Byte(,) {{1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}})
m2 = matr2.Mat
Dim mout As New Mat
'方法1:
mout = m1 Or m2
'方法2:
'CvInvoke.BitwiseOr(m1, m2, mout)
Call outputMatdata8U1C(mout)
End Sub
显示结果如下:
图2-49 Mat的Or运算
2.3.9.7 Xor运算
【代码位置:frmChapter2_2_1】Button22_Click
'xor运算
Private Sub Button22_Click(sender As Object, e As EventArgs) Handles Button22.Click
Dim m1 As New Mat()
Dim matr1 As New Matrix(Of Byte)(New Byte(,) {{0, 0, 0}, {1, 1, 1}, {2, 2, 2}, {3, 3, 3}})
m1 = matr1.Mat
Dim m2 As New Mat()
Dim matr2 As New Matrix(Of Byte)(New Byte(,) {{1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}})
m2 = matr2.Mat
Dim mout As New Mat
'Mat没有Xor方法,直接使用xor是错误的
'mout = m1 Xor m2
CvInvoke.BitwiseXor(m1, m2, mout)
Call outputMatdata8U1C(mout)
End Sub
显示结果如下:
图2-50 Mat的Xor运算
2.3.9.8 Not运算
【代码位置:frmChapter2_2_1】Button23_Click
'not运算
Private Sub Button23_Click(sender As Object, e As EventArgs) Handles Button23.Click
Dim m1 As New Mat()
Dim matr1 As New Matrix(Of Byte)(New Byte(,) {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}, {9, 10, 11}})
m1 = matr1.Mat
Dim mout As New Mat
'方法1:
mout = Not m1
'方法2:
'CvInvoke.BitwiseNot(m1, mout)
Call outputMatdata8U1C(mout)
End Sub
显示结果如下:
图2-51 Mat的Not运算
2.3.10 载入并显示图像
在【2.3.1 构造函数】中讲述过使用Mat载入图片,但是未进行显示。以下代码将显示Lena的彩色图像和灰度图像,请在窗体上放置2个ImageBox控件。ImageBox控件也是在Vs的【工具箱】|【Emgu.CV】下面。
【代码位置:frmChapter2_2_2】Button1_Click
'载入并显示图片
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'彩色图片
Dim m1 As New Mat("C:\learnEmgucv\lena.jpg", CvEnum.ImreadModes.Color)
ImageBox1.Image = m1
'灰度图片
Dim m2 As New Mat("C:\learnEmgucv\lena.jpg", CvEnum.ImreadModes.Grayscale)
ImageBox2.Image = m2
End Sub
显示结果如下:
图2-52 图像显示
可以将图像简单理解为包含了很多像素点的矩阵,也就对应了Mat这个矩阵。
2.3.11 图像通道的分离与合并
图像通道的分离,使用Mat的Split方法,将多通道图像按照通道分别存入一个Mat数组。
图像通道的合并,使用 CvInvoke的Merge方法,将多个单通道图像合并成一个多通道图像。Merge方法的语法如下:
Public Shared Sub Merge(src As VectorOfMat, dst As Mat)
需要注意的是,要合并的单通道Mat对象必须有相同的行数、列数和深度类型。合并后的多通道Mat对象的通道数等于要合并的单通道Mat对象的数量。
在进行分离和合并的时候,需要使用到VectorOfMat类型。VectorOf~直译为向量,可以简单理解为类似于数组或者集合,用来表示不定长度数组,可以通过索引来获得里面指定的元素。例如:VectorOfMat理解为多个Mat的数组,VectorOfPoint理解为多个点的数组。在EmguCV中还定义有VectorOfRect、VectorOfFloat、VectorOfTriangle2DF等等,特别是还会有VectorOfVectorOf~,表示数组的数组,例如在后面处理图像轮廓时会用到VectorOfVectorOfPoint,表示多个点的数组然后又做了个数组。
VectorOf~通过Push方法向数组中增加元素。VectorOfMat中使用Push,也就是增加通道,但需要注意,如果要显示合并后的图像,图像必须有正确的通道数,如,不能有2通道的图像。
在以下代码中可以看到上述方法和类型的用法。
【代码位置:frmChapter2_2_2】Button2_Click
'Mat按照通道分离,然后合并
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim m As New Mat("C:\learnEmgucv\lena.jpg", CvEnum.ImreadModes.Color)
Dim mDst() As Mat
'按通道数分离
mDst = m.Split()
'显示0号通道,即蓝色(B)通道
ImageBox1.Image = mDst(0)
'将0,1,1即BGG通道合并成新的图像
Dim vm As New VectorOfMat
vm.Push(mDst(0))
vm.Push(mDst(1))
vm.Push(mDst(1))
Dim mDstMerge As New Mat
CvInvoke.Merge(vm, mDstMerge)
ImageBox2.Image = mDstMerge
End Sub
显示结果如下:
图2-53 图像通道的分离与合并
在以下代码中加入了一个自定义的通道,最后把图像显示出来。
【代码位置:frmChapter2_2_2】Button3_Click
'Mat按照通道分离,然后组合,更多的效果
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim m As New Mat("C:\learnEmgucv\lena.jpg", CvEnum.ImreadModes.Color)
'显示源图像
ImageBox1.Image = m
Dim mDst() As Mat
'按通道数分离
mDst = m.Split()
'将0,1,1即BGG通道组合成新的图像
Dim vm As New VectorOfMat
vm.Push(mDst(0))
vm.Push(mDst(1))
'设置第三个通道,元素的值都为0
Dim m2 As New Mat(m.Size, CvEnum.DepthType.Cv8U, 1)
m2.SetTo(New MCvScalar(0))
vm.Push(m2)
Dim mDstMerge As New Mat
CvInvoke.Merge(vm, mDstMerge)
ImageBox2.Image = mDstMerge
End Sub
显示结果如下:
图2-54 图像通道的分离与合并之二