Skip to main content

Project: A Procedure is Worth a Thousand Pictures

Part 1A (Proposal) Due: 10:30 p.m. Monday, April 24, 2017

Part 1B (Sketches) Due: Start of class, Tuesday, April 25, 2017

Part Two (Implementation) Due: 10:30 p.m., Tuesday, May 2, 2017

Summary: At this point in your career, you’ve learned a number of techniques for making images algorithmically. This project is an opportunity for you to explore some techniques in greater depth.

Purposes: To explore some aspect of image generation in depth. To emphasize the more creative components of this course. To encourage more purposeful image creation and reflection on algorithms.

Collaboration:
We encourage you to work in groups of size two. You may, however, work alone or work in a group of size three. You may discuss this assignment with anyone, provided you credit such discussions when you submit the assignment.

Submitting: Email your submissions to csc151-01-grader@grinnell.edu. The subject of your email should have the form [CSC 151.01] Project. and your response to the appropriate part of the assignment. Scheme code or plain text should be in the body of the message. For part two, you will also submit two sets of three images and your statements following the instructions at the bottom of this assignment.

Some resources:

  • The project grading rubric tells you a bit more about what we expect you to do in this project and how we will assess your work.
  • The reading on project ideas helps provide you with some approaches to generating interesting images.
  • The project tips page provides a few additional tips on working on the project.
  • Some of the most successful projects have used artists or found images as inspiration.
  • Almost every student in the CS department has done this project. Many will be happy to talk about their project.

Warning: So that this assignment is a learning experience for everyone, we will almost certainly spend class time publicly critiquing your work.

Preliminaries

We highly recommend that you read and understand all parts of the assignment document before you begin your work on the project.

While the primary focus of this course is on learning how to write, describe, test, and analyze algorithms, our secondary focus is on writing algorithms that generate “interesting” images. We have certainly explored a wide variety of techniques for generating images. We have seen a variety of basic approaches. In particular, you can

  • generate a simple visualization of a list of colors;
  • write instructions to create images using the GIMP tools, particularly tools for selecting regions and drawing lines;
  • write instructions for turtle graphics;
  • describe images as sequences of simple geometric primitives (i.e., drawings), which you can also generate algorithmically;
  • create interesting patterns and shapes using image-compute-pixels!;
  • transform images (that you have created or loaded) using image-variant and image-transform!; and
  • transform images (that you have created or loaded) using the copy and paste operations.

You also know a number of other algorithmic techniques that could support creating images, such as various numeric functions, using lists and vectors to store collections of data (e.g., colors and brushes), and techniques for repetition and recursion.

It is now time to challenge yourself to use some of these ideas together to create images of your own design.

Rather than writing code that creates a single image, however, we want to challenge you to think about algorithmic processes that can create many different images depending on the given parameters. Hence the title, “A procedure is worth a thousand pictures.

Assignment

Background: Specification

You will write a procedure, (image-series n width height), that generates the nth image in a series of at least 1000 related images. (You should be sure your procedure works for at least the integers between 0 and 999, inclusive; however, you may be more successful if you support a larger range of values.) The images should be reproducible: that is, if a user gives the same n, width, and height twice, the resulting images should be the same. You should not use randomness to vary the images; instead, the differences should be based solely on the choice of n.

In creating these images, you should use at least three different techniques for generating images that we have learned this semester (see list above for most of the approaches).

You should strive to make these “interesting” images, images that will hold the viewer’s attention. As we have noted, representational images and completely symmetrical images are less likely to hold the viewer’s attention. (For example, in a picture of a cat on a landscape, the viewer is likely to say “yup, that’s a cat on a landscape” and move on.) You should also be intentional about choosing colors for your images.

Your process should scale appropriately. That is, a 1000x1000 image should look similar to a 100x100 image, just bigger (and probably slower to compute). We will certainly use your procedure to create relatively large images that we could comfortably print on a full page or use as a desktop background. You should also deal with different aspect ratios: an image that is 200x100 should look similar to the one that is 100x100, just twice as wide. (Two exceptions to this scaling rule: you do not have to scale the non-scalable brushes , such as the pattern brushes; and the shapes you draw with a turtle need not accommodate different aspect ratios. However, brush sizes should scale with width, height, or some other aspect of the size of the image)

Part One: Design Proposal

To begin your project, write a proposal consisting of two major parts.

The first part, which we will call the design statement, is intended for non-programmers and should explain the intent of your series. Are you exploring color? The use of shapes? The illusion of depth or motion? The effects of unpredictability on an image? Why? Incorporate the Elements and Principles of Two Dimensional Design and ideas about Design and Color.

After your design statement, include a sketch of at least two images from the series showing what you intend your images to look like and how they are likely to differ. (We would prefer hand-drawn sketches, although you may also generate them on the computer.)

The second part, which we will call the technique statement, is intended for your peers (that is, folks who know about as much programming as you do). This statement should explain your algorithm at a high level (in English) and how it will create the images you described and sketched in the first part. Be sure to explicitly list the three (or more) algorithmic image-generating techniques from class that you plan to use. Also explain clearly and convincingly how you will ensure that your algorithm creates at least 1000 distinct images.

Your statements should display the hallmarks of quality writing. Given that the paragraph is the “unit of composition” (according to Strunk and White), and each of these statements asks you to address several important considerations, it is likely that each statement will consist of several coherent, unified paragraphs.

We will do our best to respond to your proposal in a timely manner. However, given other constraints, we may not be able to do so.

Part Two: Procedure and Images

After finishing your proposal, you should set to work on implementing your design, making sure to meet all of the specifications outlined above.

Once you complete your procedure, update and revise your design and technique statements to reflect how your project actually turned out. If there are points of divergence, you should explain why you changed your approach.

In addition, be certain your final, revised technique statement mentions the three (or more) algorithmic image-generating techniques you are using. It should also explain–via careful, precise reasoning–how you know that your procedure creates at least 1000 distinct images.

Finally, tell us three values of n that, when given as parameters to your procedure, cause it to produce especially interesting (and distinct) images.

Submitting the Project

Part One

Copy your proposal into the body of an email message and send it to csc151-01-grader@grinnell.edu. Turn in your sketches at the beginning of class the next day (Tuesday).

Part Two

Email your program, updated statements, and representative values of n to to csc151-01-grader@grinnell.edu.

Because it might take a while to create images, you should also provide us with copies of certain images. We would like two sets of images: one giving the three images you’ve chosen as particularly nice representatives of your series, and the other showing how well your procedure scales the images to different sizes. Here’s how.

  1. From the Desktop, open your home directory. Create a new folder inside that is named using the usernmaes of your project group members, e.g., username1-username2
  2. Save or copy a .rkt file containing your Scheme code into this directory.
  3. Create three of your favorite images at a large size (at least 500x500).
  4. Pick a single n and create images of size 50x50, 100x100, 200x200, 100x200, and 200x100 with that n.
  5. Save each image into the new folder, using PNG file format and with a filename of the form username1-username2.nnn.size.png, where username1, etc., are the usernames of the team members, nnn is the number used to generate the image, and size is the size of the image (e.g., 50x50, 100x100, 100x200, 200x100, 200x200, or 500x500). For example, for the 100x200 image with an n of 32, Professor Rebelsky would use rebelsky.032.100x200.png.
  6. Save your revised design statement as a text file in this new directory, using the name username1-username2.design-statment.txt.
  7. Save your revised technique statement as a text file in this new directory, using the name username1-username2.technique-statment.txt.
  8. Again, from the Desktop, open your home directory. Open your new folder and verify that it contains the images, design statement, and Scheme code. Then close it. 9.From your home directory, right click on your new folder and choose “Create Archive …”. Without changing any of the settings in the dialog, click the “Create” button.<!–listitem> If you do not see the “Create Archive …” menu item, then you may do this instead. Open a new terminal, and type “tar czf username1-username2.tar.gz username1-username2``” (using the name of your project directory rather than username1-username2). Press Enter to execute the command and create the archive.
  9. Attach the .tar.gz file you just created to the email where you turn in your assignment.

Scaling

Making your image scale correctly is one of the more difficult aspects of this assignment, but the requirement is there to help ensure that you consider important computational aspects of the problem.

Some parts of your image should be relatively straightforward, if not not necessarily simple. For the Gimp tools procedures like image-select-ellipse! and image-draw-line!, you will need to calculate parameters based on the width and height of the image. For parts of your drawing based on procedures like image-compute, you should modify the column and row in such a way that you take the width and height into account. And if we scale both width and height by the same amount, your calls to turtle-forward! can scale by that amount

Other parts may be slightly more difficult, particularly when the aspect ratio changes. For example, suppose we double the width of an image, and keep the height the same. It’s hard to take a turtle-generated polygon and make it twice as wide. And it’s equally hard to make a brush twice as wide without making it twice as tall. For cases like this, you still need to “scale” your computation, but you can do it in terms of a computed value of the image (e.g., just the width, just the height, the maximum of the two, the minimum of the two, the arithmetic or geometric average, or something similar). Whatever choice you make, you need to document.

A few parts may be essentially impossible to scale. For example, Gimp provides some useful and interesting brushes whose size it seems difficult to change. You may certainly use such brushes, but you should take the time and effort to explain their use and the ways in which their use affects your ability to scale and stretch.

Note that there is a procedure that resizes and rescales any image.
You may not use that procedure (except to resize or rescale a source image).

Questions

Can we reuse code from the assignments and labs?
You may certainly reuse code from the assignments and labs, provided you cite that code. However, you should make sure that your project goes beyond what you did for the assignment or lab. Hence, you will likely want to extend or otherwise rewrite that code. (Even if you extend or rewrite code, you should still cite its origin and influence.)
Can we make representational images?
We would prefer that you not make representational images. In our experience, the representational projects are often less interesting. (A thousand cartoonish houses is, well, not all that exciting.)
While we have envisioned a lot in our proposal, we are worried
that we will not be able to implement everything we envision.
Will we be penalized if we do less than we originally planned?
We would prefer that you aim high in the original proposals and then simplify as necessary. You will be graded primarily on your final results. Note, however, that the quality of your initial proposal will also factor in to your final grade.
Does our procedure have to work equally well for different
aspect ratios?
We would prefer that your procedure handle different aspect ratios. However, if you find it appropriate to focus on a particular aspect ratio, that is acceptable. Just make sure that your statements explain why.</varlistentry>
There is some fine detail in our image that gets lost at smaller
image sizes, making smaller images look identical. Is that okay?
Your images need not be distinguishable at smaller sizes.
Some aspects of my drawing, such as the brush sizes, don’t scale
perfectly. Is that okay?
Yes, that is fine. We only expect you to scale the things that are reasonably scalable (such as the positions and lengths of shapes in your image). However, since you can choose brush sizes, you should make some attempt to have the brush size scale (e.g., relatively to the smallest, largest, or average dimension).
You mentioned fractals. I really like the Julia Set and/or the
Mandlebrot set. Can making an image of one of those sets be
my project?
At least one of us finds the Julia Set and Mandlebrot set trite. (They are conceptually interesting and they can be attractive images. But they are not a novel project in 151.) So, you might explore variants of these sets, but please don’t do the standard versions of these fractals.
I’d like to save many images in my series to disk. Can you
help me do so automatically?
See the sample code below.

Saving Project Images

The (image-save image path-to-file) procedure lets you save an image you’ve made. By calling it with appropriate parameters, you can save large swaths of your series (at least until GIMP or DrRacket crashes).

;;; Procedure:
;;;   num->id
;;; Parameters:
;;;   num, an integer in the range 0 to 999
;;; Purpose:
;;;   Compute a string id (typically for file names)
;;; Produces:
;;    id, a string
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   (string-length id) = 3
;;;   (string->number id) = num
;;; Practica:
;;;   > (num->id 5)
;;;   "005"
;;;   > (num->id 55)
;;;   "055"
;;;   > (num->id 555)
;;;   "555"
(define num->id
  (lambda (num)
    (let ([idplus (string-append "00" (number->string num))])
      (substring idplus (- (string-length idplus) 3)))))

;;; Procedure:
;;;   image-series-file
;;; Parameters:
;;;   prefix, a string (e.g., "/home/student/Desktop/student")
;;;   n, an integer in the range 0 .. 999, inclusive
;;;   width, a positive integer
;;;   height, a positive integer
;;; Purpose:
;;;   Create the nth image in the series at the specified size,
;;;   and save in an appropriate file
;;; Produces:
;;;   [Nothing; called for the side effect]
;;; Practica:
;;;   > (for-each (lambda (i)
;;;                 (image-series-file "/home/student/Desktop/student" 
;;;                                    i 200 200))
;;;               (iota 100))
(define image-series-file
  (lambda (prefix n width height)
    (let ([filename (string-append prefix 
                                   "." (num->id n)
                                   "." (number->string width)
                                   "x" (number->string height)
                                   ".png")]
          [image (image-series n width height)])
      (image-save image filename)
      (gimp-image-delete image))))

Important Evaluation Criteria

In class, we will have two discussions, one about the aesthetics of your work and your success in meeting the criteria you have stated for yourself, and another about the techniques you use to create your images.

Your grade will be based on your statements, your code, and your success in combining multiple techniques. We will use this rubric (or a slight variant) to assess your project.

Tips for Success/Avoiding Penalties

While we appreciate clever and interesting images, if you read the rubric carefully, you’ll note that most of the points depend on your success at meeting the basic requirements: Do you have 1000 distinct images? Do your images scale properly? Are you using at least three different techniques? Make sure that you are sure that you’ve addressed each of these requirements.

Here are some issues that normally lead to teams losing points. Make sure that you check these issues.

  • The procedure signature is (image-series n width height). The procedure is supposed to return an image id. The procedure is not supposed to show the image.
  • The image-series procedure is supposed to accommodate all values of n between 0 and 999. Make sure that you’ve checked your procedure with n values of 0, 1, 2, 998, and 999.
  • We will often check your image-series procedure on relatively small images (e.g., 200x200). Make sure that your procedure functions at that size.
  • Note that polygons become indistinguishable once they have more than fifty or so sides (and maybe even fewer, depending on how big they are). So, don’t rely only on the number of sides in a polygon to argue that all the images are distinguishable.
  • If you are scaling some basic shape, note that scaling by 1/999 and 1/998 is likely to be indistinguishable.
  • “These four components of the image all depend on n” is not by itself an argument that you have 1000 different images. We’ve seen cases in which there are four choices for one component, five for another, eight for another, and ten for another. 458*10 is 1600, which is more than 1000. However, the components were chosen by taking (mod n 4), (mod n 5), (mod n 8), and (mod n 10). In that case, the images cycle every 40 values. Argue more carefully and precisely.