ASCII Art
Your task is to write a Racket library for converting bitmaps into ASCII art. An example of such a transformation is depicted below. The aim of this homework assignment is to practice applying higher-order functions for manipulating lists.
Bitmap | ASCII art |
---|---|
![]() | ![]() |
If you are particularly ambitious, you can even render videos using your solution. Thanks a lot for this @Jiří Svítil!
Specification
To simplify your task, we have prepared a project template, including a couple of helper functions, as well as unit-tests to help you get started. You can also find a couple of solved test examples here along with an example wrapper application to test your final implementation.
Important:
All your code is required to be in a single file named hw1.rkt
. The file should behave as a module providing two functions img->mat
and ascii-art
. Therefore, your file should start with the following lines:
#lang racket
(require 2htdp/image)
(provide img->mat ascii-art)
The template includes the two required provide
statements to ensure everything works properly on BRUTE, along with additional type-checks to catch errors. You are not required to use the template.
Image to Matrix conversion Function
You must first implement a procedure which takes an image and converts it into a matrix of grayscale values:
(img->mat img)
Although Racket provides libraries that handle matrices, our goal is to practice working with lists. Therefore, we will represent matrices as lists of lists.
To implement img->mat
, you should use the image-width
function, which returns the width of the image in pixels, and the image->color-list
function, which transforms an image into a list of pixel colors. Try to test everything in the REPL directly to get quick feedback. Start with simple images, such as:
(beside (rectangle 2 1 "solid" "red") (rectangle 3 1 "solid" "blue"))
Next, you need to convert colors into grayscale intensities using the following formula:
Finally, use the function list-split
that you implemented during the second lab to finish the procedure.
ASCII Art Rendering Function
Your second task is to implement the higher-order function
(ascii-art width height chars)
which takes a specification consisting of block-width, block-height and a character-set; and then returns a new function which can convert any image into a 2D ASCII-art string. The function is going to be called e.g. as follows:
(define chars " .,:;ox%#@")
((ascii-art 5 8 chars) (bitmap "grad.png"))
where the string chars
is used to represent different intensity levels from an image.
The function returned by ascii-art
should first split the matrix of intensities into blocks of width
and height
specified by the arguments. In case the matrix is not perfectly divisible by the block size, incomplete blocks must be removed from the matrix as illustrated by the red area:
Once the matrix is separated into blocks, intensities in each block have to be averaged.
The resulting scaled matrix should then be mapped to characters. Each average intensity is converted to a character from the chars
string based on the index formula: For a given pixel intensity i
, calculate the index k
in chars
using:
where d
is the length of the chars
string. Finally, the resulting matrix of characters is transformed into a string composed of the characters in each row, followed by the newline character "\n"
.
Testing
The project template already includes a few tests to get you started. Run the tests with:
raco test ./hw1-test.rkt
Since testing is an important part of every assignment you should extend the test file with additional unit and end-to-end tests to verify the correctness of your solution. Do not rely on BRUTE to do the testing for you! At the very least, take advantage of the Racket REPL and test as you write.
We recommend that you create images directly using functions provided by the 2htdp/image
library, such as: circle
, rectangle
, above
, and beside
. For further details see the library documentation.
End to End Testing
While images can be also be loaded from a file:
(define img (bitmap/path "grad.png"))
it is important to start with simple tests first. Once you are confident that your solution works, you may also try the included wrapper application example-app.rkt
to play around with your solution:
# block-width block-height bitmap-path
racket ./example-app.rkt 8 16 ../gradient.png