Fundamentals of Computer Science I: Media Computing (CS151.02 2007F)
[Skip to Body]
Primary:
[Front Door]
[Glance]
-
[Academic Honesty]
[Instructions]
Current:
[Outline]
[EBoard]
[Reading]
[Lab]
[Assignment]
Groupings:
[Assignments]
[EBoards]
[Examples]
[Exams]
[Handouts]
[Labs]
[Outlines]
[Projects]
[Readings]
[Reference]
Reference:
[Scheme Report (R5RS)]
[Scheme Reference]
[DrScheme Manual]
Related Courses:
[CSC151.01 2007F (Davis)]
[CSC151 2007S (Rebelsky)]
[CSCS151 2005S (Stone)]
This lab is also available in PDF.
Summary: In this laboratory, you will practice writing procedures in Scheme, primarily by writing anonymous functions that you use to filter images.
Contents:
Reference:
The syntax for an anonymous function of one paramter is as follows:
(lambda (parameter) expression)
a. Load a moderate-sized image (no more than about 250x250) and name
the loaded image source
.
b. Create and show a new 3x3 image and name it canvas
.
c. Zoom in on canvas
.
d. Fill in the pixels of canvas
as follows. Note that you
may want to open this lab in a Web browser and cut and paste.
(image.set-pixel! canvas 0 0 (rgb.new 127 0 0)) (image.set-pixel! canvas 1 0 (rgb.new 127 0 127)) (image.set-pixel! canvas 2 0 (rgb.new 0 0 127)) (image.set-pixel! canvas 0 1 (rgb.new 127 127 0)) (image.set-pixel! canvas 1 1 (rgb.new 0 127 127)) (image.set-pixel! canvas 2 1 (rgb.new 255 255 255)) (image.set-pixel! canvas 0 2 (rgb.new 0 127 0)) (image.set-pixel! canvas 1 2 (rgb.new 0 0 0)) (image.set-pixel! canvas 2 2 (rgb.new 127 127 127))
As you may recall from the reading, we can remove the red component of all the pixels an image by mapping an anonymous function that uses 0 for the red component and maintains the green and blue components. That function might looks something like the following:
(lambda (color) (rgb.new 0 (rgb.green color) (rgb.blue color)))
a. Using image.map
and this function, build a new image from
canvas
and see if it has the expected effect.
b. Using image.map
and this function, build a new image from
source
and see if it has the expected effect.
c. Using image.map
and an anonymous function you define, build
new images from canvas
and source
that delete
both the green and the blue components from each pixel.
d. Using image.map
and an anonymous function you define, build
new images from canvas
and source
that set
the blue component to 255 (and preserve the red and green components).
We have seen two ways to change the components of a color: We can set them to particular values (as in the previous exercise) or we can add or subtract fixed amounts from them. It is certainly possible to do more. For example, we could multiply the components by some value, rather than add to the components.
For example, here's an anonymous procedure that doubles the green component.
(lambda (color) (rgb.new (rgb.red color) (min 255 (* 2 (rgb.green color))) (rgb.blue color)))
a. Confirm that the procedure works as advertised.
b. Why do you think there is a min
in the computation of the green component of the new color? After you have discussed your answer to this question with a nearby student, you may want to read the notes on this problem.
c. Using image.map
and an anonymous procedure of your own
design, build new images by halving the red, blue, and green components
of each pixel in source
and canvas
.
d. In both the initial example and the procedure you wrote, we have used whole numbers to scale the components. What do you expect to have happen if we use some other number, such as 1.2 or 0.8, to scale the components?
e. Check your answer experimentally.
One common image transformation is to that of converting a color image to greyscale. We know from past work that a grey has equal red, green, and blue components. Hence, our problem is to figure out what the identical component should be.
a. The simplest way to compute the common component value is to simply find the average of the three components:
(/ (+ (rgb.red color) (rgb.green color) (rgb.blue color)) 3)
Write a call to image.map
, using an anonymous procedure of your
design, that builds a greyscale version of source
using this
averaging technique.
b. Most careful computer graphics programmers have learned that the different colors have different apparent brightness. Hence, we tend to see a pure green as lighter than a pure red and a pure red as lighter than a pure blue. Careful analysis suggests that 30% of the red value, 59% of the green, and 11% of the blue provides the appropraite combination.
Write a call to image.map
, using an anonymous procedure of your
design, that builds a greyscale version of source
using this
weighted averaging technique.
In a preceding exercise, you found that you could make colors much
lighter by multiplying components by some factor and much darker by dividing
compnents by some factor. In the past, you found that rgb.darker
subtracted values from every component and rgb.lighter
added
values to each component.
But what if you want to make the values more extreme, not just darker or
lighter. In particular, what if you want to make the dark colors (or at
least the components that make them dark) darker, and light colors lighter?
Traditionally, we think about solving this problem with conditionals. That
is, we'd like to write code equivalent to if the blue component is less than
128, decrease it, otherwise increase it
. But it is possible to do that
computation without using explicit conditionals. How? That's your
challenge in this exercise. But here's a hint: Rather than changing
the value of the component, change the difference between that value and
some other value (say, 127 or 128).
a. Write an instruction using image.transform-pixel!
and an anonymous function of your choice that transforms pixel (0,0)
of canvas
so that the blue component increases if
the component is more then 127 and decreases by if the component is
less than 128.
We've asked you to transform a pixel, rather than a whole image, because transforming a pixel makes it easier to do testing. For example, we might see what happens with a dark blue color with three instructions.
(image.set-pixel! canvas 0 0 (rgb.new 0 0 100)) (image.transform-pixel! canvas 0 0 (lambda (color) (rgb.new (rgb.red color) (rgb.green color) __))) (rgb->string (image.get-pixel canvas 0 0))
By changing the initial color, you can then ensure that your instruction works correctly on a wide variety of colors (or at least three: one in which the blue component is less than 128, one in which it is 128, and one in which it is more than 128).
b. Once you are confident that your procedure works, build new images by
applying it to both source
and canvas
.
c. Use a similar technique to write an instruction that builds a new image by converting the red, green, and blue component of each pixel to either 0 or 255, whichever it is closest to.
Using the techniques from the previous exercises, write a
call to image.map
that converts your images to black and white.
In many applications, such as traditional offset printing, it is difficult, if not impossible, to get the full 255 different red, green, and blue components. In fact, in most cases, we can get no more than four or eight different intensities of the color contribution. Hence, it is common practice to flatten an image, say, by ensuring that each component is either 0 or 1 less than a multiple of 32 (8 different levels) or 64 (4 different levels). Some designers have found this transformation useful as it gives a nice, cartoony, version of an image.
How do we do this conversion? Well, you could certainly use a conditional, if you knew conditionals. However, even if you knew conditionals, the code would be long. Instead, we should work out a formula, like the one that we used a few problems previous.
In this case, we can transform a component by dividing by, say, 32, rounding, and then multiplying by 32. In Scheme, our code for the red component might then look something like
(- (* 64 (round (/ (rgb.red coor) 64))) 1)
Using that idea, write an instruction to build a flattened version of
canvas
and source
.
As most of you have noted, RGB colors are represented in DrFu as numbers. (We don't yet know what those numbers mean.) Hence, we can also write transformations that work directly with the numbers, instead of with the individual components.
a. Try each of the following (perhaps multiple times)
(image.show (image.map (lambda (color) (* 2 color)) source)) (image.show (image.map (lambda (color) (* 0.5 color)) source)) (image.show (image.map (lambda (color) (+ 500 color)) source))
b. Write a few mathematical transformations of your own. See if you can find a particularly appealing one. If so, share it with the class and we'll see if works well on other images.
Why do we have the min
in the computation of the green
component? That is, why do we write (min 255 (* 2 (rgb.green
color)))
and not just (* 2 (rgb.green color))
. Well,
if the green component is more than 127, 2 times the green component
will be more than 255, and we're not supposed to have a component whose
value is more than 255.
What does the min
do? Well, if the computed green component
is less than 255, (min 255 computed-value)
will give
the computed component. If the computed component is more than 255,
(min 255 computed-value)
will give 255. Hence,
we have ensured that the component is never more than 255, and, whenever
possible, is the computed value.
When you do computations of color components and those computations may
result in values less than 0, you should use (max 0 computed-value)
.
Note: The current implementation of rgb.new
reduces too-large
components to 255 and increments too-small components to 0. Nonetheless,
it is certainly possible the the implementation may change. More importantly,
is is good practice to make sure that you provide reasonable numbers to the
procedures you call.
http://www.cs.grinnell.edu/~rebelsky/Courses/CS151/History/Labs/anonymous-transformations.html
.
[Skip to Body]
Primary:
[Front Door]
[Glance]
-
[Academic Honesty]
[Instructions]
Current:
[Outline]
[EBoard]
[Reading]
[Lab]
[Assignment]
Groupings:
[Assignments]
[EBoards]
[Examples]
[Exams]
[Handouts]
[Labs]
[Outlines]
[Projects]
[Readings]
[Reference]
Reference:
[Scheme Report (R5RS)]
[Scheme Reference]
[DrScheme Manual]
Related Courses:
[CSC151.01 2007F (Davis)]
[CSC151 2007S (Rebelsky)]
[CSCS151 2005S (Stone)]
Disclaimer:
I usually create these pages on the fly
, which means that I rarely
proofread them and they may contain bad grammar and incorrect details.
It also means that I tend to update them regularly (see the history for
more details). Feel free to contact me with any suggestions for changes.
This document was generated by
Siteweaver on Mon Dec 3 09:54:38 2007.
The source to the document was last modified on Sun Sep 16 09:57:02 2007.
This document may be found at http://www.cs.grinnell.edu/~rebelsky/Courses/CS151/2007F/Labs/anonymous-transformations-lab.html
.
You may wish to
validate this document's HTML
;
;
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.