65.9K
CodeProject 正在变化。 阅读更多。
Home

模糊和混合的乐趣

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (4投票s)

2013 年 5 月 30 日

CPOL

7分钟阅读

viewsIcon

21616

downloadIcon

489

一个有趣的实用工具,用于对图像应用混合模式。

引言

模糊和混合可以将普通图像变成不寻常的图像。通常,结果令人惊讶且难以预测。我想看看在一个32位机器上使用C#和WPF,通过一个模糊和混合实用工具,在代码尽可能简单的情况下,能达到什么程度。由于性能在图像处理中始终是一个问题,代码经过了检测,允许操作员查看其运行情况。如果你对图像处理感兴趣,你可能会喜欢这个实用工具。无论如何,你可能会对我如何规避一些问题或常见混合模式的定义感兴趣。我们没有尝试对混合模式进行分类或精确展示它们的作用。用户界面基于奇思妙想而非设计原则。遵循了MVVM模式,但并非严格遵守。希望任何不足之处都能通过娱乐价值来弥补。

Blur and Blend

入门

该实用工具将图像与其自身的模糊副本进行混合。操作员可以指定模糊的像素量(0-50)以及要模糊的RGB通道。支持29种常见混合模式。正常是无操作混合。该实用工具在一个可调整大小的窗口中。当窗口调整大小时,输出图像也会调整大小。缩放表示输出图像的图像压缩或拉伸百分比。100表示图像以其实际大小显示。性能信息显示在文本框中。显示了模糊量、C#垃圾回收堆的内存大小以及5个关键方法使用的毫秒时间。这些信息可以滚动以显示任何操作。  

内存问题

图像数据消耗大量内存。一个10MB的图像需要40MB的内存来存储alpha、红色、绿色和蓝色通道。该实用工具使用图像数据的几个副本进行工作。这些副本分配在大对象堆(LOH)上。在32位机器上进行测试时,我随机遇到内存不足异常。有时我可以运行一整天而不会重现问题。其他时候,该实用工具从一开始就无法使用。重新编译为64位消除了异常,但没有解决糟糕的内存使用问题。观察到GC内存随机翻倍。我从未确定问题的原因。为了解决这个问题,我使用了一个固定大小的LohRgbArray类来保存所有图像数据。使用其默认大小,可以存储高达10 MB的任何图像。LohRgbArray的实例在启动期间分配,并且永不释放。该实用工具会重复使用它们来处理加载的任何图像,无论其大小如何。不幸的是,这限制了可以加载的图像的最大尺寸。但是,该实用工具在32位机器上不再出现内存不足的问题。

性能

在相当大的图像上执行交互式图像处理需要在短时间内消耗大量的CPU资源。当拖动WPF滑块时,图像应至少每秒更新10次,以避免操作员感到沮丧。当执行复杂的图像操作时,很难达到这种性能。特别是使用实用工具中提供的模糊方法。它并不慢。你可以尝试使用Paint.NET进行图像模糊作为比较。然而,它的CPU使用率随图像大小的平方而增加。在超过一定大小的图像上使用它并不愉快。像Photoshop这样的程序通过使用OpenCL在GPU上运行图像处理代码来解决这个问题。它很复杂,在任何GPU上都很难实现,并且失去了尝试算法的乐趣。这里采用的方法是使用多个CPU核心来足够快地处理适合屏幕的图像。你的体验将因你的CPU而异。避免了对C#的调整。我尝试的优化加快了一个CPU的速度,但减慢了另一个CPU的速度。

为了支持在多个核心上运行图像处理代码,图像被分成相邻的行和列组。组的数量等于核心的数量。一个PicSeg数组存储每个组的起始和结束行或列。PicSeg类如下所示...

public class PicSeg
{
    public int startRow;
    public int endRow;
    public int startColumn;
    public int endColumn;

一旦图像被分成行和列的条带,C# Task类可用于将计算分配到所有核心。例如,在numTasks个核心上对行和列进行高斯模糊的编码如下...

Task[] tasks = new Task[numTasks];
//start Horizontal Blur tasks and wait till done
for (int i = 0; i < numTasks; ++i)
{
    PicSeg seg = ps[i]; //ps array was computed when Numtasks was set
    tasks[i] = Task.Factory.StartNew(() => 
          BlurRows(seg.startRow, seg.endRow));     //blur a strip of rows
}
Task.WaitAll(tasks);

//start Vertical Blur tasks and wait till done
for (int i = 0; i < numTasks; ++i)
{
    PicSeg seg = ps[i];
    tasks[i] = Task.Factory.StartNew(() => 
         BlurColumns(seg.startColumn, seg.endColumn));
         //blur a strip of columns    
}
Task.WaitAll(tasks);

所有图像处理操作都使用了类似的逻辑,包括计算混合和设置不透明度。与模糊相比,这两个操作花费的时间很少。但是使用所有核心可以节省每次操作的时间。每秒10次,零星的几毫秒加起来就很多了。只执行必需的操作。例如,更改不透明度只会重新计算不透明度,而不会重新计算模糊或混合。只有当所有操作都执行时,仪表才处于活动状态。通过更改Sigma来完成此操作。当模糊更改时,所有操作都必须执行。请注意,Task.WaitAll会阻塞WPF。这会稍微影响性能,但其简单性太具吸引力而无法放弃。

如果你调出任务监视器并在自动重复Sigma滑块时查看性能,你会发现只使用了80%的CPU。这令人失望,因为图像处理已分发到所有核心。理想情况下,应消耗100%的CPU。该实用工具会打开一个控制台窗口,用于显示关键的“模糊之间”性能指标。这是连续模糊之间的毫秒时间。由于所有占用大量CPU的方法都在单独的线程上执行,“模糊之间”是衡量WPF通过消息循环以及发生的其他任何事情所需的时间。要消耗100%的CPU,“模糊之间”必须降至0。不幸的是,“模糊之间”随着模糊的执行而缓慢增加。这种增加是由于在可滚动文本框中显示性能数据造成的。性能数据显示为一个字符串,当进行模糊操作时,其长度会增加。要阻止“模糊之间”的增加,请转到MyModel.cs中的Msg属性并注释掉set中的代码。

混合模式

支持最著名的一些和少数不常见的混合模式。请记住,混合模式没有官方定义。如有疑问,请与Photoshop进行比较。该实用工具可用作开发新混合模式的框架。可以自由地尝试任何你能想到的混合模式。请记住,要使混合模式被接受,它应该平滑。如果它有突然的跳跃,它的使用将受到抵制。一些著名的混合模式并不平滑,但现在纠正它们已经太晚了。

选择要应用的正确混合模式取决于图像。对于一个图像看起来无法使用的混合模式,对于另一个图像可能是完美的选择。如果一种模式看起来过于极端,请降低不透明度。有些混合模式在应用模糊之前是无用的。例如,图像与自身进行差异运算会得到黑色。开始应用模糊,你可能会感到惊讶。

该实用程序的默认照片是Liz.jpg。下面是Liz在应用除法模式前后的对比。降低不透明度会带回更多的肤色。

Divide mode

下面使用一些简单的形状来说明在差异模式下模糊通道的组合。

Difference mode and channels

结束语

可以很容易地使用更少的混合模式。尽管有些可能看起来相同,但查看代码会发现所有模式都是独一无二的。高质量的模糊算法对于实用工具有效并不是必需的。这里使用的模糊算法精确,但对于大图像来说太慢。它可以被更快速的方法替换。将工作分解到多个核心可以加快模糊速度,但不足以舒适地处理大型数字图像。一个可能的解决方法可能是将大图像调整大小以适应屏幕。在较小调整大小的图像上进行工作可以避免烦人的延迟。如果以及当图像保存时,可以在不实时的情况下再次对较大的原始图像执行操作。我的开发环境是2.67 GHz的4核Intel。我无法找到的一个开发工具是一个好的免费内存分析器。

历史

  • 首次发布 - 2013年6月。
© . All rights reserved.