packages feed

picedit (empty) → 0.1.0.0

raw patch · 5 files changed

+280/−0 lines, 5 filesdep +JuicyPixelsdep +basedep +clisetup-changed

Dependencies added: JuicyPixels, base, cli, hmatrix, picedit, vector

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Author name here (c) 2016++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Author name here nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ app/Main.hs view
@@ -0,0 +1,77 @@+module Main where+  import Data.Picture+  import System.Environment++  data Options = Options { file :: FilePath+                         , output :: FilePath+                         , argInvert :: Bool+                         , argGrayscale :: Bool+                         , argRotate :: Double+                         , argFade :: Double+                         , argContrast :: Double+                         , argGamma :: Int+                         , argBrightness :: Double+                         }++  opts = Options { file = ""+                 , output = "output.png"+                 , argInvert = False+                 , argGrayscale = False+                 , argRotate = 0+                 , argFade = 100+                 , argContrast = 0+                 , argGamma = 1+                 , argBrightness = 0+                 }++  main :: IO ()+  main = do+    args <- getArgs++    if null args+      then do+        putStrLn "Usage: picedit <input> [OPTIONS]"+        putStrLn "Options:"+        putStrLn "  --contrast <n> - a number between -255 and 255"+        putStrLn "  --brightness <n> - a number between -255 and 255"+        putStrLn "  --gamma <n>"+        putStrLn "  --fade <n> - a number between 0 and 100"+        putStrLn "  --rotate <n> - rotate image by n degrees"+        putStrLn "  --grayscale - turn the image grayscale"+        putStrLn "  --invert - invert (negative) the image"+        putStrLn "  --output <filename> - output name, defaults to output.png"+      else do +        let options = parseArgs args opts++        pic <- readPicture (file options)++        case pic of+          Left err -> print err+          Right p -> do+            let edited = rotate (argRotate options) Nothing +                      . fade (argFade options / 100)+                      . contrast (argContrast options)+                      . gamma (argGamma options)+                      . brightness (argBrightness options)+                      . conditionalFn grayscale (argGrayscale options) +                      . conditionalFn invert (argInvert options) $ p+            writePicturePng "output.png" edited+            +        return ()++    where+      conditionalFn f True = f+      conditionalFn f False = id++  parseArgs :: [String] -> Options -> Options+  parseArgs [] opts = opts+  parseArgs ("--invert":rest) opts = parseArgs rest (opts { argInvert = True })+  parseArgs ("--grayscale":rest) opts = parseArgs rest (opts { argGrayscale = True })+  parseArgs ("--rotate":n:rest) opts = parseArgs rest (opts { argRotate = read n })+  parseArgs ("--fade":n:rest) opts = parseArgs rest (opts { argFade = read n })+  parseArgs ("--contrast":n:rest) opts = parseArgs rest (opts { argContrast = read n })+  parseArgs ("--brightness":n:rest) opts = parseArgs rest (opts { argBrightness = read n })+  parseArgs ("--gamma":n:rest) opts = parseArgs rest (opts { argGamma = read n })+  parseArgs ("--output":n:rest) opts = parseArgs rest (opts { output = n })+  parseArgs (name:rest) opts = parseArgs rest (opts { file = name })+
+ picedit.cabal view
@@ -0,0 +1,36 @@+name:                picedit+version:             0.1.0.0+synopsis:            simple image manipulation functions+description:         Simple set of functions for image manipulation: contrast, brightnesss, rotation, etc.+homepage:            https://github.com/mdibaiee/picedit#readme+license:             GPL-3+license-file:        LICENSE+author:              Mahdi Dibaiee+maintainer:          mdibaiee@aol.com+copyright:           2016 Mahdi Dibaiee+category:            Image, Picture, Matrix+build-type:          Simple+-- extra-source-files:+cabal-version:       >=1.10++library+  hs-source-dirs:      src+  exposed-modules:     Data.Picture+  build-depends:       base >= 4.7 && < 5,+                       JuicyPixels >= 3.2.8 && < 3.3,+                       hmatrix >= 0.17.0.2 && < 0.18,+                       vector >= 0.11.0.0 && < 0.12+  default-language:    Haskell2010++executable picedit+  hs-source-dirs:      app+  main-is:             Main.hs+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N+  build-depends:       base+                     , picedit+                     , cli >= 0.1.2 && < 0.2+  default-language:    Haskell2010++source-repository head+  type:     git+  location: https://github.com/mdibaiee/picedit
+ src/Data/Picture.hs view
@@ -0,0 +1,135 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}++{-|+Module      : Picture+Description : manipulation functions+Copyright   : (c) Mahdi Dibaiee, 2016+License     : GPL-3+Maintainer  : mdibaiee@aol.com+Stability   : experimental+Portability : POSIX+-}+module Data.Picture ( grayscale+                    , readPicture+                    , fromImage+                    , toImage+                    , writePicturePng+                    , fade+                    , rotate+                    , contrast+                    , brightness+                    , gamma+                    , invert+                    )+  where++    import Codec.Picture+    import Numeric.LinearAlgebra+    import qualified Data.Vector.Storable as V+    import System.IO+    import Data.Maybe++    -- | 'Picture' type is just a triple of color channel matrices: (R, G, B)+    type Picture = (Matrix Double, Matrix Double, Matrix Double)++    -- |Converts a JuicyPixel 'Image PixelRGB8' to 'Picture'+    fromImage :: Image PixelRGB8 -> Picture+    fromImage Image { imageWidth = w, imageHeight = h, imageData = vec } =+     let [r, g, b] = map (reshape w . V.fromList . reverse) (snd $ V.foldl' gp (0, [[],[],[]]) (V.map fromIntegral vec))+     in (r, g, b)+     where+       gp acc x =+         case acc of+           (0, [r, g, b]) -> (1, [x:r, g, b])+           (1, [r, g, b]) -> (2, [r, x:g, b])+           (2, [r, g, b]) -> (0, [r, g, x:b])++    -- |Converts a 'Picture' to JuicyPixel 'Image PixelRGB8'+    toImage :: Picture -> Image PixelRGB8+    toImage (r, g, b) = +      let (fr, fg, fb) = (toList $ flatten r, toList $ flatten g, toList $ flatten b)+          img = V.map (fromIntegral . floor) . V.concat $ zipWith3 (\a b c -> vector [a, b, c]) fr fg fb+      in Image { imageWidth = cols r, imageHeight = rows r, imageData = img }+++    -- | Reads a 'Picture' from specified path+    readPicture :: FilePath -> IO (Either String Picture)+    readPicture path = do+      img <- readImage path+      return $ case img of +        Left err -> Left err+        Right im -> Right $ fromImage (convertRGB8 im)++    -- | Write the specified 'Picture' to a PNG file+    writePicturePng :: FilePath -> Picture -> IO ()+    writePicturePng path pic = writePng path (toImage pic)++    -- | Turn the 'Picture' grayscale+    grayscale :: Picture -> Picture+    grayscale (r, g, b) =+      let (fr, fg, fb) = (flatten r, flatten g, flatten b)+          mean = reshape (cols r) $ V.map (/ 3) (fr + fg + fb)+      in (mean, mean, mean)++    -- | Fade the 'Picture' by a number between 0 and 1+    fade :: Double -> Picture -> Picture+    fade opacity (r, g, b) = (f r, f g, f b)+      where+        f = cmap (*opacity)++    -- | Set contrast level of 'Picture', a number between -255 and 255+    contrast :: Double -> Picture -> Picture+    contrast level (r, g, b) = (f r, f g, f b)+      where+        cfactor = (259 * (level + 255)) / (255 * (259 - level))+        f = cmap (\x -> pixelBound $ cfactor * (x - 128) + 128)++    -- | Set brightness level of 'Picture', a number between -255 and 255+    brightness :: Double -> Picture -> Picture+    brightness level (r, g, b) = (f r, f g, f b)+      where+        f = cmap (pixelBound . (+level))++    -- | Set gamma level of 'Picture'+    gamma :: Int -> Picture -> Picture+    gamma level (r, g, b) = (f r, f g, f b)+      where+        f = cmap (\x -> pixelBound $ 255 * (x / 255) ^ level)++    -- | Inverts the 'Picture'+    invert :: Picture -> Picture+    invert (r, g, b) = (f r, f g, f b)+      where+        f = cmap (`subtract` 255)++    {- | Rotate 'Picture' for the specified degrees, around the specified origin.+     - If the origin is `Nothing`, rotates around the center+     -}+    rotate :: Double -> Maybe (Int, Int) -> Picture -> Picture+    rotate deg orig (r, g, b) = (f r, f g, f b)+      where+        -- rotation in radians+        rad = deg * pi / 180++        -- rotation matrix (clockwise)+        rm = fromLists [[cos rad, sin rad],+                        [negate $ sin rad, cos rad]]++        -- origin of rotation+        (originX, originY) = if isJust orig then fromJust orig else (cols r `div` 2, rows r `div` 2)++        -- all index pairs+        indices = [vector [fromIntegral x - fromIntegral originX, fromIntegral y - fromIntegral originY] | y <- [0..rows r - 1], x <- [0..cols r - 1]]++        -- rotate them using rotation matrix+        rotatedIndices :: [[Int]] = map (\r -> toList . V.map (fromIntegral . round) $ rm #> r) indices++        -- move them back to the origin+        movedIndices = map (\[x, y] -> [x + originX, y + originY]) rotatedIndices++        f m = reshape (cols m) $ fromList $ map (\[x, y] -> if y < 0 || y >= rows r || x < 0 || x >= cols r then 255 else m `atIndex` (y, x)) movedIndices++    bound (l, u) x = max l $ min u x+    pixelBound = bound (0, 255)