imagefilters (empty) → 0.1
raw patch · 5 files changed
+268/−0 lines, 5 filesdep +basedep +gdsetup-changed
Dependencies added: base, gd
Files
- Graphics/Filters/GD.hs +193/−0
- Graphics/Filters/Util.hs +19/−0
- LICENSE +30/−0
- Setup.hs +2/−0
- imagefilters.cabal +24/−0
+ Graphics/Filters/GD.hs view
@@ -0,0 +1,193 @@+module Graphics.Filters.GD +( + -- * Filters+ brightness+ , colorize+ , contrast+ , gaussianBlur+ , grayscale+ , edgeDetect+ , emboss+ , meanRemoval+ , negative+ , smoothing+ -- * Pixel transformation functions+ , pixelTransform+ , convolute+) where+ +import Graphics.GD+import Graphics.Filters.Util+import Control.Monad (mapM_,foldM)++{- |+ Performs the supplied transform function on every pixel of the image.+ The transform function should take as it's argument a quadruple of Ints (RGBA) and returns a new RGBA quadruple+ which is will be the new RGBA values of the the pixel.++ For example, the 'colorize' filter's inner workings are implemented with this as the transform function:++ @+ (\\(r,g,b,a) -> let+ nr = clamp 0 255 (r+ar)+ ng = clamp 0 255 (g+ag)+ nb = clamp 0 255 (b+ab)+ na = clamp 0 127 (a+aa)+ in (nr,ng,nb,na))+ @+-}+pixelTransform :: Image + -> ( RGBA -> RGBA ) -- ^ Transform function to be performed on each pixel+ -> IO ()+pixelTransform img fx = do+ (width,height) <- imageSize img+ mapM_ (\y -> + mapM_ (\x -> do+ curr <- getPixel (x,y) img+ let+ (r,g,b,a) = toRGBA curr+ (nr,ng,nb,na) = fx (r,g,b,a)+ setPixel (x,y) (rgba nr ng nb na) img+ ) [0..(width-1)]+ ) [0..(height-1)] + return ()++{- |+ Performs the convolution matrix on each pixel of the original image.+ After the matrix has been applied, the resulting RGBA value has each of it's elements divided by the Divisor argument+ and then the Offset argument is added to each element++ For example, the 'emboss' filter is implemented with the following convolution:++ > emboss img = convolute img [[1.5,0.0,0.0],[0.0,0.0,0.0],[0.0,0.0,-1.5]] 1 127+-}+convolute :: Image + -> [[Float]] -- ^ Convolution matrix+ -> Float -- ^ Divisor+ -> Float -- ^ Offset+ -> IO ()+convolute img matrix fdiv offset = do+ (width,height) <- imageSize img+ imgCpy <- copyImage img+ mapM_ (\y -> + mapM_ (\x -> convoluteImage img imgCpy matrix fdiv offset x y) [0..(width-1)]+ ) [0..(height-1)] + return ()++convoluteImage :: Image -> Image -> [[Float]] -> Float -> Float -> Int -> Int -> IO ()+convoluteImage img imgCpy matrix fdiv offset x y = do+ (nr,ng,nb,na) <- foldM (\(or,og,ob,oa) j -> do+ let yy = min (max (y-(1+j)) 0) (max (y-1) 0)+ (pr,pg,pb,pa) <- foldM (\(ar,ag,ab,aa) k -> do+ let xx = min (max (x-(1+k)) 0) (max (x-1) 0)+ curr <- getPixel (xx,yy) imgCpy+ let (r,g,b,a) = toRGBA curr+ return (ar + fromIntegral r * ((matrix!!j)!!k)+ ,ag + fromIntegral g * ((matrix!!j)!!k)+ ,ab + fromIntegral b * ((matrix!!j)!!k)+ ,fromIntegral a)+ ) (or,og,ob,oa) [0.. (length (matrix!!j) - 1)]+ return (pr,pg,pb,pa)+ ) ((0.0,0.0,0.0,0.0) :: (Float,Float,Float,Float)) [0.. (length matrix - 1)]+ let+ new_r = clamp 0 255 . truncate $ (nr/fdiv)+offset+ new_g = clamp 0 255 . truncate $ (ng/fdiv)+offset+ new_b = clamp 0 255 . truncate $ (nb/fdiv)+offset+ setPixel (x,y) (rgba new_r new_g new_b (truncate na)) img++{- |+ Applies the supplied color transformation to the image.+ The range of the passed RGB values are -255 to +255, and the range of the A value is -127 to +127.+-}+colorize :: Image -> RGBA -> IO ()+colorize img (ar,ag,ab,aa) =+ if or [ar > 255, ar < (-255), ag > 255, ag < (-255), ab > 255, ab < (-255), aa > 127, aa < (-127)] then+ error "Argument out of bounds. Colorize expects the color argument's RGB components to be between -255 and +255\+ \ and it's A component between -127 and +127"+ else+ pixelTransform img (\(r,g,b,a) -> let+ nr = clamp 0 255 (r+ar)+ ng = clamp 0 255 (g+ag)+ nb = clamp 0 255 (b+ab)+ na = clamp 0 127 (a+aa)+ in (nr,ng,nb,na))++{- |+ Inverts the image's color.+-}+negative :: Image -> IO ()+negative img =+ pixelTransform img (\(r,g,b,a) -> let+ nr = abs 255-r+ ng = abs 255-g+ nb = abs 255-b+ in (nr,ng,nb,a))++{- |+ Converts an image to grayscale+-}+grayscale :: Image -> IO ()+grayscale img = + pixelTransform img (\(r,g,b,a) -> let+ newcol = truncate $ 0.299 * fromIntegral r + 0.587 * fromIntegral g + 0.114 * fromIntegral b+ in (newcol, newcol, newcol, a)) ++{- |+ Applies the supplied contrast adjustment to the image. + The range of the contrast argument is -100 to +100, with+ -100 being maximum contrast, and +100 being minimum contrast+-}+contrast :: Image -> Int -> IO ()+contrast img contVal = + if or [contVal > 100, contVal < (-100)] then+ error "Argument out of bounds. Contrast expects the contrast argument to be between -100 and +100"+ else+ pixelTransform img (\(r,g,b,a) -> let+ contr = (**2) $ (100.0 - fromIntegral contVal )/100.0+ (ur,ug,ub,_) = normalizeRGBA (r,g,b,a)+ nr = clamp 0 255 $ (*) 255 . (+) 0.5 . (*) contr $ ur-0.5+ ng = clamp 0 255 $ (*) 255 . (+) 0.5 . (*) contr $ ug-0.5+ nb = clamp 0 255 $ (*) 255 . (+) 0.5 . (*) contr $ ub-0.5+ in (truncate nr, truncate ng, truncate nb, a))++{- |+ Applies the supplied brightness adjustment to the image.+ The range of the brightness argument is -255 to +255+-}+brightness :: Image -> Int -> IO ()+brightness img brightVal = + if or [brightVal > 255, brightVal < (-255)] then+ error "Argument out of bounds. Brightness expects the brightness argument to be between -255 and +255"+ else+ colorize img (brightVal,brightVal,brightVal,0)++{- |+ Applies Gaussian blur to the image+-}+gaussianBlur :: Image -> IO ()+gaussianBlur img = convolute img [[1.0,2.0,1.0],[2.0,4.0,2.0],[1.0,2.0,1.0]] 16 0++{- |+ Applies an Emboss effect to the image+-}+emboss :: Image -> IO ()+emboss img = convolute img [[1.5,0.0,0.0],[0.0,0.0,0.0],[0.0,0.0,-1.5]] 1 127++{- | + Applies Edge Detection to the image+-}+edgeDetect :: Image -> IO ()+edgeDetect img = convolute img [[-1.0,0.0,-1.0],[0.0,4.0,0.0],[-1.0,0.0,-1.0]] 1 127++{- |+ Applies a Mean Removal effect to the image+-}+meanRemoval :: Image -> IO ()+meanRemoval img = convolute img [[-1.0,-1.0,-1.0],[-1.0,9.0,-1.0],[-1.0,-1.0,-1.0]] 1 0++{- |+ Applies weighted Smoothing to the image. The smoothing amount is technically unbounded, + but larger values produce a less noticeable result+-}+smoothing :: Image -> Float -> IO ()+smoothing img weighting= convolute img [[1.0,1.0,1.0],[1.0,weighting,1.0],[1.0,1.0,1.0]] (weighting+8.0) 0
+ Graphics/Filters/Util.hs view
@@ -0,0 +1,19 @@+module Graphics.Filters.Util where++type RGBA = (Int,Int,Int,Int)++-- | Utility function for clamping a value between a minimum and maximum value+clamp :: (Ord a, Num a) => a -- ^ Minimum+ -> a -- ^ Maximum+ -> a -- ^ Value to clamp+ -> a+clamp minm maxm num + | num < minm = minm+ | num > maxm = maxm+ | otherwise = num++-- | Function for converting RGBA from an Int 0-255 range to a Float 0.0-1.0 range +normalizeRGBA :: RGBA -> (Float,Float,Float,Float)+normalizeRGBA (r,g,b,a) = (fromIntegral r/255,fromIntegral g/255,fromIntegral b/255,fromIntegral a/127)++
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c)2012, Tim Channell++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 Tim Channell 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
+ imagefilters.cabal view
@@ -0,0 +1,24 @@+Name: imagefilters+Version: 0.1+Synopsis: Image Filters (contrast, brightness, gaussian blur, etc)+Description: + This package brings image filters comparable to PHP's imagefilter functions. In the current release,+ these filters may only be used with the GD Haskell extension library, but in the future it may support+ other image management libraries, such as the new JuicyPixels library.+ .+ imagefilters also supplies functionality for creating custom filters by using the 'pixelTransform' function+ for per-pixel transformations, or the 'convolute' function for the application of convolution matrices++Homepage: https://github.com/tchannel/imagefilters+License: BSD3+License-file: LICENSE+Author: Tim Channell+Maintainer: tim.channell@gmail.com+Category: Graphics+Build-type: Simple+Cabal-version: >=1.2++Library+ Exposed-modules: Graphics.Filters.Util, Graphics.Filters.GD + Build-depends: base >= 4 && < 5, gd >= 3000 +