glade-0.12.1: demo/scaling/Scaling.hs
{-# 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 <image.jpg>"
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