Laboratory: Encoding Images with Color PalettesSummary:
In this laboratory, you will explore the use of palettes to more
efficiently store images and the effects the approximations such
palettes require have on images.
Preparation
a. This laboratory requires a wide variety of procedures, some
introduced in this reading, some introduced in previous readings,
and some of which you've written. We have reproduced all of those
procedures at the end of this
reading. Copy them into your definitions pane (or your library).
b. We'll do some work with reading and writing images. Create a
10x10 image called canvas for such experimentation and
fill it with random colors.
(define canvas (image-show (image-new 10 10)))
(image-random-fill! canvas)
You'll probably want to zoom in on this image.
c. We'll also do some work with an existing image, which we'll call
picture. You might use our long-suffering kitten.
(define picture (image-show (image-load "/home/rebelsky/MediaScheme/Images/kitten.png")))
You can also use a public-domain photograph of a butterfly, taken from
http://public-photo.net/displayimage-2139.html.
(define picture (image-show (image-load "/home/rebelsky/MediaScheme/Images/butterfly.png")))
ExercisesExercise 1: Experiments with the Simple Encoding
As you may recall from the reading, rgb->approx
approximates an RGB color by converting it to a number between 0 and
255 that represents a nearby color. Another function,
approx->rgb converts the number to that nearby
color.
a. Pick five colors and ensure that rgb->approx
converts each to a number between 0 and 255. Try to pick colors that
are not at the extremes (that is, that have components whose values are
not just 0 and 255). You may find
it easiest to do this process with map.
>(map (compose rgb->approx color-name->rgb) (list "red" ....))
b. Convert those approximations back to RGB colors with
approx->rgb. Then, compare those values to
the original colors. You might want to try something like the
following:
>(define colors (map color-name->rgb (list "red" ...))>(map rgb->string colors)>(map rgb->color-name colors)>(define encodings (map rgb->approx colors))>(define approximations (map approx->rgb encodings))>(map rgb->string approximations)>(map rgb->color-name approximations)
c. What do you notice about the component values of these various colors?
d. Note that we can approximate an RGB color by encoding it and then
decoding it. Write a procedure, rgb-approximate, that
does just that. (Note that rgb-approximate takes
as input an RGB color and returns as output an RGB color. You should
use rgb->approx and
approx->rgb to do the approximation.)
e. Using image-variant, apply
rgb-approximate to canvas (which
should contain random colors).
>(define approximated (image-variant canvas rgb-approximate))>(image-show approximated)
f. How good is the approximation? What could you do to improve the
approximation?
g. Using image-variant, apply rgb-approximate to picture.
h. Does this experiment change your perspective on the success of the
approximation?
Exercise 2: Rainbow Palettes
As you may recall, palette-encode-color encodes
a color using a color palette (a list of colors) and
palette-decode-color decodes a color using a
palette.
a. Add the following definition of a simple color palette to your definitions
pane.
(define rainbow-plus
(map color->rgb (list "red" "orange" "yellow" "green"
"blue" "indigo" "violet"
"black" "white")))
b. Using that palette, encode the same colors you encoded in exercise
1. Are these encodings better or worse? Why?
c. Write a procedure, (palette-select palette color),
that selects the color in palette used to represent
color. (Your procedure will make use of
palette-encode-color and
palette-decode-color.)
d. See what happens if you apply that procedure to canvas
with
(image-variant canvas (lambda (color) (palette-select rainbow-plus color)))
What does that tell you about the usefulness of this palette?
e. See what happens if you apply that procedure to picture.
What does that tell you about the usefulness of this palette?
Exercise 3: Palettes from Images
Clearly, using a small palette of fairly restricted colors is not
our best approach for approximating an image. What can we do instead?
We can choose colors that already appear in an image. Consider the
following procedure.
a. What do you expect the result to look like if you use
image-sample-colors to sample colors from
picture?
b. Check your answer experimentally.
c. What do you expect the result to look like if you use
image-sample-colors to sample colors from
canvas? About how many colors do you expect to be
in the list?
d. Check your answer experimentally.
e. As you've probably noticed, the number of colors that
image-sample-colors returns depends on the
size of the image it is working with. Why? Because it samples every
411th pixel. We could, of course, change the 411 to some smaller
number when we were sampling smaller images, but it would be painful
to have to change image-sample-colors each
time we wanted to work with a different image. Instead, we could
add a parameter, n to represent the number of
samples to take (i.e, the size of the palette).
We could then use n to compute the
distance between samples. (That is, replace 411 with
a formula that involves width, height, and n so that we get
approximately n colors selected.)
Make those changes.
Exercise 4: Palettes from Images, Revisited
a. Using your modified version of image-sample-colors,
create palettes of size 2, 4, 8, and 16 from picture.
>(define palette-2 (image-sample-colors picture 2))>(define palette-4 (image-sample-colors picture 4))>(define palette-8 (image-sample-colors picture 8))>(define palette-16 (image-sample-colors picture 16))
b. Here's a procedure, (palette-mappaletteimage), which
maps a palette onto an image by converting each color
in the image to the closest color in the palette.
Using that procedure, map each of your palettes onto canvas.
(Yes, although we created the palettes with picture, you
should see what happens with canvas.)
d. As you may recall, image-variant can take a
while with larger images. Let's see the effect of palette size on the
amount of time it takes to apply a palette to picture
First, map palette-2 onto picture and,
using a clock or by counting one-Mississippi two-Mississippi,
determine approximately how many seconds it takes.
Next, map palette-4 onto picture and,
using a clock, determine approximately how many seconds it takes.
Finally, predict how long it will take to map palettes of size 8 and 16.
If you have time, check those results experimentally.
e. As you may have noted, the bigger the palette, the slower it is
to compute a variant. At the same time, it is likely that the bigger
the palette, the better the approximation. To test this hypothesis,
we'll need a relatively small image to work with. However, we want
a sensible image, rather than a random image. Pick a 64x64 square
from picture, copy it, create a new 64x64 image called
selection, and paste into that image.
f. Map palette-16 onto selection. Do you
consider that a reasonable approximation?
g. Create a new palette, selection-16, by selecting 16
colors from selection, rather than from
picture. Then, map that palette onto
selection. Is the approximation better? Worse?
Exercise 5: Storing with Palettes
Although we've been exploring palettes by looking at what happens when
we use them interactively with images, most frequently palettes are
used when saving and restoring files.
a. Write (palette-save-imagepaletteimagefilename), which saves a palettized
pixmap of an image to a file. The procedure should use the following
format for the file:
The width of the image.The height of the image.The colors in the image, represented as indices into the palette.
In writing this procedure, you may
want to use image-write-pixmap as a pattern
for how to write an image to a file.
b. Write (palette-load-imagepalettefilename),
which loads the palettized pixmap of an image from a file, assuming that
the image was stored
using the same palette. In writing this procedure, you may want to
use image-read-pixmap as a pattern for how to
read an image from a file.
For Those With Extra TimeExtra 1: More Efficient Encoding
One disadvantage of the current version of
palette-encode-color is that it ends up scanning
the list of colors twice: once to find the closest color and once to
find the index of that color. We might instead write a procedure that
does the two activities simultaneously.
The following provides one implementation of that technique.
a. Confirm, using a variety of examples, that it seems to work correctly.
b. Explain, in your own words, how this procedure works.
c. Rewrite palette-encode-color to use
rgb-index-of-closest.
d. Rerun one of your timing tests to determine whether this change
had any effect.
Extra 2: Storing Palettes
Reproducing the saved image accurately
requires that our image file include not just the indices into
the palette, but also the palette itself. Rewrite
palette-save-image and
palette-load-image to use this technique.
You may find the following procedures helpful.
Extra 3: Better Palettes
Some disadvantages of the current mechanism for selecting palettes based
on an image are (1) it may result in duplicate colors; (2) it may
contain a variety of close colors and, but doing so, approximate some
colors better than others. An alternative is to over-sample the
image, getting about twice as many colors as you want in the palette,
and then to hone the palette, removing or averaging nearby colors.
a. Write a procedure, (palette-slightly-improvepalette) that improves the palette by
replacing one pair of nearby colors with their average.
b. Write a procedure, (palette-improvepaletten),
that repeatedly calls palette-slightly-improve
until there are n colors left.
c. Determine experimentally whether this technique makes better
palettes.
ExplorationsExploration 1: Switching Palettes
You can obtain some very interesting effects by encoding an image using
one palette and then decoding it using another.
a. Define two palettes, each with sixteen colors. Name them
pal and ette.
b. Rather than storing the image to a file and then reloading it using
the new palette, we will use image-variant to build a new
version of the image.
>(image-variant (lambda (color) (palette-decode palette (palette-encode palette color)))
canvasNotesUseful Procedures
; +--------------------------+----------------------------------------
; | Approximating RGB Colors |
; +--------------------------+
; +------------------------+------------------------------------------
; | Palette-Based Encoding |
; +------------------------+
; +---------------------+---------------------------------------------
; | Reading and Writing |
; +---------------------+
; +-------------------------+-----------------------------------------
; | Miscellaneous Utilities |
; +-------------------------+
; +--------------+----------------------------------------------------
; | Student Work |
; +--------------+