liveBook · Manning

JuicyPixels

As explained in Learn Haskell by Example

JuicyPixels is a Haskell library for decoding, encoding, and manipulating common raster image formats.

JuicyPixels provides Haskell types and image-building functions for turning validated PNM data into PNG files. Its Codec.Picture module distinguishes grayscale and RGB images at 8- and 16-bit depths, allowing image processing code to preserve pixel representation. section 13.1.1 section 13.3.2

The main pieces used in a PNM-to-PNG pipeline are:

  • Pixel8 and Pixel16 represent grayscale channel values; PixelRGB8 and PixelRGB16 construct RGB pixels at matching bit depths. section 13.1.1
  • DynamicImage stores image alternatives such as ImageY8, ImageY16, ImageRGB8, and ImageRGB16. section 13.1.1 section 13.3.2
  • generateImage builds an Image from a coordinate-to-pixel function, width, and height. section 13.3.2
  • writeDynamicPng writes a DynamicImage to a filesystem path and returns IO (Either String Bool). section 13.3.2

Converting a validated PNM image

Represent the image polymorphically as PnmImage px, then convert each dynamic case with its matching JuicyPixels constructor. For example, a grayscale 8-bit value becomes ImageY8 (pnmToImage img), while a color 16-bit value becomes ImageRGB16 (pnmToImage img). The conversion uses generateImage and retrieves each pixel by its row-major vector index. Finally, writeAsPng path img calls writeDynamicPng to save the result. section 13.3.2

Listing 13.17 The function to transform PNM images to JuicyPixels image values
dynamicPnmToDynamicImage :: DynamicPnmImage -> P.DynamicImage
dynamicPnmToDynamicImage (DynamicPnmImage img) =
  case img of
    PnmGray8Bit {} -> P.ImageY8 $ pnmToImage img        #1
    PnmGray16Bit {} -> P.ImageY16 $ pnmToImage img      #1
    PnmColor8Bit {} -> P.ImageRGB8 $ pnmToImage img     #1
    PnmColor16Bit {} -> P.ImageRGB16 $ pnmToImage img   #1
  where #1
    pnmToImage :: P.Pixel px => PnmImage px -> P.Image px
    pnmToImage pnmImg =
      P.generateImage build (pnmWidth pnmImg) (pnmHeight pnmImg)  #2
      where
        build x y =  #3
          pnmPixels pnmImg V.! (y * (pnmWidth pnmImg) + x)

Use the complete conversion listing to inspect how pixel type refinement selects ImageY8, ImageY16, ImageRGB8, or ImageRGB16. section 13.3.2

How Learn Haskell by Example covers it

How deep it goes
Explains pixel types, dynamic constructors, image generation, and file output, including GADT-scoped type refinement.
Examples
Demonstrates grayscale and RGB images at 8- and 16-bit depths through PNM conversion and PNG writing.
What it assumes
Assumes validated PNM image data and Haskell type design, while supplying pixel representations and PNG conversion APIs.
What you can do after
Build it from scratch by mapping typed PNM pixels to JuicyPixels images and writing them as PNG files.
How it connects
Connects PNM, PNG, DynamicImage, pixel types, GADTs, vectors, and parallel image processing.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Learn Haskell by Example. The section links point to the parts of the book it draws on.