实际上,我认为主要问题是 Repa 没有扫描原语。(然而,一个非常相似的库Accelerate可以做到。)扫描有两种变体,前缀扫描和后缀扫描。给定一维数组
[a_1, ..., a_n]
前缀扫描返回
[0, a_0, a_0 + a_1, ..., a_0 + ... + a_{n-1} ]
而后缀扫描产生
[a_0, a_0 + a_1, ..., a_0 + a_1 + ... + a_n ]
我假设这就是您使用累积总和 ( cumsum
) 函数的目的。
前缀和后缀扫描很自然地推广到多维数组,并具有基于树缩减的有效实现。关于该主题的一篇相对较旧的论文是“Scan Primitives for Vector Computers”。此外,Conal Elliott 最近写了几篇关于在 Haskell中推导高效并行扫描的博文。
积分图像(在二维阵列上)可以通过两次扫描来计算,一次水平扫描,一次垂直扫描。在没有扫描原语的情况下,我实现了一个,效率非常低。
horizScan :: Array DIM2 Int -> Array DIM2 Int
horizScan arr = foldl addIt arr [0 .. n - 1]
where
addIt :: Array DIM2 Int -> Int -> Array DIM2 Int
addIt accum i = accum +^ vs
where
vs = toAdd (i+1) n (slice arr (Z:.All:.i))
(Z:.m:.n) = arrayExtent arr
--
-- Given an @i@ and a length @len@ and a 1D array @arr@
-- (of length n) produces a 2D array of dimensions n X len.
-- The columns are filled with zeroes up to row @i@.
-- Subsequently they are filled with columns equal to the
-- values in @arr.
--
toAdd :: Int -> Int -> Array DIM1 Int -> Array DIM2 Int
toAdd i len arr = traverse arr (\sh -> sh:.(len::Int))
(\_ (Z:.n:.m) -> if m >= i then arr ! (Z:.n) else 0)
计算积分图像的函数可以定义为
vertScan :: Array DIM2 Int -> Array DIM2 Int
vertScan = transpose . horizScan . transpose
integralImage = horizScan . vertScan
鉴于已为 Accelerate 实施扫描,将其添加到 Repa 应该不会太难。我不确定使用现有 Repa 原语的有效实现是否可行。