我在我的一个旧项目中得到以下代码:
-- |ImageOperation is a name for unary operators that mutate images inplace.
newtype ImageOperation c d = ImgOp (Image c d-> IO ())
-- |Compose two image operations
(#>) :: ImageOperation c d-> ImageOperation c d -> ImageOperation c d
(#>) (ImgOp a) (ImgOp b) = ImgOp (\img -> (a img >> b img))
-- |An unit operation for compose
nonOp = ImgOp (\i -> return ())
-- |Apply image operation to a Copy of an image
img <# op = unsafeOperate op img
-- | Apply the operation on a clone of an image
operate (ImgOp op) img = withClone img $ \clone ->
op clone >> return clone
unsafeOperate op img = unsafePerformIO $ operate op img
Run Code Online (Sandbox Code Playgroud)
其主要目的是允许组合opencv运算符,这些运算符在适当的位置运行并接受相同格式和维度的图像.这是一个重要的优化,因为例如,如果没有它绘制100行将分配1mb图像数百次.当前的界面工作得很好,但我觉得可能有一些标准的方法来做这样的事情.所以,
编辑: 二进制操作的一个示例是"拍摄图像,制作模糊的副本并从原始图像中减去".返回结果'.只有IO monad中具有最小副本的有效版本将类似于:
poorMansHighPass img = do
x <- clone img
gaussian (5,5) x
subtract x img
return x
Run Code Online (Sandbox Code Playgroud)
虽然我可以创建这样的运算符,但我更喜欢的东西更多是原始运算符的组合,而不是丑陋的不安全的io代码.
好吧,我至少可以指出你目前正在使用的一些模式.
所以我们有一个表示对一些可变数据的引用的类型,以及一个表示对它的不透明操作的类型.我们还有一个null op和一个组合函数,它给出了一个明显的Monoid例子:
instance Monoid (ImageOperation c d) where
mempty = nonOp
mappend = (#>)
Run Code Online (Sandbox Code Playgroud)
这至少是你可以使用的一个标准名称.
此外,上述Monoid实际上是两种其他众所周知类型的属性的直接结果:
的Applicative和/或Monad例如用于(->) a描述通过将所有的人都以一个单一的参数,与所述组合物的功能的图像合成功能.基本上是Readermonad 的轻量级内联版本.
它的Monad实例IO,或者更确切地说是它所暗示的幺半群结构.通过固定IO类型参数(),monad定律简化为一个简单的monoid,具有return ()单位和(>>)幺半群运算.
为了重建你的组合,给定两个函数(unwrapped ImageOperations)并想象隐含的monoid IO ()是一个实际的实例,我们可以写:
nonOp = pure mempty
x #> y = mappend <$> x <*> y
Run Code Online (Sandbox Code Playgroud)
同样值得注意的是,像读者monad和monad允许可变状态的组合实质上描述了"具有可变引用的周围环境",也称为可变全局变量,除了"global"在这里意味着"在单个计算中的组合"单子".我实际上已经构造了这样一个monad,使用ReaderT和STM.
它处理组合操作.要实际运行一个操作,你需要一个Image并且我正在收集你只想操作克隆,其创建效率低下.幸运的是,考虑到上面的结构非常普遍,在实际运行它之前,Monoid你真的没有什么可以塞进ImageOperation去的.生成克隆可能是一个IO操作,并且是我假设正在进行operate的操作 - 可能没有任何其他方法可以做到这一点.
除此之外,如果你对构造整个事物的其他方法感兴趣,一个明显的变体就是将其Image换成代表构造一个过程的东西,运算符被合并以转换使用类似的东西生成的图像operate.不过,我不知道这是否会让你获得任何好处.
事实上,我倾向于怀疑还有其他方法可以做到这一点.你正在编写一个FFI绑定到一个高度命令性的库,你可以做的就是伪装它.
但是,我不确定为什么你有一个不安全的版本operate.这有什么实际意义?
我也不确定你想要将这种二元运算符概括为什么 - ImageOperation除了你在这里之外你没有什么可以做的.你的意思是推广ImageOperation到一个以上的图像可变参考?或者一些涉及返回不仅仅是其他东西上的图像操作IO ()?
编辑:好的,让我们来看看如何分解poorMansHighPass.希望我正确地阅读它在这里做的事情:
首先,它gaussian是独立的,可以作为自己的操作来考虑:gauss' = ImgOp . gaussian.
接下来,subtract也可以通过额外的参数化进行分解Image:subtr' = ImgOp . flip subtract.
这两个是函数的核心,它们可以通常的方式组合:poorMansHP' img = gauss' (5, 5) #> subtr' img.恢复原始函数所需的最后一件事是,img赋予的参数poorMansHP'必须是克隆传递给内部函数的相同图像operate.
首先,我们将明确解包ImageOperation并在重新实现中使用它:
poorMansHighPass img =
let (ImgOp op) = gauss' (5, 5) #> subtr' img
in do x <- clone img
op x
return x
Run Code Online (Sandbox Code Playgroud)
替代withClone使用在clone这里:
poorMansHighPass img =
let (ImgOp op) = gauss' (5, 5) #> subtr' img
in withClone img $ \x -> do op x
return x
Run Code Online (Sandbox Code Playgroud)
Desugar do块:
poorMansHighPass img =
let (ImgOp op) = gauss' (5, 5) #> subtr' img
in withClone img $ \x -> op x >> return x
Run Code Online (Sandbox Code Playgroud)
...显然包含重新实现operate,所以替换它并简化:
poorMansHighPass img = operate (gauss' (5, 5) #> subtr' img) img
Run Code Online (Sandbox Code Playgroud)
更有趣poorMansHighPass的是以修改参数而不是克隆的方式实现,这将允许将其打包为ImageOperation自身.可能这是它应该做的,我误读了你的代码?
无论如何,重构的基本结构是相同的,但是你需要一个不同的组合运算符 - 而不是按顺序将两个运算符应用于同一个输入,它需要在重新组合之前在内部创建一个输入的克隆.结果.我粗略地了解了什么样的结构可以使这项工作顺利进行,如果您愿意,我可以详细说明,但我必须稍微努力以确保它的行为正常.
| 归档时间: |
|
| 查看次数: |
209 次 |
| 最近记录: |