Assignment 7: ASCII Art (30 Points)

Chris Tralie

Due Friday 4/24/2020



Overview / Logistics

The purpose of this assignment is to familiarize you with 2D arrays in Java with a concrete, visual application. Students will write a program to automatically generate "ASCII Art", which is text that approximates a given image. An example is given above with the UC logo. In this assignment, we will restrict ourselves to characters in the ASCII subset of UTF-8, including lowercase letters, uppercase letters, and some special characters; hence the name "ASCII Art" (Click here to review the last assignment where we discussed ASCII/Unicode).

As an optional part of this assignment, students may create a submission as part of an art contest. Any submission whatsoever will receive 2 extra credit points, and the winner will receive 5 extra cedit points.

Click here to download the skeleton code for this assignment. You will be editing src/ASCIIArt.java.

What to submit:

When you are finished, you should submit ASCIIArt.java to Canvas. Please also submit answers to the following questions: (you can simply number your answers from 1 to 7 as a comment on Canvas)

  1. Do you have a submission for the art contest? If so, do you give your permission to post publicly on the class web site? What name/pseudonym should I use when posting?
  2. Did you work with a buddy on this assignment? If so, who?
  3. Did you use any resources outside of the class textbook and provided links to help you complete the assignment? Please list them here.
  4. Are you using up any grace points to buy lateness days? If so, how many?
  5. Approximately how many hours it took you to finish this assignment (I will not judge you for this at all...I am simply using it to gauge if the assignments are too easy or hard)
  6. Your overall impression of the assignment. Did you love it, hate it, or were you neutral? One word answers are fine, but if you have any suggestions for the future let me know.
  7. Any other concerns that you have. For instance, if you have a bug that you were unable to solve but you made progress, write that here. The more you articulate the problem the more partial credit you will receive (fine to leave this blank)

Background: Grayscale Images And Text

As we discussed in our 2D arrays module, a 2D array is a natural data structure for image data, since an image is a two dimensional object with both an x and y coordinate for each pixel. In this assignment, we will be dealing with grayscale images, in which each pixel goes from black (0) to white (1). We usually treat the outer array index as a row (y coordinate) and the inner array index as a column (x coordinate). For instance, let's run the following code, using the methods provided in the assignment This code will load in the Ursinus image at the top of this page as a grayscale image. The figure below shows an example of how to index three different pixels in this image, as well as their value in the array. As you can see, pixels which are darker have a value closer to 0, and pixels which are lighter have a value closer to 1.

To turn this into text, we observe that certain characters are "darker" than others if we squint our eyes; that is, certain characters require more ink than others to draw in the same area. Our goal, therefore, will be to print out darker characters for darker images in the region, and lighter characters for lighter regions. Let's consider using all of the character below, for example:

In fact, this is the constant string GRAYSCALE_CHARS defined at the top of the provided code. If you look closely, you will see that these characters have been sorted roughly in order from darkest to lightest. Assuming you're using the default font in Netbeans's console, the image below shows these characters blown up, along with the percentage of the canvas taken up by their ink

A constant array called GRAYSCALE_VALUES has also been provided, which is parallel to the array GRAYSCALE_CHARS, and which holds a 0 for the darkest one and a 1 for the lightest one. For instance prints out B 0.08205. This indicates that a B is a darker character, with a grayscale value of only about 0.08.

Background: Rectangular averaging

At this point, we could simply print out a row of text for each row in the grayscale image, and print out the character at the index of GRAYSCALE_VALUES with the closest grayscale value. However, not only would this print out way too many characters, but the characters are only printed out half as wide as they are tall, so this would end up printing out an image that's stretched out by a factor of 2 vertically (the "aspect ratio" would be wrong). To address both of these problems, we can output a single character per block of pixels. In particular, we can average all of the grayscale values in a rectangular block that's twice as tall as it is wide, and print out a single character for this block. To see an example, let's consider taking a block that's 8 pixels tall and 4 pixels wide in different parts of an image of an Ursinus flag.

If we take a block in a bright region, the average grayscale is very high, so it ends up getting a single quote, which has very little ink.

If we take a dark region in the tree, the average is quite low, and it gets an uppercase B, which is one of the characters that uses the most ink.

If we take a block that's one one of the bricks, it's somewhere in between, so the average is about middle gray, which ends up getting a lowercase c, which uses a medium amount of ink

When you do this, you should take all non-overlapping blocks in the image that you can. So, for instance, the above image is 466x466 pixels. This means that if we use a block that's 8 tall and 4 wide, we can fit 59 blocks from top to bottom and 117 blocks from left to right. For instance, the block that started at row 28 and column 32 in the above image is 35 rows down and 8 columns over.

Be careful not to run out of bounds when taking pixels in a block. The rightmost and bottommost blocks won't actually have 8x4 pixels in them, because you'll run out of space.

Code To Write

You should fill in ASCIIArt.java to print out rows of text to create ASCII art following the above procedure. You will have to come up with your own methods and way of organizing the code, but you should do the following steps at some point

  • Specify a width and height of a block
  • For each non-overlapping block, average the grayscale values in that block, find the character with the closest grayscale value according to GRAYSCALE_VALUES, and print that character out. Print line breaks at the end of each row of blocks.

Tips

  • In the Horstmann book, example 6.3.2 is a helpful reference for finding averages, and example 6.3.3 is a helpful example for finding the closest value to another value when you're converting from brightness to a character.
  • You'll have to come up with your own way of organizing the code, but it may be a good idea to make a separate method to compute the average grayscale value in a block, and to call this method within a loop that loops through all blocks. You may also want to make a method that finds the character that's closest to a particular grayscale value.
  • As usual, a part of your grade will be on style. So, among other things, be sure you're naming your variables and methods appropriately.
  • Be careful not to go out of bounds when extracting blocks. Blocks at the edges may be smaller than other blocks since you'll run out of space.
  • When you look at the text printed to the console, you will probably have to make the font size very small to see the full picture. CTRL DOWN makes the console text smaller and CTRL UP makes the text larger

Examples

Below are some examples of ASCII art generated with a working implementation

Ursinus Bear Logo

The following image has been provided in the starter code HW7_ASCIIArt/Examples/ directory

You can load this in with the following code

Using blocks with 4 rows tall and 2 columns wide, we get the following ASCII art image (Click here to view in the browser))

Parker's Pumpkin

The following image has been provided in the starter code HW7_ASCIIArt/Examples/ directory. It was created by Parker Fairchild in CS 476 last semester

You can load this in with the following code

Using blocks with 4 rows tall and 2 columns wide, we get the following ASCII art image (Click here to view in the browser))

Mona Lisa

The following image has been provided in the starter code HW7_ASCIIArt/Examples/ directory.

You can load this in with the following code

Using blocks with 4 rows tall and 2 columns wide, we get the following ASCII art image (Click here to view in the browser. You may have to zoom out to see fully))

R2 D2

The following image has been provided in the starter code HW7_ASCIIArt/Examples/ directory.

You can load this in with the following code

Using blocks with 8 rows tall and 4 columns wide, we get the following ASCII art image (Click here to view in the browser. You may have to zoom out to see fully))

Ursinus Flag

The following image has been provided in the starter code HW7_ASCIIArt/Examples/ directory

You can load this in with the following code

Using blocks with 2 rows tall and 1 column wide, we get the following ASCII art image (Click here to view in the browser. You may have to zoom out to see fully))

Art Contest

Submit one of your own examples! Any submission whatsoever is 2 extra credit points, and the winner will receive 5 extra credit points. Please submit your output as a text file on Canvas with a blurb about what you've done, and please indicate in your README whether you're comfortable with your submission being posted publicly on the class web site, and, if so, what name/pseudonym you would like me to use.