使用边界情况在修复数组上映射窗口函数

时间:2014-05-12 15:09:00

标签: haskell repa

问题

我正在寻找可能已经存在的修复库中的函数。我想要一个功能:

  1. 采用2D数组
  2. 指定窗口大小的两个整数
  3. 在2D阵列上的给定大小的每个窗口中,计算新值,例如这个特定窗口中的小值。
  4. 实施例

    min函数与3x3窗口映射到:

    | 1 2 3 4 3
      4 5 6 7 2
      7 8 9 4 2
      5 4 8 1 6
      8 5 3 3 2 |
    

    会回来:

    | 1 1 2 2 2
      1 1 2 2 2
      4 4 1 1 1
      4 3 1 1 1
      4 3 1 1 1 |
    

    请注意,我使用的方案类似于Data.Array.Repa.Stencil中的BoundClamp构造函数。这不是模板卷积,即它没有将模板应用于2D阵列的每个元素。相反,它在阵列上的每个窗口执行一个函数,边缘处的超出范围的元素在2D数组的边缘被赋予最接近的值。

    可能的解决方案类型

    该功能可能如下所示:

    mapF
      :: Source r a
      => Boundary a            -- ^ How to handle the boundary of the array.
      -> (Int,Int)             -- ^ window size in the X and Y direction.
      -> (Array r DIM2 a -> b) -- ^ function over window e.g. to return the minimum value.
      -> Array r DIM2 a        -- ^ Array to apply function to.
      -> Array r DIM2 b
    

    这个问题是否已存在,或者编写代码是否微不足道?

1 个答案:

答案 0 :(得分:1)

我在修复时生锈了,但是你可以使用traverse并手动检测数组边界。考虑类型:

traverse ::
  (Source r a, Shape sh, Shape sh') =>
  Array r sh a
  -> (sh -> sh') -> ((sh -> a) -> sh' -> b) -> Array D sh' b

该函数采用原始数组,一个产生新形状的函数,一个采用查找函数的函数和一个生成新值的索引,并生成新的(延迟的)数组。

一个简单的解决方案是检查索引的所有邻居,使用minmax控制绑定:

import qualified Data.Array.Repa as R
import           Data.Array.Repa (Z(..), traverse, fromListUnboxed, toList, (:.)(..))
import Prelude
import Data.List.Split

main = do let a = fromListUnboxed (Z :. h :. w) ( [1..6] ++ [2..7] ++ [3..8] ++ [4..9] ++ [5..10] ::  [Int])
              r = traverse a id (\f (Z :. y :. x) -> minimum [f (Z :. yi :. xi) | xi <- idx w x, yi <- idx h y])
          printArray a
          printArray r
  where
    idx b i = map (bound b) [i, i+1, i-1]
    bound b = min (b-1) . max 0
    w = 6 :: Int
    h = 5 :: Int

    printArray = putStrLn . unlines . map show . chunksOf w . toList

反对这个解决方案的主要原因是性能(许多相同数字的比较,应该静态消除的许多边界检查)。 OTOH,你的问题只是要求一个简单的解决方案,而且似乎并没有过分关注性能。

如果内置了更快的解决方案,我也很感兴趣。