| 
    
| 
 module ImageTypes 
where 
-- ---------------------------------------- 
type Lightness  = Double 
type Channel    = Int -> Int -> Lightness 
data Geo 
    = Geo { width  :: ! Int 
          , height :: ! Int 
          } 
      deriving (Eq) 
data Image 
    = Image { geo :: ! Geo 
            , img :: Channel 
            } 
-- ---------------------------------------- 
-- mothers little helper 
on2             :: (a -> b) -> 
                   (c -> d -> a) -> (c -> d -> b) 
f `on2` c       = \ x y -> f (c x y) 
merge           :: (a -> b -> c) -> 
                   (x -> y -> a) -> 
                   (x -> y -> b) -> 
                   (x -> y -> c) 
merge op f1 f2  = \ x y -> f1 x y `op` f2 x y 
-- ---------------------------------------- 
-- predefined Lightness values 
dark            :: Lightness 
dark            = 0.0 
light           :: Lightness 
light           = 1.0 
grey            :: Lightness 
grey            = 0.5 
lightOrDark     :: Lightness -> Lightness 
lightOrDark c 
    | c <= 0.5  = 0.0 
    | otherwise = 1.0 
gammaLight      :: Lightness -> Lightness -> Lightness 
gammaLight g x  = x ** (1/g) 
invertLight     :: Lightness -> Lightness 
invertLight     = (1.0 -) 
reduceLight     :: Int -> Lightness -> Lightness 
reduceLight n c = fromIntegral c' / fromIntegral (n - 1) 
                where 
                c' :: Int 
                c' = floor (fromIntegral n * c) 
-- ---------------------------------------- 
-- predefined channel values and combinators 
uniChan         :: Lightness -> Channel 
uniChan         = const . const 
darkChan        :: Channel 
darkChan        = uniChan dark 
lightChan       :: Channel 
lightChan       = uniChan light 
mergeChan       :: Lightness -> Channel -> Channel -> Channel 
mergeChan a c1 c2 
                = \ x y -> a * c1 x y + (1 - a) * c2 x y 
addChan         :: Channel -> Channel -> Channel 
addChan         = mergeChan grey 
addChans        :: [Channel] -> Channel 
addChans cs     = \ x y -> 
                  sum (map (\ c -> c x y) cs) 
                  / fromIntegral (length cs) 
rectangleChan   :: Geo -> Channel 
rectangleChan (Geo w h) 
                = \ x y -> 
                  if 0 <= x && x < w && 0 <= y && y < h 
                  then light 
                  else dark 
-- ---------------------------------------- 
-- 
-- predefined geometry values anf functions 
instance Show Geo where 
    show (Geo w h) = show w ++ "x" ++ show h 
mkGeo   :: Int -> Int -> Geo 
mkGeo w h 
    | w > 0 && h > 0    = Geo w h 
    | otherwise         = error $ "illegal geometry " ++ show (Geo w h) 
maxGeo                                  :: Geo -> Geo -> Geo 
(Geo w1 h1) `maxGeo` (Geo w2 h2)        = Geo (w1 `max` w2) (h1 `max` h2) 
minGeo                                  :: Geo -> Geo -> Geo 
(Geo w1 h1) `minGeo` (Geo w2 h2)        = Geo (w1 `min` w2) (h1 `min` h2) 
flipGeo                 :: Geo -> Geo 
flipGeo (Geo w h)       = Geo h w 
scaleGeo                :: Int -> Int -> Geo -> Geo 
scaleGeo n m (Geo w h)  = mkGeo (n * w) (m * h) 
shiftGeo                :: Int -> Int -> Geo -> Geo 
shiftGeo n m (Geo w h)  = mkGeo (w + n) (h + m) 
-- ---------------------------------------- 
mkBitmap        :: Geo -> (Int -> Int -> Bool) -> Image 
mkBitmap g f    = Image g ((fromIntegral . fromEnum) `on2` f) 
-- ---------------------------------------- 
invert          :: Image -> Image 
invert  (Image g c) 
                = Image g $ \ x y -> 1 - c x y 
gamma           :: Lightness -> Image -> Image 
gamma           = undefined 
bitmap          :: Image -> Image 
bitmap          = undefined 
reduceColors    :: Int -> Image -> Image 
reduceColors n  = transLight (reduceLight n) 
transLight :: (Lightness -> Lightness) -> 
              Image -> Image 
transLight tf (Image g c) 
                = Image g $ tf `on2` c 
-- ---------------------------------------- 
flipV           :: Image -> Image 
flipV (Image g@(Geo w _h) c) 
                = Image g $ \ x y -> c (w - x - 1) y 
flipH           :: Image -> Image 
flipH (Image g i) 
                = undefined 
rot90           :: Image -> Image 
rot90 (Image g i) 
                = undefined 
rot180          :: Image -> Image 
rot180          = undefined 
rot270          :: Image -> Image 
rot270          = undefined 
-- das Bild um n Spalten nach rechts rotieren und 
-- um m Zeilen nach unten 
shiftRot        :: Int -> Int -> Image -> Image 
shiftRot n m (Image g i) 
                = undefined 
-- das Bild n mal in der Wagerechten und 
-- m mal in der Senkrechten wiederhohlen 
-- (kacheln) 
tile            :: Int -> Int -> Image -> Image 
tile n m (Image g i) 
                = undefined 
-- das gleiche wie tile, nur mit abwechselnd gespiegelten 
-- Bildern, so dass keine sichtbaren Kanten an den Bildgrenzen 
-- entstehen 
tileMirr        :: Int -> Int -> Image -> Image 
tileMirr n m (Image g i) 
                = undefined 
-- Groesse halbieren 
halfSize        :: Image -> Image 
halfSize (Image (Geo w h) i) 
                = undefined 
-- vergroessern um die Faktoren n un m 
scale           :: Int -> Int -> Image -> Image 
scale n m (Image g i) 
                = undefined 
-- Zwei Bilder kombinieren, z.B. addieren 
merge2          :: (Lightness -> Lightness -> Lightness) -> 
                   Image -> Image -> Image 
merge2 op (Image g1 i1) (Image g2 i2) 
                = undefined 
-- ---------------------------------------- 
 | 
    
| 
 module Main where 
import           Data.Maybe 
import           ImageTypes 
import           ReadImage 
import           ShowImage 
import           System.Environment 
import           System.IO 
-- -------------------------------------- 
processImage :: FilePath -> FilePath 
             -> (Image -> Image) 
             -> IO () 
processImage src dst f 
    = do ih <- openBinaryFile src ReadMode 
         c  <- hGetContents ih 
         let res = showImage True . f . readImage $ c 
         oh <- openBinaryFile dst WriteMode 
         hPutStr oh res 
         hClose ih 
         hClose oh 
-- -------------------------------------- 
main    :: IO() 
main 
    = getArgs >>= main1 
main1   :: [String] -> IO() 
main1 [opr, inp, outp] 
    = do 
      processImage  inp outp (lookupOp opr) 
    where 
    lookupOp op 
        = (fromMaybe id . lookup op) fctTab 
    fctTab      :: [(String, Image -> Image)] 
    fctTab 
        = [ ("id"       , id            ) 
          , ("bitmap"   , bitmap        ) 
          , ("fliph"    , flipH         ) 
          , ("flipv"    , flipV         ) 
          , ("gamma.5"  , gamma 0.5     ) 
          , ("gamma2"   , gamma 2.0     ) 
          , ("halfsize" , halfSize      ) 
          , ("invert"   , invert        ) 
          , ("reduce3"  , reduceColors 3        ) 
          , ("rot90"    , rot90         ) 
          , ("rot180"   , rot180        ) 
          , ("rot270"   , rot270        ) 
          , ("double"   , scale 2 3     ) 
          , ("tile4"    , tile 2 2      ) 
          , ("tilemir4" , tileMirr 2 2  ) 
          , ("shiftrot" , shiftRot 20 20) 
          , ("mult"      , \ i -> merge2 (*) i (flipV i)) 
          ] 
main1 args 
    = hPutStrLn stderr $ "usage: Uebung7 <fct> <input> <output> (got: " ++ show args ++ ")" 
-- -------------------------------------- 
 | 
    
| 
 module ReadImage 
    (readImage) 
where 
import           Data.Array.Unboxed 
import           Data.Word 
import           ImageTypes 
readImage       :: String -> Image 
readImage       = readPNM . item 
readPNM         :: (String, String) -> Image 
readPNM ("P1", str) 
    = mkBitmap (mkGeo w h) at 
      where 
      (ns, rest)= readRow 2 str 
      [w, h]    = map read ns 
      pixels    = fst . readRow len $ rest 
      len       = w * h 
      bits      :: [Int] 
      bits      = map read pixels 
      bitMx     :: UArray Int Bool 
      bitMx     = listArray (0, len - 1) . map (== 0) $ bits 
      at x y 
          | 0 <= x && x < w && 
            0 <= y && y < h 
                =  bitMx ! (x + w * y) 
          | otherwise 
              = False 
readPNM ("P2", str) 
    = Image (mkGeo w h) at 
      where 
      ([w', h', m'], rest)      = readRow 3 str 
      w         = (read w')::Int 
      h         = (read h')::Int 
      m         = (read m')::Lightness 
      l         = w * h 
      pixels    = fst . readRow l $ rest 
      bytes     :: [Word8] 
      bytes     = map read pixels 
      byteMx    :: UArray Int Word8 
      byteMx    = listArray (0, l - 1) bytes 
      at x y 
          | 0 <= x && x < w && 
            0 <= y && y < h 
                =  fromIntegral (byteMx ! (x + w * y)) / m 
          | otherwise 
              = dark 
readPNM ("P3", str) 
    = Image (mkGeo w h) at 
      where 
      ([w', h', m'], rest)      = readRow 3 str 
      w         = (read w')::Int 
      h         = (read h')::Int 
      m         = (read m')::Lightness 
      len       = w * h 
      len3      = len * 3 
      pixels    = fst . readRow len3 $ rest 
      bytes     :: [Word8] 
      bytes     = merge3 (map read pixels) 
                  where 
                  merge3 (r:g:b:xs) = (r + g + b) `div` 3 : merge3 xs 
                  merge3 _          = [] 
      byteMx    :: UArray Int Word8 
      byteMx    = listArray (0, len - 1) bytes 
      at x y 
          | 0 <= x && x < w && 
            0 <= y && y < h 
                =  fromIntegral (byteMx ! (x + w * y)) / m 
          | otherwise 
              = dark 
readPNM ("P5", str) 
    = Image (mkGeo w h) at 
      where 
      ([w', h', m'], rest)      = readRow 3 str 
      w         = (read w')::Int 
      h         = (read h')::Int 
      m         = (read m')::Lightness 
      l         = w * h 
      bytes     :: [Word8] 
      bytes     = map (toEnum . fromEnum) .tail $ rest 
      byteMx    :: UArray Int Word8 
      byteMx    = listArray (0, l - 1) bytes 
      at x y 
          | 0 <= x && x < w && 
            0 <= y && y < h 
                =  fromIntegral (byteMx ! (x + w * y)) / m 
          | otherwise 
              = dark 
readPNM ("P6", str) 
    = Image (mkGeo w h) at 
      where 
      ([w', h', m'], rest)      = readRow 3 str 
      w         = (read w')::Int 
      h         = (read h')::Int 
      m         = (read m')::Lightness 
      len       = w * h 
      bytes     :: [Word8] 
      bytes     = merge3 . map (toEnum . fromEnum) . tail $ rest 
                  where 
                  merge3 (r:g:b:xs) = (r + g + b) `div` 3 : merge3 xs 
                  merge3 _          = [] 
      byteMx    :: UArray Int Word8 
      byteMx    = listArray (0, len - 1) bytes 
      at x y 
          | 0 <= x && x < w && 
            0 <= y && y < h 
                =  fromIntegral (byteMx ! (x + w * y)) / m 
          | otherwise 
              = dark 
readPNM (fmt, _) 
    = error $ "unsupported PNM format " ++ show fmt 
-- ---------------------------------------- 
item    :: String -> (String, String) 
item str 
    = i1 
      where 
      i@(w,r) = head (lex str) 
      i1 | head w == '#' 
             = item r1 
         | otherwise 
             = i 
         where 
         r1 = ( drop 1 . snd . break (== '\n') . drop 1) r 
readFct :: (String -> (a, String)) -> 
           Int -> 
           String -> ([a], String) 
readFct _f 0 str 
    = ([],str) 
readFct f n str 
    = (i:r, str2) 
      where 
      (i, str1) = f str 
      (r, str2) = readFct f (n-1) str1 
readRow         :: Int -> String -> ([String], String) 
readRow         = readFct item 
-- ---------------------------------------- 
 | 
    
| 
 module ShowImage 
    (showImage) 
where 
import           ImageTypes 
-- ---------------------------------------- 
showImage       :: Bool -> Image -> String 
showImage bin (Image (Geo w h) at) 
    = imgType ++ imgGeo ++ "255\n" ++ imgData 
    where 
    imgGeo      = show w ++ " " ++ show h ++ "\n# Haskell PPM Tools\n" 
    imgType 
        | bin           = "P5\n" 
        | otherwise     = "P2\n" 
    imgData 
        = pixToChar . pixToList $ at 
    pixToChar 
        | bin           = map toEnum 
        | otherwise     = concatMap ((++ "\n") . show) 
    pixToList f 
        = [ toPix (f x y) | y <- [0..h-1], x <- [0..w-1] ] 
        where 
        toPix   :: Lightness -> Int 
        toPix c = floor (c * 256.0) `min` 255 `max` 0 
-- ---------------------------------------- 
 | 
    
| 
 -- Initial Uebung7.cabal generated by cabal init.  For further
-- documentation, see http://haskell.org/cabal/users-guide/
name:                Uebung7
version:             0.1.0.0
synopsis:            Simple Image Manipulation
build-type:          Simple
cabal-version:       >=1.10
executable Uebung7
  main-is:             Main.hs
  other-modules:       ImageTypes
                       ReadImage
                       ShowImage
  other-extensions:    OverloadedStrings
  build-depends:       base       >=4
                     , array      >=0.4
                     , bytestring >=0.10
  default-language:    Haskell2010
  ghc-options: -Wall  -funbox-strict-fields -fwarn-tabs
 
 | 
    
| 
    | 
    
| Letzte Änderung: 27.03.2015 | © Prof. Dr. Uwe Schmidt |