module MatchImageBorders where
import KneadShape (Vec2(Vec2), Dim2)
import qualified Data.Array.Knead.Simple.Physical as Phys
import qualified Data.PQueue.Prio.Max as PQ
import qualified Data.Set as Set
import Data.PQueue.Prio.Max (MaxPQueue)
import Data.Set (Set)
import qualified Data.Array.CArray.Base as CArrayPriv
import Data.Array.IOCArray (IOCArray)
import Data.Array.MArray (readArray, writeArray, freeze, thaw)
import Data.Array.CArray (CArray)
import Data.Array.IArray
(Ix, amap, bounds, range, rangeSize, inRange, (!), (//))
import Foreign.Storable (Storable)
import Data.Traversable (forM)
import Data.Tuple.HT (mapSnd)
import Data.Maybe (mapMaybe, listToMaybe)
import Data.Word (Word8)
import Control.Monad (filterM)
import Control.Applicative ((<$>))
arrayCFromKnead :: Phys.Array Dim2 a -> IO (CArray (Int,Int) a)
arrayCFromKnead (Phys.Array (Vec2 height width) fptr) =
CArrayPriv.unsafeForeignPtrToCArray fptr
((0,0), (fromIntegral height 1, fromIntegral width 1))
arrayKneadFromC ::
(Storable a) => CArray (Int,Int) a -> Phys.Array Dim2 a
arrayKneadFromC carray =
case bounds carray of
((ly,lx), (uy,ux)) ->
Phys.Array
(Vec2
(fromIntegral (rangeSize (ly,uy)))
(fromIntegral (rangeSize (lx,ux))))
(snd $ CArrayPriv.toForeignPtr carray)
findBorder :: (Ix i, Enum i, Ix j, Enum j) => CArray (i,j) Bool -> Set (i,j)
findBorder mask =
let ((yl,xl), (yu,xu)) = bounds mask
revRange (l,u) = [u, pred u .. l]
findLeft y =
listToMaybe $ dropWhile (\x -> not $ mask!(y,x)) $ range (xl,xu)
findRight y =
listToMaybe $ dropWhile (\x -> not $ mask!(y,x)) $ revRange (xl,xu)
findTop x =
listToMaybe $ dropWhile (\y -> not $ mask!(y,x)) $ range (yl,yu)
findBottom x =
listToMaybe $ dropWhile (\y -> not $ mask!(y,x)) $ revRange (yl,yu)
in Set.fromList $
mapMaybe (\y -> (,) y <$> findLeft y) (range (yl,yu)) ++
mapMaybe (\y -> (,) y <$> findRight y) (range (yl,yu)) ++
mapMaybe (\x -> flip (,) x <$> findTop x) (range (xl,xu)) ++
mapMaybe (\x -> flip (,) x <$> findBottom x) (range (xl,xu))
pqueueFromBorder :: (Ix ix) => CArray ix Float -> Set ix -> MaxPQueue Float ix
pqueueFromBorder weights =
PQ.fromList . map (\pos -> (weights!pos, pos)) . Set.toList
type Location = Word8
locOutside, locBorder, locInside :: Location
locOutside = 0
locBorder = 1
locInside = 2
prepareLocations :: (Ix ix) => CArray ix Bool -> Set ix -> CArray ix Location
prepareLocations mask border =
amap (\b -> if b then locInside else locOutside) mask
//
map (flip (,) locBorder) (Set.toList border)
prepareShaping ::
(Ix i, Enum i, Ix j, Enum j) =>
[(CArray (i,j) Bool, CArray (i,j) Float)] ->
IO ([IOCArray (i,j) Location],
MaxPQueue Float ((IOCArray (i,j) Location, CArray (i,j) Float), (i,j)))
prepareShaping maskWeightss =
fmap (mapSnd PQ.unions . unzip) $
forM maskWeightss $ \(mask, weights) -> do
let border = findBorder mask
locations <- thaw $ prepareLocations mask border
return
(locations,
fmap ((,) (locations, weights)) $ pqueueFromBorder weights border)
shapeParts ::
IOCArray (Int, Int) Int ->
[IOCArray (Int, Int) Location] ->
MaxPQueue Float
((IOCArray (Int, Int) Location, CArray (Int, Int) Float), (Int, Int)) ->
IO [CArray (Int, Int) Bool]
shapeParts count masks =
let loop queue =
case PQ.maxView queue of
Nothing -> mapM (fmap (amap (/=locOutside)) . freeze) masks
Just (((locs, diffs), pos@(y,x)), remQueue) -> do
n <- readArray count pos
if n<=1
then loop remQueue
else do
writeArray count pos (n1)
writeArray locs pos locOutside
envPoss <-
filterM (fmap (locInside ==) . readArray locs) $
filter (inRange (bounds diffs)) $
map
(\(dy,dx) -> (y+dy, x+dx))
[(0,1), (1,0), (0,1), (1,0)]
mapM_
(\envPos -> writeArray locs envPos locBorder)
envPoss
loop $ PQ.union remQueue $ PQ.fromList $
map (\envPos -> (diffs!envPos, ((locs, diffs), envPos))) envPoss
in loop