liveBook · Manning

Digital image

As explained in Learn Haskell by Example

An image file is a digital file containing encoded visual data such as pixels or vector descriptions.

A digital image can be modeled as dimensions plus pixel data. For PNM processing, the pixel type matters: images may be grayscale or color, with 8-bit or 16-bit pixel values. Encoding that distinction in the image type lets functions accept and return the correct kind of image without repeatedly inspecting a catch-all pixel container. section 13.1.1 section 13.1.3

Use a parameterized PnmImage px type with constructors for grayscale and RGB images at each supported bit depth. Each constructor carries width, height, and a Vector of the matching pixel type. Accessor functions can expose the dimensions and pixels uniformly while the constructors preserve the pixel-type information. section 13.1.3

Listing 13.3 Type to store PNM images
{-# LANGUAGE GADTs #-}  #1

module Graphics.PNM.Types where

import qualified Codec.Picture as P   #2
import qualified Data.Vector as V     #2

...

data PnmImage px where  #3
  PnmGray8Bit ::  #4
    Int ->
    Int ->
    V.Vector P.Pixel8 ->
    PnmImage P.Pixel8
  PnmGray16Bit ::         #5
    Int ->
    Int ->
    V.Vector P.Pixel16 ->
    PnmImage P.Pixel16
  PnmColor8Bit ::         #5
    Int ->
    Int ->
    V.Vector P.PixelRGB8 ->
    PnmImage P.PixelRGB8
  PnmColor16Bit ::        #5
    Int ->
    Int ->
    V.Vector P.PixelRGB16 ->
    PnmImage P.PixelRGB16

Inspect the four image constructors and how each couples dimensions with a Vector of the appropriate JuicyPixels pixel type. section 13.1.3

A Vector suits image data when processing mostly reads pixels or maps over them. It supports constant-time indexing and common slicing operations, unlike a list’s slower element access. Build data with list-oriented parsing when convenient, then convert it to a Vector for compact, efficient image access. Vectors are a poor fit for frequent appends and are not generally suitable for infinite data. section 13.1.3

Exporting a PNM image as PNG

Convert a dynamically typed PNM image by matching its constructor. For each case, generate a JuicyPixels Image from a coordinate-to-pixel function, then wrap it in the corresponding DynamicImage constructor: ImageY8, ImageY16, ImageRGB8, or ImageRGB16. Pixel lookup uses the row-major index y * width + x before JuicyPixels writes the PNG. 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)

Follow the constructor match that turns typed PNM pixel vectors into JuicyPixels images for PNG output. section 13.3.2

How Learn Haskell by Example covers it

How deep it goes
Builds a typed image representation, explains Vector trade-offs, and implements conversion to JuicyPixels output values.
Examples
Uses grayscale and RGB PNM variants at 8- and 16-bit depth, then converts them to PNG images.
What it assumes
Introduces image storage through familiar array comparisons while using Haskell types, constructors, and package dependencies.
What you can do after
Build it from scratch as a typed PNM image representation and export it through JuicyPixels as PNG.
How it connects
Connects pixel types, GADTs, Data.Vector, DynamicImage, and PNG serialization.

Also explained in

Compare how 16 books explain digital image →

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.