[Haskell-cafe] some newbie FFI questions

John Kozak jk at xylema.org
Thu Jul 1 06:53:13 EDT 2004

I'm trying to experiment with image processing in haskell (with which
I haven't much experience).  I've written some FFI code to talk to
the ImageMagick library which provokes a few questions (environment is
ghc 6.2.1 on debian):

1. Speed: I'm reading in a 2000x1500 pixel image, and have defined a
   Pixel type like this:

data Pixel a = Pixel !a !a !a deriving Show

  I use ImageMagick to load the image, then build an Array of Pixel
  Floats.  Building the array takes 45 seconds on a 2.5Ghz P4 with
  code compiled -O2, which seems slow to me - are my expectations
  unrealistic?  I've tried various UNPACK things which didn't make
  much difference.

2. How do I convert a CFloat into a Float?

3. I get the wrong answer ;-)  I expect the C and haskell code below
   to produce the same pixel data, but they don't (the C code is right).

C code:

#include <stdio.h>
#include <wand/magick_wand.h>

int main(int argc,char *argv[])
    MagickWand *wand = NewMagickWand();
    int         width,height;
    float      *pixels;
    int         i,j;
    if (argc!=2)
	fprintf(stderr,"bad args\n");
    if (!MagickReadImage(wand,argv[1]))
	fprintf(stderr,"can't read file\n");
    height = MagickGetImageHeight(wand);
    width  = MagickGetImageWidth(wand);
    printf("dimension: %dx%d\n",width,height);
    pixels = (float*) malloc(3*width*height*sizeof(float));
    if (!MagickGetImagePixels(wand,0,0,width,height,"RGB",FloatPixel,(unsigned char*) pixels))
	fprintf(stderr,"can't get pixels\n");

    printf("%f %f %f\n",pixels[0],pixels[1],pixels[2]);
    printf("%f %f %f\n",pixels[4],pixels[5],pixels[6]);
    printf("%f %f %f\n",pixels[7],pixels[8],pixels[9]);

haskell code:

-- requires flags:   -fffi -fglasgow-exts

import Foreign
import Foreign.C
import MarshalArray

import Char
import List
import Array
import Bits

data Pixel a = Pixel !a !a !a deriving Show

data MagickWand 			-- opaque

foreign import ccall "wand/magick_wand.h NewMagickWand" newMagickWand :: IO (Ptr MagickWand)
foreign import ccall "wand/magick_wand.h DestroyMagickWand" destroyMagickWand :: (Ptr MagickWand) -> IO ()
foreign import ccall "wand/magick_wand.h MagickSetFilename" setFilename :: Ptr MagickWand -> CString -> IO ()
foreign import ccall "wand/magick_wand.h MagickReadImage" readImage :: Ptr MagickWand -> CString -> IO CInt
foreign import ccall "wand/magick_wand.h MagickGetImageWidth" getWidth :: Ptr MagickWand -> CULong
foreign import ccall "wand/magick_wand.h MagickGetImageHeight" getHeight :: Ptr MagickWand -> CULong
foreign import ccall "wand/magick_wand.h MagickGetImagePixels" getPixels :: Ptr MagickWand -> CULong -> CULong -> CULong -> CULong -> CString -> CInt -> (Ptr a) -> IO CInt	

-- StorageType: enum Char=1,Short,Integer,Long,Float=5,Double

type Frame a = Array (Int,Int) (Pixel a)

getPixel :: ([a],Int) -> Pixel a
getPixel (floats,i) = Pixel (floats!!(i*3)) (floats!!(i*3+1)) (floats!!(i*3+2))

getFrame :: (Storable a) => (Ptr a) -> Int -> Int -> IO (Frame a)
getFrame arr h w = do
		     putStr ("getFrame: "++(show (w,h))++"\n")
		     floats <- peekArray (3*h*w) arr
		     return (array ((1,1),(h,w)) [((i,j),getPixel (floats,((w*i)+j))) | i<-[1..h],j<-[1..w]])

loadFrameFloat :: FilePath -> IO (Frame CFloat)
loadFrameFloat filename = do
			   wand <- newMagickWand  
			   f <- (newCString filename)
		           ok <- readImage wand f
		           let w = (fromInteger.toInteger) (getWidth wand)
			       h = (fromInteger.toInteger) (getHeight wand) in
		             arr <- mallocArray (w*h*3) :: (IO (Ptr CFloat))
		             rgb <- (newCString "RGB")
		             ok1 <- getPixels wand 0 0 (getHeight wand) (getWidth wand) rgb 5 arr -- PixelFloat==5
			     frame <- getFrame arr h w
                             free arr
			     destroyMagickWand wand
			     return frame


More information about the Haskell-Cafe mailing list