14.2.6 实现模糊效果

14.2.6 实现模糊效果

 

    我们最终的效果不只是一个颜色滤镜。模糊图像的过程依赖于计算新的像素值,基于多个原始像素。我们仍然可以执行逐个像素转换图像。不过,转型需要访问整个图像,以及我们想要转换的像素坐标。

    我们把 RunEffect 和 RunEffectParallel 的实现留下作为练习,但它是相当简单的;这是只需改变循环的具体内容,给转换函数更多的信息。从序列到并行形式的转换,与这个至于效果颜色滤镜是相同的。如果你被卡住,去本书的网站上看看完整的源代码。

    模糊转型本身是很有趣,如清单 14.19 中所示。它并不特别困难,它提供了一个很好的声明性编程的示范。

 

Listing 14.19 Implementing the Blur effect (F#)

 

let blur(arr:SimpleColor[,], x, y) =
  let height, width = arr.GetLength(0) - 1, arr.GetLength(1) – 1
  let checkW x = max 0 (min width x)
  let checkH y = max 0 (min height y)

  [ for dy in -2 .. 2 do
    for dx in -2 .. 2 do
      yield arr.[checkH(y + dy), checkW(x + dx)] ]
  |> List.average

 

    blur 函数取三个参数,指定的图像,我们想要计算像素的 X,Y 坐标。如果以命令式风格实现 blur,就需要创建一个变量,将其初始化为 0,把附近所有的像素的颜色加起来,再把结果除以像素数,以获得平的均颜色。

    在 F# 中,我们可以使用更加声明式的方法,来写平均颜色的计算。我们首先声明一些工具函数,用来检查该索引在数组的范围中。接下来,要使用序列表达式,来创建一个列表,包含所有邻近像素的颜色, 用 List.average 计算平均值。

 

探索 List.average

 

    要计算值的平均值,List.average 函数需要知道三件事:

    ■ 对于特定的类型,"零"值是什么。

    ■ 如何将值相加。

    ■ 如何将特定类型的值除以整数。

    前两项对于计算这个列表的和是足够的。然后,需要把结果除以列表中的元素的个数,就是这里的第三项。在我们的效果中,我们将处理 SimpleColor 类型的值,这个类型实现了加法运算符。我们还添加了特殊成员,Zero 和  DivideByInt。average 函数使用这些成员,它是一个泛型函数,但它需要这个类型实现适当的成员。像这样的约束并不能用 .NET 的泛型表示。可用于泛型类型的约束,可能要求无参数构造函数或接口,但不是特定的成员。

    为此,F# 实现了自己的编译时成员约束的机制。在这种情况下,该约束由 F# 编译器在编译时解决(相对于 .NET 泛型的约束,也是由 .NET 的运行时检查)。下面的示例演示了如何使用此功能,来定义一个函数,计算任何值的一半,这个值支持除以整数:

 

> let inline half (num: ^a) : ^a =
      LanguagePrimitives.DivideByInt< (^a) > num 2
;;
val inline half : ^a -> ^a
when ^a : (static member DivideByInt : ^a * int -> ^a)

> half(42.0);;
val it : float = 21.0

> half(SimpleColor(255, 128, 0));;
val it : SimpleColor = SimpleColor {B = 0; G = 64; R = 127;}

> half("hello");;
error FS0001: The type 'string' does not
support any operators named 'DivideByInt'

 

    这个函数叫 half,我们使用显式类型注释,来指定参数 num 的类型,结果的类型是泛型参数 ^a。注意,我们将使用的类型参数,以(^) 开头,而不是使用惯常的撇号(')。在函数体中,我们调用来自 LanguagePrimitives 模块的 DivideByInt 函数。这是一个有成员约束的基元函数,它把数值类型除以整数,或者,使用 DivideByInt 成员,如果可用的话。

    正如你所看到的,成员约束表现为推导出的类型签名。现在,我们有了这个函数,可以将它用于各种数值类型,而且,也可以处理我们的 SimpleColor 类型。如果我们尝试调用它,用不支持 DivideByInt 操作的参数值,就会发生编译时错误。

 

    在这个示例中,我们已经研究了一个大型应用程序的关键部分。可以从本书的网站上获得完整的源代码,看到我们这一章中实现的部分如何相连。虽然这个应用程序的最重要的部分使用了可变数组,但是,设计的整个应用程序还是函数式的,包括第 11 章中所述的以函数式风格使用数组。这种方法让我们轻松和安全地并行化核心算法。

    这是一个以行为为中心的应用程序的示例。我们主要关心的是如何并行化单独的行为。并行化以行为为中心的应用程序的另一种方式,是以并行的方式运行不同的行为。我们可能想要批处理一系列的图像,并每个图像应用多个效果。在下一节中,我们会把注意力转向以数据为中心的应用程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值