patch-image 0.3.0.1 → 0.3.1
raw patch · 10 files changed
+468/−336 lines, 10 filesdep +storable-tupledep ~knead
Dependencies added: storable-tuple
Dependency ranges changed: knead
Files
- Changes.md +4/−0
- patch-image.cabal +8/−4
- src/Accelerate.hs +26/−8
- src/Arithmetic.hs +2/−11
- src/Complex.hs +29/−0
- src/Knead.hs +103/−136
- src/Knead/CArray.hs +119/−0
- src/Knead/Shape.hs +176/−0
- src/KneadShape.hs +0/−176
- src/MatchImageBorders.hs +1/−1
Changes.md view
@@ -1,5 +1,9 @@ # Change log for the `patch-image` package +## 0.3.1:++ * Speed up computation by moving more stuff to Knead/LLVM.+ ## 0.3: * Allow to save program state, modify it manually
patch-image.cabal view
@@ -1,5 +1,5 @@ Name: patch-image-Version: 0.3.0.1+Version: 0.3.1 License: BSD3 License-File: LICENSE Author: Henning Thielemann <haskell@henning-thielemann.de>@@ -41,7 +41,7 @@ README.md Source-Repository this- Tag: 0.3.0.1+ Tag: 0.3.1 Type: darcs Location: http://hub.darcs.net/thielema/patch-image/ @@ -67,7 +67,9 @@ Main-Is: Knead.hs Other-Modules: MatchImageBorders- KneadShape+ Knead.CArray+ Knead.Shape+ Complex State LinearAlgebra Arithmetic@@ -81,7 +83,7 @@ If flag(llvm) Build-Depends:- knead >=0.2.1 && <0.3,+ knead >=0.2.2 && <0.3, llvm-extra >=0.7 && <0.8, llvm-tf >=3.1 && <3.2, tfp >=1.0 && <1.1,@@ -92,6 +94,7 @@ enumset >=0.0.4 && <0.1, containers >=0.4.2 && <0.6, fft >=0.1.7 && <0.2,+ storable-tuple >=0.0.3 && <0.1, carray >=0.1.5 && <0.2, array >=0.4 && <0.6, cassava >=0.4.5 && <0.5,@@ -112,6 +115,7 @@ State LinearAlgebra Arithmetic+ Complex Degree Option.Utility Option
src/Accelerate.hs view
@@ -5,6 +5,7 @@ import qualified State import qualified Arithmetic as Arith+import qualified Complex as Komplex import qualified Degree import LinearAlgebra ( absolutePositionsFromPairDisplacements, fixAtLeastOnePosition,@@ -724,11 +725,24 @@ type GenDIM2 a = Z :. a :. a -shrinkFactors :: (Integral a) => DIM2 -> GenDIM2 a -> GenDIM2 a -> GenDIM2 a-shrinkFactors (Z:.heightPad:.widthPad)+-- cf. Arithmetic.minimumOverlapAbsFromPortion+minimumOverlapAbsFromPortion ::+ (Num a, Ord i) => (a -> i, i -> a) -> a -> (i, i) -> i+minimumOverlapAbsFromPortion+ (afloor, fromInt) minOverlapPortion (width, height) =+ afloor $ minOverlapPortion * fromInt (min width height)++shrinkFactors ::+ (Num a, Integral i) =>+ (a -> i, i -> a) ->+ DIM2 -> a -> GenDIM2 i -> GenDIM2 i -> GenDIM2 i+shrinkFactors methods (Z:.heightPad:.widthPad) minOverlapPortion (Z :. heighta :. widtha) (Z :. heightb :. widthb) =- let yk = divUp (heighta+heightb) $ fromIntegral heightPad- xk = divUp (widtha +widthb) $ fromIntegral widthPad+ let minOverlap =+ minimumOverlapAbsFromPortion methods minOverlapPortion+ (min widtha widthb, min heighta heightb)+ yk = divUp (heighta+heightb-minOverlap) $ fromIntegral heightPad+ xk = divUp (widtha +widthb -minOverlap) $ fromIntegral widthPad in Z :. yk :. xk {-@@ -740,7 +754,8 @@ let run = Run.with CUDA.run1 $ \minimumOverlap a b -> let factors@(_z:.yk:.xk) =- shrinkFactors padExtent+ shrinkFactors (A.floor, A.fromIntegral) padExtent+ minimumOverlap (A.unlift $ A.shape a) (A.unlift $ A.shape b) scalePos = Exp.modify (expr, (expr,expr)) $@@ -792,7 +807,9 @@ Run.with CUDA.run1 $ \minimumOverlap a b -> let shapeA = A.unlift $ A.shape a shapeB = A.unlift $ A.shape b- factors@(_z:.yk:.xk) = shrinkFactors padExtent shapeA shapeB+ factors@(_z:.yk:.xk) =+ shrinkFactors (A.floor, A.fromIntegral) padExtent+ minimumOverlap shapeA shapeB coarsed@(coarsedx,coarsedy) = mapPair ((xk*), (yk*)) $ Exp.unliftPair $ A.snd $ A.the $ argmaximum $@@ -862,7 +879,8 @@ in \minimumOverlap mMaximumDiff a b -> let factors@(Z:.yk:.xk) =- shrinkFactors padExtent (A.arrayShape a) (A.arrayShape b)+ shrinkFactors (floor, fromIntegral) padExtent+ minimumOverlap (A.arrayShape a) (A.arrayShape b) (_score, shrunkd@(shrunkdx, shrunkdy)) = Acc.the $ overlapShrunk minimumOverlap factors a b@@ -1597,7 +1615,7 @@ map picColored pics, map (mapPair- (mapPair (realToFrac, realToFrac), Arith.mapComplex realToFrac))+ (mapPair (realToFrac, realToFrac), Komplex.map realToFrac)) posRots)
src/Arithmetic.hs view
@@ -1,5 +1,6 @@ module Arithmetic where +import qualified Complex as Komplex import qualified Data.Complex as Complex import Data.Complex (Complex, ) @@ -71,7 +72,7 @@ let posRotPics = zipWith (\(angle,pic) (pos,rot) ->- (pos, (pairFromComplex (Complex.cis angle * rot), pic)))+ (pos, (Komplex.toPair (Complex.cis angle * rot), pic))) picAngles floatPosRots bbox (rot, pic) = case extent pic of@@ -307,16 +308,6 @@ divUp :: (Integral a) => a -> a -> a divUp a b = - div (-a) b ---pairFromComplex :: (RealFloat a) => Complex a -> (a,a)-pairFromComplex z = (Complex.realPart z, Complex.imagPart z)--mapComplex :: (a -> b) -> Complex a -> Complex b-mapComplex f (r Complex.:+ i) = f r Complex.:+ f i--mulConj :: (RealFloat a) => Complex a -> Complex a -> Complex a-mulConj x y = x * Complex.conjugate y -- ToDo: move to a new utility module
+ src/Complex.hs view
@@ -0,0 +1,29 @@+module Complex where++import qualified Data.Complex as HComplex+import Data.Complex (Complex((:+)))+++toPair :: (RealFloat a) => Complex a -> (a,a)+toPair z = (HComplex.realPart z, HComplex.imagPart z)++map :: (a -> b) -> Complex a -> Complex b+map f (r :+ i) = f r :+ f i++conjugate :: (Num a) => Complex a -> Complex a+conjugate (r :+ i) = r :+ negate i++add :: (Num a) => Complex a -> Complex a -> Complex a+add (xr:+xi) (yr:+yi) = (xr+yr) :+ (xi+yi)++sub :: (Num a) => Complex a -> Complex a -> Complex a+sub (xr:+xi) (yr:+yi) = (xr-yr) :+ (xi-yi)++mul :: (Num a) => Complex a -> Complex a -> Complex a+mul (xr:+xi) (yr:+yi) = (xr*yr-xi*yi) :+ (xr*yi+xi*yr)++mulConj :: (Num a) => Complex a -> Complex a -> Complex a+mulConj x y = mul x $ conjugate y++mulConj_ :: (RealFloat a) => Complex a -> Complex a -> Complex a+mulConj_ x y = x * Complex.conjugate y
src/Knead.hs view
@@ -6,6 +6,8 @@ import qualified MatchImageBorders import qualified Arithmetic as Arith+import qualified Knead.CArray as KneadCArray+import qualified Complex as Komplex import qualified Degree import MatchImageBorders (arrayCFromKnead, arrayKneadFromC) import Arithmetic (guardedPairs, maximum0)@@ -13,7 +15,7 @@ absolutePositionsFromPairDisplacements, fixAtLeastOnePosition, layoutFromPairDisplacements, fixAtLeastOneAnglePosition, )-import KneadShape+import Knead.Shape (Size, Vec2(Vec2), Dim1, Dim2, Shape2, Index2, Ix2, verticalVal, horizontalVal) import Degree (Degree(Degree), getDegree)@@ -31,7 +33,6 @@ import Data.Array.Knead.Expression (Exp, (==*), (/=*), (<*), (<=*), (>=*), (&&*)) -import qualified Data.Array.CArray as CArray import Data.Array.IArray (amap) import Data.Array.CArray (CArray) import Data.Array.MArray (thaw)@@ -45,7 +46,7 @@ import qualified LLVM.Core as LLVM import qualified Data.Complex as Complex-import Data.Complex (Complex((:+)), conjugate, realPart)+import Data.Complex (Complex((:+))) import qualified Codec.Picture as Pic @@ -61,7 +62,7 @@ import Text.Printf (printf) import qualified Control.Monad.HT as MonadHT-import Control.Monad (liftM2, when, join, foldM, (<=<))+import Control.Monad (when, join, foldM, (<=<)) import Control.Applicative (pure, (<$>), (<*>)) import qualified Data.Foldable as Fold@@ -71,12 +72,12 @@ import Data.Monoid ((<>)) import Data.Maybe.HT (toMaybe) import Data.Maybe (mapMaybe, isJust, isNothing)+import Data.Bits (Bits) import Data.Traversable (forM) import Data.Foldable (forM_) import Data.Ord.HT (comparing) import Data.Tuple.HT- (mapPair, mapFst, mapSnd, mapTriple, swap,- mapThd3, fst3, thd3, uncurry3)+ (mapPair, mapFst, mapSnd, mapTriple, swap, mapThd3, fst3, uncurry3) import Data.Word (Word8, Word32) @@ -563,125 +564,63 @@ -- counterpart to 'clip'-pad ::- (MultiValue.C a) =>- Exp a -> Exp Dim2 -> SymbPlane a -> SymbPlane a+pad :: (MultiValue.C a) => Exp a -> Exp Dim2 -> SymbPlane a -> SymbPlane a pad a sh img = let Vec2 height width = Expr.decompose atomDim2 $ Symb.shape img in generate sh $ \p -> let Vec2 y x = Expr.decompose atomIx2 p in Expr.ifThenElse (y<*height &&* x<*width) (img ! p) a -padCArray ::- (SV.Storable a) =>- a -> (Int,Int) -> CArray (Int,Int) a -> CArray (Int,Int) a-padCArray a (height, width) img =- CArray.listArray ((0,0), (height-1, width-1)) (repeat a)- CArray.//- CArray.assocs img--clipCArray ::- (SV.Storable a) => (Int,Int) -> CArray (Int,Int) a -> CArray (Int,Int) a-clipCArray (height, width) =- CArray.ixmap ((0,0), (height-1, width-1)) id--mapPairInt :: (Integral i, Integral j) => (i,i) -> (j,j)-mapPairInt = mapPair (fromIntegral, fromIntegral)--correlatePaddedSimpleCArray ::- (FFTWReal a) =>- (Int,Int) ->- CArray (Int,Int) a ->- CArray (Int,Int) a ->- CArray (Int,Int) a-correlatePaddedSimpleCArray sh =- let forward = FFT.dftRCN [0,1] . padCArray 0 sh- inverse = FFT.dftCRN [0,1]- in \ a b ->- inverse $ CArray.liftArray2 Arith.mulConj (forward a) (forward b)---- expects zero-based arrays-cyclicReverse2d :: (SV.Storable a) => CArray (Int,Int) a -> CArray (Int,Int) a+cyclicReverse2d :: (MultiValue.C a) => SymbPlane a -> SymbPlane a cyclicReverse2d spec =- let (height, width) = mapPair ((1+), (1+)) $ snd $ CArray.bounds spec- in CArray.ixmap (CArray.bounds spec)- (\(y,x) -> (mod (-y) height, mod (-x) width)) spec+ let (Vec2 height width) = Expr.decompose atomDim2 $ Symb.shape spec+ in Symb.backpermute (Symb.shape spec)+ (Expr.modify atomIx2 $ \(Vec2 y x) ->+ Vec2+ (wrap height height (height-y))+ (wrap width width (width-x)))+ spec -untangleCoefficient ::- (RealFloat a) => Complex a -> Complex a -> (Complex a, Complex a)-untangleCoefficient a b =- let bc = conjugate b- in ((a + bc) / 2, (a - bc) * (0 :+ (-1/2)))+atomComplex :: Complex (Atom a)+atomComplex = atom:+atom --- ToDo: could be moved to fft package untangleSpectra2d ::- (RealFloat a, SV.Storable a) =>- CArray (Int,Int) (Complex a) -> CArray (Int,Int) (Complex a, Complex a)+ (MultiValue.C a, MultiValue.Field a,+ MultiValue.Real a, MultiValue.RationalConstant a) =>+ SymbPlane (Complex a) -> SymbPlane (Complex a, Complex a) untangleSpectra2d spec =- CArray.liftArray2 untangleCoefficient spec (cyclicReverse2d spec)--{- |-Equivalent to @amap (uncurry Arith.mulConj) . untangleSpectra2d@-but much faster, since it avoids the slow @instance Storable (a,b)@-based on @storable-tuple:storePair@.--}-mulConjUntangledSpectra2d ::- (RealFloat a, SV.Storable a) =>- CArray (Int,Int) (Complex a) -> CArray (Int,Int) (Complex a)-mulConjUntangledSpectra2d spec =- CArray.liftArray2- ((uncurry Arith.mulConj .) . untangleCoefficient)+ Symb.zipWith+ (Expr.modify2 atomComplex atomComplex KneadCArray.untangleCoefficient) spec (cyclicReverse2d spec) --{--This is more efficient than 'correlatePaddedSimpleCArray'-since it needs only one complex forward Fourier transform,-where 'correlatePaddedSimpleCArray' needs two real transforms.-Especially for odd sizes-two real transforms are slower than a complex transform.-For the analysis part,-perform two real-valued Fourier transforms using one complex-valued transform.-Afterwards we untangle the superposed spectra.--}-correlatePaddedComplexCArray ::- (FFTWReal a) =>- (Int,Int) ->- CArray (Int,Int) a ->- CArray (Int,Int) a ->- CArray (Int,Int) a-correlatePaddedComplexCArray sh a b =- amap realPart $ FFT.idftN [0,1] $- mulConjUntangledSpectra2d $ FFT.dftN [0,1] $- CArray.liftArray2 (:+) (padCArray 0 sh a) (padCArray 0 sh b)+correlatePadded ::+ (FFTWReal a, MultiValue.Real a, MultiMem.C a,+ MultiValue.Field a, MultiValue.RationalConstant a) =>+ Dim2 -> IO (Plane a -> Plane a -> IO (Plane a))+correlatePadded padExtent@(Vec2 height width) = do+ let sh = Expr.cons padExtent+ mergePlanes <-+ RenderP.run $ \a b ->+ Symb.zipWith Expr.consComplex (pad 0 sh a) (pad 0 sh b)+ let (halfWidth,parity) = divMod width 2+ let exprFromInt = Expr.cons . fromIntegral+ mulSpecs <-+ RenderP.run $+ clip (0,0) (exprFromInt $ halfWidth+1, exprFromInt height) .+ Symb.map+ (Expr.modify (atomComplex, atomComplex) $ uncurry Komplex.mulConj) .+ untangleSpectra2d -{- |-Should be yet a little bit more efficient than 'correlatePaddedComplexCArray'-since it uses a real back transform.--}-correlatePaddedCArray ::- (FFTWReal a) =>- (Int,Int) ->- CArray (Int,Int) a ->- CArray (Int,Int) a ->- CArray (Int,Int) a-correlatePaddedCArray sh@(height,width) a b =- (case divMod width 2 of- (halfWidth,0) -> FFT.dftCRN [0,1] . clipCArray (height,halfWidth+1)- (halfWidth,_) -> FFT.dftCRON [0,1] . clipCArray (height,halfWidth+1)) $- mulConjUntangledSpectra2d $ FFT.dftN [0,1] $- CArray.liftArray2 (:+) (padCArray 0 sh a) (padCArray 0 sh b)+ return $ \ a b ->+ liftCArray (if parity==0 then FFT.dftCRN [0,1] else FFT.dftCRON [0,1]) =<<+ mulSpecs =<< liftCArray (FFT.dftN [0,1]) =<< mergePlanes a b -liftCArray2 ::- (SV.Storable a) =>- (CArray (Int,Int) a -> CArray (Int,Int) a -> CArray (Int,Int) a) ->- Plane a -> Plane a -> IO (Plane a)-liftCArray2 f a b =- arrayKneadFromC <$>- liftM2 f- (arrayCFromKnead a)- (arrayCFromKnead b)+liftCArray ::+ (SV.Storable a, SV.Storable b) =>+ (CArray (Int,Int) a -> CArray (Int,Int) b) ->+ Plane a -> IO (Plane b)+liftCArray f a = arrayKneadFromC . f <$> arrayCFromKnead a fixArray :: Id (Symb.Array sh a)@@ -776,17 +715,17 @@ allOverlapsRun :: Dim2 -> IO (Float -> Plane Float -> Plane Float -> IO (Plane Word8))-allOverlapsRun padExtent@(Vec2 height width) = do+allOverlapsRun padExtent = do run <- RenderP.run $ \minOverlapPortion sha shb img -> imageByteFromFloat $ Symb.map (0.0001*) $ Symb.map Expr.fst $ allOverlapsFromCorrelation padExtent minOverlapPortion sha shb img+ correlate <- correlatePadded padExtent return $ \overlap a b ->- run overlap (Phys.shape a) (Phys.shape b)- =<< liftCArray2 (correlatePaddedCArray $ mapPairInt (height, width)) a b+ run overlap (Phys.shape a) (Phys.shape b) =<< correlate a b argmax ::@@ -802,15 +741,15 @@ optimalOverlap :: Dim2 -> IO (Float -> Plane Float -> Plane Float -> IO (Float, (Size, Size)))-optimalOverlap padExtent@(Vec2 height width) = do+optimalOverlap padExtent = do run <- RenderP.run $ \minOverlapPortion (sha, shb) img -> argmaximum $ allOverlapsFromCorrelation padExtent minOverlapPortion sha shb img+ correlate <- correlatePadded padExtent return $ \overlap a b ->- run overlap (Phys.shape a, Phys.shape b)- =<< liftCArray2 (correlatePaddedCArray $ mapPairInt (height, width)) a b+ run overlap (Phys.shape a, Phys.shape b) =<< correlate a b shrink ::@@ -826,14 +765,42 @@ (Expr.modify (atomIx2, atomIx2) $ \(Vec2 yi xi, Vec2 yj xj) -> Vec2 (yi*yk+yj) (xi*xk+xj)) -shrinkFactors :: (Integral a) => Dim2 -> Shape2 a -> Shape2 a -> Shape2 a-shrinkFactors (Vec2 heightPad widthPad)+{-+The implementation accepts overlapping of at most minOverlapPortion+of the two shrunken images.+However, in practice this optimization is rarely effective.+In most cases the shrink factors are the same+independent from whether minOverlap is zero or not.+-}+shrinkFactors ::+ (Integral a) => Dim2 -> Float -> Shape2 a -> Shape2 a -> Shape2 a+shrinkFactors (Vec2 heightPad widthPad) minOverlapPortion (Vec2 heighta widtha) (Vec2 heightb widthb) =- Vec2- (Arith.divUp (heighta+heightb) $ fromIntegral heightPad)- (Arith.divUp (widtha +widthb) $ fromIntegral widthPad)+ let minOverlap =+ Arith.minimumOverlapAbsFromPortion minOverlapPortion+ (min widtha widthb, min heighta heightb)+ in Vec2+ (Arith.divUp (heighta+heightb-minOverlap) $ fromIntegral heightPad)+ (Arith.divUp (widtha +widthb -minOverlap) $ fromIntegral widthPad) +{-+Should compute almost the same as shrinkFactors+but is less optimized and more idiomatic.+@correlationSize@ has a final @ceilingSmooth7@.+This is not necessary here+since we expect that the user chooses an FFT friendly target size.+-}+shrinkFactorsAlt ::+ (Bits a, Integral a) => Float -> Dim2 -> Shape2 a -> Shape2 a -> Shape2 a+shrinkFactorsAlt minOverlapPortion (Vec2 heightPad widthPad) a b =+ let (widthc,heightc) =+ Arith.correlationSize minOverlapPortion $+ map (\(Vec2 height width) -> (width, height)) [a,b]+ in Vec2+ (Arith.divUp heightc $ fromIntegral heightPad)+ (Arith.divUp widthc $ fromIntegral widthPad) + optimalOverlapBig :: Dim2 -> IO (Float -> Plane Float -> Plane Float -> IO (Float, (Size, Size))) optimalOverlapBig padExtent = do@@ -841,7 +808,7 @@ optOverlap <- optimalOverlap padExtent return $ \minimumOverlap a b -> do let factors@(Vec2 yk xk) =- shrinkFactors padExtent (Phys.shape a) (Phys.shape b)+ shrinkFactors padExtent minimumOverlap (Phys.shape a) (Phys.shape b) aSmall <- shrnk factors a bSmall <- shrnk factors b mapSnd (mapPair ((*xk), (*yk))) <$>@@ -883,7 +850,7 @@ Dim2 -> IO (Float -> Plane Float -> Plane Float -> IO (Float, (Size, Size))) optimalOverlapBigFine padExtent@(Vec2 heightPad widthPad) = do overlap <- optimalOverlap padExtent- -- optimalOverlap is rendered again here+ -- optimalOverlap is compiled again here overlapBig <- optimalOverlapBig padExtent clp <- RenderP.run clip return $ \minimumOverlap a b -> do@@ -936,7 +903,7 @@ return $ \minimumOverlap mMaximumDiff a b -> do let factors@(Vec2 yk xk) =- shrinkFactors padExtent (Phys.shape a) (Phys.shape b)+ shrinkFactors padExtent minimumOverlap (Phys.shape a) (Phys.shape b) aSmall <- shrnk factors a bSmall <- shrnk factors b @@ -1306,9 +1273,7 @@ MultiValue.NativeFloating a ar) => Exp (Geometry b) -> SymbPlane a -> SymbPlane Word8 scaleDistanceMapGeom geom img =- let scale =- (4/) $ fromInt $- Expr.modify (atom,atom,(atom,atom)) (uncurry Expr.min . thd3) geom+ let scale = (4/) $ fromInt $ Expr.uncurry Expr.min $ Expr.thd3 geom in imageByteFromFloat $ Symb.map (scale*) img @@ -1689,7 +1654,7 @@ map picColored pics, map (mapPair- (mapPair (realToFrac, realToFrac), Arith.mapComplex realToFrac))+ (mapPair (realToFrac, realToFrac), Komplex.map realToFrac)) posRots) @@ -1728,20 +1693,22 @@ when False $ do notice "write fft" let pic0 : pic1 : _ = map snd rotated- size = (1024,768)- cpic0 <- arrayCFromKnead pic0- cpic1 <- arrayCFromKnead pic1+ size = Vec2 1024 768 makeByteImage <- RenderP.run $ \k -> imageByteFromFloat . Symb.map (k*) . fixArray+ runPad <- RenderP.run pad writeGrey (Option.quality opt) "/tmp/padded.jpeg" =<<- (makeByteImage 1 $ arrayKneadFromC $ padCArray 0 size cpic0)+ (makeByteImage 1 =<< runPad 0 size pic0)+ runMagnitude <-+ RenderP.run $+ Symb.map (Expr.modify atomComplex $ \(r:+i) -> Expr.sqrt$ r*r+i*i)+ . fixArray writeGrey (Option.quality opt) "/tmp/spectrum.jpeg" =<<- (makeByteImage 0.1 $ arrayKneadFromC $- CArray.liftArray Complex.magnitude $- FFT.dftRCN [0,1] $ padCArray 0 size cpic0)+ (makeByteImage 0.1 =<< runMagnitude =<<+ liftCArray (FFT.dftRCN [0,1]) =<< runPad 0 size pic0)+ correlate <- correlatePadded size writeGrey (Option.quality opt) "/tmp/convolution.jpeg" =<<- (makeByteImage 0.1 $ arrayKneadFromC $- correlatePaddedCArray size cpic0 cpic1)+ (makeByteImage 0.1 =<< correlate pic0 pic1) return $ zipWith3
+ src/Knead/CArray.hs view
@@ -0,0 +1,119 @@+module Knead.CArray where++import qualified Complex as Komplex++import qualified Math.FFT as FFT+import Math.FFT.Base (FFTWReal)++import Foreign.Storable (Storable)+import Foreign.Storable.Tuple ()++import qualified Data.Array.CArray as CArray+import Data.Array.IArray (amap)+import Data.Array.CArray (CArray)++import Data.Complex (Complex((:+)), realPart)++import Data.Tuple.HT (mapPair)+++pad ::+ (Storable a) => a -> (Int,Int) -> CArray (Int,Int) a -> CArray (Int,Int) a+pad a (height, width) img =+ CArray.listArray ((0,0), (height-1, width-1)) (repeat a)+ CArray.//+ CArray.assocs img++clip :: (Storable a) => (Int,Int) -> CArray (Int,Int) a -> CArray (Int,Int) a+clip (height, width) = CArray.ixmap ((0,0), (height-1, width-1)) id++correlatePaddedSimple ::+ (FFTWReal a) =>+ (Int,Int) ->+ CArray (Int,Int) a ->+ CArray (Int,Int) a ->+ CArray (Int,Int) a+correlatePaddedSimple sh =+ let forward = FFT.dftRCN [0,1] . pad 0 sh+ inverse = FFT.dftCRN [0,1]+ in \ a b ->+ inverse $ CArray.liftArray2 Komplex.mulConj (forward a) (forward b)++-- expects zero-based arrays+cyclicReverse2d :: (Storable a) => CArray (Int,Int) a -> CArray (Int,Int) a+cyclicReverse2d spec =+ let (height, width) = mapPair ((1+), (1+)) $ snd $ CArray.bounds spec+ in CArray.ixmap (CArray.bounds spec)+ (\(y,x) -> (mod (-y) height, mod (-x) width)) spec++untangleCoefficient ::+ (Fractional a) => Complex a -> Complex a -> (Complex a, Complex a)+untangleCoefficient a b =+ let bc = Komplex.conjugate b+ in (Komplex.mul (Komplex.add a bc) ((1/2) :+ 0),+ Komplex.mul (Komplex.sub a bc) (0 :+ (-1/2)))++untangleCoefficient_ ::+ (RealFloat a) => Complex a -> Complex a -> (Complex a, Complex a)+untangleCoefficient_ a b =+ let bc = Komplex.conjugate b+ in ((a + bc) / 2, (a - bc) * (0 :+ (-1/2)))++-- ToDo: could be moved to fft package+untangleSpectra2d ::+ (Fractional a, Storable a) =>+ CArray (Int,Int) (Complex a) -> CArray (Int,Int) (Complex a, Complex a)+untangleSpectra2d spec =+ CArray.liftArray2 untangleCoefficient spec (cyclicReverse2d spec)++{- |+Equivalent to @amap (uncurry Komplex.mulConj) . untangleSpectra2d@+but much faster, since it avoids the slow @instance Storable (a,b)@+based on @storable-tuple:storePair@.+-}+mulConjUntangledSpectra2d ::+ (Fractional a, Storable a) =>+ CArray (Int,Int) (Complex a) -> CArray (Int,Int) (Complex a)+mulConjUntangledSpectra2d spec =+ CArray.liftArray2+ ((uncurry Komplex.mulConj .) . untangleCoefficient)+ spec (cyclicReverse2d spec)+++{-+This is more efficient than 'correlatePaddedSimpleCArray'+since it needs only one complex forward Fourier transform,+where 'correlatePaddedSimpleCArray' needs two real transforms.+Especially for odd sizes+two real transforms are slower than a complex transform.+For the analysis part,+perform two real-valued Fourier transforms using one complex-valued transform.+Afterwards we untangle the superposed spectra.+-}+correlatePaddedComplex ::+ (FFTWReal a) =>+ (Int,Int) ->+ CArray (Int,Int) a ->+ CArray (Int,Int) a ->+ CArray (Int,Int) a+correlatePaddedComplex sh a b =+ amap realPart $ FFT.idftN [0,1] $+ mulConjUntangledSpectra2d $ FFT.dftN [0,1] $+ CArray.liftArray2 (:+) (pad 0 sh a) (pad 0 sh b)++{- |+Should be yet a little bit more efficient than 'correlatePaddedComplexCArray'+since it uses a real back transform.+-}+correlatePadded ::+ (FFTWReal a) =>+ (Int,Int) ->+ CArray (Int,Int) a ->+ CArray (Int,Int) a ->+ CArray (Int,Int) a+correlatePadded sh@(height,width) a b =+ (case divMod width 2 of+ (halfWidth,0) -> FFT.dftCRN [0,1] . clip (height,halfWidth+1)+ (halfWidth,_) -> FFT.dftCRON [0,1] . clip (height,halfWidth+1)) $+ mulConjUntangledSpectra2d $ FFT.dftN [0,1] $+ CArray.liftArray2 (:+) (pad 0 sh a) (pad 0 sh b)
+ src/Knead/Shape.hs view
@@ -0,0 +1,176 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE EmptyDataDecls #-}+module Knead.Shape where++import qualified Data.Array.Knead.Index.Nested.Shape as Shape+import qualified Data.Array.Knead.Expression as Expr++import qualified LLVM.Extra.Multi.Value.Memory as MultiMem+import qualified LLVM.Extra.Multi.Value as MultiValue+import qualified LLVM.Extra.Arithmetic as A+import LLVM.Extra.Multi.Value (atom)++import qualified LLVM.Core as LLVM++import qualified Type.Data.Num.Decimal as TypeNum++import Foreign.Storable+ (Storable, sizeOf, alignment, poke, pokeElemOff, peek, peekElemOff)+import Foreign.Ptr (Ptr, castPtr)++import qualified Control.Monad.HT as Monad+import Control.Monad (join)++import Data.Int (Int64)+++{- |+I choose a bit complicated Dim2 definition+to make it distinct from size pairs with width and height swapped.+Alternatives would be Index.Linear or intentionally complicated Shape types like:++type Dim0 = ()+type Dim1 = ((), Size)+type Dim2 = ((), Size, Size)++Problems with Index.Linear is that it is fixed to Word32 dimensions+which causes trouble with negative coordinates+that we encounter on rotations.++The custom shape type requires lots of new definitions+but it is certainly the cleanest solution.+-}+type Size = Int64+type Dim0 = ()+type Dim1 = Size+type Dim2 = Shape2 Size+type Ix2 = Index2 Size++data Vec2 tag i = Vec2 {vertical, horizontal :: i}++data ShapeTag+data IndexTag++type Shape2 = Vec2 ShapeTag+type Index2 = Vec2 IndexTag++++squareShape :: n -> Vec2 tag n+squareShape n = Vec2 n n++castToElemPtr :: Ptr (Vec2 tag a) -> Ptr a+castToElemPtr = castPtr++instance (Storable n) => Storable (Vec2 tag n) where+ -- cf. sample-frame:Frame.Stereo+ sizeOf ~(Vec2 n m) =+ sizeOf n + mod (- sizeOf n) (alignment m) + sizeOf m+ alignment ~(Vec2 n _) = alignment n+ poke p (Vec2 n m) =+ let q = castToElemPtr p+ in poke q n >> pokeElemOff q 1 m+ peek p =+ let q = castToElemPtr p+ in Monad.lift2 Vec2 (peek q) (peekElemOff q 1)++instance (MultiValue.C n) => MultiValue.C (Vec2 tag n) where+ type Repr f (Vec2 tag n) = Vec2 tag (MultiValue.Repr f n)+ cons (Vec2 n m) =+ MultiValue.compose $ Vec2 (MultiValue.cons n) (MultiValue.cons m)+ undef = MultiValue.compose $ squareShape MultiValue.undef+ zero = MultiValue.compose $ squareShape MultiValue.zero+ phis bb a =+ case MultiValue.decompose (squareShape atom) a of+ Vec2 a0 a1 ->+ fmap MultiValue.compose $+ Monad.lift2 Vec2 (MultiValue.phis bb a0) (MultiValue.phis bb a1)+ addPhis bb a b =+ case (MultiValue.decompose (squareShape atom) a,+ MultiValue.decompose (squareShape atom) b) of+ (Vec2 a0 a1, Vec2 b0 b1) ->+ MultiValue.addPhis bb a0 b0 >>+ MultiValue.addPhis bb a1 b1++type instance+ MultiValue.Decomposed f (Vec2 tag pat) =+ Vec2 tag (MultiValue.Decomposed f pat)+type instance+ MultiValue.PatternTuple (Vec2 tag pat) =+ Vec2 tag (MultiValue.PatternTuple pat)++instance (MultiValue.Compose n) => MultiValue.Compose (Vec2 tag n) where+ type Composed (Vec2 tag n) = Vec2 tag (MultiValue.Composed n)+ compose (Vec2 n m) =+ case (MultiValue.compose n, MultiValue.compose m) of+ (MultiValue.Cons rn, MultiValue.Cons rm) ->+ MultiValue.Cons (Vec2 rn rm)++instance (MultiValue.Decompose pn) => MultiValue.Decompose (Vec2 tag pn) where+ decompose (Vec2 pn pm) (MultiValue.Cons (Vec2 n m)) =+ Vec2+ (MultiValue.decompose pn (MultiValue.Cons n))+ (MultiValue.decompose pm (MultiValue.Cons m))++instance (MultiMem.C i) => MultiMem.C (Vec2 tag i) where+ type Struct (Vec2 tag i) =+ LLVM.Struct (MultiMem.Struct i, (MultiMem.Struct i, ()))+ decompose nm =+ Monad.lift2 zipShape+ (MultiMem.decompose =<< LLVM.extractvalue nm TypeNum.d0)+ (MultiMem.decompose =<< LLVM.extractvalue nm TypeNum.d1)+ compose nm =+ case unzipShape nm of+ Vec2 n m -> do+ sn <- MultiMem.compose n+ sm <- MultiMem.compose m+ rn <- LLVM.insertvalue (LLVM.value LLVM.undef) sn TypeNum.d0+ LLVM.insertvalue rn sm TypeNum.d1+++unzipShape :: MultiValue.T (Vec2 tag n) -> Vec2 tag (MultiValue.T n)+unzipShape = MultiValue.decompose (squareShape atom)++zipShape :: MultiValue.T n -> MultiValue.T n -> MultiValue.T (Vec2 tag n)+zipShape y x = MultiValue.compose $ Vec2 y x++instance (tag ~ ShapeTag, Shape.C i) => Shape.C (Vec2 tag i) where+ type Index (Vec2 tag i) = Index2 (Shape.Index i)+ intersectCode a b =+ case (unzipShape a, unzipShape b) of+ (Vec2 an am, Vec2 bn bm) ->+ Monad.lift2 zipShape+ (Shape.intersectCode an bn)+ (Shape.intersectCode am bm)+ sizeCode nm =+ case unzipShape nm of+ Vec2 n m ->+ join $ Monad.lift2 A.mul (Shape.sizeCode n) (Shape.sizeCode m)+ size (Vec2 n m) = Shape.size n * Shape.size m+ flattenIndexRec nm ij =+ case (unzipShape nm, unzipShape ij) of+ (Vec2 n m, Vec2 i j) -> do+ (ns, il) <- Shape.flattenIndexRec n i+ (ms, jl) <- Shape.flattenIndexRec m j+ Monad.lift2 (,)+ (A.mul ns ms)+ (A.add jl =<< A.mul ms il)+ loop code nm =+ case unzipShape nm of+ Vec2 n m ->+ Shape.loop (\i -> Shape.loop (\j -> code (zipShape i j)) m) n+++instance (Expr.Compose n) => Expr.Compose (Vec2 tag n) where+ type Composed (Vec2 tag n) = Vec2 tag (Expr.Composed n)+ compose (Vec2 n m) = Expr.lift2 zipShape (Expr.compose n) (Expr.compose m)++instance (Expr.Decompose p) => Expr.Decompose (Vec2 tag p) where+ decompose (Vec2 pn pm) vec =+ Vec2+ (Expr.decompose pn (verticalVal vec))+ (Expr.decompose pm (horizontalVal vec))++verticalVal, horizontalVal :: (Expr.Value val) => val (Vec2 tag n) -> val n+verticalVal = Expr.lift1 (MultiValue.lift1 vertical)+horizontalVal = Expr.lift1 (MultiValue.lift1 horizontal)
− src/KneadShape.hs
@@ -1,176 +0,0 @@-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE EmptyDataDecls #-}-module KneadShape where--import qualified Data.Array.Knead.Index.Nested.Shape as Shape-import qualified Data.Array.Knead.Expression as Expr--import qualified LLVM.Extra.Multi.Value.Memory as MultiMem-import qualified LLVM.Extra.Multi.Value as MultiValue-import qualified LLVM.Extra.Arithmetic as A-import LLVM.Extra.Multi.Value (atom)--import qualified LLVM.Core as LLVM--import qualified Type.Data.Num.Decimal as TypeNum--import Foreign.Storable- (Storable, sizeOf, alignment, poke, pokeElemOff, peek, peekElemOff)-import Foreign.Ptr (Ptr, castPtr)--import qualified Control.Monad.HT as Monad-import Control.Monad (join)--import Data.Int (Int64)---{- |-I choose a bit complicated Dim2 definition-to make it distinct from size pairs with width and height swapped.-Alternatives would be Index.Linear or intentionally complicated Shape types like:--type Dim0 = ()-type Dim1 = ((), Size)-type Dim2 = ((), Size, Size)--Problems with Index.Linear is that it is fixed to Word32 dimensions-which causes trouble with negative coordinates-that we encounter on rotations.--The custom shape type requires lots of new definitions-but it is certainly the cleanest solution.--}-type Size = Int64-type Dim0 = ()-type Dim1 = Size-type Dim2 = Shape2 Size-type Ix2 = Index2 Size--data Vec2 tag i = Vec2 {vertical, horizontal :: i}--data ShapeTag-data IndexTag--type Shape2 = Vec2 ShapeTag-type Index2 = Vec2 IndexTag----squareShape :: n -> Vec2 tag n-squareShape n = Vec2 n n--castToElemPtr :: Ptr (Vec2 tag a) -> Ptr a-castToElemPtr = castPtr--instance (Storable n) => Storable (Vec2 tag n) where- -- cf. sample-frame:Frame.Stereo- sizeOf ~(Vec2 n m) =- sizeOf n + mod (- sizeOf n) (alignment m) + sizeOf m- alignment ~(Vec2 n _) = alignment n- poke p (Vec2 n m) =- let q = castToElemPtr p- in poke q n >> pokeElemOff q 1 m- peek p =- let q = castToElemPtr p- in Monad.lift2 Vec2 (peek q) (peekElemOff q 1)--instance (MultiValue.C n) => MultiValue.C (Vec2 tag n) where- type Repr f (Vec2 tag n) = Vec2 tag (MultiValue.Repr f n)- cons (Vec2 n m) =- MultiValue.compose $ Vec2 (MultiValue.cons n) (MultiValue.cons m)- undef = MultiValue.compose $ squareShape MultiValue.undef- zero = MultiValue.compose $ squareShape MultiValue.zero- phis bb a =- case MultiValue.decompose (squareShape atom) a of- Vec2 a0 a1 ->- fmap MultiValue.compose $- Monad.lift2 Vec2 (MultiValue.phis bb a0) (MultiValue.phis bb a1)- addPhis bb a b =- case (MultiValue.decompose (squareShape atom) a,- MultiValue.decompose (squareShape atom) b) of- (Vec2 a0 a1, Vec2 b0 b1) ->- MultiValue.addPhis bb a0 b0 >>- MultiValue.addPhis bb a1 b1--type instance- MultiValue.Decomposed f (Vec2 tag pat) =- Vec2 tag (MultiValue.Decomposed f pat)-type instance- MultiValue.PatternTuple (Vec2 tag pat) =- Vec2 tag (MultiValue.PatternTuple pat)--instance (MultiValue.Compose n) => MultiValue.Compose (Vec2 tag n) where- type Composed (Vec2 tag n) = Vec2 tag (MultiValue.Composed n)- compose (Vec2 n m) =- case (MultiValue.compose n, MultiValue.compose m) of- (MultiValue.Cons rn, MultiValue.Cons rm) ->- MultiValue.Cons (Vec2 rn rm)--instance (MultiValue.Decompose pn) => MultiValue.Decompose (Vec2 tag pn) where- decompose (Vec2 pn pm) (MultiValue.Cons (Vec2 n m)) =- Vec2- (MultiValue.decompose pn (MultiValue.Cons n))- (MultiValue.decompose pm (MultiValue.Cons m))--instance (MultiMem.C i) => MultiMem.C (Vec2 tag i) where- type Struct (Vec2 tag i) =- LLVM.Struct (MultiMem.Struct i, (MultiMem.Struct i, ()))- decompose nm =- Monad.lift2 zipShape- (MultiMem.decompose =<< LLVM.extractvalue nm TypeNum.d0)- (MultiMem.decompose =<< LLVM.extractvalue nm TypeNum.d1)- compose nm =- case unzipShape nm of- Vec2 n m -> do- sn <- MultiMem.compose n- sm <- MultiMem.compose m- rn <- LLVM.insertvalue (LLVM.value LLVM.undef) sn TypeNum.d0- LLVM.insertvalue rn sm TypeNum.d1---unzipShape :: MultiValue.T (Vec2 tag n) -> Vec2 tag (MultiValue.T n)-unzipShape = MultiValue.decompose (squareShape atom)--zipShape :: MultiValue.T n -> MultiValue.T n -> MultiValue.T (Vec2 tag n)-zipShape y x = MultiValue.compose $ Vec2 y x--instance (tag ~ ShapeTag, Shape.C i) => Shape.C (Vec2 tag i) where- type Index (Vec2 tag i) = Index2 (Shape.Index i)- intersectCode a b =- case (unzipShape a, unzipShape b) of- (Vec2 an am, Vec2 bn bm) ->- Monad.lift2 zipShape- (Shape.intersectCode an bn)- (Shape.intersectCode am bm)- sizeCode nm =- case unzipShape nm of- Vec2 n m ->- join $ Monad.lift2 A.mul (Shape.sizeCode n) (Shape.sizeCode m)- size (Vec2 n m) = Shape.size n * Shape.size m- flattenIndexRec nm ij =- case (unzipShape nm, unzipShape ij) of- (Vec2 n m, Vec2 i j) -> do- (ns, il) <- Shape.flattenIndexRec n i- (ms, jl) <- Shape.flattenIndexRec m j- Monad.lift2 (,)- (A.mul ns ms)- (A.add jl =<< A.mul ms il)- loop code nm =- case unzipShape nm of- Vec2 n m ->- Shape.loop (\i -> Shape.loop (\j -> code (zipShape i j)) m) n---instance (Expr.Compose n) => Expr.Compose (Vec2 tag n) where- type Composed (Vec2 tag n) = Vec2 tag (Expr.Composed n)- compose (Vec2 n m) = Expr.lift2 zipShape (Expr.compose n) (Expr.compose m)--instance (Expr.Decompose p) => Expr.Decompose (Vec2 tag p) where- decompose (Vec2 pn pm) vec =- Vec2- (Expr.decompose pn (verticalVal vec))- (Expr.decompose pm (horizontalVal vec))--verticalVal, horizontalVal :: (Expr.Value val) => val (Vec2 tag n) -> val n-verticalVal = Expr.lift1 (MultiValue.lift1 vertical)-horizontalVal = Expr.lift1 (MultiValue.lift1 horizontal)
src/MatchImageBorders.hs view
@@ -10,7 +10,7 @@ -} module MatchImageBorders where -import KneadShape (Vec2(Vec2), Dim2)+import Knead.Shape (Vec2(Vec2), Dim2) import qualified Data.Array.Knead.Simple.Physical as Phys