Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9c1d31b
Preliminary Hough Transform Implementation
khilanravani May 15, 2018
08c298f
remove outdated file
khilanravani May 28, 2018
9b016b3
PR Corrections 1
khilanravani May 29, 2018
c2cdcb5
PR Corrections 1
khilanravani May 29, 2018
e6b0015
PR Corrections 2
khilanravani Jun 7, 2018
ea50a9b
Merge pull request #14 from khilanravani/hough-transform
lehins Jun 12, 2018
1509901
Adaptive Histogram Equalization
khilanravani Jun 7, 2018
4733cf2
PR Corrections 1
khilanravani Jun 22, 2018
2d1de1a
Merge pull request #15 from khilanravani/ahe-patch-1
lehins Jun 22, 2018
1915739
minor change to travis
Mar 21, 2018
e1f5626
drop travis for older GHC
Jun 22, 2018
8757017
Laplacian Filter Version 1
khilanravani Jul 9, 2018
aeefdc3
Laplacian Filter Version 2
khilanravani Jul 9, 2018
eded16d
Laplacian PR Corrections 1
khilanravani Jul 9, 2018
4ee7104
Laplacian PR Corrections 2
khilanravani Jul 9, 2018
5436509
Merge pull request #16 from khilanravani/laplacian
lehins Jul 10, 2018
bf8c508
Laplacian of Gaussian Version 1
khilanravani Jul 9, 2018
d92fdc4
Laplacian of Gaussian Version 2
khilanravani Jul 9, 2018
df4db10
Laplacian of Gaussian Version 3
khilanravani Jul 9, 2018
a1f23dd
LOG PR Corrections 1
khilanravani Jul 9, 2018
8fd18c6
Gaussian Smoothing PR Corrections 1 (#19)
khilanravani Jul 11, 2018
258b6ad
Fix the horizontal kernel of Sobel filter.
vonavi Jul 12, 2018
fe345d2
Merge pull request #20 from vonavi/fix-sobel
lehins Jul 12, 2018
22a7003
Mean filter (#22)
khilanravani Jul 19, 2018
26c9a06
Deconvolution algorithm
khilanravani Aug 18, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 11 additions & 24 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,6 @@ matrix:
include:
# We grab the appropriate GHC and cabal-install versions from hvr's PPA. See:
# https://github.com/hvr/multi-ghc-travis
- env: BUILD=cabal GHCVER=7.8.4 CABALVER=1.24 HAPPYVER=1.19.5 ALEXVER=3.1.7
compiler: ": #GHC 7.8.4"
addons:
apt:
packages: [cabal-install-1.24,ghc-7.8.4,happy-1.19.5,alex-3.1.7]
sources: [hvr-ghc]

- env: BUILD=cabal GHCVER=7.10.3 CABALVER=1.24 HAPPYVER=1.19.5 ALEXVER=3.1.7
compiler: ": #GHC 7.10.3"
Expand Down Expand Up @@ -76,22 +70,18 @@ matrix:
# The Stack builds. We can pass in arbitrary Stack arguments via the ARGS
# variable, such as using --stack-yaml to point to a different file.

- env: BUILD=stack ARGS="--stack-yaml stack.lts-3.yaml"
compiler: ": #stack GHC-7.10.2 (lts-3.22)"
- env: BUILD=stack ARGS="--stack-yaml stack.lts-6.yaml"
compiler: ": #stack GHC-7.10.3 (lts-6.30)"
addons:
apt:
packages: [libgmp-dev]

- env: BUILD=stack ARGS="--stack-yaml stack.lts-6.yaml"
compiler: ": #stack GHC-7.10.3 (lts-6.30)"
- env: BUILD=stack ARGS="--resolver lts-7"
compiler: ": #stack GHC-8.0.1 (lts-7.24)"
addons:
apt:
packages: [libgmp-dev]

# - env: BUILD=stack ARGS="--resolver lts-7"
# compiler: ": #stack GHC-8.0.1 (lts-7)"
# addons: {apt: {packages: [libgmp-dev]}}

- env: BUILD=stack ARGS="--resolver lts-8.24"
compiler: ": #stack GHC-8.0.2 (lts-8.24)"
addons:
Expand All @@ -111,17 +101,19 @@ matrix:
packages: [libgmp-dev]

# Nightly builds are allowed to fail
# - env: BUILD=stack ARGS="--resolver nightly"
# compiler: ": #stack nightly"
# addons: {apt: {packages: [libgmp-dev]}}
- env: BUILD=stack ARGS="--resolver nightly"
compiler: ": #stack nightly"
addons:
apt:
packages: [libgmp-dev]

# - env: BUILD=stack ARGS="--resolver lts-8"
# compiler: ": #stack 8.0.2 osx"
# os: osx

allow_failures:
- env: BUILD=cabal GHCVER=head CABALVER=head HAPPYVER=1.19.5 ALEXVER=3.1.7
#- env: BUILD=stack ARGS="--resolver nightly"
- env: BUILD=stack ARGS="--resolver nightly"

branches:
except:
Expand Down Expand Up @@ -152,11 +144,6 @@ before_install:
mkdir -p $HOME/.cabal
echo 'remote-repo: hackage.haskell.org:http://hackage.fpcomplete.com/' > $HOME/.cabal/config
echo 'remote-repo-cache: $HOME/.cabal/packages' >> $HOME/.cabal/config

# if [ "$CABALVER" != "1.16" ]
# then
# echo 'jobs: $ncpus' >> $HOME/.cabal/config
# fi
;;
esac

Expand All @@ -177,7 +164,7 @@ install:
cabal --version
travis_retry cabal update

# Get the list of packages from the stack.yaml file
# Set the list of packages we will build
PACKAGES="$TRAVIS_BUILD_DIR"
echo $PACKAGES

Expand Down
3 changes: 3 additions & 0 deletions hip.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Library
, repa >= 3.2.1.1 && < 4
, temporary >= 1.1.1
, vector >= 0.10
, array
Other-Extensions: BangPatterns
, ConstraintKinds
, CPP
Expand All @@ -67,6 +68,8 @@ Library
, Graphics.Image.Processing.Binary
, Graphics.Image.Processing.Complex
, Graphics.Image.Processing.Filter
, Graphics.Image.Processing.Hough
, Graphics.Image.Processing.Ahe
, Graphics.Image.Types
Other-Modules: Graphics.Image.ColorSpace.Binary
, Graphics.Image.ColorSpace.CMYK
Expand Down
Binary file added images/GSM_gsn_yield_IP.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/GSM_gsn_yield_OP.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/frog_deconvolve.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/yield_ahe.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/yield_hough.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/yield_laplacian.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/yield_log.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/yield_mean.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
85 changes: 85 additions & 0 deletions src/Graphics/Image/Processing/Ahe.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}

-- | Adaptive histogram equalization is used to improve contrast in images.
-- It adjusts image intensity in small regions (neighborhood) in the image.
module Graphics.Image.Processing.Ahe where

import Control.Monad (forM_, when)
import Control.Monad.ST
import Data.STRef
import Debug.Trace (trace)

import Prelude as P hiding (subtract)
import Graphics.Image.Processing.Filter
import Graphics.Image.Interface as I
import Graphics.Image
import Graphics.Image.Types as IP
import Graphics.Image.ColorSpace (X)

-- | Supplementary function for applying border resolution and a general mask.
simpleFilter :: (Array arr cs e, Array arr X e) => Direction -> Border (Pixel cs e) -> Filter arr cs e
simpleFilter dir !border =
Filter (correlate border kernel)
where
!kernel =
case dir of
Vertical -> fromLists $ [ [ 0, -1, 0 ], [ -1, 4, -1 ], [ 0, -1, 0 ] ]
Horizontal -> fromLists $ [ [ 0, -1, 0 ], [ -1, 4, -1 ], [ 0, -1, 0 ] ]

-- | 'ahe' operates on small 'contextual' regions of the image. It enhances the contrast of each
-- region and this technique works well when the distribution of pixel values is similar throughout
-- the image.
--
-- The idea is to perform contrast enhancement in 'neighborhood region' of each pixel and the size
-- of the region is a parameter of the method. It constitutes a characteristic length scale: contrast
-- at smaller scales is enhanced, while contrast at larger scales is reduced (For general purposes, a size
-- factor of 5 tends to give pretty good results).
--
-- <<images/yield.jpg>> <<images/yield_ahe.png>>
--
-- Usage :
--
-- >>> img <- readImageY VU "images/yield.jpg"
-- >>> input1 <- getLine
-- >>> input2 <- getLine
-- >>> let thetaSz = (P.read input1 :: Int)
-- >>> let distSz = (P.read input2 :: Int)
-- >>> let neighborhoodFactor = (P.read input2 :: Int)
-- >>> let aheImage :: Image VU RGB Double
-- >>> aheImage = ahe img thetaSz distSz neighborhoodFactor
-- >>> writeImage "images/yield_ahe.png" (toImageRGB aheImage)
--
ahe
:: forall arr e cs . ( MArray arr Y Double, IP.Array arr Y Double, IP.Array arr Y Word16, MArray arr Y Word16, Array arr X Double)
=> Image arr Y Double
-> Int -- ^ width of output image
-> Int -- ^ height of output image
-> Int -- ^ neighborhood size factor
-> Image arr Y Word16
ahe image thetaSz distSz neighborhoodFactor = I.map (fmap toWord16) accBin
where
ip = applyFilter (simpleFilter Horizontal Edge) image -- Pre-processing (Border resolution)
widthMax, var1, heightMax, var2 :: Int
var1 = ((rows ip) - 1)
widthMax = ((rows ip) - 1)
var2 = ((cols ip) - 1)
heightMax = ((cols ip) - 1)

accBin :: Image arr Y Word16
accBin = runST $ -- Core part of the Algo begins here.
do arr <- I.new (thetaSz, distSz) -- Create a mutable image with the given dimensions.
forM_ [0 .. var1] $ \x -> do
forM_ [0 .. var2] $ \y -> do
rankRef <- newSTRef (0 :: Int)
let neighborhood a maxValue = filter (\a -> a >= 0 && a < maxValue) [a-5 .. a+5]
forM_ (neighborhood x var1) $ \i -> do
forM_ (neighborhood y var2) $ \j -> do
when (I.index ip (x, y) > I.index ip (i, j)) $ modifySTRef' rankRef (+1)
rank <- readSTRef rankRef
let px = ((rank * 255))
I.write arr (x, y) (PixelY (fromIntegral px))
freeze arr

49 changes: 49 additions & 0 deletions src/Graphics/Image/Processing/Deconvolution.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}

module Graphics.Image.Processing.Deconvolution where

import Graphics.Image as I
import Graphics.Image.Interface as I
import Prelude as P

-- | Supplementary function needed to blur an image and test the algorithm.
randomBlur :: Image VS X Double
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anything that is needed for testing, suppose to live in the test suite

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lehins Okay sir, so this should be in the usage section in the documentation?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes you could move it to function documentation as an example. In that case I'd also recommend collapsing the big example (note ====__Examples__):

--  ====__Examples__
--
-- <<images/frog.jpg>>   <<images/frog_deconvolve.jpg>>
-- 
--  Usage:
-- >>> u0 <- readImage' "images/frog.jpg" :: IO (Image VS RGB Double)
-- ...

In order to check out the affect, run stack haddock --open

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lehins Okay sir, will change it.

randomBlur = randomBlur' / scalar (I.sum randomBlur')
where randomBlur' = fromLists [[0, 0, 4, 3, 2]
,[0, 1, 3, 4, 3]
,[1, 2, 3, 3, 4]
,[0, 1, 2, 1, 0]
,[0, 0, 1, 0, 0]]

-- | 'deconvolve' is used to correct the systematic error of blur (loss of
-- contrast in smaller features) in images. This particular variant is used
-- when no information about the distortion (blurring and noise) is known.
-- More info about deconvolution at <https://en.wikipedia.org/wiki/Deconvolution>
--
-- <<images/frog.jpg>> <<images/frog_deconvolve.jpg>>
--
-- Usage:
-- >>> u0 <- readImage' "images/frog.jpg" :: IO (Image VS RGB Double)
-- >>> let p = randomBlur
-- >>> let d = convolve Edge p u0
-- >>> let us = iterate (deconvolve p d) d -- Iterative deconvolution
-- >>> u1 = us !! 1 -- one iteration
-- >>> u2 = us !! 20 -- twenty iterations
-- >>> let output = makeImage (rows u0, cols u0 * 4)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an extremely inefficient way to concat images together, try using leftToRight function

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lehins Sure sir, sorry I was unaware of that. Will change it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lehins Sir, I tested the code using leftToRight and I think that the computation time is more or less same as compared when using the previous technique. Please let me know if you would like me to replace the current version. I'm very sorry for the delay in addressing the PR's, was a bit engaged in my academic activities. I will now take up the PR's one by one and will try to address all the issues as soon as possible.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the computation time is more or less same

Without some numbers/benchmarks I will not believe that.

You probably aren't noticing the difference because deconvolution is an expensive operation and 20 iterations take long time. The operation of concatenation of 4 images the way you have it implemented in the example on its own will be much slower than if you use leftToRight, so I do still recommend you change it.

No worries on the delay. There isn't a particular rush on any of this, especially considering that GSoC is over. Congrats on a successful project. ;)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lehins I think I might be doing something wrong. I implemented it like :
let output1 = leftToRight u0 d
let output2 = leftToRight output1 u1
let output3 = leftToRight output2 u2
Okay sir, I'll try to give you the compile time for both cases. Thank you very much sir!!

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are doing it right.
It's not the compile time that's important, but the runtime :)
If you'd like to benchmark the difference, you'll have to isolate that operation of concatenation from deconvolution. Best way would be to simply read one image and try to concatenate 4 copies of it together using both approaches and record the time.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lehins Very sorry, I actually did mean runtime but wrote compile time instead. Okay, I'll try to record the time.

-- >>> (\(r,c) ->
-- >>> let (i, c') = c `quotRem` cols u0
-- >>> in index ([u0,d,u1,u2] !! i) (r,c'))
-- >>> :: Image VS RGB Double
-- >>> writeImage "images/frog_deconvolve.jpg" output
--
deconvolve :: Image VS X Double
-> Image VS RGB Double
-> Image VS RGB Double
-> Image VS RGB Double
deconvolve p d u
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This algorithm isn't restricted to RGB images only. Also restricting to Edge might be a bad idea, so let's have it available as another argument.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lehins Okay sir, just one thing. I'll try my best to address the feedback on all my PR's asap but I've lectures and lab sessions today so won't be able to work on them before night time. Hope you understand. Thank you!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lehins Sorry sir, I didn't exactly understand. Do you mean that I should take input images as Image arr Y ?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, what I meant is, it should be more polymorphic. deconvolve is the opposite of convolve, so look at it's type signature and you'll see there is no reason it shouldn't be almost the same:

convolve :: (Array arr X e, Array arr cs e) =>
            Border (Pixel cs e) -- ^ Approach to be used near the borders.
         -> Image arr X e -- ^ Kernel image.
         -> Image arr cs e -- ^ Source image.
         -> Image arr cs e

So, try to fix up the code so the type signature is:

deconvolve :: (Array arr X e, Array arr cs e) =>
            Border (Pixel cs e) -- ^ Approach to be used near the borders.
         -> Image arr X e -- ^ Blurring Kernel.
         -> Image arr cs e -- ^ Image that is being deconvolved.
         -> Image arr cs e -- ^ Image from previous iteration of deconvolution
         -> Image arr cs e -- ^ Deblurred image

= u * conv (transpose p) (d / conv p u)
where conv = convolve Edge

106 changes: 99 additions & 7 deletions src/Graphics/Image/Processing/Filter.hs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
--
module Graphics.Image.Processing.Filter
( -- * Filter
Filter
Filter (..)
, applyFilter
, Direction(..)
-- * Gaussian
Expand All @@ -26,6 +26,14 @@ module Graphics.Image.Processing.Filter
-- * Prewitt
, prewittFilter
, prewittOperator
-- * Laplacian
, laplacianFilter
-- * Laplacian of Gaussian
, logFilter
-- * Gaussian Smoothing
, gaussianSmoothingFilter
-- * Mean
, meanFilter
) where


Expand All @@ -46,8 +54,6 @@ data Direction
= Vertical
| Horizontal



-- | Create a Gaussian Filter.
--
-- @since 1.5.3
Expand Down Expand Up @@ -95,7 +101,7 @@ sobelFilter dir !border =
, [ 0, 0, 0 ]
, [ 1, 2, 1 ] ]
Horizontal -> fromLists $ [ [ -1, 0, 1 ]
, [ -2, 0, 1 ]
, [ -2, 0, 2 ]
, [ -1, 0, 1 ] ]
{-# INLINE sobelFilter #-}

Expand All @@ -118,8 +124,6 @@ sobelOperator !img = sqrt (sobelX ^ (2 :: Int) + sobelY ^ (2 :: Int))
{-# INLINE sobelOperator #-}




prewittFilter :: (Array arr cs e, Array arr X e) =>
Direction -> Border (Pixel cs e) -> Filter arr cs e
prewittFilter dir !border =
Expand All @@ -132,10 +136,98 @@ prewittFilter dir !border =
{-# INLINE prewittFilter #-}



prewittOperator :: (Array arr cs e, Array arr X e, Floating e) => Image arr cs e -> Image arr cs e
prewittOperator !img = sqrt (prewittX ^ (2 :: Int) + prewittY ^ (2 :: Int))
where !prewittX = applyFilter (prewittFilter Horizontal Edge) img
!prewittY = applyFilter (prewittFilter Vertical Edge) img
{-# INLINE prewittOperator #-}


-- |The Laplacian of an image highlights regions of rapid intensity change
-- and is therefore often used for edge detection. It is often applied to an
-- image that has first been smoothed with something approximating a
-- Gaussian smoothing filter in order to reduce its sensitivity to noise.
-- More info about the algo at <https://homepages.inf.ed.ac.uk/rbf/HIPR2/log.htm>
--
-- <<images/yield.jpg>> <<images/yield_laplacian.png>>
--
laplacianFilter :: (Array arr cs e, Array arr X e) =>
Border (Pixel cs e) -> Filter arr cs e
laplacianFilter !border =
Filter (correlate border kernel)
where
!kernel = fromLists $ [ [ -1, -1, -1 ] -- Unlike the Sobel edge detector, the Laplacian edge detector uses only one kernel.
, [ -1, 8, -1 ] -- It calculates second order derivatives in a single pass.
, [ -1, -1, -1 ]] -- This is the approximated kernel used for it. (Includes diagonals)
{-# INLINE laplacianFilter #-}

-- | 'Laplacian of Gaussian' (LOG) filter is a two step process of smoothing
-- an image before applying some derivative filter on it. This comes in
-- need for reducing the noise sensitivity while working with noisy
-- datasets or in case of approximating second derivative measurements.
--
-- The LoG operator takes the second derivative of the image. Where the image
-- is basically uniform, the LoG will give zero. Wherever a change occurs, the LoG will
-- give a positive response on the darker side and a negative response on the lighter side.
-- More info about the algo at <https://homepages.inf.ed.ac.uk/rbf/HIPR2/log.htm>
--
-- <<images/yield.jpg>> <<images/yield_log.png>>
--
logFilter :: (Array arr cs e, Array arr X e) =>
Border (Pixel cs e) -> Filter arr cs e
logFilter !border =
Filter (correlate border kernel)
where
!kernel = fromLists $ [ [ 0, 1, 1, 2, 2, 2, 1, 1, 0 ]
, [ 1, 2, 4, 5, 5, 5, 4, 2, 1 ]
, [ 1, 4, 5, 3, 0, 3, 5, 4, 1 ]
, [ 2, 5, 3, -12, -24, -12, 3, 5, 2 ]
, [ 2, 5, 0, -24, -40, -24, 0, 5, 2 ]
, [ 2, 5, 3, -12, -24, -12, 3, 5, 2 ]
, [ 1, 4, 5, 3, 0, 3, 5, 4, 1 ]
, [ 1, 2, 4, 5, 5, 5, 4, 2, 1 ]
, [ 0, 1, 1, 2, 2, 2, 1, 1, 0 ] ]
{-# INLINE logFilter #-}

-- | The Gaussian smoothing operator is a 2-D convolution operator that is used to
-- `blur' images and remove detail and noise. The idea of Gaussian smoothing is to use
-- this 2-D distribution as a `point-spread' function, and this is achieved by convolution.
-- Since the image is stored as a collection of discrete pixels we need to produce a
-- discrete approximation to the Gaussian function before we can perform the convolution.
-- More info about the algo at <https://homepages.inf.ed.ac.uk/rbf/HIPR2/gsmooth.htm>
--
-- <<images/GSM_gsn_yield_IP.jpg>> <<images/GSM_gsn_yield_OP.png>>
--
gaussianSmoothingFilter :: (Fractional e, Array arr cs e, Array arr X e) =>
Border (Pixel cs e) -> Filter arr cs e
gaussianSmoothingFilter !border =
Filter (I.map (/ 273) . correlate border kernel)
where
!kernel = fromLists $ [[ 1, 4, 7, 4, 1 ] -- Discrete approximation to the Gaussian function.
,[ 4, 16, 26, 16, 4 ] -- 273 is the sum of all values in the mask and hence used in rescaling.
,[ 7, 26, 41, 26, 7 ]
,[ 4, 16, 26, 16, 4 ]
,[ 1, 4, 7, 4, 1 ]]

{-# INLINE gaussianSmoothingFilter #-}


-- | The mean filter is a simple sliding-window spatial filter that replaces the
-- center value in the window with the average (mean) of all the pixel values in
-- the window. The window, or kernel, can be any shape, but this one uses the most
-- common 3x3 square kernel.
-- More info about the algo at <http://homepages.inf.ed.ac.uk/rbf/HIPR2/mean.htm>
--
-- <<images/yield.jpg>> <<images/yield_mean.png>>
--
meanFilter :: (Fractional e, Array arr cs e, Array arr X e) =>
Border (Pixel cs e) -> Filter arr cs e
meanFilter !border =
Filter (I.map (/ 9) . correlate border kernel)
where
!kernel = fromLists $[ [ 1, 1, 1 ] -- Replace each pixel with the mean value of its neighbors, including itself.
, [ 1, 1, 1 ]
, [ 1, 1, 1 ]]

{-# INLINE meanFilter #-}

Loading