Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/xpath/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Image Haskell中的图像邻域处理_Image_Haskell_Image Processing_Functional Programming - Fatal编程技术网

Image Haskell中的图像邻域处理

Image Haskell中的图像邻域处理,image,haskell,image-processing,functional-programming,Image,Haskell,Image Processing,Functional Programming,我是Haskell的新手,试图从图像处理的角度来学习它 到目前为止,我一直在思考如何在Haskell(或者任何函数式编程语言)中实现邻域过滤算法 空间平均滤波器(比如3x3内核,5x5图像)在功能上是如何编写的?来自一个完全必要的背景,我似乎无法想出一种方法来构造数据以使解决方案优雅,或者不通过迭代图像矩阵来实现,这似乎不是很具有声明性。使用函数式语言很容易优雅地处理邻里关系。像内核卷积这样的操作是高阶函数,可以用函数式编程语言的一种常用工具——列表来编写 为了编写一些真正有用的代码,我们将首先

我是Haskell的新手,试图从图像处理的角度来学习它

到目前为止,我一直在思考如何在Haskell(或者任何函数式编程语言)中实现邻域过滤算法


空间平均滤波器(比如3x3内核,5x5图像)在功能上是如何编写的?来自一个完全必要的背景,我似乎无法想出一种方法来构造数据以使解决方案优雅,或者不通过迭代图像矩阵来实现,这似乎不是很具有声明性。

使用函数式语言很容易优雅地处理邻里关系。像内核卷积这样的操作是高阶函数,可以用函数式编程语言的一种常用工具——列表来编写

为了编写一些真正有用的代码,我们将首先假装解释一个库

假装 可以将每个图像视为从图像中的坐标到该坐标处保存的数据值的函数。这将在所有可能的坐标上定义,因此将它与一些告诉我们函数定义位置的
边界配对将非常有用。这将建议使用以下数据类型

data Image coordinate value = Image {
    lowerBound :: coordinate,
    upperBound :: coordinate,
    value      :: coordinate -> value
}
Haskell有一种非常类似的数据类型,称为。此数据类型附带了
图像
中的
函数不具备的附加功能-它记住每个坐标的值,因此无需重新计算。我们将使用三个函数来处理
Array
s,我将描述如何为上面的
Image
定义它们。这将帮助我们看到,即使我们使用非常有用的
数组
类型,所有内容都可以用函数和代数数据类型来编写

 type Array i e = Image i e
bounds
获取
数组的边界

 bounds :: Array i e -> (i, i)
 bounds img = (lowerBound img, upperBound img)
 (!) :: Array i e -> i -> e
 img ! coordinate = value img coordinate
 makeArray :: Ix i => (i, i) -> (i -> e) -> Array i e
 makeArray (lower, upper) f = Image lower upper f
数组中查找值

 bounds :: Array i e -> (i, i)
 bounds img = (lowerBound img, upperBound img)
 (!) :: Array i e -> i -> e
 img ! coordinate = value img coordinate
 makeArray :: Ix i => (i, i) -> (i -> e) -> Array i e
 makeArray (lower, upper) f = Image lower upper f
最后,
makeArray
构建一个
Array

 bounds :: Array i e -> (i, i)
 bounds img = (lowerBound img, upperBound img)
 (!) :: Array i e -> i -> e
 img ! coordinate = value img coordinate
 makeArray :: Ix i => (i, i) -> (i -> e) -> Array i e
 makeArray (lower, upper) f = Image lower upper f
Ix
是一个类型类,用于处理类似于图像坐标的对象,它们有一个
范围
。大多数基本类型都有实例,如
Int
Integer
Bool
Char
,等等。例如
(1,5)
范围是
[1,2,3,4,5]
。还有一个产品实例或事物元组实例,它们本身具有
Ix
实例;元组的实例覆盖每个组件范围的所有组合。例如,
范围(('a',1),('c',2))

[('a',1),('a',2),
 ('b',1),('b',2),
 ('c',1),('c',2)]`
array ((1,1),(5,5))
[((1,1),2.0),((1,2),3.0),((1,3),4.0),((1,4),5.0),((1,5),6.0)
,((2,1),3.0),((2,2),4.0),((2,3),5.0),((2,4),6.0),((2,5),7.0)
,((3,1),4.0),((3,2),5.0),((3,3),6.0),((3,4),7.0),((3,5),8.0)
,((4,1),5.0),((4,2),6.0),((4,3),7.0),((4,4),8.0),((4,5),9.0)
,((5,1),6.0),((5,2),7.0),((5,3),8.0),((5,4),9.0),((5,5),10.0)]
array ((1,1),(5,5))
[((1,1),1.3333333333333333),((1,2),2.333333333333333),((1,3),2.9999999999999996),((1,4),3.6666666666666665),((1,5),2.6666666666666665)
,((2,1),2.333333333333333),((2,2),3.9999999999999996),((2,3),5.0),((2,4),6.0),((2,5),4.333333333333333)
,((3,1),2.9999999999999996),((3,2),5.0),((3,3),6.0),((3,4),7.0),((3,5),5.0)
,((4,1),3.6666666666666665),((4,2),6.0),((4,3),7.0),((4,4),8.0),((4,5),5.666666666666666)
,((5,1),2.6666666666666665),((5,2),4.333333333333333),((5,3),5.0),((5,4),5.666666666666666),((5,5),4.0)]
我们只对
Ix
typeclass中的两个函数感兴趣,
range::Ix a=>(a,a)->[a]
inRange::Ix a=>a->(a,a)->Bool
inRange
快速检查值是否在
范围的结果中

现实 实际上,
makeArray
不是由
Data.Array
提供的,但是我们可以用
listArray
来定义它,它从一个项目列表中以与其
边界的
范围
相同的顺序构造一个
数组

import Data.Array

makeArray :: (Ix i) => (i, i) -> (i -> e) -> Array i e
makeArray bounds f = listArray bounds . map f . range $ bounds
当我们用一个核对一个数组进行卷积时,我们将通过把核中的坐标加到我们正在计算的坐标来计算邻域。
Ix
typeclass不要求我们可以将两个索引组合在一起。在基中有一个候选类型类用于“组合的事物”,即
幺半群,但没有
Int
Integer
或其他数字的实例,因为组合它们的方法不止一种:
+
*
。为了解决这个问题,我们将为与名为
+的新操作符相结合的东西创建自己的类型类
Offset
。通常我们不创建类型类,除非有规律。我们只能说,
Offset
应该“合理地”使用
Ix

class Offset a where
    (.+.) :: a -> a -> a
Integer
s,Haskell在编写类似
9
的整数文本时使用的默认类型,可以用作偏移量

instance Offset Integer where
    (.+.) = (+)
此外,
偏移量
的成对或元组可以成对组合

instance (Offset a, Offset b) => Offset (a, b) where
    (x1, y1) .+. (x2, y2) = (x1 .+. x2, y1 .+. y2)
在写卷积之前,我们还有一条皱纹——我们将如何处理图像的边缘?为了简单起见,我打算用
0
填充它们<代码>键盘背景
制作一个版本的
数组的
边界之外的任何地方定义,它返回
背景

pad :: Ix i => e -> Array i e -> i -> e
pad background array i =
    if inRange (bounds array) i
    then array ! i
    else background
我们现在准备为
卷积
编写一个高阶函数<代码>卷积AB
将图像
b
与内核
a
卷积<代码>卷积
是高阶的,因为它的每个参数及其结果都是一个
数组
,它实际上是一个函数的组合
及其
边界

convolve :: (Num n, Ix i, Offset i) => Array i n -> Array i n -> Array i n
convolve a b = makeArray (bounds b) f
    where
        f i = sum . map (g i) . range . bounds $ a
        g i o = a ! o * pad 0 b (i .+. o)
为了使用内核
a
对图像
b
进行
卷积,我们在与
b
相同的
边界上定义了一个新图像。图像中的每个点都可以通过函数
f
计算,该函数
求和
s内核
a
中的值与
pad
ded图像
b
中的值的乘积(
*
),该函数用于内核
a
范围
中的每个偏移
o

例子 使用上一节中的六个声明,我们可以编写您请求的示例,一个应用于5x5图像的3x3内核的空间平均过滤器。下面定义的内核
a
是一个3x3图像,它使用9个采样邻居中每一个的值的九分之一。5x5图像
b
是一个渐变,从左上角的
2
增加到右下角的
10

main = do 
    let
        a = makeArray ((-1, -1), (1, 1)) (const (1.0/9))
        b = makeArray ((1,1),(5,5)) (\(x,y) -> fromInteger (x + y))
        c = convolve a b
    print b
    print c
打印
ed输入