Vadim
Vadim

Reputation: 175

How to work with readPixel and writePixel in JuicyPixels, Haskell?

In this article I've found some examples of using MutableImage with readPixel and writePixel functions, but I think it's too complicated, I mean, can I do that without ST Monad?

Let's say I have this

eDynamicImage <- readImage
eImage <- convertRGBA8 <$> eDynamicImage

so I can apply pixelMap based filters to that image like this negative <$> eImage, but how do I work with pixels and it's coordinates? Can someone explain me what is Mutable Image and how can I get this from DynamicImage or Image?

Is there any clean code that can rotate image using this function?

UPD 2.0: I've made totally working rotation function using built-in JuicyPixels generateImage function:

rotate :: Double -> Image PixelRGBA8 -> Image PixelRGBA8
rotate n img@Image {..} = generateImage rotater newW newH
  where rotater x y = if srcX x y < imageWidth && srcX x y >= 0 && srcY x y < imageHeight && srcY x y >= 0
                       then pixelAt img (srcX x y) (srcY x y)
                       else PixelRGBA8 255 255 255 255
        srcX x y = getX center + rounding (fromIntegral (x - getX newCenter) * cos' + fromIntegral (y - getY newCenter) * sin')
        srcY x y = getY center + rounding (fromIntegral (y - getY newCenter) * cos' - fromIntegral (x - getX newCenter) * sin')
        center = (imageWidth `div` 2, imageHeight `div` 2)
        newCenter = (newW `div` 2, newH `div` 2)
        newW = rounding $ abs (fromIntegral imageHeight * sin') + abs (fromIntegral imageWidth * cos')
        newH = rounding $ abs (fromIntegral imageHeight * cos') + abs (fromIntegral imageWidth * sin')
        sin' = sin $ toRad n
        cos' = cos $ toRad n

where

rounding a = floor (a + 0.5)
getX = fst 
getY = snd
toRad deg = deg * (pi/180)

That's it if someone need it. Thanks @Carsten for advice!

Upvotes: 0

Views: 246

Answers (1)

Random Dev
Random Dev

Reputation: 52280

An MutableImage is one you can mutate (change in place) - Images are immutable by default. You'll need some kind of monad that allows that though (see the documentation - there are a few including ST and IO).

To get an MutableImage you can use thawImage - then you can work (get/set) pixels with readPixel and writePixel - after you can freezeImage again to get back an immutable Image

If you want to know how you can rotate images you can check the source code of rotateLeft :

rotateLeft90 :: Pixel a => Image a -> Image a
rotateLeft90 img@Image {..} =
  generateImage gen imageHeight imageWidth
  where
    gen x y = pixelAt img (imageWidth - 1 - y) x

as you can see it does not use a mutable image but generates a new one from the olds pixels (using pixelAt instead of readPixel)

just in case - this is using the record-wildcards extension to extract all the fields from the Image a - parameter (that is what the Image {..} does - imageHeight,imageWidth is pulled from there)

Upvotes: 1

Related Questions