{-# LANGUAGE ParallelListComp, PatternGuards, ScopedTypeVariables #-} module Main where import FFT import DFT import Roots import StrictComplex import Vector import Matrix import PPM import ColorRamp import Data.Array.Repa as A import Data.List as L import Data.Maybe import Prelude as P import Control.Monad import System.Environment -- Arg Parsing ------------------------------------------------------------------------------------ data Arg -- | Use DFT instead of FFT = ArgDFT -- | Compute the inverse transform. | ArgInverse -- | Use a vector step function for input. | ArgVectorRealStep Int -- length of 'on' part Int -- total length of vector -- | Read the a real valued vector from this file. | ArgVectorReal FilePath -- | Read a PPM file as input. | ArgPPM FilePath -- | Write the result to this file. | ArgOutMagnitude FilePath -- | Write the magnitude of the transformed matrix as a PPM file. | ArgOutPPMMagnitude FilePath -- | Clip tranformed values to this level. | ArgOutPPMClip Double deriving (Show, Eq) parseArgs [] = [] parseArgs (flag:xx) | "-dft" <- flag = ArgDFT : parseArgs xx | "-inverse" <- flag = ArgInverse : parseArgs xx | "-vector-real-step" <- flag , onlen:len:rest <- xx = ArgVectorRealStep (read onlen) (read len) : parseArgs rest | "-vector-real" <- flag , fileName:rest <- xx = ArgVectorReal fileName : parseArgs rest | "-ppm" <- flag , fileName:rest <- xx = ArgPPM fileName : parseArgs rest | "-ppm-clip" <- flag , num:rest <- xx = ArgOutPPMClip (read num) : parseArgs rest | "-out-magnitude" <- flag , fileName:rest <- xx = ArgOutMagnitude fileName : parseArgs rest | "-out-ppm-magnitude" <- flag , fileName:rest <- xx = ArgOutPPMMagnitude fileName : parseArgs rest | "-out-ppm-clip" <- flag , level:rest <- xx = ArgOutPPMClip (read level) : parseArgs rest | otherwise = error $ "bad arg " ++ flag ++ "\n" help = unlines [ "Usage: fft [args..]" , "" , " -dft Use (slow) DFT instead of (fast) FFT." , " -inverse Compute the inverse transform." , "" , "INPUT:" , " -vector-real Read a real valued input vector from this file." , " -vector-real-step Use a real valued step function for input." , " -ppm Use a PPM file for input." , "" , "OUTPUT:" , " -out-vector-magnitude Write the magnitute of the transformed vector to file." , " -out-ppm-magnitude Write transformed matrix to a ppm file." , " -out-ppm-clip ... while clipping transformed values to this level." , "" , "NOTE:" , " - For the fast algorithm, the length of the input vector/dimensions of the array" , " must be powers of two." , "" , " - When using PPM input files, pixels are converted to grey-scale and then used as" , " the real values of the input matrix." ] -- Main ------------------------------------------------------------------------------------------- main :: IO () main = do args <- liftM parseArgs $ getArgs -- Decide which algorithm to use let alg | elem ArgDFT args , elem ArgInverse args = idft | elem ArgDFT args = dft | elem ArgInverse args = ifft | otherwise = fft main' args alg main' args alg -- | Transform a real-valued step function | [(onLength, vecLength)] <- [(ol, vl) | ArgVectorRealStep ol vl <- args] = let offLength = vecLength - onLength step_real = P.replicate onLength 1 ++ P.replicate offLength 0 step = P.map (:*: 0) step_real arr = A.fromList (Z :. vecLength) step arrT = alg arr in outVector args arrT -- | Transform some vector from a file | [fileName] <- [f | ArgVectorReal f <- args] = do arr_real :: Array DIM1 Double <- readVectorFromTextFile fileName let arr = A.map (\r -> r :*: 0) arr_real let arrT = alg arr outVector args arrT -- | Transform a PPM file. | [fileName] <- [f | ArgPPM f <- args] = do let loadPixel r g b = sqrt (fromIntegral r^2 + fromIntegral g^2 + fromIntegral b^2) arr_double <- readPPMAsMatrix loadPixel fileName let arr_real = (A.map (\r -> r :*: 0) arr_double) :: Array DIM2 Complex let arrT = fft2d arr_real outPPM args arrT -- Not sure what you mean... | otherwise = putStr help outVector args vec | [fileName] <- [f | ArgOutMagnitude f <- args ] = writeVectorAsTextFile (A.map mag vec) fileName | otherwise = return () outPPM :: [Arg] -> Array DIM2 Complex -> IO () outPPM args arr | [fileName] <- [f | ArgOutPPMMagnitude f <- args ] , mClipLevel <- listToMaybe [l | ArgOutPPMClip l <- args] = do let arr_mag = A.map mag arr let arr_clipped = maybe arr_mag (\level -> A.map (\x -> if x > level then level else x) arr_mag) mClipLevel writeMatrixAsNormalisedPPM fileName pixelGrey arr_clipped pixelColor x = (x, x, x) pixelGrey x = (x, x, x)