Now that you have been introduced to the basics of writing functions, we are going to start having more interesting assignments that take advantage of the graphics capabilities of Python. This assignment is the first of several GUI applications we will work on in this course.
One of the main things that you will learn in this assignment is that there are many different ways to represent color, and the choice of color model often depends on the application. For example, RGB is used when the colors need to be displayed on a computer monitor (such as a web site), while CMYK is often used for printing out colors on paper.
You have a lot longer to do this assignment than the previous one – a little less than two weeks. However, you should still start early on this assignment as the due date is just before you leave for Fall Break. If you do not know where to start, or if you are completely lost, please go to office hours page immediately. A little in-person help can do wonders.
As before, remember to fill out the survey telling us how long you worked on this assignment.
Important: Python has a built-in color conversion module called colorsys
.
You are forbidden from using that module in this assignment.
Authors: D. Gries, W. White, L. Lee, and S. Marschner
Learning Objectives
This assignment is designed to help you understand the following concepts.
- It introduces three color models that are used in computing and graphics.
- It gives you practice in writing complex functions with conditionals.
- It gives you practice with both fruitful and mutable functions.
- It introduces you to the notion of attribute invariants.
- It demonstrates a complex Python application that spans multiple modules.
Even though this is a complex Python application, we have provided most of the modules
for you. You only need to focus on one module: a3.py
(as well as the test script
a3test.py
).
Table of Contents
- Academic Integrity and Collaboration
- Introduction to Color Models
- The
introcs
Color Classes - The ColorModel Application
- Assignment Instructions
- Finishing Touches
Academic Integrity and Collaboration
Academic Integrity
This assignment is a (heavily in places) modified version of an assignment given in a previous semester. Please do this assignment without consulting (or seeking) previous solutions. Consulting any prior solution is a violation of CS1110’s academic integrity policies.
Unlike the previous assignments, it is highly unlikely that your code for this assignment will look exactly like someone else’s. This is the first assignment that we will actively be using Moss to check for instances of plagiarism.
We also ask that you do not enable violations of academic policy. Do not post your code to Pastebin, GitHub, or any other publicly accessible site.
Important: Python has a built-in color conversion module called colorsys
.
You are forbidden from using that module in this assignment.
Collaboration Policy
You may do this assignment with one other person. If you are going to work together, form your group on CMS as soon as possible. If you do this assignment with another person, you must work together. It is against the rules for one person to do some programming on this assignment without the other person sitting nearby and helping.
With the exception of your CMS-registered partner, we ask that you do not look at anyone else’s code or show your code to anyone else (except a CS1110 staff member) in any form whatsoever. This includes posting your code on Piazza to ask for help. It is okay to post error messages on Piazza, but not code. If we need to see your code, we will ask for it.
Introduction to Color Models
Color Model RGB
The RGB system is named after the initials of the three color names: red, green, and blue. In this color model, light from these three colors is mixed to produce other colors, as shown in the image to the left. Black is the absence of color; white the maximum presence of all three.
In the upper right is a colored image. Below it is its separation into red, green, and blue (here is a high resolution version). In the three separation panels, the closer to black a point is, the less of that color it has. For example, the white snow is made up of a large amount of all three colors, whereas the brown barn is made up of red and green with very little blue. Because it works by adding colors to black, the RGB system is additive.
The color model RGB is used in your TV and computer screens, and hence on web pages. Its
roots are in the 1953 RCA color-TV standards. However, the idea has been around longer.
See this exhibit for some amazing full-color images
taken with an RGB camera over 100 years ago.
In the RGB model used in most systems, the amount of each of red (R), green (G) and blue
(B) is represented by a number in the range 0..255. Black, the absence of color, is [0, 0, 0].
White, the maximum presence of (R), (G), and (B), is [255, 255, 255]. This means that
there are 16,777,216 different colors.
In some graphics systems, RGB is used with float
numbers in the range 0.0..1.0
instead of int values 0..255. The reasons for this discrepancy is that the mathematical
formulas for color require real numbers 0.0..1.0, but it takes a lot less memory to
store ints instead (and images require a lot of memory). In your program, you may have
to convert each number in the integer range 0..255 to a float
in 0.0..1.0,
calculate a mathematical formula, and then convert back to 0..255.
Color Model CMYK
For your ink-jet printer, you buy expensive ink cartridges in the colors cyan, magenta, yellow, and black. The printer mixes these inks in different amounts on paper to make the full range of colors. Black is referred to using K (originally for "Key") to avoid confusion with Blue.
The process works similarly to RGB on a monitor, but in reverse. The paper starts off white (equal parts red, green, and blue), and the colors of these inks are chosen so that cyan ink absorbs red light, removing it from the color of the paper. Similarly, magenta removes green, and yellow removes blue. Black ink removes all three colors in equal amounts. For instance,paper printed with only yellow ink appears the same color as a monitor that is displaying a yellow color [255, 255, 0] because it has removed all the blue, leaving the red and green. Printing magenta and cyan removes red and yellow and results in blue [0, 0, 255]. Because it works by removing color, this kind of system is subtractive.
Theoretically, only C, M, and Y are needed to achieve any color, but in practice it is hard to get a good black by mixing colored inks. Instead you get a soggy, expensive brown-black. By using the black ink to do the “heavy lifting” of absorbing most of the light when printing dark colors, a lot of ink can be saved (This is a simplified view of color printing; more complicated calculations are needed to get accurate colors with real inks).
To demonstrate this issue, look at the image in the upper right. The left version of the image is its separation into cyan, magenta, and yellow (enlarged version). To the right of that, you see the same image separated into four components; C, M, Y, K (enlarged version). As you can see, much less CMY ink is needed to make the image when black is also used.
In the CMYK system, each of the four components is traditionally represented by a
percentage. We will use float
values in the range 0.0..100.0.
Color Model HSV
The HSV model, used heavily in graphics applications, was created in 1978 by Alvy Ray Smith. Artists prefer the HSV model over others because of its similarities to the way humans perceive color. HSV can be explained in terms of the cone that appears to the left. It is broken up into three values.
H, the hue, defines the basic color. H is an angle in the range 0 ≤ H < 360, if one views the top of the cone as a disk. Red is at angle 0. As the angle increases, the hue changes to orange, yellow, green, cyan, blue, violet, magenta, and back to red. The color wheel in the ColorModel application makes this correlation explicit.
S, in the range 0 ≤ S ≤ 1, is the saturation. It indicates the distance from the center of the disk. The lower the S value, the more faded and grayer the color. The higher the S value, the stronger and more vibrant the color.
V is the value or brightness. It is also in the range 0 ≤ V ≤ 1. It indicates the distance along the line from the point of the cone to the disk at the top. If V is 0, the color is black; if 1, the color is as bright as possible.
To the right at the top is a picture. Below it we see its hue, saturation (white is zero
saturation, red is full saturation), and brightness components. The hue component shows
color. The snow has color, but its saturation is low, making it almost grayish. Look
at the various components of the image —the sky, the green grass, the snow, the
dark side of the barn, and so on to see how each component H
, S
, and V
contributes.
You can see more detail in this high-resolution version.
The introcs
Color Classes
All of the color models of the previous section are provided by the module introcs
,
which was installed with Cornell Extensions. This module provides three different
classes: RGB
, CMYK
, and HSV
. It lacks the ability to convert between these classes.
That is the focus of this assignment.
The Class RGB
The class RGB
is the type of objects that represent RGB color. Objects of type RGB
have three attributes: red
, green
, and blue
. Tthey also have a secret attribute
alpha
, but this will not be used in this assignment). For example, if c
is a
variable containing a (name of) an RGB object, you would use the expression
c.red
to access the red value.
The RGB
constructor function takes three arguments, assigning
these values to the attributes in the order red
, green
, and blue
.
For example, to create an RGB
object representing the color red, use the
assignment
The Class CMYK
The class CMYK
is the type of objects that represent CMYK color.
Objects of type CMYK
have four attributes: cyan
, magenta
, yellow
, and
black
. For example, if c
is a variable containing a (name of) a CMYK object,
you would use the expression c.cyan
to
access the cyan value.
The CMYK
constructor function takes four arguments, assigning these values to
the attributes in the order cyan
, magenta
, yellow
, and black
. For example,
to create a CMYK
object representing the color red, use the assignment
The Class HSV
The class HSV
is the type of objects that represent HSV color. Objects of type
HSV
have three attributes: hue
saturation
, and value
. For example,
if c
is a variable containing a (name of) a HSV object, you
would use the expression c.hue
to access
the hue value.
The HSV
constructor function takes three arguments, assigning these values to the
attributes in the order hue
, saturation
, and value
. For example, to create an
HSV
object representing the color red, use the assignment
Attribute Invariants
All of the objects in this assignment have attribute invariants. An attribute invariant is a property of an attribute (which is essentially a variable) inside an object. The invariant cannot be violated. Attempting to violate an invariant will cause an error and crash Python.
For example, for RGB objects, the red
attribute has an invariant that it must be an
int
and it must be in the range 0..255, inclusive. The following code produces an error:
The invariants in the introcs color classes are provided for your benefit. They are there to help you catch errors. All you need to do is to make sure that you never assign a value to an attribute that violates an invariant.
The invariants for this assignment are as follows:
- All attributes of
RGB
must be ints, and in the range 0 to 255, inclusive. - All attributes of
CMYK
must be floats, and in the range 0.0 to 100.0, inclusive. - The
hue
attribute ofHSV
must be a float in the range 0.0 to 360.0, not including 360. - The
saturation
andvalue
attributes must be floats in the range 0.0 to 1.0, inclusive.
For more details see the color class documentation
for the module introcs
.
The ColorModel Application
Unlike the first assignment, this assignment provides you with a lot of the code already written. In that respect, this assignment is going to be a lot more like a lab, where you fill in the extra details. In addition, we are providing you with an online color conversion tool, so that you know what your answers should look like.
Assignment Source Code
Download the source code to this assignment before you do anything else. As we said above, this assignment will involve several files. Two are completed by us, and the other two have functions stubs or incomplete implementations that you must finish yourselves. They must all be in the same directory for this assignment to work
The following are the two completed source code files (You should not need to modify the contents of any of these files at all):
a3app.py
: The Kivy script providing the GUI application.colormodel.kv
: A layout file used to arrange the input controls in the GUI window.
You should not expect to understand the code in a3app.py
(most of the TAs cannot
understand it). That is not the point. As long as you follow the directions of
the assignment, you should be able to run run this script
without understanding it. In fact, it is not necessary to run this script to complete
this assignment, but it does make it a little more fun.
In addition to those two files, the following source code files are skeletons (i.e. they are incomplete and you are expected to add functionality to them):
a3.py
: The module with the functions that you are to implement.a3test.py
: The test script for the functions ina3.py
.
The file a3.py
is completely unfinished. It has nothing but stubs. However, to make
the assignment easier, we have provided many of the test cases for you already in
a3test.py
. With that said, not all test procedures are complete, and you should
add more tests as the instructions tell you to do so.
Technically, you can complete this assignment with just a3.py
and a3test.py
. As
we said, the GUI is just intended to make the assignment a little more fun.
Understanding the GUI
To run the GUI, make sure all four files are in the same directory. Then navigate to
this directory and run a3app.py
as a script as follows:
You will see a bunch of crazy messages that look like this:
[INFO ] [Logger ] Record log in ... [INFO ] [Kivy ] v1.11.1 ... [INFO ] [Factory ] 184 symbols loaded [INFO ] [Image ] Providers: img_tex, img_imageio, ... [INFO ] [Text ] Provider: sdl2 [INFO ] [Window ] Provider: sdl2 [INFO ] [GL ] Using the "OpenGL ES 2" graphics system ...
That is Kivy (our GUI library) initializing the application. When the messages are done, you should see a GUI window that looks like the window shown below.
In this application, you see some sliders (and a color wheel) up top, and some colored panels down at the bottom. Right now, very little works. If you move the RGB sliders, you will see the color panels change color. However, none of the other controls work. You can also change the color by entering the new color value into the R, G, and B fields.
Your job is to write and test, one by one, the functions in module a3
. As you do,
more and more of the GUI will work properly.
A working GUI should look like the one shown below. The first two color panels at the bottom should be two different colors, and they should each be the complement of the other. The text will also be in the complement color, and it will display the color values, in RGB, CMYK, and HSV, for the background color. The right color panel will have a contrast setting that allows you to make the central color brighter or darker.
You can change these colors by moving the controls. If you move the controls in one color model, then the controls in the other color models will follow automatically. This way, all three models (RGB, CMYK, and HSV) register the same color. The contrast control is completely separate and disconnected, which is why it is at the bottom.
The numbers in the text field should be in their respective ranges: 0..255 for R, G, B; 0.0..100.0 for C, M, Y, K; 0.0..1.0 for S, V; and 0.0..359.999 for H. The CMYK numbers will display to one decimal place, while the HSV numbers will display to 3 decimal places (the RGB numbers are integers). If you type in something that is not a string, or a number out of range, the input will be ignored and it will revert back to the previous value.
We will show off our solution several times in class. We will also make it available to TAs to show in office hours.
Experimenting with the Color Models
If we could, we would give you a sample solution to play with so that you would see what a working program is supposed to look like. Unfortunately, we cannot do that. Python is not a compiled language, so there is no easy way to give you a solution without showing you the source code.
Instead, we are doing the next best thing. We have provided you with an online color converter. Click on the image above to go to a special web page where you can enter various color values. This web page shows you what the answers should look like for various inputs (after you enter a value in a field on that page, you have to click on a different field to get the whole page to update to the new values). For CMYK and HSV the answers provided are accurate to three decimals places. We do not guarantee more accuracy than that. You should use this page to help you design your test cases for the rest of the assignment.
Testing and Debugging
You should use the testing and debugging methodologies that you used for the
first assignment. You will want to test with
the script a3test.py
; we have already started several of the test procedures for you.
You will be graded on the completeness of your test cases. Remember our comments
from the first assignment.
As you debug your code, you may want to add print statements to a3.py
as
watches (e.g. print statements that display the contents of a variable) and
traces (e.g. print statements that indicate the line of code that is currently
executing). Traces will be particularly valuable for this assignment because
it is the first major assignment involving conditionals. We talked about
how to use traces in lecture.
Because this is a much more complex assignment than the first, we recommend that you
be very descriptive with your watches and traces. Suppose you are trying to find an
error in function rgb_to_hsv
(or in a function that calls rgb_to_hsv
) and that
rgb_to_hsv
changes a variable h
at line 70. Then, you might insert at line 171
a statement like
Assignment Instructions
In this section we outline the functions that you are expected to write for this assignment. You should write and test your functions, one at a time, in the order given. Read the text below, but also make sure you carefully read the specifications and comments in the provided code.
Function complement_rgb
The first function is a warm-up. We gave you some partial code that you need to fix.
The complement of a color is like a color negative. If R, G, and B were color
components of the RGB value in the range 0.0..1.0 (not 0..255!), then the color
components of the complement would be 1-R, 1-G, and 1-B. However, since we are
using values in the range 0..255, the complementary color of the RGB color
(r, g, b)
is the color (255-r, 255-g, 255-b)
.
Currently this function does not work. Instead, it makes a copy of the RGB object
in the parameter variable. You will need to change the arguments to the
RGB constructor to get it to return the complement instead.
We have given you a complete test procedure in a3test.py
for you to verify that
your function implementation is correct.
After completing this function, run a3app.py
as a script. You will now
see text in the colored boxes. This text will have a lot of information, including
the RGB value of the current color. However, the CMYK and HSV information will
still be blank.
The str5
Functions
To turn a color object into a string, you can use the str
function which you have
seen in class. This is fine for RGB objects, as the attributes are integers.
However, CMYK and HSV objects have float attributes, and they are a lot messier.
Floats could potentially have 18 digits!
To solve this problem, we want you to create some functions that limit all of the
numbers to five characters (not digits). The following example shows the difference
between str
and str5_hsv
:
str5
This function simply rounds a number and converts it to a string. The challenge is that
the number of places to round to is not constant. For example,
str5(1.0567)
returns
'1.057'
, while
str5(10.567)
returns
'10.57'
. When implementing this function
you should pay close attention to the precondition (as it will be helpful).
Within this assignment, this function should only be used by the GUI to give it a
consistent format. You should never use this function anywhere in your code other than
str5_cmyk
or str5_hsv
. In particular, you should never use it in a
conversion function, since that results in a loss of
mathematical precision.
str5_cmyk
Implement this function according to the specification in a3.py
. This function should
call function str5
to round each CMYK value to 5 characters. We have provided you with
two test cases in a3test.py
to show you how to test this function. You are welcome
to add more, but this is not necessary.
str5_hsv
Implement this function according to the specification in a3.py
. This function should
call function str5
to round each CMYK value to 5 characters. We do not provide
test cases for this function. You must write at least two of them.
Test Cases
As we mentioned above, the tests for str5
and str5_cmyk
are complete. You are welcome
to add more, but this is not necessary. However, there are no tests for str5_hsv
.
We expect you to add at least two. They go in the test procedure test_str5_color
.
Look at the tests for str5_cmyk
as an example for how to create these tests.
The CMYK Functions
The next two functions convert back-and-forth between RGB and CMYK colors. When you
implement the function rgb_to_cmyk
, the numerical CMYK color will display properly
in the lower color panes (assuming that str5_cmyk
is implemented correctly). In
addition, moving the RGB sliders will cause the CMYK sliders to move as well. However,
the reverse will not be true until you finish cmyk_to_rgb
. There will also be no
affect to the HSV controls.
When you complete the second function cmyk_to_rgb
, moving the CMYK sliders will
cause the RGB sliders to also move. At this point, the two sets of sliders will work
in tandem.
rgb_to_cmyk
This function converts an RGB value to a CMYK value. There are several different ways to convert, depending on how much black is used in the CMYK model. Our conversion uses as much black as possible. Let R, G, and B be the color components of the RGB value in the range 0.0..1.0 (not 0..255!). That means that you will need to first divide the values in the RGB object by 255.0. Once you do that, then the conversion is as follows. First compute
If K is 1, then the other color (C, M, Y) value are all 0. Otherwise, compute them with the following formulas:
The resulting CMYK values are in the range 0.0..1.0, and they must be converted to the
range 0..100.0. And that is it! Not too bad, right? However, do not round your answers.
That is an unacceptable loss of precision.
Important: While we use capital letters in the mathematical formulas above, the style guidelines forbid capital letters as variables. Make them lower case in your code.
cmyk_to_rgb
This function converts a CMYK value to an RGB value. Let C, M, Y, and K be the color components of the CMYK value, all in the range 0.0..1.0 (not 0..100.0; you will need to convert this first). Then the conversion is as follows:
This produces RGB values in the range 0.0..1.0, and they must be converted to the range
0..255. You should use rounding in converting the answer to an int. We will say it
again because students have not complied in previous semesters: ROUND; DO NOT TRUNCATE.
Remember that casting truncates, and does not round.
Test Cases
We have already provided the test cases for rgb_to_cmyk
. However, you should study them
as you will need to emulate them when you work on the
HSV functions. Test cases for CMYK values are difficult because
the float
attributes are only approximations to the real values, and slightly different
ways of computing might produce different results. In fact, we saw this problem with
currencies in the first assignment.
This time, instead of using assert_floats_equal
, we round each attribute to three
decimal places (in the test, not the function). That is because this is degree of
accuracy we are requiring. Your answers only need to be correct to three decimal places.
For cmyk_to_rgb
we expect you to create your own tests. This is straight-forward, since
the attributes of RGB are all integers. We will not tell you how many tests that you need,
but you do need to have proper code coverage. We talked about this in the
lecture on conditionals. Do you have an if-statement
in the code for cmyk_to_rgb
? If so, you need to make sure that you have a test for
each possibile branch of the if.
In picking color values to test, you should make use of the online color converter. This tool is accurate up to three decimal places.
The HSV Functions
The next two functions convert back-and-forth between RGB and HSV colors. When you
implement the function rgb_to_hsv
, the numerical HSV color will display properly
in the lower color panes (assuming that str5_hsv
is implemented correctly). In
addition, moving the RGB sliders will cause the HSV color wheel to move as well.
However, the reverse will not be true until you finish hsv_to_rgb
.
When you complete the second function hsv_to_rgb
, moving the color wheel will
cause the RGB sliders to also move. At this point, all of the upper level controls
should work together.
rgb_to_hsv
This color conversion is a little more complicated that the previous ones. First, convert the RGB values so that R, G, and B are in the range 0..1. Next let be the maximum and be the minimum of the (R, G, B) values.
The value H is now given by 5 different cases:
The value S is given by the formula
Finally, V = . Do not round your answers. That is an unacceptable loss of precision.
hsv_to_rgb
This function converts an HSV value to an RGB value. To perform the conversion, you first need to compute the following values.
Once you have this computed, the values R, G, and B depend on the value Hi as follows:
This produces RGB values in the range 0.0..1.0, and they must be converted to the range
0..255. You should use rounding in converting the answer to an int
. We will
say it again because students have not complied in previous semesters:
ROUND; DO NOT TRUNCATE. Remember that casting truncates, and does not round.
Note: If you look at the Wikipedia entry, you will note that it uses
Because H satisfies 0 <= H < 360 (degrees), the values (floor(H/60) % 6) and floor(H/60) are equivalent. Therefore, we use the simpler one.
Test Cases
We have not provided you with any tests for either rgb_to_hsv
or hsv_to_rgb
. You
must supply the tests for both. Once again, you must have proper code coverage.
Do you have an if-statement in either function? If so, you need to make sure that you
have a test for each possibile branch of the if.
For the tests for rgb_to_hsv
, you only need to test that the hue
, saturation
,
and value
attributes are accurate up to three decimal places. To help with this test,
you should make use of the
online color converter,
which is also accurate up to three decimal places.
The Contrast Functions
The last two functions implement the contrast slider in the bottom right-hand corner of the GUI application. A contrast value is a a number between -1 and 1. At 0 contrast, all colors are left untouched. For a postive contrast, bright colors are made brighter and dark colors are made darker. For a negative contrast, colors come closer together and are harder to tell apart. This is similar to how a contrast setting works on a television or a computer monitor.
There are many ways to implement a contrast slider. We are going to use a sawtooth curve.
This curve is the line (between 0 and 1) when the contrast is 0. As approaches -1, it becomes the horizontal line . As approaches 1, it splits into two horizontal lines at 0 and 1.
More formally, for , we define this curve as
In the case where , it becomes the discontinuous curve
contrast_value
To implement this function, simply follow the formula above. The parameter value
is
and the parameter contrast
is . The return value is . There are no
color objects involved in this function; only numbers.
contrast_rgb
Note that this function is a mutable function. It does not return a new RGB object.
Instead, it modifies the RGB object that was passed as an argument. If you look at the
test in the procedure test_contrast_rgb
of a3test.py
you can get some idea of what
it should do.
To apply contrast to an RGB object, first convert the values R, G, B to the range 0.0..1.0.
Then apply the function contrast_value
above to get new values for R, G, B. Finally
convert the result back to 0..255 using mutliplication and rounding.
Test Cases
The sawtooth curve has a lot of possibilities, and so it requires a lot of test cases.
For that reason, we provided all of the tests for contrast_value
you already.
However, we have only provided one test for contrast_rgb
. We want you to provide
at least two more tests. Use the tests in test_contrast_value
as a guide for how
to come up with tests for this function.
Finishing Touches
Before you submit this assignment, you should be sure that everything is working and polished. Unlike the first assignment, you only get one submission for this assignment. If you make a mistake, you will not get an opportunity to correct it. With that said, you may submit multiple times before the due date. We will grade the most recent version submitted.
Once you have everything working you should go back and make sure that your program meets the class coding conventions. In particular, you should check that the following are all true:
- You have indented with spaces, not tabs (Atom Editor handles this automatically).
- Functions are each separated by two blank lines.
- Lines are short enough (~80 characters) that horizontal scrolling is not necessary.
- The specifications for all of the functions are complete.
- Function specifications are immediately after the function header and indented.
- Docstrings are only used for specifications, not general comments.
- Your name(s) and netid(s) are in the comments at the top of the modules.
Upload the files a3.py
and a3test.py
to CMS by the due date:
Friday, October 11. We do not need any
other files. Do not submit any files with the extension/suffix .pyc
. It will help
to set the preferences in your operating system so that extensions always appear.
Completing the Survey
In addition to turning in the assignment, we ask that you complete the survey posted in CMS. Once again, the surveys will ask about things such as how long you spent on the assignment, your impression of the difficulty, and what could be done to improve it. Please try to complete the survey within a day of turning in this assignment. Remember that participation in surveys is 1% of your final grade.