CS200: Computer Science, Spring 2002
|
Problem Set 5: Non-Prosaic Mosaics Out: 27 February 2002
Due: 8 March 2002, before class
Turn-in Checklist: On Friday 8 March, bring to class a stapled turn in containing your answer to Question 1 and all the code you wrote for Question 2. Please only turn in the code you changed, not all the photomosaic code. Also include one or more URLs for primosaics you created. Collaboration Policy (Same as Problem Set 3)
For this problem set, you may either work alone and turn in a problem set with just your name on it, or work with one other student in the class of your choice. If you work with a partner, you and your partner should turn in one assignment with both of your names on it.PurposeRegardless of whether you work alone or with a partner, you are encouraged to discuss this assignment with other students in the class and ask and provide help in useful ways. You may consult any outside resources you wish including books, papers, web sites and people. If you use resources other than the class materials, indicate what you used along with your answer.
Downloads:
- Introduce mutation.
- Understand how we can use greedy algorithms to approximate solutions to NP-complete problems.
- Really understand all the code from PS1.
- Make a non-prosaic mosaic.
- primosaic.ss — This is the mosaic code from PS1. Feel free to use your PS1 code instead.
- images.zip — To make a primosaic, we need more tiles than for the prosaic mosaics from PS1. Unzip this file to your J:\CS200\PS5 directory. This produces a file rotunda.gif and a directory uvaimages\small containing 330 tile images.
Background
In Problem Set 1, you constructed photomosaics by choosing the best tile for each master image region without worrying about reusing tiles. This led to prosaic mosaics. In this assignment, you will use a greedy algorithm and mutation to produce more pleasing photomosaics. We will call these primosaics for short. They are un-prosaic mosaics. An example primosaic is http://www.cs.virginia.edu/cs200/problem-sets/ps5/primo.html.
We will call a tiling that minimizes the total color difference a supremosaic. That is, a tiling is a supremosaic for a given master image, set of tiles, and color difference function if there is no other tiling for the same input where the sum of the color difference function for every tile is smaller than the sum of the color difference function for this tiling. For an arbitrary color difference function, finding a supremosaic is an NP-complete function. For certain color difference functions, finding a supremosaic is in P. As an degenerate example, if the color difference function always evaluates to 0, then any tiling is a supremosaic since the sum of the color differences is always 0 no matter what tiling we choose.
To solve an NP-complete problem in a reasonable amount of time, we need to settle for an approximation of the best solution. That is, our program will produce a good primosaic for most inputs, but cannot guarantee the best result for any inputs.
One kind of approximation method is know as a greedy algorithm. A greedy algorithm follows the “eat dessert first, since you never know if you will get to finish your meal” mantra. That is, we make the best local decisions without worrying about the future (or what Mom will say if you eat dessert before finishing your brocolli).
So, to make a primosaic, we pick the best remaining tile for the current region and then move on to the next region. This strategy is not likely to produce a supremosaic, but it is fast and in most cases will produce a good primosaic.
Reading: Before going further, you should have finished reading SICP, Chapter 3-3.3.2 (p. 217-261). (Added 1 March (I forgot to include this on original handout).)
MU Mutations
Before trying the primosaic, some mutation practice is in order.Predict the missing values in this sequence of interactions. You don't need to turn in your answers, but check them with DrScheme and make sure you understand any discrepancies.
> (define MU 42)
> (set! MU MU)
> MU
a. ___________________________
> (set! MU 27)
> (set! MU (+ MU MU))
> MU
b. ___________________________
> (define MU-lst (list MU MU MU))
> (set! MU 42)
> MU-lst
c. ___________________________ tricky think about how the list application is evaluated
> (define MUMU-lst (list MU-lst MU-lst))
> MUMU-lst
d. ___________________________
> (set! MU-lst (list 1 2 3))
> MUMU-lst
e. ___________________________
> (define MUMU-lst (list MU-lst MU-lst))
> (set-car! MU-lst 42)
> MUMU-lst
f. ___________________________
> (set-cdr! MUMU-lst MUMU-lst)
> MUMU-lstg. ___________________________ very tricky - see Question 1
> (define (mlength lst) (if (null? lst) 0 (+ 1 (mlength (cdr lst)))))
> (mlength MUMU-lst)h. ___________________________
Note: you can use the Break button to terminate an evaluation. If DrScheme crashes before you do this, don't worry about evaluating it again, but make sure you understand why DrScheme can't.
Question 1: (2 points)
a. Draw the global environment at the end of the interactions above, showing the values of MU, MU-lst and MUMU-lst.
b. For the evaluation of MUMU-lst labeled g, DrScheme prints #0=((42 2 3) . #0#). Explain why it prints this instead of the normal way of printing the value of MUMU-lst and why the evaluation of (mlength MUMU-lst) does what it does.
Primosaics
Unlike previous problem sets, this problem set is not broken down into small problems. It is up to you to think of a good way to change the prosaic mosaic code from Problem Set 1 into a program that produces a primosaic. The rest of this document provides some hints for doing that, but you are free to think creatively and do this in any way you want.
Question 2: (8 points) Modify the mosaic code to create a program that makes a primosaic. Produce a good primosaic and put it in your public_html directory. For full credit, your program should be elegant and concise. Before reading further, you should think for yourself about how to do this. If your stuck, try following the hints below.
Hints
One strategy for making a primosaic is to keep track of how many times a tile image is used. Since we don't have enough tile images to never reuse one, we will add tile use to the color difference. That is, we will prefer to select a tile that has not been used yet over a tile that has been used once already with a slightly lower color difference score. But, we will prefer to use a tile that has been used once already over a tile that has not been used yet but with a very high color difference score.We can then use a greedy algorithm to pick the best tile for a particular square. Since our algorithm is greedy, we should expect it to make the primosaic look better in the part of the master image we do first, and get gradually worse as we work to the end of the image.
Here is the make-photomosaic procedure from PS1:
(define (make-photomosaic master-image ;;; Filename for the "big" picture tiles-directory ;;; Directory containing the tile images tile-width tile-height ;;; Display width and height of the tiles (doesn't have to match actual size) sample-width sample-height ;;; Sample width and height (each tile covers this size area in master) output-filename ;;; Name of file to generate (.html) color-comparator) ;;; Function for comparing colors (printf "Loading tiles...~n") (let ((tile-names (get-image-names tiles-directory))) (printf "Found ~a tiles.~n" (length tile-names)) (if (null? tile-names) ;;; If there we no images found in the tiles-directory, we quit. (printf "No tiles loaded. Cannot create photomosaic.") (begin (printf "Selecting photomosaic tiles...~n") (produce-tiles-page output-filename (choose-tiles (get-one-image master-image) (merge-lists (list tile-names (calculate-average-colors (load-bitmaps tile-names)))) sample-width sample-height color-comparator) tile-width tile-height) (printf "Done.") ) ) ) )The second parameter to choose-tiles is(merge-lists (list tile-names (calculate-average-colors (load-bitmaps tile-names))))The procedure merge-lists is defined in primosaic.ss. You should be able to figure out what it does yourself.To make a primosaic, we want to add another value to each tile in this list that keeps track of the number of times it has been used. Initially, it should be 0 for all tiles. When a tile is used, its use count should increase by 1. So, instead of having the second parameter to choose-tiles being something like (("DSCN0386.JPG" (113/2 127/2 273/4)) ("DSCN0387.JPG" (467/5 431/5 335/4)) ...) we want it to be (("DSCN0386.JPG" (113/2 127/2 273/4) 0) ("DSCN0387.JPG" (467/5 431/5 335/4) 0) ...) where the 0's represent the number of times this tile has been used so far. (Hint: something you did on Exam 1 might be useful!)
In PS1, we defined tile-name and tile-color to select the name and color from a tile element. You will want to define a procedure tile-count that selects the count for a tile element. We want to change the matching function to prefer less used tiles. The procedure that determines the which tile is better is pick-better-match:
(define (pick-better-match sample tile1 tile2 color-comparator) (if (not tile2) tile1 (if (not tile1) tile2 ;;; color-comparator returns true if the first one is better (if (color-comparator sample (tile-color tile1) (tile-color tile2)) tile1 tile2) ) ) )To make primosaics you'll need to make the color-comparator also take into account the number of times a tile has been used. So, instead of just comparing colors we want a tile-comparator function that compares both the colors and use counts of the tiles. Depending on your color difference function, you need to figure out how to weight the use counts. They should be weighted (that is, multiplied by a constant) so that a more used tile is prefered to a less used tile only when the color difference is large.This depends on keeping track of the number of times each tile is used. To do that you will need to change select-mosaic-tiles:
(define (select-mosaic-tiles samples tiles color-comparator) (map2d (lambda (sample) (tile-name (find-best-match sample tiles color-comparator))) samples))The procedure passed as the first parameter to map2d should find the best match for this region and evaluate to its name as before, but it also need to increment the use count for that tile. Start by defining (and test) a procedure increment-tile-count that takes a tile (that is a list of a tile name, color and use count) as a parameter and adds one to the use count. Be careful to mutate the tile parameter, not produce a new tile. (Make sure you understand Question 1 if you are confused on this.)A good tile comparison function would allow you to easily adjust the frequency of tile reuse and the quality of color matching. For example, you should be able to easily make it so tiles are only reused after every other tile has been used. Then it should be clear from looking at the resulting primosaics that the greedy algorithm is making good matches at the beginning but bad ones at the end, before it starts to reuse tiles.
University of Virginia Department of Computer Science CS 200: Computer Science |
David Evans evans@virginia.edu Using these Materials |