image - Filling the enclosed areas with random colors - Haskell - Friday -
i trying perform not complex image analysis try , find distinct shapes , calculate of parameters area , perimeter (in pixels) , trying in haskell (i wanted try , work functional programming language).
the first task in line count amount of spoons on image:
using friday haskell package work images.
my idea use friday's edge detection , fill of enclosed areas it's fill function. first 1 require me iterate on image's pixels until i've stumbled upon black pixel. fill area , continue search in image (which has 1 of it's objects filled). color different objects random colors , associate these colors objects find areas , perimeters.
here how image looks after i've applied edge detection it: 
i unable find way of iterating on of pixels though. i've found read , readlinear functions in following package: https://hackage.haskell.org/package/friday-0.2.2.0/docs/vision-image-mutable.html#v:linearread, not sure how use them , unable deduce type signature since very new haskell.
here code of image reading, grayscaling , edge detecting:
{-# language scopedtypevariables #-} import prelude hiding (filter) import system.environment (getargs) import vision.detector.edge (canny) import vision.image import vision.image.storage.devil (autodetect (..), load, save) detectedges :: rgba -> grey detectedges img = let grey = convert img :: grey -- img blurring -- blurradius = 2 blurred = gaussianblur blurradius (nothing :: maybe double) grey :: grey -- sobel applying -- sobelradius = 2 lowthreshold = 256 highthreshold = 1024 in (canny sobelradius lowthreshold highthreshold blurred) :: grey processimg :: rgba -> rgba processimg img = let edges = detectedges img -- here goes of important stuff in convert edges :: rgba main :: io () main = [input, output] <- getargs io <- load autodetect input case io of left err -> putstrln "unable load image:" print err right (img :: rgba) -> merr <- save autodetect output (processimg img) case merr of nothing -> putstrln "success." err -> putstrln "unable save image:" print err thank in advance.
how find area , perimeter of connected components?
you can use contour tracing vision.image.contour contour perimeters. first lets start getting edges have:
{-# language scopedtypevariables #-} import prelude p import system.environment (getargs) import vision.detector.edge (canny) import vision.image import vision.primitive.shape import vision.image.storage.devil (autodetect (..), load, save) import vision.image.transform(floodfill) import control.monad.st (runst, st) import vision.image.contour -- detects edge of image canny's edge detector. -- -- usage: ./canny input.png output.png main :: io () main = [input, output] <- getargs -- loads image. automatically infers format. io <- load autodetect input case io of left err -> putstrln "unable load image:" print err right (grey :: grey) -> let blurred, edges :: grey edges = canny 2 256 1024 blurred :: grey here's acquire contours. due bug in draw function, use later, i'll blur first contours distinct inner , outer points. patched eventually...
cs = contours (blur 2 edges :: grey) goodcontours = p.filter goodsize (allcontourids cs) now have value of contours type includes valid contourid each connected component. each contourid can area contoursize , perimeter contourperimeter. size of perimeter length of list of perimeter points.
i did overly-tailored filter, called goodsize spoons, can play area , perimeter you'd like:
goodsize x = let ((xmin,xmax),(ymin,ymax)) = contourbox cs x in xmax-xmin > 60 && xmax-xmin < 500 && ymax-ymin > 100 && ymax-ymin < 500 final, filledcontours :: rgba filledcontours = convert $ drawcontours cs (shape edges) fill goodcontours optionally, each contour use floodfill color. here use 3 colors , fill whichever contours first in list. contours list ordered top-to-bottom left-to-right odd-ish. sortby xmin goodcontours left-right ordering.
floodstart = concatmap (take 1 . contourperimeter cs) goodcontours colors = cycle [rgbapixel 255 0 0 255, rgbapixel 0 255 0 255, rgbapixel 0 0 255 255] final = runst dofill the fill operation using st monad, can find many questions here on stackoverflow.
dofill :: forall s. st s rgba dofill = m <- thaw filledcontours :: st s (mutablemanifest rgbapixel s) mapm_ (\(p,c) -> floodfill p c m) (zip floodstart colors) return =<< unsafefreeze m -- saves edges image. automatically infers output format. merr <- save autodetect output final case merr of nothing -> putstrln "success." err -> putstrln "unable save image:" print err contourbox cs x = let ps = contourperimeter cs x (xs,ys) = unzip $ p.map (\(z :. x :. y) -> (x,y)) ps in ((minimum xs, maximum xs), (minimum ys, maximum ys)) the end result is:

Comments
Post a Comment