hip 1.0.1 → 1.0.1.1
raw patch · 10 files changed
+131/−53 lines, 10 filesbinary-addedPVP: minor bump suggested
API additions: PVP suggests at least a minor version bump
API changes (from Hackage documentation)
+ Graphics.Image.Interface.Repa: fromRepaArray :: (ColorSpace cs, Unbox (PixelElt cs e)) => Array D DIM2 (Pixel cs e) -> Image RD cs e
+ Graphics.Image.Interface.Repa: toRepaArray :: (ColorSpace cs, Unbox (PixelElt cs e)) => Image RD cs e -> Array D DIM2 (Pixel cs e)
+ Graphics.Image.Processing: rotate :: (Array arr cs e, Elevator e, Interpolation method) => method (Pixel cs e) -> Double -> Image arr cs e -> Image arr cs e
Files
- hip.cabal +1/−1
- images/frog_rotate330.png binary
- src/Graphics/Image.hs +4/−4
- src/Graphics/Image/IO.hs +17/−17
- src/Graphics/Image/IO/External/JuicyPixels.hs +6/−9
- src/Graphics/Image/Interface/Repa.hs +2/−0
- src/Graphics/Image/Interface/Repa/Internal.hs +30/−17
- src/Graphics/Image/Processing/Binary.hs +7/−2
- src/Graphics/Image/Processing/Geometric.hs +61/−2
- src/Graphics/Image/Types.hs +3/−1
hip.cabal view
@@ -1,5 +1,5 @@ Name: hip-Version: 1.0.1+Version: 1.0.1.1 License: BSD3 License-File: LICENSE Author: Alexey Kuleshevich
+ images/frog_rotate330.png view
binary file changed (absent → 446222 bytes)
src/Graphics/Image.hs view
@@ -10,11 +10,11 @@ -- -- Haskell Image Processing (HIP) library is a wrapper around any array like -- data structure and is fully agnostic to the underlying representation. All of--- the functionality in this library relies on few type classes that+-- the functionality in this library relies upon a few type classes, which -- corresponding representation types are instances of: ----- * @__`Graphics.Image.Types.Array` arr cs e__@ - this is a base class for every--- __@`Graphics.Image.Types.Image`@ @arr@ @cs@ @e@__, where @__arr__@ stands for an underlying array+-- * @__`Array` arr cs e__@ - this is a base class for every+-- __@`Image`@ @arr@ @cs@ @e@__, where @__arr__@ stands for an underlying array -- representation, @__cs__@ is the `ColorSpace` of an image and @__e__@ is the -- type denoting precision of an image. --@@ -116,7 +116,7 @@ import Graphics.Image.Processing import Graphics.Image.Processing.Complex---import Graphics.Image.Processing.Binary+import Graphics.Image.Processing.Geometric import Graphics.Image.IO.Histogram
src/Graphics/Image/IO.hs view
@@ -199,61 +199,61 @@ * 'BMP': - * __in__: ('Y' 'Word8'), ('RGB' 'Word8'), ('RGBA' 'Word8')- * __out__: ('Y' 'Word8'), ('RGB' 'Word8'), ('RGBA' 'Word8')+ * __read__: ('Y' 'Word8'), ('RGB' 'Word8'), ('RGBA' 'Word8')+ * __write__: ('Y' 'Word8'), ('RGB' 'Word8'), ('RGBA' 'Word8') * 'GIF': - * __in__: ('RGB' 'Word8'), ('RGBA' 'Word8')- * __out__: ('RGB' 'Word8')+ * __read__: ('RGB' 'Word8'), ('RGBA' 'Word8')+ * __write__: ('RGB' 'Word8') * Also supports reading and writing animated images, when used as @['GIF']@ * 'HDR': - * __in__: ('RGB' 'Float')- * __out__: ('RGB' 'Float')+ * __read__: ('RGB' 'Float')+ * __write__: ('RGB' 'Float') * 'JPG': - * __in__: ('Y' 'Word8'), ('YA' 'Word8'), ('RGB' 'Word8'), ('CMYK' 'Word8'),+ * __read__: ('Y' 'Word8'), ('YA' 'Word8'), ('RGB' 'Word8'), ('CMYK' 'Word8'), ('YCbCr', 'Word8')- * __out__: ('Y' 'Word8'), ('YA', 'Word8'), ('RGB' 'Word8'), ('CMYK' 'Word8'),+ * __write__: ('Y' 'Word8'), ('YA', 'Word8'), ('RGB' 'Word8'), ('CMYK' 'Word8'), ('YCbCr', 'Word8') * 'PNG': - * __in__: ('Y' 'Word8'), ('Y' 'Word16'), ('YA' 'Word8'), ('YA' 'Word16'),+ * __read__: ('Y' 'Word8'), ('Y' 'Word16'), ('YA' 'Word8'), ('YA' 'Word16'), ('RGB' 'Word8'), ('RGB' 'Word16'), ('RGBA' 'Word8'), ('RGBA' 'Word16')- * __out__: ('Y' 'Word8'), ('Y' 'Word16'), ('YA' 'Word8'), ('YA' 'Word16'),+ * __write__: ('Y' 'Word8'), ('Y' 'Word16'), ('YA' 'Word8'), ('YA' 'Word16'), ('RGB' 'Word8'), ('RGB' 'Word16'), ('RGBA' 'Word8'), ('RGBA' 'Word16') * 'TGA': - * __in__: ('Y' 'Word8'), ('RGB' 'Word8'), ('RGBA' 'Word8')- * __out__: ('Y' 'Word8'), ('RGB' 'Word8'), ('RGBA' 'Word8')+ * __read__: ('Y' 'Word8'), ('RGB' 'Word8'), ('RGBA' 'Word8')+ * __write__: ('Y' 'Word8'), ('RGB' 'Word8'), ('RGBA' 'Word8') * 'TIF': - * __in__: ('Y' 'Word8'), ('Y' 'Word16'), ('YA' 'Word8'), ('YA' 'Word16'),+ * __read__: ('Y' 'Word8'), ('Y' 'Word16'), ('YA' 'Word8'), ('YA' 'Word16'), ('RGB' 'Word8'), ('RGB' 'Word16'), ('RGBA' 'Word8'), ('RGBA' 'Word16'), ('CMYK' 'Word8'), ('CMYK' 'Word16')- * __out__: ('Y' 'Word8'), ('Y' 'Word16'), ('YA' 'Word8'), ('YA' 'Word16'),+ * __write__: ('Y' 'Word8'), ('Y' 'Word16'), ('YA' 'Word8'), ('YA' 'Word16'), ('RGB' 'Word8'), ('RGB' 'Word16'), ('RGBA' 'Word8'), ('RGBA' 'Word16') ('CMYK' 'Word8'), ('CMYK' 'Word16'), ('YCbCr' 'Word8') * 'PBM': - * __in__: ('Binary' 'Bit')+ * __read__: ('Binary' 'Bit') * Also supports sequence of images in one file, when read as @['PBM']@ * 'PGM': - * __in__: ('Y' 'Word8'), ('Y' 'Word16')+ * __read__: ('Y' 'Word8'), ('Y' 'Word16') * Also supports sequence of images in one file, when read as @['PGM']@ * 'PPM': - * __in__: ('RGB' 'Word8'), ('RGB' 'Word16')+ * __read__: ('RGB' 'Word8'), ('RGB' 'Word16') * Also supports sequence of images in one file, when read as @['PPM']@ -}
src/Graphics/Image/IO/External/JuicyPixels.hs view
@@ -506,7 +506,7 @@ -- PNG Format Reading instance (Array arr Y Word8, Array arr Binary Bit) => Readable (Image arr Binary Bit) PNG where- decode _ = either Left (Right . toImageBinary) . jpImageY8ToImage . JP.decodePng+ decode _ = fmap toImageBinary . jpImageY8ToImage . JP.decodePng instance Array arr Y Word8 => Readable (Image arr Y Word8) PNG where decode _ = jpImageY8ToImage . JP.decodePng@@ -548,7 +548,7 @@ -- TGA Format Reading instance (Array arr Y Word8, Array arr Binary Bit) => Readable (Image arr Binary Bit) TGA where- decode _ = either Left (Right . toImageBinary) . jpImageY8ToImage . JP.decodeTga+ decode _ = fmap toImageBinary . jpImageY8ToImage . JP.decodeTga instance Array arr Y Word8 => Readable (Image arr Y Word8) TGA where decode _ = jpImageY8ToImage . JP.decodeTga@@ -575,7 +575,7 @@ -- TIF Format Reading instance (Array arr Y Word8, Array arr Binary Bit) => Readable (Image arr Binary Bit) TIF where- decode _ = either Left (Right . toImageBinary) . jpImageY8ToImage . JP.decodeTiff+ decode _ = fmap toImageBinary . jpImageY8ToImage . JP.decodeTiff instance Array arr Y Word8 => Readable (Image arr Y Word8) TIF where decode _ = jpImageY8ToImage . JP.decodeTiff@@ -926,13 +926,10 @@ encode _ opts = encodeJPG opts ((convert :: Pixel RGB Word8 -> JP.PixelRGB8) . toWord8) -instance ManifestArray arr CMYK Double => Writable (Image arr CMYK Double) JPG where- encode _ opts = encodeJPG opts ((convert :: Pixel CMYK Word8 -> JP.PixelCMYK8)- . toWord8) +instance ManifestArray arr RGBA Double => Writable (Image arr RGBA Double) JPG where+ encode _ opts = encodeJPG opts ((convert :: Pixel RGB Word8 -> JP.PixelRGB8)+ . toWord8 . dropAlpha) -instance ManifestArray arr YCbCr Double => Writable (Image arr YCbCr Double) JPG where- encode _ opts = encodeJPG opts ((convert :: Pixel YCbCr Word8 -> JP.PixelYCbCr8)- . toWord8) -- Writable PNG
src/Graphics/Image/Interface/Repa.hs view
@@ -16,6 +16,8 @@ computeS, computeP, delay, -- * Representation RD(..), RS(..), RP(..),+ -- * Conversion+ fromRepaArray, toRepaArray ) where import Graphics.Image.IO
src/Graphics/Image/Interface/Repa/Internal.hs view
@@ -11,7 +11,8 @@ -- Portability : non-portable -- module Graphics.Image.Interface.Repa.Internal (- RD(..), RP(..), RS(..), computeP, computeS, delay+ RD(..), RP(..), RS(..), computeP, computeS, delay,+ fromRepaArray, toRepaArray ) where #if MIN_VERSION_base(4,8,0)@@ -223,12 +224,12 @@ index (RSImage (RUImage arr)) (i, j) = R.index arr (Z :. i :. j) index (RSImage (RScalar px)) (_, _) = px- index _ _ = _error_compute+ index _ _ = _errorCompute {-# INLINE index #-} deepSeqImage (RSImage (RUImage arr)) = deepSeqArray arr deepSeqImage (RSImage (RScalar px)) = seq px- deepSeqImage _ = _error_compute+ deepSeqImage _ = _errorCompute {-# INLINE deepSeqImage #-} (|*|) i1@(RSImage img1) i2@(RSImage img2) =@@ -237,11 +238,11 @@ fold !f !px0 (RSImage (RUImage arr)) = R.foldAllS f px0 $ arr fold !f !px0 (RSImage (RScalar px)) = f px0 px- fold _ _ _ = _error_compute+ fold _ _ _ = _errorCompute {-# INLINE fold #-} eq (RSImage (RUImage arr1)) (RSImage (RUImage arr2)) = R.equalsS arr1 arr2- eq _ _ = _error_compute+ eq _ _ = _errorCompute {-# INLINE eq #-} @@ -250,12 +251,12 @@ index (RPImage (RUImage arr)) (i, j) = R.index arr (Z :. i :. j) index (RPImage (RScalar px)) (0, 0) = px index (RPImage (RScalar _)) (_, _) = error "Scalar Image can only be indexed at (0,0)."- index _ _ = _error_compute+ index _ _ = _errorCompute {-# INLINE index #-} deepSeqImage (RPImage (RUImage arr)) = deepSeqArray arr deepSeqImage (RPImage (RScalar px)) = seq px- deepSeqImage _ = _error_compute+ deepSeqImage _ = _errorCompute {-# INLINE deepSeqImage #-} (|*|) i1@(RPImage img1) i2@(RPImage img2) =@@ -264,11 +265,11 @@ fold !f !px0 (RPImage (RUImage arr)) = head . R.foldAllP f px0 $ arr fold !f !px0 (RPImage (RScalar px)) = f px0 px- fold _ _ _ = _error_compute+ fold _ _ _ = _errorCompute {-# INLINE fold #-} eq (RPImage (RUImage arr1)) (RPImage (RUImage arr2)) = head $ R.equalsP arr1 arr2- eq _ _ = _error_compute+ eq _ _ = _errorCompute {-# INLINE eq #-} @@ -379,7 +380,7 @@ instance Exchangable RS VU where exchange _ img@(RSImage (RUImage arr)) = fromUnboxedVector (dims img) (R.toUnboxed arr) exchange _ (RSImage (RScalar px)) = singleton px- exchange _ _ = _error_compute+ exchange _ _ = _errorCompute {-# INLINE exchange #-} @@ -387,7 +388,7 @@ instance Exchangable RP VU where exchange _ img@(RPImage (RUImage arr)) = fromUnboxedVector (dims img) (R.toUnboxed arr) exchange _ (RPImage (RScalar px)) = singleton px- exchange _ _ = _error_compute+ exchange _ _ = _errorCompute {-# INLINE exchange #-} -- | Computes an image in parallel and ensures that all elements are evaluated.@@ -424,7 +425,7 @@ getPx (Z :. i :. j) = sumAllS (slice arr1 (Any :. (i :: Int) :. All) *^ slice arr2 (Any :. (j :: Int))) {-# INLINE getPx #-}-mult _ _ = _error_compute+mult _ _ = _errorCompute {-# INLINE mult #-} @@ -450,13 +451,25 @@ getDelayed _ = error "Scalar image is not an array." {-# INLINE getDelayed #-} ++-- | Create an image from a 2D Repa delayed array.+fromRepaArray :: (ColorSpace cs, Unbox (PixelElt cs e)) =>+ R.Array D DIM2 (Pixel cs e) -> Image RD cs e+fromRepaArray = RDImage+++-- | Retrieve an underlying Repa array from `RD` image type.+toRepaArray :: (ColorSpace cs, Unbox (PixelElt cs e)) =>+ Image RD cs e -> R.Array D DIM2 (Pixel cs e)+toRepaArray (RUImage arr) = R.delay arr+toRepaArray (RDImage arr) = arr+toRepaArray (RScalar px) = R.fromFunction (Z :. 1 :. 1) $ const px -_error_compute :: t-_error_compute = error "Image should be computed at ths point. Please report this error"+_errorCompute :: t+_errorCompute = error "Image should be computed at ths point. Please report this error" -_error_scalar_op :: t-_error_scalar_op =- error "This operation is not allowed on scalar images."+_errorScalarOp :: t+_errorScalarOp = error "This operation is not allowed on scalar images." instance R.Elt Bit where
src/Graphics/Image/Processing/Binary.hs view
@@ -35,7 +35,12 @@ -- | 'Thresholding' contains a convenient set of functions for binary image -- construction, which is done by comparing either a single pixel with every--- pixel in an image or two same size images pointwise.+-- pixel in an image or two same size images pointwise. For example:+--+-- >>> frog <- readImageY "images/frog.jpg"+-- >>> frog .==. PixelY 0 -- (or: PixelY 0 .==. frog)+-- >>> frog .<. flipH frog -- (or: flipH frog .>. frog)+-- class Array arr Binary Bit => Thresholding a b arr | a b -> arr where (.==.) :: (Eq (Pixel cs e), Array arr cs e) => a cs e -> b cs e -> Image arr Binary Bit (./=.) :: (Eq (Pixel cs e), Array arr cs e) => a cs e -> b cs e -> Image arr Binary Bit@@ -119,7 +124,7 @@ {-# INLINE (.||.) #-} --- | Complement each pixel in the image+-- | Complement each pixel in a binary image invert :: Array arr Binary Bit => Image arr Binary Bit -> Image arr Binary Bit invert = map complement {-# INLINE invert #-}
src/Graphics/Image/Processing/Geometric.hs view
@@ -18,7 +18,7 @@ -- ** Flipping flipV, flipH, -- ** Rotation- rotate90, rotate180, rotate270,+ rotate90, rotate180, rotate270, rotate, -- ** Scaling resize, scale ) where@@ -115,6 +115,7 @@ {-# INLINE newPx #-} {-# INLINE topToBottom #-} + -- | Crop an image, i.e. retrieves a sub-image image with @m@ rows and @n@ -- columns. Make sure @(m + i, n + j)@ is not greater than dimensions of a -- source image.@@ -193,6 +194,39 @@ {-# INLINE rotate270 #-} ++-- | Rotate an image clockwise by an angle Θ in radians.+--+-- >>> frog <- readImageRGBA "images/frog.jpg"+-- >>> writeImage "images/frog_rotate330.png" $ rotate (Bilinear (Fill 0)) (11*pi/6) frog+--+-- <<images/frog.jpg>> <<images/frog_rotate330.png>>+--+rotate :: (Array arr cs e, Elevator e, Interpolation method) =>+ method (Pixel cs e) -- ^ Interpolation method to be used+ -> Double -- ^ Angle in radians+ -> Image arr cs e -- ^ Source image+ -> Image arr cs e -- ^ Rotated image+rotate !method !theta' !img = traverse img getNewDims getNewPx where+ !theta = angle0to2pi (-theta') -- invert angle direction and put it into [0, 2*pi) range+ !sz@(m, n) = dims img+ !(mD, nD) = (fromIntegral m, fromIntegral n)+ !(sinTheta, cosTheta) = (sin' theta, cos' theta)+ !(sinThetaAbs, cosThetaAbs) = (abs sinTheta, abs cosTheta)+ !(mD', nD') = (mD * cosThetaAbs + nD * sinThetaAbs, nD * cosThetaAbs + mD * sinThetaAbs)+ !(iDelta, jDelta) =+ case (sinTheta >= 0, cosTheta >= 0) of+ (True , True ) -> (nD * sinTheta, 0) -- I quadrant+ (True , False ) -> (mD', -nD * cosTheta) -- II quadrant+ (False , False ) -> (-mD * cosTheta, nD') -- III quadrant+ (False , True ) -> (0, -mD * sinTheta) -- IV quadrant+ getNewDims _ = (ceiling mD', ceiling nD')+ getNewPx getPx (i, j) = interpolate method sz getPx (i', j') where+ (iD, jD) = (fromIntegral i - iDelta + 0.5, fromIntegral j - jDelta + 0.5)+ i' = iD * cosTheta + jD * sinTheta - 0.5+ j' = jD * cosTheta - iD * sinTheta - 0.5++ -- | Resize an image using an interpolation method. -- -- >>> frog <- readImageRGB "images/frog.jpg"@@ -204,7 +238,7 @@ method (Pixel cs e) -- ^ Interpolation method to be used during scaling. -> (Int, Int) -- ^ Dimensions of a result image. -> Image arr cs e -- ^ Source image.- -> Image arr cs e -- ^ Reuslt image.+ -> Image arr cs e -- ^ Result image. resize !method !sz'@(m', n') !img = traverse img (const sz') getNewPx where !sz@(m, n) = dims img !(fM, fN) = (fromIntegral m' / fromIntegral m, fromIntegral n' / fromIntegral n)@@ -229,3 +263,28 @@ then error "scale: scaling factor must be greater than 0." else resize method (round (fM * fromIntegral m), round (fN * fromIntegral n)) img {-# INLINE scale #-}++++-- | Put an angle into @[0, 2*pi)@ range.+angle0to2pi :: Double -> Double+angle0to2pi !f = f - 2 * pi * floor' (f / (2 * pi))+ where floor' :: Double -> Double+ floor' !x = fromIntegral (floor x :: Int)+ {-# INLINE floor' #-}+{-# INLINE angle0to2pi #-}+++-- | Make sure @sin' pi == 0@ instead of @sin pi == 1.2246467991473532e-16@+sin' :: Double -> Double+sin' a = if abs sinA <= _0 then 0 else sinA+ where !_0 = 10 * sin pi+ !sinA = sin a+{-# INLINE sin' #-}+++-- | Make sure @cos' (pi/2) == 0@ instead of @cos (pi/2) == 6.123233995736766e-17@+-- and @cos' (3*pi/2) == 0@ instead of @cos (3*pi/2) == -1.8369701987210297e-16@+cos' :: Double -> Double+cos' a = sin' (a + pi/2)+{-# INLINE cos' #-}
src/Graphics/Image/Types.hs view
@@ -8,9 +8,10 @@ -- module Graphics.Image.Types ( module Graphics.Image.ColorSpace,+ module Graphics.Image.IO.External, Array, Image, ManifestArray, SequentialArray, MutableArray, MImage, Exchangable, Border(..),- VU(..), RD(..), RS(..), RP(..), + VU(..), RD(..), RS(..), RP(..), ) where @@ -18,3 +19,4 @@ import Graphics.Image.Interface import Graphics.Image.Interface.Vector (VU(..)) import Graphics.Image.Interface.Repa (RD(..), RS(..), RP(..))+import Graphics.Image.IO.External