Assignment 4: Don’t be afraid to ASCII
Due Friday, February 18, before midnight
The goals for this assignment are:
-
Work with more complex files
-
Deepen our understanding of data types
-
Work with malloc/free
-
Work with 2D arrays
All programs must run without memory errors and leaks!
Background: PPM
PPM (Portable Pix Map) is an image file format that stores the colors of an image as a 2D array of colors. Each color is represented as a RGB triplet, representing red, green and blue respectively. The properties of the image, such as its size and color format, are specified at the start of the file (called its "header information"). PPM supports both ASCII (plain text) and binary data (raw).
For example, consider the following image
In ASCII format, the above image is stored as follows. To see for yourself, do cat feep-ascii.ppm
.
P3
# This is a comment
4 4
255
0 0 0 100 0 0 0 0 0 255 0 255
0 0 0 0 255 175 0 0 0 0 0 0
0 0 0 0 0 0 0 15 175 0 0 0
255 0 255 0 0 0 0 0 0 255 255 255
Regardless of format, every PPM file starts the following information in its header.
-
A "magic number" indicating the type of PPM. ASCII types start with "P3". Binary types start with "P6".
-
Whitespace (blanks, tabs, \n, \r, etc)
-
Width and height as ASCII decimal integers (separated by whitespace)
-
Maximum color value as an ASCII decimal integer. You can assume the Maxval is less than 256, meaning each RGB value is 1 byte.
-
A single whitespace character
-
A 2D raster (e.g. 2D array) of Height number of rows and Width number of columns, in order from top to bottom.
PPM images may contain comments in their header. These must be on their own line and start with the symbol #
.
The header information is always in plain text. It is only the pixel data that differs between ASCII and raw formats. |
PPM files can be viewed using tools such as Photoshop and Gimp. |
1. Read PPM
For this question, you will implement a function, read_ppm()
, that can read
PPM files stored in ASCII format. This function should take a filename as input
and return a 2D array of struct pixel
. A struct pixel
has the following definition
struct ppm_pixel {
unsigned char red;
unsigned char green;
unsigned char blue;
};
The user of the function read_ppm
is reponsible for freeing the memory allocated by this function.
You will re-use this function for the next question. For this reason, we place its implementation
in it’s own file, read_ppm.c
, and use a header file, read_ppm.h
, to include it in our main application.
You may implement your 2D array of pixels as either a flat array or an array or
arrays. For example, if you return a flat array, your function should be defined
as struct ppm_pixel* read_ppm(const char* filename, int* width, int* height)
.
If you return an array or arrays, your function should be defined as struct
ppm_pixel** read_ppm(const char* filename, int* width, int* height)
. In both cases,
use the parameters width
and height
to return the width and height of the image.
In the file, test_ppm.c
, write a short test that calls your function and prints the
contents of feep-ascii.ppm
like so:
$ make test_ppm
gcc -g -Wall -Wvla -Werror test_ppm.c read_ppm.c -o test_ppm
$ ./test_ppm
Testing file feep-ascii.ppm: 4 4
(0,0,0) (100,0,0) (0,0,0) (255,0,255)
(0,0,0) (0,255,175) (0,0,0) (0,0,0)
(0,0,0) (0,0,0) (0,15,175) (0,0,0)
(255,0,255) (0,0,0) (0,0,0) (255,255,255)
Requirements/Hints:
-
Your function should return NULL if the filename is invalid
-
Your function should return NULL if memory cannot be allocated for the image data
-
Your function should return a pointer to the array you create in read_ppm
-
You can assume that it is safe to read the header line by line (e.g. using
fgets
). -
You can use
fscanf(fp, " %hhu %hhu %hhu", &r, &g, &b)
to read unsigned char color values -
Don’t forget to free your data!
2. ASCII Art
When I was a kid, my mom used to bring home dot-matrix printouts of ascii art for my sister and I to use as coloring books.
For this question, write a program, ascii_art.c
, that takes a PPM image as a
command line argument and displays it as
ASCII Art.
$ make ascii_image
gcc -g -Wall -Wvla -Werror ascii_image.c read_ppm.c -o ascii_image
$ ./ascii_image feep-ascii.ppm
Reading feep-ascii.ppm with width 4 and height 4
@#@:
@;@@
@@%@
:@@
$ ./ascii_image smile-ascii.ppm
Reading smile-ascii.ppm with width 14 and height 19
@@@@@@@@@@@@@@
@@@@@@@@@@@@@@
@@ @@
@@ @@
@@ @@
@@ @ @ @@
@@ @@
@@ @ @ @@
@@ @@@@@@ @@
@@ @@
@@ @@
@@ @@
@@ @@
@@ @@
@@ @@
@@ @@
@@ @@
@@@@@@@@@@@@@@
@@@@@@@@@@@@@@
Algorithm
Step 1: For each RGB pixel in the image, compute the average intensity as follows
Step 2: Then, assign an ASCII character based on the intensity
Intensity | Symbol |
---|---|
0-25 |
@ |
26-50 |
# |
51-75 |
% |
76-100 |
* |
101-125 |
o |
126-150 |
; |
151-175 |
: |
176-200 |
, |
201-225 |
. |
226-255 |
<space> |
3. Submit your work
Push you work to github to submit your work.
$ cd A01
$ git status
$ git add *.c
$ git status
$ git commit -m "assignment 3 complete"
$ git status
$ git push
$ git status