I would like to use Haskell to do some image processing so I have been writing small programs to test performance. I wrote a program to batch convert PNG images from color to greyscale. I compared the performance of the program below with a Python script using PIL. I found when compiled with -threaded -O2
, the Haskell code takes four times longer than the python script (this comparison was done on a folder of 50 images).
Is there a way to speed this process up?
Haskell Code
import Codec.Picture
import System.Environment
import System.Directory
import System.FilePath
import Control.Monad
dynToImg :: DynamicImage -> Image PixelRGB8
dynToImg (ImageRGB8 img) = img
procAvg :: FilePath -> FilePath -> IO ()
procAvg inpath outpath = do
Right inImage <- readPng inpath
let image = avgImage . dynToImg $ inImage
writePng outpath image
main :: IO ()
main = do
args <- getArgs
let [infolder,outfolder,tstr] = args
t = read tstr :: Int
files <- getDirectoryContents infolder
let baseNames = filter ((==) ".png" . takeExtension) files
infiles = map (infolder </>) baseNames
outfiles = map (outfolder </>) baseNames
zipWithM_ procAvg infiles outfiles
avgImage :: Image PixelRGB8 -> Image Pixel8
avgImage = pixelMap pixelAvg
pixelAvg :: PixelRGB8 -> Pixel8
pixelAvg (PixelRGB8 r g b) =
(r `div` 3 + g `div` 3 + b `div` 3 +
(r `mod` 3 + g `mod` 3 + b `mod` 3) `div` 3)
Python Code
from PIL import Image
import sys
import os
if __name__ == "__main__":
infolder = sys.argv[1]
outfolder = sys.argv[2]
fnames = os.listdir(infolder)
for f in fnames:
infile = os.path.join(infolder,f)
outfile = os.path.join(outfolder,f)
inimg = Image.open(infile)
gray = inimg.convert('L')
gray.save(outfile)
inimg.close()