写在前面
C# Tips是博主开启的第一个项目,以C#为示例语言,旨在普及编程技巧、经典算法以及计算机视觉、图形学知识。
EmguCV
计算机视觉是研究计算机如何模拟人眼的学科,最常用、最稳定、最权威的库就是OpenCV,但是OpenCV主要是由C++开发的,编译起来相对麻烦,好在针对.NET平台的EmguCV被伟大的开源工程师们开发了出来,这个库实现了OpenCV的大部分功能,并且集成了图像显示控件ImageBox,随着其版本的不断更新,功能也会越来越接近OpenCV。值得一提的是,EmguCV的版本号与OpenCV基本一致,OpenCV2.x时可用的SIFT算法在EmguCV2.x中仍然可用,但是可能性能不太好【笑哭】。
矩阵创建
计算机视觉的基本单位是图像,图像就是一种特殊的矩阵(MxNx3),另外图像的处理需要其他矩阵的辅助,所以矩阵是计算机视觉库中必不可少的类,EmguCV和OpenCV中都用Mat类来表示,下面的例子都来自于EmguCV3.4.3,需要注意的是:一定要将下载的EmguCV的bin中的x86文件夹复制到与exe文件同目录下。
构造方法
| 方法 | 含义 |
|---|---|
| Mat() | 创建一个空矩阵 |
| Mat(Mat, Rectangle)/Mat(Mat, Range, Range) | 按ROI提取矩阵的一个部分 |
| Mat(Size, DepthType, Int32) /Mat(Int32, Int32, DepthType, Int32) | 创建一个指定大小、类型、通道数的矩阵 |
特殊方法
| 方法 | 含义 |
|---|---|
| Mat Clone() | 复制矩阵 |
| Mat Diag(int d = 0) | 提取对角阵 |
| Mat Eye(int rows,int cols,DepthType type,int channels) | 创建单位矩阵 |
| Mat Ones(int rows,int cols,DepthType type,int channels) | 创建全1矩阵 |
| void SetTo(T[] data) | 从数组中复制数据到矩阵 |
| Mat Zeros(int rows,int cols,DepthType type,int channels) | 创建零矩阵 |
矩阵运算
和OpenCV不同的是,EmguCV并未实现矩阵基础运算及取值赋值运算,但实现了像矩阵转置、矩阵求逆、向量点乘等更高级的运算,为了能兼顾二者,可以自建一个ME(Mat Extension)类继承Mat类,以下的ME类特指单通道矩阵。
首先得继承Mat的构造方法,根据自己的需要选择不同的方法。
public ME()
{
}
public ME(int rows, int cols, DepthType type): base(rows, cols, type, 1)
{
}
/// <summary>
/// Mat转化为ME
/// </summary>
/// <param name="mat">待转化的Mat</param>
public ME(Mat mat): base(mat, new System.Drawing.Rectangle(0, 0, mat.Width, mat.Height))
{
}
然后实现基本的取值和赋值运算,使得矩阵的取值赋值和二维数组一样方便。
/// <summary>
/// 获得或设置矩阵的值
/// </summary>
/// <param name="row">行号</param>
/// <param name="col">列号</param>
/// <returns>获取的值</returns>
public dynamic this[int row,int col=0]
{
get
{
var value1 = CreateElement();
Marshal.Copy(this.DataPointer + (row * this.Cols + col) * this.ElementSize, value1, 0, 1);
return value1[0];
}
set
{
var target = CreateElement();
target[0] = value;
Marshal.Copy(target, 0, this.DataPointer + (row * this.Cols + col) * this.ElementSize, 1);
}
}
/// <summary>
/// 创建大小为1的基本类型数组
/// </summary>
/// <param name="depthType">Mat的深度类型</param>
/// <returns>数组结果</returns>
private dynamic CreateElement()
{
DepthType depthType = this.Depth;
if (depthType == DepthType.Cv8S)
{
return new sbyte[1];
}
else if (depthType == DepthType.Cv8U)
{
return new byte[1];
}
else if (depthType == DepthType.Cv16S)
{
return new short[1];
}
else if (depthType == DepthType.Cv16U)
{
return new ushort[1];
}
else if (depthType == DepthType.Cv32S)
{
return new int[1];
}
else if (depthType == DepthType.Cv64F)
{
return new double[1];
}
else
{
return new float[1];
}
}
最后实现矩阵的加、减、乘和取负等基础运算,和一般类型一样,需要重载+、-、*运算符。
/// <summary>
/// 将矩阵转化为相应类型的数组
/// </summary>
/// <returns>数组结果</returns>
public dynamic ToArray()
{
DepthType depthType = this.Depth;
int length = this.Rows * this.Cols;
dynamic arr;
if (depthType == DepthType.Cv8S)
{
arr=new sbyte[length];
this.CopyTo<sbyte>(arr);
}
else if (depthType == DepthType.Cv8U)
{
arr = new byte[length];
this.CopyTo<byte>(arr);
}
else if (depthType == DepthType.Cv16S)
{
arr = new short[length];
this.CopyTo<short>(arr);
}
else if (depthType == DepthType.Cv16U)
{
arr = new ushort[length];
this.CopyTo<ushort>(arr);
}
else if (depthType == DepthType.Cv32S)
{
arr = new int[length];
this.CopyTo<int>(arr);
}
else if (depthType == DepthType.Cv64F)
{
arr = new double[length];
this.CopyTo<double>(arr);
}
else
{
arr = new float[length];
this.CopyTo<float>(arr);
}
return arr;
}
/// <summary>
/// 矩阵加法
/// </summary>
/// <param name="lhs">左矩阵</param>
/// <param name="rhs">右矩阵</param>
/// <returns>结果</returns>
public static ME operator +(ME lhs, ME rhs)
{
if (lhs.Rows != rhs.Rows || lhs.Cols != rhs.Cols)
return null;
dynamic data1 = lhs.ToArray();
dynamic data2 = rhs.ToArray();
double[] result = new double[lhs.Rows * lhs.Cols];
for (int i = 0; i < data1.Length; i++)
result[i] = data1[i] + data2[i];
ME mat = new ME(lhs.Rows, lhs.Cols, DepthType.Cv64F);
mat.SetTo<double>(result);
return mat;
}
/// <summary>
/// 矩阵加法
/// </summary>
/// <param name="lhs">矩阵</param>
/// <param name="rhs">常数</param>
/// <returns>结果</returns>
public static ME operator +(ME lhs, double rhs)
{
dynamic data1 = lhs.ToArray();
double[] result = new double[lhs.Rows * lhs.Cols];
for (int i = 0; i < data1.Length; i++)
result[i] = data1[i] + rhs;
ME mat = new ME(lhs.Rows, lhs.Cols, DepthType.Cv64F);
mat.SetTo<double>(result);
return mat;
}
/// <summary>
/// 矩阵减法
/// </summary>
/// <param name="lhs">左矩阵</param>
/// <param name="rhs">右矩阵</param>
/// <returns>结果</returns>
public static ME operator -(ME lhs, ME rhs)
{
if (lhs.Rows != rhs.Rows || lhs.Cols != rhs.Cols)
return null;
dynamic data1 = lhs.ToArray();
dynamic data2 = rhs.ToArray();
double[] result = new double[lhs.Rows * lhs.Cols];
for (int i = 0; i < data1.Length; i++)
result[i] = data1[i]-data2[i];
ME mat = new ME(lhs.Rows, lhs.Cols, DepthType.Cv64F);
mat.SetTo<double>(result);
return mat;
}
/// <summary>
/// 矩阵减法
/// </summary>
/// <param name="lhs">矩阵</param>
/// <param name="rhs">常数</param>
/// <returns>结果</returns>
public static ME operator -(ME lhs, double rhs)
{
dynamic data1 = lhs.ToArray();
double[] result = new double[lhs.Rows * lhs.Cols];
for (int i = 0; i < data1.Length; i++)
result[i] = data1[i]-rhs;
ME mat = new ME(lhs.Rows, lhs.Cols, DepthType.Cv64F);
mat.SetTo<double>(result);
return mat;
}
/// <summary>
/// 矩阵取负
/// </summary>
/// <param name="lhs">矩阵</param>
/// <returns>结果</returns>
public static ME operator -(ME lhs)
{
dynamic data1 = lhs.ToArray();
double[] result = new double[lhs.Rows * lhs.Cols];
for (int i = 0; i < data1.Length; i++)
result[i] = -data1[i];
ME mat = new ME(lhs.Rows, lhs.Cols, DepthType.Cv64F);
mat.SetTo<double>(result);
return mat;
}
/// <summary>
/// 矩阵乘法
/// </summary>
/// <param name="lhs">左矩阵</param>
/// <param name="rhs">右矩阵</param>
/// <returns>结果</returns>
public static ME operator *(ME lhs, ME rhs)
{
if (lhs.Cols != rhs.Rows)
return null;
double[] result = new double[lhs.Rows * rhs.Cols];
for (int i = 0; i < lhs.Rows * rhs.Cols; i++)
result[i] = lhs.Row(i / rhs.Cols).Dot(rhs.Col(i % rhs.Cols).T());
ME mat = new ME(lhs.Rows, rhs.Cols, DepthType.Cv64F);
mat.SetTo<double>(result);
return mat;
}
/// <summary>
/// 矩阵乘法
/// </summary>
/// <param name="lhs">常数</param>
/// <param name="rhs">矩阵</param>
/// <returns>结果</returns>
public static ME operator *(double rhs, ME lhs)
{
dynamic data1 = lhs.ToArray();
double[] result = new double[lhs.Rows * lhs.Cols];
for (int i = 0; i < data1.Length; i++)
result[i] = data1[i]*rhs;
ME mat = new ME(lhs.Rows, lhs.Cols, DepthType.Cv64F);
mat.SetTo<double>(result);
return mat;
}

本文介绍C#编程语言结合EmguCV库进行计算机视觉应用,包括矩阵操作、图像处理技巧及如何自定义矩阵扩展类实现基础矩阵运算。

被折叠的 条评论
为什么被折叠?



