Fundamentals of Computer Science I: Media Computing (CS151.01 2008S)
Primary: [Front Door] [Syllabus] - [Academic Honesty] [Instructions]
Current: [Outline] [EBoard] [Reading] [Lab] [Assignment]
Groupings: [Assignments] [EBoards] [Examples] [Exams] [Handouts] [Labs] [Outlines] [Projects] [Readings]
References: [A-Z] [Primary] [Scheme Report (R5RS)] [Scheme Reference] [DrScheme Manual]
Related Courses: [CSC151.02 2008S (Davis)] [CSC151 2007F (Rebelsky)] [CSC151 2007S (Rebelsky)] [CSCS151 2005S (Stone)]
Summary: We explore how to expand the power of color transformations, first by applying them to images rather than to individual pixels then by combining them into new transformations.
If you think back to the end of the previous reading on
transforming RGB images, you may recall that we were starting
to write filters that transformed not just individual colors but whole
images. Here is one set of commands that transforms a four-by-three
image called canvas
.
(image-transform-pixel! canvas 0 0 rgb-complement) (image-transform-pixel! canvas 0 1 rgb-complement) (image-transform-pixel! canvas 0 2 rgb-complement) (image-transform-pixel! canvas 1 0 rgb-complement) (image-transform-pixel! canvas 1 1 rgb-complement) (image-transform-pixel! canvas 1 2 rgb-complement) (image-transform-pixel! canvas 2 0 rgb-complement) (image-transform-pixel! canvas 2 1 rgb-complement) (image-transform-pixel! canvas 2 2 rgb-complement) (image-transform-pixel! canvas 3 0 rgb-complement) (image-transform-pixel! canvas 3 1 rgb-complement) (image-transform-pixel! canvas 3 2 rgb-complement)
That's an awful lot of typing. (Even though it seems that we've
been typing a lot over the past few days, we do want to type less,
and will look for techniques for doing so.) It's also a lot to
change if we decide to, say, make the image darker rather than
complementing it. We can certainly make both issues a bit easier
by using foreach!
.
(foreach! (lambda (pos) (image-transform-pixel! canvas (car pos) (cadr pos) rgb-complement)) '((0 0) (1 0) (2 0) (3 0) (0 1) (1 1) (2 1) (3 1) (0 2) (1 2) (2 2) (3 2)))
However, neither technique can be easily adapted to images of different sizes or to typical-sized images. (In a 200x200 image, which is still fairly small, there are 40,000 positions. No one wants to type that many lines of code, particularly when it is that repetitive. Writing a list of that size is also not very tempting.) Both are also fairly slow. Both techniques are also impure: The affect the original image, and may not be possible or easy to undo.
So, is there a better way to write image filters? That is, how
can we write filters that are faster, that are pure when we want
purity, and that don't require thousands of lines of incredibly
repetitive code? DrFu includes a pair of helpful procedures,
(
and
image-variant
image
transformation
)(
. The first builds a
new image by setting each pixel in the new image to the result of
applying the given transformation to every pixel in another image.
The second changes an existing image by applying the transformation
“in place”. You will explore the differences between
the two in the lab.
image-transform!
image
transformation
)
You may find that the order of the parameters in
image-variant
is a bit different than you'd expect.
In the case of map
, we put the function first and
the list second. In the case of image-variant
, we put
the function second. As you've noticed, we've made it custom to put the
type mentioned in the function name first. However, the tradition with
map
is to make the function the first parameter. As we'll
see again and again, there are multiple customs for how one writes or
designs procedures, and sometimes these customs come into conflict.
(At first, we used the name image-map
for
image-variant
. However, we decided that the
order of parameters would be inappropriate with the “map”,
and chose this new name.)
Using image-variant
(or image-transform!
)
and the color transformations we learned in the previous reading, we can
now transform images in a few basic ways: we can lighten images,
darken images, complement images (and perhaps even compliment the
resulting images), and so on and so forth.
We are almost done on our quest to learn how to write filters. We've learned some new functions provided by DrFu, such as the transformations. We've learned about one new technique, refactoring, which involves writing new functions that encapsulate common code. We've seen that Scheme permits procedures to take other procedures as parameters, and that this permission supports refactoring. Finally, we've learned a new kind of operation, map, which lets us do something to every component of a compound value. All this put together lets us write some simple filters. For example, we can write one line of Scheme code to make a a picture bluer.
>
(define bluer-picture (image-variant picture rgb-bluer))
But what if we want more interesting filters, ones that can't be described with just a single built-in transformation? One thing that we can do is to combine transformations. There are two ways to transform an image using more than one transformation: You can do each transformation in sequence, or you can use function composition, an old mathematical trick, to first combine the transformations. Consider, for example, the problem of lightening an image and then increasing the red component. We can certainly write the following:
>
(define modified-picture (image-variant (image-variant picture rgb-redder) rgb-lighter))
However, what we really want to do is make a new image in which
each pixel in the result is lighter and redder than the pixel in
the original. In mathematics, when you want to build a function
that does the work of two functions, you compose
those functions. In DrFu, the compose
procedure
lets you combine multiple procedures into one. Here's a new procedure
that makes colors lighter and then redder.
>
(define lr (compose rgb-redder rgb-lighter))
>
(define sample (cname->rgb "blue violet"))
>
(rgb->string (lr sample))
"183/111/175"
Using function composition, we can therefore rewrite the earlier transformation as
or, as simply,>
(define modified-picture (image-variant picture lr))
>
(define modified-picture (image-variant picture (compose rgb-redder rgb-lighter)))
What's the big difference between this instruction and the series of two instructions? In effect, we've changed the way you sequence operations. That is, rather than having to write multiple instructions, in sequence, to get something done, we could instead insert information about the sequencing into a single instruction. By using composition, along with nesting, we can then express our algorithms more concisely and often more clearly. It is also likely to be a bit more efficient, since we make one new image, rather than two.
Because compose
is a bit long to write, because
a circle is used for the mathematical composition function, and because
we sometimes want to compose more than two procedures, DrFu also
provides a procedure, (
,
that composes all of the procedures (applying them from right to
left).
o
proc1
proc2
...
procn-1
procn
)
In most of the initial readings for this course, you were learning
some basic operations that you could use in writing algorithms.
In this reading, while you learned about new operations, they were
a different kind of operation. image-variant
,
image-transform!
, and compose
all provide a way to add control to your program.
The first two do something to each pixel in an image, the last sequences
operations within a single Scheme instruction.
(image-variant
image
fun
)
image
, each of whose pixels is computed
by applying fun
to the color of the
corresponding pixel in image
.
(image-transform!
image
fun
)
image
in place by setting
each pixel to the result of applying fun
to
that current pixel color.
(compose
f
g
)
((compose f g) x)
is the same as (f (g x))
.
(o
f1
f2
...
fn-1
fn
)
f
, in turn, starting with
fn
and
working backwards. The composition, when applied to a value,
x
, produces the same result as
(f1
(f2
(...
(fn-1
(fn x)))))
.
Primary: [Front Door] [Syllabus] - [Academic Honesty] [Instructions]
Current: [Outline] [EBoard] [Reading] [Lab] [Assignment]
Groupings: [Assignments] [EBoards] [Examples] [Exams] [Handouts] [Labs] [Outlines] [Projects] [Readings]
References: [A-Z] [Primary] [Scheme Report (R5RS)] [Scheme Reference] [DrScheme Manual]
Related Courses: [CSC151.02 2008S (Davis)] [CSC151 2007F (Rebelsky)] [CSC151 2007S (Rebelsky)] [CSCS151 2005S (Stone)]
Copyright (c) 2007-8 Janet Davis, Matthew Kluber, and Samuel A. Rebelsky. (Selected materials copyright by John David Stone and Henry Walker and used by permission.)
This material is based upon work partially supported by the National Science Foundation under Grant No. CCLI-0633090. Any opinions, findings, and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation.
This work is licensed under a Creative Commons
Attribution-NonCommercial 2.5 License. To view a copy of this
license, visit http://creativecommons.org/licenses/by-nc/2.5/
or send a letter to Creative Commons, 543 Howard Street, 5th Floor,
San Francisco, California, 94105, USA.