I'm trying to do some image processing with Haskell (using JuicyPixels). I've done this function that apply Gaussian Blur to the image and there's some strange artifacts in processed image:
blur :: Image PixelRGBA8 -> Image PixelRGBA8
blur img@Image {..} = generateImage blurrer imageWidth imageHeight
where blurrer x y | x >= (imageWidth - offset) || x < offset
|| y >= (imageHeight - offset) || y < offset = whitePx
| otherwise = do
let applyKernel i j p | j >= matrixLength = applyKernel (i + 1) 0 p
| i >= matrixLength = p
| otherwise = do
let outPixel = pxMultNum
(pixelAt img (x + j - offset) (y + i - offset))
(gblurMatrix !! i !! j)
applyKernel i (j+1) (outPixel `pxPlus` p)
applyKernel 0 0 zeroPx
gblurMatrix = [[1 / 255, 4 / 255, 6 / 255, 4 / 255, 1 / 255],
[4 / 255, 16 / 255, 24 / 255, 16 / 255, 4 / 255],
[6 / 255, 24 / 255, 36 / 255, 24 / 255, 6 / 255],
[4 / 255, 16 / 255, 24 / 255, 16 / 255, 4 / 255],
[1 / 255, 4 / 255, 6 / 255, 4 / 255, 1 / 255]]
matrixLength = length gblurMatrix
offset = matrixLength `div` 2
whitePx = PixelRGBA8 255 255 255 255
zeroPx = PixelRGBA8 0 0 0 255
pxPlus :: PixelRGBA8 -> PixelRGBA8 -> PixelRGBA8
pxPlus (PixelRGBA8 r1 g1 b1 a1) (PixelRGBA8 r2 g2 b2 a2) = PixelRGBA8 (s r1 r2) (s g1 g2) (s b1 b2) 255
where s p1 p2 | p1 + p2 > 255 = 255
| p1 + p2 < 0 = 0
| otherwise = p1 + p2
pxMultNum :: PixelRGBA8 -> Double -> PixelRGBA8
pxMultNum (PixelRGBA8 r g b a) q = PixelRGBA8 (m r) (m g) (m b) (m a)
where m px = pxify $ fromIntegral px * q -- pxify is just synonym to function rounding a = (floor (a + 0.5))
Here are input image
and output image
It looks like blueish areas are a bit less with 1/256
coefficient in Gaussian Blur Matrix, but how to get rid of them at all?
FIX: I guess it's because of Pixel8
type 0-255 and Int, but I fixed whole thing just by changing 36
in the kernel center to 35
, so sum of matrix coefficients is (I believe) 255/255 = 1.
You write
| p1 + p2 > 255 = 255
| p1 + p2 < 0 = 0
to protect against overflow, but these conditions are never true. Word8
values are always between 0
and 255
. A correct way to check for overflow looks like this:
| p1 + p2 < p1 = 255
But better would be to avoid overflow in the first place. I note that your blur matrix's coefficients add up to 256/255
. Perhaps you could start by fixing that.