How can I do similar thing but with DynamicImage
(the result of example <- readImage "/filepath.png"
):
negative :: Image PixelRGBA8 -> Image PixelRGBA8
negative = pixelMap $ \(PixelRGBA8 r g b a) -> PixelRGBA8 (255 - r) (255 - g) (255 - b) a
And how it's working without any arguments despite signature?
I guess that's what I need, because there's no writeJpg
function provided that does work with Image
(not Dynamic) type.
I know about dynamicPixelMap
function provided by JuicyPixels, but I do not actually understand how to work with it. It will be good if someone can explain it.
UPD: I've found simple solution:
negative :: Image PixelRGBA8 -> Image PixelRGBA8
dynamicImage <- readImage filepath
let image = convertRGBA8 <$> dynamicImage
let modified = negative <$> image
case modified of
Left err -> print err
Right image -> saveJpgImage 100 outputFile $ ImageRGBA8 image
It's an example, my code is more complicated.
Let's start by looking at why DynamicImage
exists. Just looking at the definition is sufficient to make that clear: There are lot of different formats for color data in images. The input routines JuicyPixels provides preserve the color data the way it was stored in the file. But the Image
type puts the color data format into the type. This is a problem in Haskell - if you make a function polymorphic, the caller gets to choose the concrete type, not the function itself. There's no way to say "this returns some Image
type, depending on the input". So DynamicImage exists - it has a different constructor for each supported color data format. Matching on the constructor tells you the type of Image
you're working with, so you can't get the data without also knowing what type it is.
Since the constructors are public, you can match on them to get an Image
and use pixelMap
on it... But I wouldn't. JPEG files are sometimes in CMYK space, not RGB. Logic in that space is entirely different - it's a subtractive space, rather than additive. And JuicyPixels provides tools to help you out here. The convertRGBA8
function takes a DynamicImage
and does whatever colorspace conversion is necessary to give you an Image PixelRBGA8
. The conversion will lose details in some cases, but it will get you the format in the example you provided.