{-#LANGUAGE ForeignFunctionInterface, ParallelListComp, BangPatterns#-}
#include "cvWrapLEO.h"
-- |This module provides implementations for basic versions of Local Binary Pattern texture features introduced in
-- T. Ojala, M. Pietikäinen, and D. Harwood (1994), "Performance evaluation of texture measures with classification
--  based on Kullback discrimination of distributions", Proceedings of the 12th IAPR International Conference on Pattern Recognition (ICPR 1994).
module CV.Textures (rotationInvariant,lbp,lbp5,weightedLBP) where

import Foreign.C.Types
import Foreign.C.String
import Foreign.ForeignPtr
import Foreign.Ptr
import Foreign.Marshal.Array

import System.IO.Unsafe

import CV.Image 
import CV.ImageOp

import Data.List
--import C2HSTools
import qualified Data.Vector as V
import Data.Vector (Vector)
import Data.Maybe (fromMaybe)
import Data.Word
import Data.Bits (rotateL)
{#import CV.Image#}



-- * Rotation invariance

-- |Normalize Word8 according to lbp-logic.
normalize :: Word8 -> Word8
normalize x = minimum [rotateL x i | i<-[0..8] ]

-- |Make an lbp table element rotation invariant
rotationInvariantE :: Word8 -> Word8 
rotationInvariantE a =  fromIntegral
                      . fromMaybe (error "Unthinkable happened(RI)") 
                      . V.findIndex (==normalize a) $ keywords
keywords = V.fromList . nub . sort . map normalize $ allWords
allWords = [minBound .. maxBound]

-- | Convert an LBP histogram into rotation invariant form
rotationInvariant :: [Double] -> Vector Double
rotationInvariant es = V.accum (+) (V.replicate 36 0) 
                         [(fromIntegral . rotationInvariantE $ i, e) 
                         | i <- [minBound .. maxBound]
                         | e <- es]
            

-- * Various simple Local Binary Pattern operators

-- | The most basic 3x3 lbp operator
lbp :: Image GrayScale D32 -> [Double] 
lbp   = broilerPlate256 ({#call localBinaryPattern#})

-- lbp3 :: Image GrayScale D32 -> [Double] 
-- lbp3 = broilerPlate256 ({#call localBinaryPattern3#})

-- | The larger radius basic 5x5 lbp operator
lbp5 :: Image GrayScale D32 -> [Double] 
lbp5 = broilerPlate256 ({#call localBinaryPattern5#})

lbpHorizontal :: Image GrayScale D32 -> [Double] 
lbpHorizontal = broilerPlate256 
    ({#call localHorizontalBinaryPattern#})

lbpVertical :: Image GrayScale D32 -> [Double] 
lbpVertical = broilerPlate256 
    ({#call localVerticalBinaryPattern#})

-- | A variant of LBP which is weighted. This can be used to select only parts of the
-- image by using binary masks, or to give higher weight for some areas of the image.
weightedLBP :: (Integral a, Integral a1) =>
     a
     -> a1
     -> CV.Image.Image GrayScale D32
     -> CV.Image.Image GrayScale D32
     -> [Double]

weightedLBP offsetX offsetXY weights image = unsafePerformIO $ do
             withGenImage image $ \img ->
              withGenImage weights $ \ws ->
                  withArray (replicate 256 0) $ \ptrn -> do
                    {#call weighted_localBinaryPattern#} img 
                        (fromIntegral offsetX) 
                        (fromIntegral offsetXY) ws ptrn 
                    p <- peekArray 256 ptrn
                    return $ map realToFrac p

broilerPlate256  = broilerPlate' 256
broilerPlate' l op image = unsafePerformIO $ do
             withGenImage image $ \img ->
              withArray (replicate l 0 :: [CInt]) $ \ptrn -> do
                (op img ptrn )
                p <- peekArray l ptrn
                let !maximum = fromIntegral $ sum p
                return $ map (\x -> fromIntegral x / maximum) p
