{-# OPTIONS -O #-} --- {-# OPTIONS_GHC -XFlexibleContexts #-} see Makefile -- Author: Pawel Bulkowski (pawelb16@gmail.com) -- Thanks to Michal Palka for teaching me Haskell -- Photos by: Magdalena Niedziela -- based on other gtk2hs example applications -- the code is public domain import Graphics.UI.Gtk import Graphics.UI.Gtk.Gdk.EventM import Data.Array.MArray import Data.Array.IO --import Data.Array.IO.Internals import Data.Array.Storable import Data.Bits import Data.Word import Data.Maybe import Data.IORef import Data.Ord import Control.Monad ( when, unless, liftM ) import Control.Monad.Trans ( liftIO ) import Control.Monad.ST import Data.Array.Base ( unsafeWrite, unsafeRead ) import Graphics.UI.Gtk import Graphics.UI.Gtk.Glade import Graphics.UI.Gtk.ModelView as New import Graphics.UI.Gtk.Gdk.GC (gcNew) import System.CPUTime import System.Environment ( getArgs ) import System.Directory ( doesFileExist ) type ArrayType = IOUArray --type ArrayType = StorableArray -- The state and GUI data ImageState = Empty|NonEmpty data State = State { pb :: Pixbuf, is :: ImageState } main = do args <- getArgs case args of [fName] -> do exists <- doesFileExist fName if exists then runGUI fName else putStrLn ("File "++fName++" not found.") _ -> putStrLn "Usage: scaling " runGUI fName = do initGUI window <- windowNew window `onDestroy` mainQuit set window [ windowTitle := "Scaling" , windowResizable := True ] label <- labelNew (Just "Content Aware Image Scaling") vboxOuter <- vBoxNew False 0 vboxInner <- vBoxNew False 5 (mb,miOpen,miSave,miScale, miGradient, miSeamCarve, miQuit) <- makeMenuBar canvas <- drawingAreaNew containerAdd vboxInner canvas -- Assemble the bits set vboxOuter [ containerChild := mb , containerChild := vboxInner ] set vboxInner [ containerChild := label , containerBorderWidth := 10 ] set window [ containerChild := vboxOuter ] -- create the Pixbuf pb <- pixbufNew ColorspaceRgb False 8 256 256 -- Initialize the state state <- newIORef State { pb = pb, is = Empty } let modifyState f = readIORef state >>= f >>= writeIORef state canvas `onSizeRequest` return (Requisition 256 256) -- Add action handlers onActivateLeaf miQuit mainQuit -- onActivateLeaf miOpen $ modifyState $ reset gui onActivateLeaf miOpen $ modifyState $ loadImageDlg canvas window onActivateLeaf miSave $ modifyState $ saveImageDlg canvas window onActivateLeaf miScale $ modifyState $ scaleImageDlg canvas window onActivateLeaf miGradient $ modifyState $ gradientImageDlg canvas window onActivateLeaf miSeamCarve $ modifyState $ seamCarveImageDlg canvas window modifyState (loadImage canvas window fName) canvas `on` exposeEvent $ updateCanvas state boxPackStartDefaults vboxInner canvas widgetShowAll window mainGUI return () --uncomment for ghc < 6.8.3 --instance Show Rectangle where -- show (Rectangle x y w h) = "x="++show x++", y="++show y++ -- ", w="++show w++", h="++show h++";" updateCanvas :: IORef State -> EventM EExpose Bool updateCanvas rstate = do region <- eventRegion win <- eventWindow liftIO $ do state <- readIORef rstate let (State pb is) = state gc <- gcNew win width <- pixbufGetWidth pb height <- pixbufGetHeight pb pbregion <- regionRectangle (Rectangle 0 0 width height) regionIntersect region pbregion rects <- regionGetRectangles region putStrLn ("redrawing: "++show rects) (flip mapM_) rects $ \(Rectangle x y w h) -> do drawPixbuf win gc pb x y x y w h RgbDitherNone 0 0 return True {-# INLINE doFromTo #-} -- do the action for [from..to], ie it's inclusive. doFromTo :: Int -> Int -> (Int -> IO ()) -> IO () doFromTo from to action = let loop n | n > to = return () | otherwise = do action n loop (n+1) in loop from -- do the action for [to..from], ie it's inclusive. {-# INLINE doFromToDown #-} doFromToDown :: Int -> Int -> (Int -> IO ()) -> IO () doFromToDown from to action = let loop n | n < to = return () | otherwise = do action n loop (n-1) in loop from -- do the action for [from..to] with step, ie it's inclusive. {-# INLINE doFromToStep #-} doFromToStep :: Int -> Int -> Int -> (Int -> IO ()) -> IO () doFromToStep from to step action = let loop n | n > to = return () | otherwise = do action n loop (n+step) in loop from --forM = flip mapM makeMenuBar = do mb <- menuBarNew fileMenu <- menuNew open <- menuItemNewWithMnemonic "_Open" save <- menuItemNewWithMnemonic "_Save" scale <- menuItemNewWithMnemonic "_Scale" gradient <- menuItemNewWithMnemonic "_Gradient" seamCarve <- menuItemNewWithMnemonic "Seam _Carve" quit <- menuItemNewWithMnemonic "_Quit" file <- menuItemNewWithMnemonic "_File" menuShellAppend fileMenu open menuShellAppend fileMenu save menuShellAppend fileMenu scale menuShellAppend fileMenu gradient menuShellAppend fileMenu seamCarve menuShellAppend fileMenu quit menuItemSetSubmenu file fileMenu containerAdd mb file return (mb,open,save,scale,gradient,seamCarve,quit) loadImageDlg canvas window (State pb is) = do putStrLn ("loadImage") ret <- openFileDialog window case ret of Just (filename) -> (loadImage canvas window filename (State pb is)) Nothing -> return (State pb is) loadImage canvas window filename (State pb is) = do putStrLn ("loadImage") pxb <- pixbufNewFromFile filename width <- pixbufGetWidth pxb height <- pixbufGetHeight pxb widgetSetSizeRequest canvas width height widgetQueueDraw canvas -- updateCanvas canvas pxb return (State pxb NonEmpty) saveImageDlg canvas window (State pb is) = do putStrLn ("saveImage") ret <- openFileDialog window case ret of Just (filename) -> do pixbufSave pb filename "png" [] return (State pb is) Nothing -> return (State pb is) scaleImageDlg canvas window (State pb is) = do putStrLn ("scaleImage") origWidth <- pixbufGetWidth pb origHeight <- pixbufGetHeight pb ret <- scaleDialog window origWidth origHeight let update w h = do putStrLn ("seamCarveImage::update w: "++show w++" h: "++show h) --scalePixbuf :: Pixbuf -> Int -> Int -> IO Pixbuf pxb <- scalePixbuf pb w h width <- pixbufGetWidth pxb height <- pixbufGetHeight pxb widgetSetSizeRequest canvas width height widgetQueueDraw canvas --updateCanvas canvas pxb return (State pxb NonEmpty) case ret of Nothing -> return (State pb NonEmpty) Just (w,h) -> (update w h) gradientImageDlg canvas window (State pb is) = do putStrLn ("gradientImageDlg") --scalePixbuf :: Pixbuf -> Int -> Int -> IO Pixbuf pxb <- gradientPixbuf pb width <- pixbufGetWidth pxb height <- pixbufGetHeight pxb widgetSetSizeRequest canvas width height widgetQueueDraw canvas -- updateCanvas canvas pxb return (State pxb NonEmpty) seamCarveImageDlg canvas window (State pb is) = do origWidth <- pixbufGetWidth pb origHeight <- pixbufGetHeight pb ret <- seamCarveDialog window origWidth origHeight 2 let update w h grdCnt = do putStrLn ("seamCarveImageDlg::update w: "++show w++" h: "++show h) --scalePixbuf :: Pixbuf -> Int -> Int -> IO Pixbuf --pxb <- scalePixbuf pb w h cpuStart <- getCPUTime pxb <- seamCarvePixbuf pb w h grdCnt cpuEnd <- getCPUTime putStrLn ("seamCarveImageDlg::cpu time: "++show ((fromIntegral (cpuEnd-cpuStart) :: Double) /1e12)) width <- pixbufGetWidth pxb height <- pixbufGetHeight pxb widgetSetSizeRequest canvas width height widgetQueueDraw canvas --updateCanvas canvas pxb return (State pxb NonEmpty) case ret of Nothing -> return (State pb NonEmpty) Just (w,h,grdCnt) -> (update w h grdCnt) scaleDialog :: Window -> Int -> Int-> IO (Maybe (Int, Int)) scaleDialog parent width height = do Just xml <- xmlNew "scaling.glade" dia <- xmlGetWidget xml castToDialog "dialogScale" dialogAddButton dia stockCancel ResponseCancel dialogAddButton dia stockOk ResponseOk entryWidth <- xmlGetWidget xml castToEntry "entryScalingWidth" entryHeight <- xmlGetWidget xml castToEntry "entryScalingHeight" entrySetText entryWidth (show width) entrySetText entryHeight (show height) res <- dialogRun dia widthStr <- entryGetText entryWidth heightStr <- entryGetText entryHeight widgetDestroy dia putStrLn ("scaleDialog width: "++show width++" height: "++show height) case res of ResponseOk -> return (Just (read widthStr,read heightStr)) _ -> return Nothing seamCarveDialog :: Window -> Int -> Int -> Int -> IO (Maybe (Int, Int, Int)) seamCarveDialog parent width height grdCnt= do Just xml <- xmlNew "scaling.glade" dia <- xmlGetWidget xml castToDialog "dialogSeamCarve" dialogAddButton dia stockCancel ResponseCancel dialogAddButton dia stockOk ResponseOk entryWidth <- xmlGetWidget xml castToEntry "entryWidth" entryHeight <- xmlGetWidget xml castToEntry "entryHeight" entryGrdCnt <- xmlGetWidget xml castToEntry "entryGrdCnt" entrySetText entryWidth (show width) entrySetText entryHeight (show height) entrySetText entryGrdCnt (show grdCnt) res <- dialogRun dia widthStr <- entryGetText entryWidth heightStr <- entryGetText entryHeight grdCntStr <- entryGetText entryGrdCnt widgetDestroy dia putStrLn ("scaleDialog width: "++show width++" height: "++show height++" grdCnt: "++show grdCnt) case res of ResponseOk -> return (Just (read widthStr,read heightStr, read grdCntStr)) _ -> return Nothing openFileDialog :: Window -> IO (Maybe String) openFileDialog parentWindow = do dialog <- fileChooserDialogNew (Just "Open Profile... ") (Just parentWindow) FileChooserActionOpen [("gtk-cancel", ResponseCancel) ,("gtk-open", ResponseAccept)] widgetShow dialog response <- dialogRun dialog widgetHide dialog case response of ResponseAccept -> fileChooserGetFilename dialog _ -> return Nothing --simple pixbuf scaling scalePixbuf :: Pixbuf -> Int -> Int -> IO Pixbuf scalePixbuf pb newWidth newHeight = do width <- pixbufGetWidth pb height <- pixbufGetHeight pb row <- pixbufGetRowstride pb chan <- pixbufGetNChannels pb bits <- pixbufGetBitsPerSample pb pbData <- (pixbufGetPixels pb :: IO (PixbufData Int Word8)) pbn <- pixbufNew ColorspaceRgb False 8 newWidth newHeight pbnData <- (pixbufGetPixels pbn :: IO (PixbufData Int Word8)) newRow <- pixbufGetRowstride pbn putStrLn ("bytes per row: "++show row++", channels per pixel: "++show chan++ ", bits per sample: "++show bits) putStrLn ("width: "++show width++", height: "++show height++", newWidth: "++show newWidth++", newHeight: "++show newHeight++" bytes per row new: "++show newRow) let stepX = (fromIntegral width) / (fromIntegral newWidth) :: Double let stepY = (fromIntegral height) / (fromIntegral newHeight) :: Double doFromTo 0 (newHeight-1) $ \y -> do let y1 = truncate ((fromIntegral y) * stepY) doFromTo 0 (newWidth-1) $ \x -> do let x1 = truncate ((fromIntegral x) * stepX) let off = (x1*chan+y1*row) let offNew = (x*chan+y*newRow) --putStrLn ("x: "++show x++", y: "++show y++" x1: "++show x1++", y1: "++show y1++" off:"++show off++" offNew:"++show offNew) r <- unsafeRead pbData (off) g <- unsafeRead pbData (1+off) b <- unsafeRead pbData (2+off) unsafeWrite pbnData (offNew) r unsafeWrite pbnData (1+offNew) g unsafeWrite pbnData (2+offNew) b return pbn {-# INLINE arrmove #-} arrmove :: (Ix i, MArray a e IO) => a i e -> Int -> Int -> Int -> IO () arrmove arr src dst size = do --putStrLn("arrmove "++show src++" "++show dst++" "++show size) doFromTo 0 (size-1) $ \x -> do --forM [0..(size-1)] $ \x -> do v <- unsafeRead arr (src+x) unsafeWrite arr (dst+x) v --putStrLn("arrmove2 "++show src++" "++show dst++" "++show size) return () {-# INLINE arrmovesd #-} arrmovesd :: (Ix b, MArray a c IO) => a b c -> a b c -> Int -> Int -> Int -> IO () arrmovesd arrsrc arrdst src dst size = do doFromTo 0 (size-1) $ \x -> do --forM [0..(size-1)] $ \x -> do v <- unsafeRead arrsrc (src+x) unsafeWrite arrdst (dst+x) v return () {-# INLINE arrmoven #-} arrmoven :: (Ix i, MArray a e IO) => a i e -> Int -> Int -> Int -> Int -> Int -> IO () arrmoven arr src dst size w n = do --putStrLn("arrmoven "++show src++" "++show dst++" "++show size++" "++show w++" "++show n) doFromToStep 0 ((n-1)*w) w $ \yoff -> do arrmove arr (src+yoff) (dst+yoff) size return () -- content Aware scaling --TODO! seamCarvePixbuf :: Pixbuf -> Int -> Int -> Int -> IO Pixbuf seamCarvePixbuf pb newWidth newHeight grdCnt = do width <- pixbufGetWidth pb height <- pixbufGetHeight pb row <- pixbufGetRowstride pb chan <- pixbufGetNChannels pb bits <- pixbufGetBitsPerSample pb pbData <- (pixbufGetPixels pb :: IO (PixbufData Int Word8)) --pbn <- pixbufNew ColorspaceRgb False 8 newWidth newHeight pbn <- pixbufNew ColorspaceRgb False 8 newWidth newHeight pbnData <- (pixbufGetPixels pbn :: IO (PixbufData Int Word8)) newRow <- pixbufGetRowstride pbn putStrLn ("bytes per row: "++show row++", channels per pixel: "++show chan++ ", bits per sample: "++show bits) putStrLn ("width: "++show width++", height: "++show height++", newWidth: "++show newWidth++", newHeight: "++show newHeight++" bytes per row new: "++show newRow) tmpPB <- pixbufCopy pb tmpData <- (pixbufGetPixels tmpPB) :: IO (PixbufData Int Word8) ----double gradient let computeSrcPic pb cnt | cnt <= 0 = do pixbufCopy pb | cnt > 0 = do pb <- computeSrcPic pb (cnt-1) gradientPixbuf pb --computing gradient but one more gradient --will be compute later by gradientArray function tmpPB2 <- computeSrcPic tmpPB (grdCnt-1) tmpData2 <- (pixbufGetPixels tmpPB2) :: IO (PixbufData Int Word8) -- array to store x coord of removed pixels coordArr <- newArray (0, (max width height)) 0 :: IO (ArrayType Int Int) let removeVPixel pixData x y w = do --unsafeWrite pixData (0+x*chan+y*row) 255 --unsafeWrite pixData (1+x*chan+y*row) 255 --unsafeWrite pixData (2+x*chan+y*row) 255 --store x-coord of removed pixel unsafeWrite coordArr y x arrmove pixData ((x+1)*chan+y*row) (x*chan+y*row) ((w-x-1)*chan) return () let removeHPixel pixData x y h = do --putStrLn("removeHPixel "++show x++" "++show y++" "++show h) --store y-coord of removed pixel unsafeWrite coordArr y x --putStrLn("removeHPixel1.5 "++show x++" "++show y++" "++show h) arrmoven pixData (y*chan+(x+1)*row) (y*chan+x*row) chan row (h-x-1) --putStrLn("removeHPixel2 "++show x++" "++show y++" "++show h) return () let removeVGrdPixel grdData x y w = do arrmove grdData (x+1+y*width) (x+y*width) (w-x-1) return () let removeHGrdPixel grdData x y h = do --putStrLn("removeHGrdPixel "++show x++" "++show y++" "++show h) arrmoven grdData (y+(x+1)*width) (y+x*width) 1 width (h-x-1) --putStrLn("removeHGrdPixel2 "++show x++" "++show y++" "++show h) return () let vPixIndex x y chan row = (x*chan)+(y*row) let hPixIndex x y chan row = (y*chan)+(x*row) -- possibly it can be made shorted let removeSeam pixIndex rmPixel rmGrdPixel seamArr grdArr x y w = do rmPixel tmpData x y w rmPixel tmpData2 x y w rmGrdPixel grdArr x y w unless (y == 0) $ do v0 <- if x==0 then return 0x7fffffff else unsafeRead seamArr (pixIndex (x-1) y 1 width) v1 <- unsafeRead seamArr (pixIndex x y 1 width) v2 <- if x==(w-1) then return 0x7fffffff else unsafeRead seamArr (pixIndex (x+1) y 1 width) let nextX | v0 < v1 && v0 < v2 = (x-1) | v2 < v1 = (x+1) | True = x removeSeam pixIndex rmPixel rmGrdPixel seamArr grdArr nextX (y-1) w -- possibly it can be update to be more general let updateGradientArray pixIndex grdArr y w h = unless (y == -1) $ do x <- unsafeRead coordArr y unless (x == 0) $ do g <- pixelGradient pixIndex tmpData2 row chan w h (x-1) y unsafeWrite grdArr (pixIndex (x-1) y 1 width) g unless (y == 0) $ do g <- pixelGradient pixIndex tmpData2 row 1 w h (x-1) (y-1) unsafeWrite grdArr (pixIndex (x-1) (y-1) 1 width) g unless (y == (h-1)) $ do g <- pixelGradient pixIndex tmpData2 row 1 w h (x-1) (y+1) unsafeWrite grdArr (pixIndex (x-1) (y+1) 1 width) g g <- pixelGradient pixIndex tmpData2 row 1 w h x y unsafeWrite grdArr (pixIndex x y 1 width) g unless (y == 0) $ do g <- pixelGradient pixIndex tmpData2 row 1 w h x (y-1) unsafeWrite grdArr (pixIndex x (y-1) 1 width) g g <- pixelGradient pixIndex tmpData2 row 1 w h x (y+1) unless (y == (h-1)) $ do g <- pixelGradient pixIndex tmpData2 row 1 w h x (y+1) unsafeWrite grdArr (pixIndex x (y+1) 1 width) g updateGradientArray pixIndex grdArr (y-1) w h return () let findMinVal pixIndex seamArr w h = do v <- unsafeRead seamArr (pixIndex 0 (h-1) 1 width) xRef <- newIORef (v :: Int, 0 :: Int) --let modifyState f = readIORef state >>= f >>= writeIORef state doFromTo 1 (w-1) $ \x -> do --putStrLn("findMinVal loop x: "++show x++" (h-1): "++show (h-1)) v <- unsafeRead seamArr (pixIndex x (h-1) 1 width) (mval, m) <- readIORef xRef writeIORef xRef (if v < mval then (v, x) else (mval, m)) (mval, m) <- readIORef xRef putStrLn("w: " ++show w++ " minSeam: " ++ show mval ++ " at: "++show m) return m grdArr <- gradientArray tmpPB2 width height let removeVSeam w = do seamArr <- (computeVSeamArray grdArr width height w) m <- findMinVal vPixIndex seamArr w (height-1) removeSeam vPixIndex removeVPixel removeVGrdPixel seamArr grdArr m (height-1) w updateGradientArray vPixIndex grdArr (height-1) w height return () let removeHSeam h = do seamArr <- (computeHSeamArray grdArr width height h) m <- findMinVal hPixIndex seamArr h (width-1) removeSeam hPixIndex removeHPixel removeHGrdPixel seamArr grdArr m (width-1) h updateGradientArray hPixIndex grdArr (width-1) h width return () --let nextX | v0 < v1 && v0 < v2 = (x-1) -- | v2 < v1 = (x+1) -- | True = x let grdSeam w h | w > newWidth && h > newHeight = do --putStrLn("grdSeam: "++show w++" "++show h) vSeamArr <- (computeVSeamArray grdArr width height w) mv <- findMinVal vPixIndex vSeamArr w (height-1) hSeamArr <- (computeHSeamArray grdArr width height h) mh <- findMinVal hPixIndex hSeamArr h (width-1) if mv < mh then do removeSeam vPixIndex removeVPixel removeVGrdPixel vSeamArr grdArr mv (height-1) w updateGradientArray vPixIndex grdArr (height-1) w height grdSeam (w-1) h else do removeSeam hPixIndex removeHPixel removeHGrdPixel hSeamArr grdArr mh (width-1) h updateGradientArray hPixIndex grdArr (width-1) h width grdSeam w (h-1) | w > newWidth = do --putStrLn("grdSeam2: "++show w++" "++show h) removeVSeam w grdSeam (w-1) h | h > newHeight = do --putStrLn("grdSeam3: "++show w++" "++show h) removeHSeam h grdSeam w (h-1) | True = do return () -- remove/add seams --doFromToDown width (newWidth+1) $ \w -> do -- removeVSeam w --doFromToDown height (newHeight+1) $ \h -> do -- removeHSeam h grdSeam width height doFromTo 0 (newHeight-1) $ \y -> do arrmovesd tmpData pbnData (y*row) (y*newRow) newRow return pbn -- compute the gradient map gradientPixbuf :: Pixbuf -> IO Pixbuf gradientPixbuf pb = do width <- pixbufGetWidth pb height <- pixbufGetHeight pb row <- pixbufGetRowstride pb chan <- pixbufGetNChannels pb bits <- pixbufGetBitsPerSample pb pbData <- (pixbufGetPixels pb :: IO (PixbufData Int Word8)) pbn <- pixbufNew ColorspaceRgb False 8 width height pbnData <- (pixbufGetPixels pbn :: IO (PixbufData Int Word8)) putStrLn ("bytes per row: "++show row++", channels per pixel: "++show chan++", bits per sample: "++show bits) putStrLn ("width: "++show width++", height: "++show height) let getpix x y c = do case (x < 1 || x >= width || y < 1 || y >= height) of True -> return 0 False -> (unsafeRead pbData (c+x*chan+y*row)) let gradient x y c = do let convM = liftM fromIntegral blah a b = convM (getpix a b c) v00 <- blah (x-1) (y-1) v10 <- blah x (y-1) v20 <- blah (x+1) (y-1) v01 <- blah (x-1) y v21 <- blah (x+1) y v02 <- blah (x-1) (y+1) v12 <- blah x (y+1) v22 <- blah (x+1) (y+1) let gx = abs ((v20-v00)+2*(v21-v01)+(v22-v02)) let gy = abs ((v02-v00)+2*(v12-v10)+(v22-v20)) let g = (gx + gy)::Int --let g8 = (shiftR g 3) let g8 = if g > 255 then 255 else g return (fromIntegral(g8) :: Word8) let totalGradient x y = do rg <- gradient x y 0 gg <- gradient x y 1 bg <- gradient x y 2 let g = rg + gg + bg return ((fromIntegral g)::Word8) doFromTo 0 (height-1) $ \y -> do let offY = y*row doFromTo 0 (width-1) $ \x -> do let offX = x*chan doFromTo 0 2 $ \c -> do let off = offY+offX + c --putStrLn ("x: "++show x++", y: "++show y++" off:"++show off) --v <- (totalGradient x y) v <- (gradient x y c) unsafeWrite pbnData (off) v return pbn -- compute gradient fo single pixel {-# INLINE pixelGradient #-} pixelGradient :: (Int -> Int -> Int -> Int -> Int) -> (PixbufData Int Word8) -> Int -> Int -> Int -> Int -> Int -> Int -> (IO Word16) pixelGradient pixIndex pbData row chan w h x y = do let getpix x y c = do case (x < 0 || x >= w || y < 0 || y >= h) of True -> return 0 False -> (unsafeRead pbData (c+(pixIndex x y chan row))) --False -> (unsafeRead pbData (c+x*chan+y*row)) let gradient x y c = do let convM = liftM fromIntegral blah a b = convM (getpix a b c) v00 <- blah (x-1) (y-1) v10 <- blah x (y-1) v20 <- blah (x+1) (y-1) v01 <- blah (x-1) y v21 <- blah (x+1) y v02 <- blah (x-1) (y+1) v12 <- blah x (y+1) v22 <- blah (x+1) (y+1) let gx = abs ((v20-v00)+2*(v21-v01)+(v22-v02)) let gy = abs ((v02-v00)+2*(v12-v10)+(v22-v20)) let g = (gx + gy)::Int --let g8 = (shiftR g 3) let g8 = if g > 255 then 255 else g return (fromIntegral(g8) :: Word8) let gradient x y c = do let convM = liftM fromIntegral blah a b = convM (getpix a b c) v00 <- blah (x-1) (y-1) v10 <- blah x (y-1) v20 <- blah (x+1) (y-1) v01 <- blah (x-1) y v21 <- blah (x+1) y v02 <- blah (x-1) (y+1) v12 <- blah x (y+1) v22 <- blah (x+1) (y+1) let gx = abs ((v20-v00)+2*(v21-v01)+(v22-v02)) let gy = abs ((v02-v00)+2*(v12-v10)+(v22-v20)) let g = gx + gy return (g :: Int) rg <- gradient x y 0 gg <- gradient x y 1 bg <- gradient x y 2 let g = rg + gg + bg return ((fromIntegral g) :: Word16) -- compute the gradient map gradientArray :: Pixbuf -> Int -> Int -> IO (ArrayType Int Word16) gradientArray pb w h = do width <- pixbufGetWidth pb height <- pixbufGetHeight pb row <- pixbufGetRowstride pb chan <- pixbufGetNChannels pb bits <- pixbufGetBitsPerSample pb pbData <- (pixbufGetPixels pb :: IO (PixbufData Int Word8)) grdArr <- newArray (0, width * height) 0 putStrLn ("bytes per row: "++show row++", channels per pixel: "++show chan++", bits per sample: "++show bits) putStrLn ("width: "++show width++", height: "++show height) let vPixIndex x y chan row = x*chan+y*row doFromTo 0 (h-1) $ \y -> do let offY = y*width doFromTo 0 (w-1) $ \x -> do let off = x + offY --v <- (totalGradient x y) v <- (pixelGradient vPixIndex pbData row chan w h x y) unsafeWrite grdArr (off) v --putStrLn ("x: "++show x++" y: "++show y++" v: "++show v) return grdArr computeVSeamArray :: (ArrayType Int Word16) -> Int -> Int -> Int -> IO (ArrayType Int Int) computeVSeamArray grdArr width height currentWidth = do seamArr <- newArray (0, width * height) 0 --grdArr <- gradientArr doFromTo 0 (currentWidth-1) $ \x -> do v <- unsafeRead grdArr x unsafeWrite seamArr x (fromIntegral v :: Int) doFromTo 1 (height-1) $ \y -> do let offY = y*width let prevOffY = offY-width doFromTo 1 (currentWidth-2) $ \x -> do p1 <- unsafeRead seamArr ((x-1)+prevOffY) p2 <- unsafeRead seamArr (x+prevOffY) p3 <- unsafeRead seamArr ((x+1)+prevOffY) v <- unsafeRead grdArr (x+offY) unsafeWrite seamArr (x+offY) ((fromIntegral v :: Int) +(min(min p1 p2) p3)) p2l <- unsafeRead seamArr (0+prevOffY) p3l <- unsafeRead seamArr (1+prevOffY) vl <- unsafeRead grdArr (0+offY) unsafeWrite seamArr (0+offY) ((fromIntegral vl)+(min p2l p3l)) p1r <- unsafeRead seamArr (currentWidth-2+prevOffY) p2r <- unsafeRead seamArr (currentWidth-1+prevOffY) vr <- unsafeRead grdArr (currentWidth-1+offY) unsafeWrite seamArr (currentWidth-1+offY) ((fromIntegral vr :: Int) +(min p1r p2r)) return seamArr computeHSeamArray :: (ArrayType Int Word16) -> Int -> Int -> Int -> IO (ArrayType Int Int) computeHSeamArray grdArr width height currentHeight = do seamArr <- newArray (0, width * height) 0 --grdArr <- gradientArr doFromTo 0 (currentHeight-1) $ \y -> do v <- unsafeRead grdArr (y*width) unsafeWrite seamArr (y*width) (fromIntegral v :: Int) doFromTo 1 (width-1) $ \x -> do doFromTo 1 (currentHeight-2) $ \y -> do let offY = y*width let prevOffY = offY-width let nextOffY = offY+width p1 <- unsafeRead seamArr (x-1+prevOffY) p2 <- unsafeRead seamArr (x-1+offY) p3 <- unsafeRead seamArr (x-1+nextOffY) v <- unsafeRead grdArr (x+offY) unsafeWrite seamArr (x+offY) ((fromIntegral v :: Int) +(min(min p1 p2) p3)) p2l <- unsafeRead seamArr (x-1+0) p3l <- unsafeRead seamArr (x-1+width) vl <- unsafeRead grdArr (x+0) unsafeWrite seamArr (x+0) ((fromIntegral vl)+(min p2l p3l)) p1r <- unsafeRead seamArr (x-1+((currentHeight-2)*width)) p2r <- unsafeRead seamArr (x-1+((currentHeight-1)*width)) vr <- unsafeRead grdArr (x+((currentHeight-1)*width)) unsafeWrite seamArr (x+((currentHeight-1)*width)) ((fromIntegral vr :: Int) +(min p1r p2r)) return seamArr