使用 WPF 进行图像处理






4.84/5 (17投票s)
在 WPF 应用程序中使用一些基本功能进行图像处理
引言
这是我在 C# 中的第十篇文章。这次,我尝试了 WPF 中的图像处理。

概述
本文的目的是构建一个类,允许任何 C# 程序员执行图像处理功能。在我之前的类似文章中,图像处理是在 Windows 应用程序中完成的。这次,目的是通过 WPF 进行尝试。我们之所以用 C# 来做,是因为它对我来说非常灵活。我们可以看到,当我们开始移动像素或更改基于考虑所有像素值的计算的值时,代码会变得更加复杂。
Application
该应用程序使用 WPF。我通过一个名为 CurrentImageHandler
的单独类来处理图像,其中完成了所有与图像相关的操作,包括保存、图形相关操作。功能包括获取图像信息、缩放、颜色滤镜、亮度、对比度、灰度滤镜、反色滤镜、全分辨率调整大小、旋转和翻转、裁剪和插入文本、任何其他图像以及一些几何形状。图像相关功能在名为 ImageFunctions.dll 的单独类库中处理。您可以从我 之前的文章 下载 ImageFunctions.dll。
什么是 WPF? (来自维基百科的定义)
Windows Presentation Foundation (WPF) 是由微软开发的,是一个用于在基于 Windows 的应用程序中渲染用户界面的计算机软件图形子系统。WPF,以前称为“Avalon”,最初作为 .NET Framework 3.0 的一部分发布。WPF 不依赖于旧的 GDI 子系统,而是利用 DirectX。WPF 试图为构建应用程序提供一致的编程模型,并实现用户界面和业务逻辑的分离。它类似于类似的面向 XML 的对象模型,例如 XUL 和 SVG 中实现的。
WPF 使用 XAML,一种 XML 的衍生语言,来定义和链接各种 UI 元素。WPF 应用程序也可以作为独立的桌面程序部署,或者作为嵌入式对象托管在网站上。WPF 旨在统一许多常见的用户界面元素,例如 2D/3D 渲染、固定和自适应文档、排版、矢量图形、运行时动画和预渲染媒体。然后,可以根据各种事件、用户交互和数据绑定来链接和操作这些元素。
自 Windows Vista 和 Windows Server 2008 起,所有 Microsoft Windows 版本都包含了 WPF 运行时库。Windows XP SP2/SP3 和 Windows Server 2003 的用户可以选择安装必要的库。
截至 2011 年,微软已发布了四个主要的 WPF 版本
- WPF 3.0 (2006 年 11 月)
- WPF 3.5 (2007 年 11 月)
- WPF 3.5sp1 (2008 年 8 月)
- WPF 4 (2010 年 4 月)
Microsoft Silverlight 利用 WPF 提供可与 Adobe Flash 相媲美的嵌入式 Web 控件,但更侧重于 UI 对象模型,而较少侧重于动画。Silverlight 不支持 3D 运行时渲染,但嵌入式 WPF 应用程序可以使用。然而,3D API 将在 Silverlight 5 中引入,该版本计划于 2011 年发布。
来自 MSDN Microsoft 的定义
Windows Presentation Foundation (WPF) 是一个下一代表示系统,用于构建具有视觉上令人惊叹的用户体验的 Windows 客户端应用程序。使用 WPF,您可以创建各种独立的和浏览器托管的应用程序。
WPF 的核心是一个独立于分辨率的矢量渲染引擎,该引擎构建用于利用现代图形硬件。WPF 通过一套全面的应用程序开发功能来扩展核心,其中包括可扩展应用程序标记语言 (XAML)、控件、数据绑定、布局、2D 和 3D 图形、动画、样式、模板、文档、媒体、文本和排版。WPF 包含在 Microsoft .NET Framework 中,因此您可以构建包含 .NET Framework 类库其他元素的应用程序。
WPF 以 .NET Framework 类型的一个子集的形式存在,这些类型大多位于 System.Windows
命名空间中。如果您之前使用托管技术(如 ASP.NET 和 Windows Forms)使用 .NET Framework 构建过应用程序,那么基本 WPF 编程体验应该很熟悉;您可以实例化类、设置属性、调用方法和处理事件,所有这些都可以使用您喜欢的 .NET Framework 编程语言(如 C# 或 Visual Basic)来完成。
WPF 为 Windows 客户端应用程序开发提供了额外的编程增强功能。一个明显的增强是能够使用标记和代码隐藏来开发应用程序,ASP.NET 开发人员应该对此很熟悉。您通常使用可扩展应用程序标记语言 (XAML) 标记来实现应用程序的外观,同时使用托管编程语言(代码隐藏)来实现其行为。这种外观和行为的分离具有以下优点
- 由于外观特定的标记与行为特定的代码没有紧密耦合,因此开发和维护成本降低。
- 开发效率更高,因为设计师可以同时实现应用程序的外观,而开发人员则实现应用程序的行为。
- 可以使用多种设计工具来实现和共享 XAML 标记,以满足应用程序开发贡献者的需求;Microsoft Expression Blend 提供适合设计师的体验,而 Visual Studio 2005 则面向开发人员。
- WPF 应用程序的全球化和本地化大大简化(参见 WPF 全球化和本地化概述)。
1. 缩略图视图
ThumbnailViewWindow tvWnd = new ThumbnailViewWindow(currImgHandler);
tvWnd.Show();
2. 颜色滤镜
颜色滤镜有时根据其光谱吸收类型进行分类:短波长通过、长波长通过或带通;漫射或锐截止;单色或转换。短波长通过滤镜传输所有直到指定波长的波长,然后吸收。长波长通过滤镜则相反。当广义地考虑时,每个滤镜都是一个带通滤镜。
这很简单——它只是向每种颜色添加或减去一个值。使用此滤镜最有用的方法是将两种颜色设置为 -255,以剥离它们并查看图像的一个颜色分量。例如,对于红色滤镜,保持红色分量不变,只需从绿色分量和蓝色分量中减去 255。
![]() |
![]() |
![]() |
![]() |
currImgHandler.CurrentFilterHandler.SetColorFilter(ColorFilterTypes.Red);
currImgHandler.CurrentFilterHandler.SetColorFilter(ColorFilterTypes.Green);
currImgHandler.CurrentFilterHandler.SetColorFilter(ColorFilterTypes.Blue);
3. 亮度
有时需要调整图像的亮度,这取决于个人选择。有时打印比查看需要更亮的图像。这只需根据用户的要求调整颜色分量即可完成。输入范围在 -255 到 255 之间。
![]() |
![]() |
BrightnessWindow bWnd = new BrightnessWindow();
bWnd.BrightnessValue = 0;
if (bWnd.ShowDialog().Value)
{
currImgHandler.CurrentBrightnessHandler.SetBrightness(bWnd.BrightnessValue);
PaintImage();
}
4. 对比度
图像的对比度无疑是一项复杂的处理。形成的颜色矩阵提供了一种略有不同的组合,可以使输入图像产生对比度。
![]() |
![]() |
ContrastWindow cFrm = new ContrastWindow();
cFrm.ContrastValue = 0;
if (cFrm.ShowDialog().Value)
{
currImgHandler.CurrentContrastHandler.SetContrast(cFrm.ContrastValue);
PaintImage();
}
5. 灰度
灰度滤镜是指特定图像的颜色模式。通俗地说,灰度图像就是黑白图像,不包含其他任何颜色。
基本上,这是一张黑白图片,图片中的任何颜色都会被转换为相应的灰色调(黑白之间的中间色调),从而使图像的每一部分仍然可区分。
![]() |
![]() |
currImgHandler.CurrentGrayscaleHandler.SetGrayscale();
6. 棕褐色调
棕褐色调滤镜在某种程度上与灰度相似。
![]() |
![]() |
currImgHandler.CurrentSepiaToneHandler.SetSepiaTone();
7. 反色
这非常简单,甚至不需要考虑颜色分量是否顺序颠倒。它只是获取当前分量的相反颜色。也就是说,例如,如果颜色分量是 00,那么我们得到的相反是 FF (255-0)。
![]() |
![]() |
currImgHandler.CurrentInvHandler.SetInversion();
8. 插入文本、任何其他图像和形状以及更多功能
这只是将任何需要的元素包含在图像中。这是通过图像的 Graphics
对象实现的。
![]() |
![]() |
![]() |
![]() |
//Inserting Text
InsertImageWindow iiWnd = new InsertImageWindow();
iiWnd.DisplayImagePositionX = iiWnd.DisplayImagePositionY = 0;
if (iiWnd.ShowDialog().Value)
{
currImgHandler.CurrentImgInsHandler.Insert
(iiWnd.DisplayImagePath, iiWnd.DisplayImagePositionX,
iiWnd.DisplayImagePositionY, iiWnd.DisplayImageWidth,
iiWnd.DisplayImageHeight, iiWnd.DisplayImageAngle, iiWnd.DisplayImageOpacity);
PaintImage();
}
//Inserting Images
InsertImageWindow iiWnd = new InsertImageWindow();
iiWnd.DisplayImagePositionX = iiWnd.DisplayImagePositionY = 0;
if (iiWnd.ShowDialog().Value)
{
currImgHandler.CurrentImgInsHandler.Insert
(iiWnd.DisplayImagePath, iiWnd.DisplayImagePositionX,
iiWnd.DisplayImagePositionY, iiWnd.DisplayImageWidth,
iiWnd.DisplayImageHeight, iiWnd.DisplayImageAngle, iiWnd.DisplayImageOpacity);
PaintImage();
}
//Inserting Shapes
InsertShapeWindow isWnd = new InsertShapeWindow();
isWnd.DisplayShapePositionX = isWnd.DisplayShapePositionY = 0;
if (isWnd.ShowDialog().Value)
{
currImgHandler.CurrentShapeInsHandler.Insert
(isWnd.DisplayShape, isWnd.DisplayShapePositionX,
isWnd.DisplayShapePositionY, isWnd.DisplayShapeWidth,
isWnd.DisplayShapeHeight, isWnd.DisplayShapeAngle,
isWnd.DisplayShapeOpacity, isWnd.DisplayShapeColor1,
isWnd.DisplayShapeColor2, isWnd.DisplayShapeGradientStyle);
PaintImage();
}
PaintImage() 方法
private void PaintImage()
{
System.IO.MemoryStream stream = new System.IO.MemoryStream();
currImgHandler.CurrentBitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
stream.Position = 0;
byte[] data = new byte[stream.Length];
stream.Read(data, 0, Convert.ToInt32(stream.Length));
BitmapImage bmapImage = new BitmapImage();
bmapImage.BeginInit();
bmapImage.StreamSource = stream;
bmapImage.EndInit();
MainImage.Source = bmapImage;
MainImage.Stretch = Stretch.Uniform;
}
关注点
我还包括了撤销选项、清除图像、图像信息、调整大小、旋转、裁剪等相对简单的功能。此外,还有一些功能与我之前关于图像处理的文章类似,所以我没有在这里包含它们。您可以从下载中获取。
另请参阅
结论
感谢您阅读本文。我期待您的反馈。您将从我这里期待更多。