创建您的第一个 EMGU 图像处理项目
一份完整的循序渐进指南,向 EMGU CV 的新手展示如何设置他们的项目
X64 架构(已更新)参阅历史记录中的注释 [2]
X86 架构
引言
以下文章旨在一步一步地向 EMGU CV 的新手展示如何设置项目。这些信息可以在 这里 免费获取,但是,本文旨在使该过程更加用户友好。EMGU 是 OpenCV 的 C# 包装器,它与其他包装器不同,因为它完全用 C# 编写,不使用不安全代码。EMGU 将主要是为实时计算机视觉设计的 OpenCV(开源计算机视觉库)编程函数库开放给 C# 开发者。OpenCV 最初由 Intel 开发,现在由 Willow Garage 支持。
x86 和 x64 架构的当前版本均可在其 Sourceforge 网站上下载。
由于 EMGU 是 C++ 代码的包装器。有两种类型的动态链接库(DLL)被使用。有带 EMGU 语法的 EMGU DLL,它们的名字总是带有引用,以及可变的 opencv DLL。设置第一个项目是许多新手普遍遇到的障碍,你并不孤单。
如果您已下载源代码,则需要阅读“一个基础程序”。如果您从 EMGU 提取文件夹中复制了一个示例,那么请查看 EMGU.CV.Invoke 异常和故障排除 部分。
假设知识
假设用户具备 C# 的基本经验,并且能够生成新的 C# 项目。假设每个用户都已下载其平台的最新更新,并且 EMGU 提取文件夹\EMGU.Examples\Hello World 中的HelloWorld
示例正在运行。
基本要求
与任何 C# 库一样,有一些必要的 DLL 需要在您的项目中进行引用。启动一个新的 C# Windows 窗体应用程序,并随意命名。所有提到的 DLL 都在 EMGU 提取文件夹\bin 中 - 您需要记住这一点。首先,您需要引用 3 个 EMGU DLL。
- Emgu.CV.dll
- Emgu.CV.UI.dll
- Emgu.Util.dll
这可以通过在解决方案资源管理器中右键单击项目名称或引用文件夹来完成。选择添加引用。
或者,可以使用菜单项项目 > 添加引用。当“添加引用”窗口打开时,选择上面列出的 DLL,然后单击确定。
您现在会在解决方案资源管理器窗口的引用文件夹中看到它们。这三个 DLL 是前面提到的 EMGU 特有的 C# 库。仅凭它们将无法使用任何图像处理功能,因此请阅读本文的其余部分。
现在您需要在任何将要使用代码的类或窗体中引用它们。您将使用的引用将取决于您在图像处理方面的具体操作。查看示例,其中将包含您需要的引用。为了帮助您入门,请将以下内容添加到Form1.cs 代码隐藏的顶部。
using Emgu.CV;
using Emgu.Util;
using Emgu.CV.Structure;
首选方法
现在是更复杂的 C++ 库,要加载、显示、访问图像数据并执行许多更简单的功能,您只需要两个文件。注意,“220”是版本号 - 这会根据更新而变化(opencv_core***.dll, opencv_imgproc***.dll)。
- opencv_core220.dll
- opencv_imgproc220.dll
现在,由于这些是包装的 C++ 文件,您不能像引用 EMGU 文件那样引用它们。您需要将这些文件添加到您的项目中。这可以通过两种方式完成 - 在解决方案资源管理器中右键单击您的项目名称(不是以“Solution ‘Your Project Name’...”开头的那个,而是它下面的那个)。选择添加 > 现有项,然后使用标准的打开文件对话框。选择上面的两个文件,以防您忘记它们位于 EMGU 提取文件夹\bin。
提示:如果您看不到.dll文件,请确保将右下角的文件过滤器更改为可执行文件。
您现在应该可以在解决方案资源管理器窗口中看到您的文件。您需要更改它们的属性,所以按住 Ctrl 键并左键单击以同时选择它们(或者,您也可以单独选择)。现在查看“属性”窗口,您会看到六个字段,其中两个会填充内容。您感兴趣的是复制到输出目录。将其从“不复制”更改为“始终复制”。
如果您使用的是 x64 编译,请转到 x64 部分,并确保您的项目设置为编译到 x64 架构,否则您已准备好开始图像处理。这是首选方法的原因是,现在,如果您从 Debug 更改为 Release,这些文件将始终可供您的程序使用,并且不会出现任何错误。跳转到阅读和显示图像部分一个基础程序以开始。
不太推荐的方法
虽然不被推荐,但这是最简单的方法,并且如果您有一个复杂的项目结构,这可以防止解决方案资源管理器看起来非常混乱。只需在 Windows 资源管理器中导航到 EMGU 提取文件夹\bin,然后将相关的 DLL 文件,opencv_core220.dll 和 opencv_imgproc220.dll 复制到您的项目bin\Debug 文件夹或bin\Release 文件夹。这会随着 x64 版本而改变,因为它将是bin\x64\Debug 文件夹或替代的Release 文件夹。
虽然在这里的好处不太明显,但想象一下,如果您需要所有 opencv DLL 文件,那么您的解决方案资源管理器中将会有额外的 34 个文件,然而,这种情况很少发生。
X64 架构和 EMGU.CV.Invoke 异常
如果您正在运行 x64 系统或为其设计,您将不得不下载单独的 DLL。项目构建步骤是相同的,但是您需要更改一个额外的构建参数。在解决方案资源管理器中右键单击您的项目文件,然后选择窗口底部的“属性”。从该窗口右侧的选项卡栏中选择“生成”。将有一个平台目标选项:带有一个下拉菜单,将其从 x86 更改为 x64。
提示:如果您使用的是 Visual Studio 的 Express 版本,您可能看不到 x64 选项。在这种情况下,请转到菜单选项工具 > 选项。在此窗口中,使用左侧的箭头展开和折叠选项。选择“项目和解决方案”并勾选“显示高级生成配置”复选框。
这将允许编译运行(如果没有正确执行)。一旦您访问任何 EMGU 代码,就会抛出异常“EMGU.CV.Invoke
”,并且“InnerException
”为“尝试加载格式不正确的程序....”。
一个基础程序
为了帮助您入门,我们提供了一个加载图像并在 picture box 中显示它的简单程序,以及一个更高级的程序,它将演示如何访问图像数据并在图像类型之间进行转换。
目前仅提供x64 版本,x86 版本将很快提供。
如果您下载了示例代码,您将看到 3 个关于未找到引用的警告。展开解决方案资源管理器中的引用文件夹,删除带有黄色警告图标的 3 个引用,然后重新添加引用,步骤可在“基本要求”部分找到。
主窗体上添加了一个按钮和一个picturebox
项。它们的默认名称未更改。当我们单击按钮时,我们希望打开一个文件对话框,选择一个图像,并在picturebox
中显示它。
双击按钮并添加以下代码
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog Openfile = new OpenFileDialog();
if (Openfile.ShowDialog() == DialogResult.OK)
{
Image<Bgr, Byte> My_Image = new Image<Bgr, byte>(Openfile.FileName);
pictureBox1.Image = My_Image.ToBitmap();
}
}
代码非常简单,使用一个名为 'Openfile
' 的OpenFileDialog
来选择一个图像文件。然后将该图像读取到一个名为 'My_Image
' 的彩色Image
对象中。通过将picturebox
的Image
属性赋值来实现图像显示。这需要一个Bitmap
,通过调用 'My_Image
' 的.ToBitmap()
函数来实现。
一旦正确设置了项目,这就可以非常简单地实现。EMGU.CV.UI
库提供了一个Picturebox
项的替代方案,并在示例中使用。如果您想使用它,请访问 http://www.emgu.com/wiki/index.php/Add_ImageBox_Control 了解如何将此控件添加到 Visual Studio。
稍多一点的图像处理
稍微高级一些的源代码项目将会有与“基础程序”中描述的相同警告,并且已经提供。需要替换引用。在此程序中,演示了如何将Image
从彩色
转换为灰度
并访问单个像素的Data
。虽然抑制图像频谱数据的这些方法不是最高效的,但它是一个很好的访问图像Data
属性的例子。
关于图像转换的一点说明
EMGU 中的图像转换可能很复杂。在示例程序中,将Bgr
彩色图像转换为Gray
或灰度。
Image<gray,byte> gray_image = My_Image.Convert<gray,byte>();
然而,您最终将希望使用不同深度的图像(Tdepth
),而不是Byte
。此方法的缺点是您每次调用只能转换一次深度或颜色。假设我们希望从Image<bgr,byte>
转换为Image<gray,double>
,您必须使用以下语法。
Image<Gray,byte> gray_image = My_Image.Convert<Gray,byte>();
Image<Gray,double> gray_image = My_Image.Convert<Gray,double>();
//or alternatively in one line
Image<Gray,> gray_image = My_Image.Convert<Gray,byte>().Convert<Gray,double>();
//alternatively
Image<Gray,Byte> gray_image = My_Image.Convert<Bgr,double>().Convert<Gray,double>();
访问图像数据
有几种访问图像数据并为其赋值的方法。有两种可用方法:直接访问图像数据属性或更远程的访问。此处演示了这两种方法。请注意,在访问Data
属性时,尊重图像频谱深度非常重要。灰度图像的深度为一,因此引用为 [x,y,0],而彩色图像的深度为 3,分别为 [x,y,0]、[x,y,1] 和 [x,y,2],分别代表蓝色、绿色和红色频谱(Bgr
)。
假设我们想给位置 [0,0] 的像素赋值。使用更简单的远程方法,我们可以使用
//Colour Image
My_Image[0, 0] = new Bgr(Color.Red);
//Gray Image
gray_image[0, 0] = new Gray(200);
或者我们使用Data
属性
//Colour Image
Color R = Color.Red;
My_Image.Data[0,0,2] = R.R; //Write to the Red Spectrum
My_Image.Data[0,0,1] = R.G; //Write to the Green Spectrum
My_Image.Data[0,0,0] = R.B; //Write to the Blue Spectrum
//Gray Image
gray_image[0, 0] = new Gray(200);
因此,写入像素相当简单,但如何读取像素值呢?
//Colour Image
Bgr my_Bgr = My_Image[0, 0];
//Gray Image
Gray my_Gray = gray_image[0, 0];
在许多情况下,您不想处理“Bgr
”或“Gray
”,因此进行转换很重要。
//BGR to Color
Color my_colour = Color.FromArgb((int)value.Red, (int)value.Blue, (int)value.Green);
//Gray to int
int my_intensity = (int) my_Gray.Intensity;
您会注意到每个值都被强制转换为整数以允许数据丢失,这是因为强度自然存储为double
。然而,在这种情况下,更简单的方法是访问图像Data
属性。如果您想处理图像数据,则不需要不断地在Gray
和整数之间进行转换等。您可以直接访问图像Data
并使用它。
//Colour
Color my_colour = Color.FromArgb(My_Image.Data[0, 0, 0],
My_Image.Data[0, 0, 1], My_Image.Data[0, 0, 2]);
//Gray Image
int my_intensity = gray_image.Data[0, 0, 0];
处理循环中的图像Data
时,这要简单得多,也容易得多。要检查如何实现循环,请下载“稍多一点的图像处理”源代码。
DllNotFound 异常和故障排除 0x8007007E
现在,在某个时候,您将收到一个错误“无法加载 DLL......(异常来自HRESULT: 0x8007007E
)”。您需要阅读下方红色圈出的Message
部分。
如果异常是EMGU.CV.Invoke
异常,那么您很可能没有针对正确的构建平台。有关详细信息,请参阅有关x64 架构的部分。
现在,如果您已经进展到高级图像处理方法,或者只是将示例复制到了 EMGU 提取文件夹以外的其他位置,您可能会看到以下错误之一
Unable to load DLL 'opencv_highgui220': The specified module could not be found.
(Exception from HRESULT: 0x8007007E)
通常是在尝试从网络摄像头获取图像时发生的。解决这个问题非常简单。正如“基本要求”部分所讨论的,您需要将 opencv_highgui220.dll 复制到输出目录,或者更推荐的方法是将文件作为现有文件添加到您的项目中,然后将其属性“复制到输出目录”更改为“始终复制”。如果需要,请回顾该部分进行参考。
Unable to load DLL 'cvextern': The specified module could not be found.
(Exception from HRESULT: 0x8007007E)
这个错误有一个更实质性的解决方案。cvextern.dll 错误无法仅通过添加此文件来修复,事实上,此文件需要以下所有文件才能正确执行。正如“基本要求”部分所讨论的,您需要将文件复制到输出目录,或者更推荐的方法是将文件作为现有文件添加到您的项目中,然后将每个文件的属性“复制到输出目录”更改为“始终复制”。如果需要,请回顾该部分进行参考。
注意,“220”是版本号 - 这会根据更新而变化。
- cudart64_32_16.dll
- cufft64_32_16.dll
- cvextern.dll
- npp64_32_16.dll
- opencv_calib3d220.dll
- opencv_contrib220.dll
- opencv_core220.dll
- opencv_features2d220.dll
- opencv_flann220.dll
- opencv_gpu220.dll
- opencv_highgui220.dll
- opencv_imgproc220.dll
- opencv_legacy220.dll
- opencv_ml220.dll
- opencv_objdetect220.dll
- opencv_video220.dll
任何其他“无法加载 DLL...”错误都可以通过尝试相同的过程并将相关列出的 DLL 加载到您的项目中来修复。如果您仍然遇到困难,请随时向社区寻求帮助。
System.TypeInitializationException
OpenCV 不支持从 Image<Emgu.CV.Structure.Bgr*,System.Byte*> 转换为 Image<Emgu.CV.Structure.Structure.Bgr*,System.Byte*>
其中*
是相关的图像深度或数据类型。
此错误在 2.3.* 版本及更高版本中出现,原因是程序无法访问 opencv_imgproc***.dll 或 opencv_core***.dll,即使它们存在于输出“bin”目录中。
有两种解决方案:将其添加到项目中并将属性设置为“始终复制”,因为它们是两个关键文件。或者用新的副本替换 Bin 文件夹中当前的副本也可以解决问题。
如果这两种方法都失败了,那么构建可能存在问题,请从 SourceForge 下载新副本再试。
希望本文能为您指明正确的方向。谢谢阅读!
历史
- [1] X64 源代码文件链接已正确链接。
- [2] 添加了关于版本号和 DLL 的注释,添加了 x86 版本并更新了 x64。
旧的 x64 版本在读取图像数据值时会遇到问题,因为最终它们会超出范围,尤其是在小型图像中。为了解决这个问题,picturebox
被设置为Autosize
并放置在一个启用了自动滚动功能的panel
中。 - [3] 版本 2.3.* 中出现了一个新错误,已添加解决方案,感谢 Mikos 找到此问题。