Friday is an image processing library for Haskell. It has been designed to be fast, generic and type-safe.
friday also provide some simple computer vision features such as edge detection or histogram processing.
Except for I/Os, friday is entirely written in Haskell.
The library uses FFI calls to the DevIL image library (written in C) to read images from a wide variety of formats, including BMP, JPG, PNG, GIF, ICO and PSD.
The library currently supports four color-spaces: RGB, RGBA, HSV and gray-scale images. Images can be converted between these color-spaces.
At this moment, the following features and algorithms have been implemented:
-
various image transformations: resizing, cropping, vertical and horizontal flop/flipping and flood fill ;
-
filters: morphological transformations (dilation and erosion), blurring (mean and Gaussian blurs) and derivative computation (Sobel and Scharr operators) ;
-
support for mutable and masked images ;
-
both adaptive and non-adaptive thresholding ;
-
edge detection using Canny's algorithm ;
-
color histograms (including comparisons and image equalization).
Images implement the Image
type-class.
This type-class give you, among other things, the index
and the shape
methods to look up for pixel values and to get the image size.
To benefit from Haskell's purity, non-mutable images are represented in two ways:
-
the
Manifest
representation stores images in HaskellVector
s.Grey
,HSV
,RGB
andRGBA
are manifest images ; -
the
Delayed
representation uses a function to produce image pixels. These images can be efficiently chained to produce complex transformations. With some inlining, Haskell compilers are able to produce fast algorithms by removing intermediate structures when working with delayed images.GreyDelayed
,HSVDelayed
,RGBDelayed
andRGBADelayed
are delayed images.
The convert
method from the convertible
package can be used to convert
images between color-spaces and representations.
As most functions work with both representations and all color-spaces, you need to help the type checker to choose the correct return type.
For example, if you want to convert an RGBA image to a greyscale image, use a type annotation to inform the compiler the image type you want to get:
toGrey :: RGBA -> Grey
toGrey = convert
When you only need to force the returned representation of a function, you can
use the delayed
and manifest
functions. These functions don't do anything
except enforcing types.
makeMiniature = delayed . resize Bilinear (ix2 150 150)
See this file for an example of a pipeline of delayed images.
When you use the load
function to read an image from a file, the returned
image is a StorageImage
:
data StorageImage = GreyStorage Grey | RGBAStorage RGBA | RGBStorage RGB
Before using an image loaded from a file, you need to convert it into an usable representation such as RGB:
toRGB :: StorageImage -> RGB
toRGB = convert
Some images are not defined for each of their pixels. These are called masked
images and implement the MaskedImage
type-class.
The MaskedImage
type-class primarily gives you a maskedIndex
method which
differs from index
by its type :
index :: Image i => i -> Point -> ImagePixel i
maskedIndex :: MaskedImage i => i -> Point -> Maybe (ImagePixel i)
Unlike index
, maskedIndex
doesn't always return a value. For convenience,
every Image
instance is also a MaskedImage
instance.
DelayedMask
can be used to create a masked image.
Images are instance of the FromFunction
type-class. This class provide a
method to create images from a function.
For example, if you want to create a black and white image from another image, you could write:
toBlackAndWhite :: (Image i, Convertible i Grey) => i -> Grey
toBlackAndWhite img =
let grey = convert img :: Grey
in fromFunction (shape img) $ \pt ->
if grey `index` pt > 127 then 255
else 0
However, most of the time, you will prefer to use the map
function instead:
toBlackAndWhite :: (Image i, Convertible i Grey) => i -> Grey
toBlackAndWhite img =
let grey = convert img :: Grey
in map (\pix -> if pix > 127 then 255 else 0) grey
The library has been sightly optimized and could be moderately used for real-time applications.
The following graph shows how the library compares to two other image processing libraries written in C. The fastest implementation for each algorithm is taken as reference:
The main reason that performances are currently below OpenCV is that OpenCV relies heavily on SIMD instructions.