From a6419416ce3e5b93ea86c8e73463c6df991d876b Mon Sep 17 00:00:00 2001 From: Raphael Javaux Date: Sun, 8 Mar 2015 15:22:28 +0100 Subject: [PATCH] Grey to/from HSV conversions. Pixel conversions are now grouped in a single module, Vision.Image.Conversion. --- README.md | 49 ++++++++++++ changelog | 3 + friday.cabal | 7 +- src/Vision/Image.hs | 2 + src/Vision/Image/Class.hs | 3 - src/Vision/Image/{HSV => }/Conversion.hs | 99 +++++++++++++++++++++--- src/Vision/Image/Grey.hs | 1 - src/Vision/Image/Grey/Conversion.hs | 48 ------------ src/Vision/Image/HSV.hs | 1 - src/Vision/Image/RGB.hs | 1 - src/Vision/Image/RGB/Conversion.hs | 35 --------- src/Vision/Image/RGBA.hs | 1 - src/Vision/Image/RGBA/Conversion.hs | 23 ------ src/Vision/Image/Type.hs | 4 +- 14 files changed, 146 insertions(+), 131 deletions(-) rename src/Vision/Image/{HSV => }/Conversion.hs (51%) delete mode 100644 src/Vision/Image/Grey/Conversion.hs delete mode 100644 src/Vision/Image/RGB/Conversion.hs delete mode 100644 src/Vision/Image/RGBA/Conversion.hs diff --git a/README.md b/README.md index 315baa6..2365297 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,55 @@ operators) ; # Quick tour +## Modules + +The library is divided in three main modules: `Vision.Primitive`, `Vision.Image` +and `Vision.Histogram`. + +You can directly import sub-modules (such as `Vision.Image.Transform`), but the +backward-compatibility is *not* enforced. That is, some functions could be moved +to another sub-module in a newer backward-compatible advertised version. + +### Vision.Primitive + +`Vision.Primitive` contains types used all over the library, such as `Shape` +which is used to define shapes and indices in images and histograms. + +The module is usually imported unqualified: + +```haskell +import Vision.Primitive +``` + +### Vision.Image + +`Vision.Image` contains functions and types to manage and transform images. + +The module can be imported unqualified but it's often better use a qualified +import as some function names (such as `map`) can conflict with `Prelude` and +`Vision.Histogram`: + +```haskell +import Vision.Image +-- or +import qualified Vision.Image as I +``` + +### Vision.Histogram + +`Vision.Histogram` contains functions and types to create and compare +histograms. + +The module can be imported unqualified but it's often better use a qualified +import as some function names (such as `map`) can conflict with `Prelude` and +`Vision.Image`: + +```haskell +import Vision.Histogram +-- or +import qualified Vision.Histogram as H +``` + ## The Image type-class Images implement the `Image` type-class. diff --git a/changelog b/changelog index 775691a..b8730c3 100644 --- a/changelog +++ b/changelog @@ -1,5 +1,8 @@ -*-change-log-*- +v0.2.1.0 March 2014 + * Grey to HSV conversion. + v0.2.0.2 February 2014 * Minor documentation improvements. diff --git a/friday.cabal b/friday.cabal index 901330c..978e298 100644 --- a/friday.cabal +++ b/friday.cabal @@ -2,7 +2,7 @@ name: friday -- +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change -version: 0.2.0.2 +version: 0.2.1.0 synopsis: A functional image processing library for Haskell. homepage: https://github.com/RaphaelJ/friday license: LGPL-3 @@ -47,24 +47,21 @@ library Vision.Histogram Vision.Image Vision.Image.Class + Vision.Image.Conversion Vision.Image.Grey - Vision.Image.Grey.Conversion Vision.Image.Grey.Specialize Vision.Image.Grey.Type Vision.Image.Filter Vision.Image.Filter.Internal Vision.Image.HSV - Vision.Image.HSV.Conversion Vision.Image.HSV.Specialize Vision.Image.HSV.Type Vision.Image.Interpolate Vision.Image.Mutable Vision.Image.RGBA - Vision.Image.RGBA.Conversion Vision.Image.RGBA.Specialize Vision.Image.RGBA.Type Vision.Image.RGB - Vision.Image.RGB.Conversion Vision.Image.RGB.Specialize Vision.Image.RGB.Type Vision.Image.Threshold diff --git a/src/Vision/Image.hs b/src/Vision/Image.hs index f72729c..634b89c 100644 --- a/src/Vision/Image.hs +++ b/src/Vision/Image.hs @@ -14,6 +14,7 @@ -- detailed usage and examples. module Vision.Image ( module Vision.Image.Class + , module Vision.Image.Conversion , module Vision.Image.Grey , module Vision.Image.Filter , module Vision.Image.HSV @@ -27,6 +28,7 @@ module Vision.Image ( ) where import Vision.Image.Class +import Vision.Image.Conversion import Vision.Image.Grey import Vision.Image.Filter import Vision.Image.HSV diff --git a/src/Vision/Image/Class.hs b/src/Vision/Image/Class.hs index cd5a64d..39ebf4e 100644 --- a/src/Vision/Image/Class.hs +++ b/src/Vision/Image/Class.hs @@ -7,11 +7,8 @@ module Vision.Image.Class ( , FunctorImage (..) -- * Functions , (!), (!?), nChannels, pixel - -- * Conversion - , Convertible (..), convert ) where -import Data.Convertible (Convertible (..), convert) import Data.Int import Data.Vector.Storable (Vector, generate, unfoldr) import Data.Word diff --git a/src/Vision/Image/HSV/Conversion.hs b/src/Vision/Image/Conversion.hs similarity index 51% rename from src/Vision/Image/HSV/Conversion.hs rename to src/Vision/Image/Conversion.hs index fcd0e81..5b541a0 100644 --- a/src/Vision/Image/HSV/Conversion.hs +++ b/src/Vision/Image/Conversion.hs @@ -1,23 +1,62 @@ {-# LANGUAGE BangPatterns - , MultiParamTypeClasses - , PatternGuards #-} + , MultiParamTypeClasses #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -module Vision.Image.HSV.Conversion () where +-- 'Convertible' instances for conversions between pixel types. +module Vision.Image.Conversion (Convertible (..), convert) where -import Data.Convertible (Convertible (..), ConvertResult) +import Data.Convertible (Convertible (..), ConvertResult, convert) import Data.Word +import qualified Data.Vector.Storable as VS + +import Vision.Image.Grey.Type (GreyPixel (..)) import Vision.Image.HSV.Type (HSVPixel (..)) -import Vision.Image.RGB.Type (RGBPixel (..)) -import Vision.Image.RGB.Conversion () import Vision.Image.RGBA.Type (RGBAPixel (..)) -import Vision.Image.RGBA.Conversion () +import Vision.Image.RGB.Type (RGBPixel (..)) + +-- to Grey --------------------------------------------------------------------- + +instance Convertible GreyPixel GreyPixel where + safeConvert = Right + {-# INLINE safeConvert #-} + +instance Convertible HSVPixel GreyPixel where + safeConvert pix = (safeConvert pix :: ConvertResult RGBPixel) + >>= safeConvert + +instance Convertible RGBAPixel GreyPixel where + safeConvert !(RGBAPixel r g b a) = + Right $ GreyPixel $ word8 $ int (rgbToGrey r g b) * int a `quot` 255 + {-# INLINE safeConvert #-} + +instance Convertible RGBPixel GreyPixel where + safeConvert !(RGBPixel r g b) = + Right $ GreyPixel $ rgbToGrey r g b + {-# INLINE safeConvert #-} + +-- | Converts the colors to greyscale using the human eye colors perception. +rgbToGrey :: Word8 -> Word8 -> Word8 -> Word8 +rgbToGrey !r !g !b = (redLookupTable VS.! int r) + + (greenLookupTable VS.! int g) + + (blueLookupTable VS.! int b) +{-# INLINE rgbToGrey #-} + +redLookupTable, greenLookupTable, blueLookupTable :: VS.Vector Word8 +redLookupTable = VS.generate 256 (\val -> round $ double val * 0.299) +greenLookupTable = VS.generate 256 (\val -> round $ double val * 0.587) +blueLookupTable = VS.generate 256 (\val -> round $ double val * 0.114) + +-- to HSV ---------------------------------------------------------------------- instance Convertible HSVPixel HSVPixel where safeConvert = Right {-# INLINE safeConvert #-} +instance Convertible GreyPixel HSVPixel where + safeConvert pix = (safeConvert pix :: ConvertResult RGBPixel) + >>= safeConvert + instance Convertible RGBPixel HSVPixel where -- Based on : -- http://en.wikipedia.org/wiki/HSL_and_HSV#General_approach @@ -47,10 +86,33 @@ instance Convertible RGBPixel HSVPixel where sat !c v = word8 $ (c * 255) `quot` v -- Keeps the value of the hue between [0, 179]. - -- As the Hue's unit is 2°, 180 is equal to 360° and to 0. + -- As the Hue's unit is 2°, 180 is equal to 360° and to 0°. fixHue !h | h < 0 = h + 180 | otherwise = h +instance Convertible RGBAPixel HSVPixel where + safeConvert pix = (safeConvert pix :: ConvertResult RGBPixel) + >>= safeConvert + +-- to RGB ---------------------------------------------------------------------- + +instance Convertible RGBPixel RGBPixel where + safeConvert = Right + {-# INLINE safeConvert #-} + +instance Convertible GreyPixel RGBPixel where + safeConvert !(GreyPixel pix) = Right $ RGBPixel pix pix pix + {-# INLINE safeConvert #-} + +instance Convertible RGBAPixel RGBPixel where + safeConvert !(RGBAPixel r g b a) = + Right $ RGBPixel (withAlpha r) (withAlpha g) (withAlpha b) + where + !a' = int a + withAlpha !val = word8 $ int val * a' `quot` 255 + {-# INLINE withAlpha #-} + {-# INLINE safeConvert #-} + instance Convertible HSVPixel RGBPixel where -- Based on : -- http://en.wikipedia.org/wiki/HSL_and_HSV#Converting_to_RGB @@ -78,14 +140,29 @@ instance Convertible HSVPixel RGBPixel where x2 d = (d * v' - d * m + h' * m - h' * v' + 30 * m) `quot` 30 {-# INLINE safeConvert #-} -instance Convertible RGBAPixel HSVPixel where - safeConvert pix = (safeConvert pix :: ConvertResult RGBPixel) - >>= safeConvert +-- to RGBA --------------------------------------------------------------------- + +instance Convertible RGBAPixel RGBAPixel where + safeConvert = Right + {-# INLINE safeConvert #-} + +instance Convertible GreyPixel RGBAPixel where + safeConvert !(GreyPixel pix) = Right $ RGBAPixel pix pix pix 255 + {-# INLINE safeConvert #-} instance Convertible HSVPixel RGBAPixel where safeConvert pix = (safeConvert pix :: ConvertResult RGBPixel) >>= safeConvert +instance Convertible RGBPixel RGBAPixel where + safeConvert !(RGBPixel r g b) = Right $ RGBAPixel r g b 255 + {-# INLINE safeConvert #-} + +-- ----------------------------------------------------------------------------- + +double :: Integral a => a -> Double +double = fromIntegral + int :: Integral a => a -> Int int = fromIntegral diff --git a/src/Vision/Image/Grey.hs b/src/Vision/Image/Grey.hs index 2d94f34..a42a182 100644 --- a/src/Vision/Image/Grey.hs +++ b/src/Vision/Image/Grey.hs @@ -2,6 +2,5 @@ module Vision.Image.Grey ( module Vision.Image.Grey.Type ) where -import Vision.Image.Grey.Conversion () import Vision.Image.Grey.Specialize () import Vision.Image.Grey.Type diff --git a/src/Vision/Image/Grey/Conversion.hs b/src/Vision/Image/Grey/Conversion.hs deleted file mode 100644 index c328319..0000000 --- a/src/Vision/Image/Grey/Conversion.hs +++ /dev/null @@ -1,48 +0,0 @@ -{-# LANGUAGE BangPatterns - , MultiParamTypeClasses #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} - -module Vision.Image.Grey.Conversion () where - -import Data.Convertible (Convertible (..)) -import qualified Data.Vector.Storable as V -import Data.Word - -import Vision.Image.Grey.Type (GreyPixel (..)) -import Vision.Image.RGBA.Type (RGBAPixel (..)) -import Vision.Image.RGB.Type (RGBPixel (..)) - -instance Convertible GreyPixel GreyPixel where - safeConvert = Right - {-# INLINE safeConvert #-} - -instance Convertible RGBAPixel GreyPixel where - safeConvert !(RGBAPixel r g b a) = - Right $ GreyPixel $ word8 $ int (rgbToGrey r g b) * int a `quot` 255 - {-# INLINE safeConvert #-} - -instance Convertible RGBPixel GreyPixel where - safeConvert !(RGBPixel r g b) = - Right $ GreyPixel $ rgbToGrey r g b - {-# INLINE safeConvert #-} - --- | Converts the colors to greyscale using the human eye colors perception. -rgbToGrey :: Word8 -> Word8 -> Word8 -> Word8 -rgbToGrey !r !g !b = (redLookupTable V.! int r) - + (greenLookupTable V.! int g) - + (blueLookupTable V.! int b) -{-# INLINE rgbToGrey #-} - -redLookupTable, greenLookupTable, blueLookupTable :: V.Vector Word8 -redLookupTable = V.generate 256 (\val -> round $ double val * 0.299) -greenLookupTable = V.generate 256 (\val -> round $ double val * 0.587) -blueLookupTable = V.generate 256 (\val -> round $ double val * 0.114) - -double :: Integral a => a -> Double -double = fromIntegral - -int :: Integral a => a -> Int -int = fromIntegral - -word8 :: Integral a => a -> Word8 -word8 = fromIntegral diff --git a/src/Vision/Image/HSV.hs b/src/Vision/Image/HSV.hs index 7304f3c..da04beb 100644 --- a/src/Vision/Image/HSV.hs +++ b/src/Vision/Image/HSV.hs @@ -2,6 +2,5 @@ module Vision.Image.HSV ( module Vision.Image.HSV.Type ) where -import Vision.Image.HSV.Conversion () import Vision.Image.HSV.Specialize () import Vision.Image.HSV.Type diff --git a/src/Vision/Image/RGB.hs b/src/Vision/Image/RGB.hs index aee93ac..2190828 100644 --- a/src/Vision/Image/RGB.hs +++ b/src/Vision/Image/RGB.hs @@ -2,6 +2,5 @@ module Vision.Image.RGB ( module Vision.Image.RGB.Type ) where -import Vision.Image.RGB.Conversion () import Vision.Image.RGB.Specialize () import Vision.Image.RGB.Type diff --git a/src/Vision/Image/RGB/Conversion.hs b/src/Vision/Image/RGB/Conversion.hs deleted file mode 100644 index f4e6fa4..0000000 --- a/src/Vision/Image/RGB/Conversion.hs +++ /dev/null @@ -1,35 +0,0 @@ -{-# LANGUAGE BangPatterns - , MultiParamTypeClasses #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} - -module Vision.Image.RGB.Conversion () where - -import Data.Convertible (Convertible (..)) -import Data.Word - -import Vision.Image.Grey.Type (GreyPixel (..)) -import Vision.Image.RGBA.Type (RGBAPixel (..)) -import Vision.Image.RGB.Type (RGBPixel (..)) - -instance Convertible RGBPixel RGBPixel where - safeConvert = Right - {-# INLINE safeConvert #-} - -instance Convertible GreyPixel RGBPixel where - safeConvert !(GreyPixel pix) = Right $ RGBPixel pix pix pix - {-# INLINE safeConvert #-} - -instance Convertible RGBAPixel RGBPixel where - safeConvert !(RGBAPixel r g b a) = - Right $ RGBPixel (withAlpha r) (withAlpha g) (withAlpha b) - where - !a' = int a - withAlpha !val = word8 $ int val * a' `quot` 255 - {-# INLINE withAlpha #-} - {-# INLINE safeConvert #-} - -int :: Integral a => a -> Int -int = fromIntegral - -word8 :: Integral a => a -> Word8 -word8 = fromIntegral diff --git a/src/Vision/Image/RGBA.hs b/src/Vision/Image/RGBA.hs index 4b0f765..086c06c 100644 --- a/src/Vision/Image/RGBA.hs +++ b/src/Vision/Image/RGBA.hs @@ -2,6 +2,5 @@ module Vision.Image.RGBA ( module Vision.Image.RGBA.Type ) where -import Vision.Image.RGBA.Conversion () import Vision.Image.RGBA.Specialize () import Vision.Image.RGBA.Type diff --git a/src/Vision/Image/RGBA/Conversion.hs b/src/Vision/Image/RGBA/Conversion.hs deleted file mode 100644 index cfc2f6d..0000000 --- a/src/Vision/Image/RGBA/Conversion.hs +++ /dev/null @@ -1,23 +0,0 @@ -{-# LANGUAGE BangPatterns - , MultiParamTypeClasses #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} - -module Vision.Image.RGBA.Conversion () where - -import Data.Convertible (Convertible (..)) - -import Vision.Image.Grey.Type (GreyPixel (..)) -import Vision.Image.RGBA.Type (RGBAPixel (..)) -import Vision.Image.RGB.Type (RGBPixel (..)) - -instance Convertible RGBAPixel RGBAPixel where - safeConvert = Right - {-# INLINE safeConvert #-} - -instance Convertible GreyPixel RGBAPixel where - safeConvert !(GreyPixel pix) = Right $ RGBAPixel pix pix pix 255 - {-# INLINE safeConvert #-} - -instance Convertible RGBPixel RGBAPixel where - safeConvert !(RGBPixel r g b) = Right $ RGBAPixel r g b 255 - {-# INLINE safeConvert #-} diff --git a/src/Vision/Image/Type.hs b/src/Vision/Image/Type.hs index 3f55a4e..ef97e21 100644 --- a/src/Vision/Image/Type.hs +++ b/src/Vision/Image/Type.hs @@ -20,6 +20,7 @@ module Vision.Image.Type ( import Control.Applicative ((<$>)) import Control.DeepSeq (NFData (..)) +import Data.Convertible (Convertible (..), convert) import Data.Vector.Storable (Vector, create, enumFromN, forM_, generate) import Data.Vector.Storable.Mutable (new, write) import Foreign.Storable (Storable) @@ -28,8 +29,7 @@ import Prelude hiding (map, read) import qualified Data.Vector.Storable as V import Vision.Image.Class ( - MaskedImage (..), Image (..), FromFunction (..), FunctorImage (..) - , Convertible (..), (!), convert + MaskedImage (..), Image (..), FromFunction (..), FunctorImage (..), (!) ) import Vision.Primitive (Z (..), (:.) (..), Point, Size, ix2)