Assignment 2: Awesome Image Format

In this assignment you will implement a program to manipulate AIF (Amazing Image Format) files.

Getting started

Create a new directory for this assignment, change to this directory, and fetch the provided code by running

mkdir -m 700 aif
cd aif
1521 fetch aif

You should write your code inside aif-tools.c.

You can fetch the example files used by autotest by running

1521 aif-examples

Background

How computers display images

image.png

The same image at 200x200, 50x50 and 10x10 pixels.

Images, like a computer screen, is arranged into a grid of ‘pixels’. Each pixel is assigned a color value and by combining these pixels you can form an image. The more pixels the more defined the image may look.

When we refer to the width or the height of an image, we are generally referring to the number of pixels in each row and the number of pixels in each column. For example 640x500 means 640 columns and 500 rows of pixels.

Color Depth

As computers have developed they have been able to display images of increasing color depth. Color depth refers to the amount of bits used to represent color where more bits = a bigger range of color values.

The following 500x500 image at 8-bit rgb, 16 color (4-bit palette) and 4 color (2-bit palette) depths

mr2comparison.png

Notice how at lower (250x250) resolutions the difference is more pronounced.

bridge-comparison.png

This is due to dithering where at a larger distance colors can merge together and combine!

Color Formats

In this assignment you only have to worry about two ways of storing color.

8-bit RGB

One of the most common color formats where each pixel is made up of 8-bits of red, green and blue and can be stored as a 24-bit value. Each pixel is 3 bytes. Each pixel will look like RR GG BB in memory.

By mixing red, green and blue we can make 2^24 different colors! For example FF FF FF is white, 00 00 00 is black, FF 00 00 would be a bright red and FF FF 00 would be a bright yellow!

Often web and graphic designers will work with colors in a hex format such as #ff00ff and these correspond to 8-bit RGB.

8-bit Grayscale

Each pixel is an 8-bit value where 0 is black, 255 is white and inbetween are shades of gray. Each pixel is 1 byte.

Storing Pixels

When working with images, pixels are generally stored in rows of color values. Each row will be the at least the width of the image.

AIF (Amazing Image Format) Format

Header

OffsetSizeField
0 bytes4Magic - “AIF\0”
4 bytes2Checksum
6 bytes1Pixel format
7 bytes1Compression
8 bytes4Width
12 bytes4Height
16 bytes4Pixel data offset
All fields are stored in little-endian byte order.

Magic

Magic values are used to identify the format of a file and verify that the header has not been corrupted. We use file extensions too (e.g. png and jpg ), but magic fields allow a file format to be identified even if this extension is missing or changed.

Checksum

The AIF format involves a simple checksum to make sure the image data is valid. A checksum is a value calculated from a larger piece of data (in our case an image) which can be used to check for corrupted data.

We use a simple Fletcher’s checksum to verify our image. Its calculation works as follows:

  1. We have two sums, sum1 and sum2 start at 0
  2. Assume the checksum value in the image header is 0
  3. Read in the first byte starting from the header
  4. Add the byte value to sum1 and take sum1 % 256
  5. Add the new value for sum1 to sum2 and take sum2 % 256
  6. Read in next byte and repeat until EOF

Finally, we place both sum1 and sum2 in a 16-bit integer by left shifting sum2 by 8 and adding the two values together.

Pixel Format

AIF_FMT_RGB8 - Each pixel is a 24-bit RGB value

AIF_FMT_GRAY8 - Each pixel is an 8-bit shade of gray

Compression

AIF_COMPRESSION_NONE - Uncompressed

AIF_COMPRESSION_RLE - RLE (Run length encoding compression, see below)

Width, Height

width refers to the number of pixels (columns) in each row

height refers to the number of rows

Pixel Data Offset

How many bytes from the start of the file the pixel data begins.

Viewing AIF Images

Run 1521 aif-convert to convert an AIF image to a PNG and have a look at what your program did!

$ 1521 aif-convert aif-examples/cat.aif 

You can then open this png in vscode!

Stage 0

Get Information About Given Image File

<aif-examples/cat.aif>:
File-size: 120020 bytes
Checksum: 7c 9c
Pixel format: 8-bit RGB
Compression: none
Width: 200 px
Height: 200 px

Stage 1

Verify the width and height

$ aif-tools info aif-examples/invalid-width.aif
<aif-examples/invalid-width.aif>
File-size: 20 bytes
Checksum: d9 e7
Pixel format: 8-bit RGB
Compression: none
Width: 0 px INVALID
Height: 2 px

Verify the pixel format

$ aif-tools info aif-examples/invalid-format.aif
<aif-examples/invalid-format.aif>:
File-size: 24 bytes
Checksum: 5d 2d
Pixel format: Invalid
Compression: none
Width: 2 px
Height: 2 px

Verify the image checksum

$ aif-tools info aif-examples/invalid-checksum.aif
<aif-examples/invalid-checksum.aif>:
File-size: 320 bytes
Checksum: 67 67 INVALID, calculated 5d f9
Pixel format: 8-bit RGB
Compression: none
Width: 10 px
Height: 10 px

Stage 2

Accessing Pixel Data

Pixel data is stored in rows meaning you access the pixels across. Carefully consider how you read and write pixel data to make accessing different color formats and compressed/uncompressed images easier!

Adjust image brightness

In this subset take the given image and brighten it by a given percentage.

$ aif-tools brighten cat.aif bright-cat.aif 20

$ aif-tools brighten cat.aif dark-cat.aif -15

For any given ‘RGB’ color it can be useful to find the chroma (how deep the color is) and the luminance (how bright the color is). By finding and manipulating this luminance we can brighten or darken the image!

Luckily, this is done for you, in brighten_rgb , however you should read below to see how it works! The color value is passed as a 24-bit integer and a new adjusted color value is returned.

uint32_t old_pixel = ...
uint32_t new_pixel = brighten_rgb(old_pixel, amount);
// write the new pixel to the output image...

For gray images we can just do pixel * (100 + amount) / 100 . However, you must make sure if the color value overflows (goes above 255) that it gets capped to 255.

brighten_rgb

To brighten or darken an RGB color, we must split each pixel into its color components (r, g, b). For a gray image we can ignore this step.

We then get the midpoint of the brightest and darkest color component:

// For an rgb image
r = pixel[0];
g = pixel[1];
b = pixel[2];
lightest = max(r, g, b)
darkest = min(r, g, b)
luminance = (brightest + darkest) / 2

// For a gray image uint8_t pixel; luminance = pixel;

We then get the ‘chroma’ (intensity of the color) by subtracting the brightest and darkest

chroma = brightest - darkest

We can then use luminance and chroma to find the base ‘lightness’ of the color. This ‘lightness’ is a constant value added to each r, g, b component. We can find it as such:

lightness = luminance - chroma / 2

By manipulating the luminance value we increase or decrease this constant ‘lightness’ and can brighten and darken the color!

Stage 3

Convert between colour formats

image.png image.png

Convert the image to grayscale

$ aif-tools convert-color gray8 cat.aif cat-gray8.aif

Converting from RGB to Grayscale

When we want to convert an RGB color value to grayscale, its not at simple as just taking the average of the red green and blue values. The eye perceives green to be brighter than blue and we need to take this into account when converting to gray.

This gives us the following formula to calculate the luma of a given RGB color.

red = pixel[0]
green = pixel[1]
blue = pixel[2]
gray = (red * 299 + green * 587 + blue * 114) / 1000

This will become our new 1-byte pixel value!

Converting from 8-bit grayscale to RGB

In order to convert back, we can just duplicate a given value across red, green and blue.

GG -> GG GG GG

i.e. A value of 128 (0x80) will become 80 80 80 .

red = gray
green = gray
blue = gray
pixel = {red, green, blue}

Stage 4

Run-length encoding decompression

Decompress the provided image file.

aif-tools decompress cat-compressed.aif cat.aif

To compress AIF images we use a simple compression method called run-length encoding. In subset 4 you only need to worry about decompression.

RLE involves simply specifying a number of times to repeat the following sequence of bytes. This looks like the following:

[NNNN] [XX] pixel_value_repeated
[NNNN] [00] [XX] pixel_value00 pixel_value01 ... pixel_valueXX

NNNN - number of bytes in row XX - number of pixels pixel_value_repeated - pixel repeated XX times pixel_value00 ... pixel_valueXX - part of a non-repeating sequence of pixels

For example, with an rgb image we have 3 bytes per pixel. For the following pixel values (as hex numbers):

000000  000000  000000  ff5555  ff5555  fcfcfc  000000  670067

First of all we need to give the number of total bytes in the row. We can leave this as zero now and come back to it once we are done.

You can see that 00 00 00 is repeated 3 times, so this becomes 03 00 00 00 meaning we want to repeat the color 00 00 00 (black) 3 times!

00 00 03 00 00 00

Then we repeat ff 55 55 twice giving 02 ff 55 55.

00 00 03 00 00 00 02 ff 55 55

As you can see the next 3 pixels repeat, so we use zero to indicate no repeating. We have 3 non-repeating pixels giving us 00 03 fc fc fc 00 00 00 67 00 67 .

00 00 03 00 00 00 02 ff 55 55 00  03 fc fc fc  00 00 00  67 00 67

We have now written 0x13 (19) bytes so we write it (as a little endian value) giving us our final row:

13 00 03 00 00 00 02 ff 55 55 00 03 fc fc fc 00 00 00 67 00 67

As you might notice this works great for simple images, such as graphics and screenshots but often actually increases the file-size of most normal photos! More advanced formats have varying ways of dealing with this, such as JPEG which can reduce image detail in order to greatly improve compression.

Stage 5

RLE Compression

Implement RLE compression for AIF images.

$ aif-tools compress cat.aif cat-compressed.aif

Assumptions

  • A single pixel value should be encoded with 00 01 <pixel> , i.e. a single 00 00 FF becomes 00 01 00 00 FF
  • If a sequence of pixels exceeds 255, write 255 <pixel> and start a new sequence
  • Rows will not exceed 65535 bytes.

Update subsets 2 and 3 to support compression

Update subsets 2 and 3 to support compressed images. If the input is compressed the output should also be compressed.

Assessment

Testing

When you think your program is working, you can use autotest to run some simple automated tests:

1521 autotest aif [optionally: any extra .c or .h files]
You can also run autotests for a specific subset. For example, to run all tests from subset 1:
1521 autotest aif stage1 [optionally: any extra .c or .h files]
Some tests are more complex than others. If you are failing more than one test, you are encouraged to focus on solving the first of those failing tests. To do so, you can run a specific test by giving its name to the autotest command:
1521 autotest aif stage1-1 [optionally: any extra .c or .h files]

1521 autotest will not test everything.
Always do your own testing.

Automarking will be run by the lecturer after the submission deadline, using a superset of tests to those autotest runs for you.

Submission

When you are finished working on the assignment, you must submit your work by running give:

give cs1521 ass2_aif aif.c [optionally: any extra .c or .h files]

You must run give before Week 10 Friday 18:00:00 to obtain the marks for this assignment. Note that this is an individual exercise, the work you submit with give must be entirely your own.

You can run give multiple times.
Only your last submission will be marked.

If you are working at home, you may find it more convenient to upload your work via give's web interface.

You cannot obtain marks by emailing your code to tutors or lecturers.

You can check your latest submission on CSE servers with:

1521 classrun check ass2_aif

You can check the files you have submitted here.

Manual marking will be done by your tutor, who will mark for style and readability, as described in the Assessment section below. After your tutor has assessed your work, you can view your results here; The resulting mark will also be available via give's web interface.

Due Date

This assignment is due Week 10 Friday 18:00:00 (2025-11-21 18:00:00).

The UNSW standard late penalty for assessment is 5% per day for 5 days - this is implemented hourly for this assignment.

Your assignment mark will be reduced by 0.2% for each hour (or part thereof) late past the submission deadline.

For example, if an assignment worth 60% was submitted half an hour late, it would be awarded 59.8%, whereas if it was submitted past 10 hours late, it would be awarded 57.8%.

Beware - submissions 5 or more days late will receive zero marks. This again is the UNSW standard assessment policy.

Assessment Scheme

This assignment will contribute 15 marks to your final COMP1521 mark.

80% of the marks for assignment 2 will come from the performance of your code on a large series of tests.

20% of the marks for assignment 2 will come from hand marking. These marks will be awarded on the basis of clarity, commenting, elegance and style. In other words, you will be assessed on how easy it is for a human to read and understand your program.

An indicative assessment scheme for performance follows. The lecturer may vary the assessment scheme after inspecting the assignment submissions, but it is likely to be broadly similar to the following:

100% for performance completely working subsets 1, 2, 3, 4 & 5 - everything works!
90% for performance completely working subsets 1, 2, 3 & 4.
80% for performance completely working subsets 1, 2 & 3
65% for performance completely working subsets 1 & 2.
50% for performance completely working subset 1.
30-40% for performance good progress, but not passing subset 1 autotests.
0% knowingly providing your work to anyone
and it is subsequently submitted (by anyone).
0 FL for
COMP1521
submitting any other person's work;
this includes joint work.
academic
misconduct
submitting another person's work without their consent;
paying another person to do work for you.

An indicative assessment scheme for style follows. The lecturer may vary the assessment scheme after inspecting the assignment submissions, but it is likely to be broadly similar to the following:

100% for style perfect style
90% for style great style, almost all style characteristics perfect.
80% for style good style, one or two style characteristics not well done.
70% for style good style, a few style characteristics not well done.
60% for style ok style, an attempt at most style characteristics.
≤ 50% for style an attempt at style.

An indicative style rubric follows:

  • Formatting (6/20):
    • Whitespace (e.g. 1 + 2 instead of 1+2)
    • Indentation (consistent, tabs or spaces are okay)
    • Line length (below 80 characters unless very exceptional)
    • Line breaks (using vertical whitespace to improve readability)
  • Documentation (8/20):
    • Header comment (with name and zID)
    • Function comments (above each function with a good description)
    • Descriptive variable names (e.g. char *home_directory instead of char *h)
    • Descriptive function names (e.g. get_home_directory instead of get_hd)
    • Sensible commenting throughout the code (don't comment every single line; leave comments when necessary)
  • Elegance (5/20):
    • Does this code avoid redundancy? (e.g. Don't repeat yourself!)
    • Are helper functions used to reduce complexity? (functions should be small and simple where possible)
    • Are constants appropriately created and used? (magic numbers should be avoided)
  • Portability (1/20):
    • Would this code be able to compile and behave as expected on other POSIX-compliant machines? (using standard libraries without platform-specific code)
    • Does this code make any assumptions about the endianness of the machine it is running on?

Note that the following penalties apply to your total mark for plagiarism:

0 for asst2 knowingly providing your work to anyone
and it is subsequently submitted (by anyone).
0 FL for
COMP1521
submitting any other person's work; this includes joint work.
academic
misconduct
submitting another person's work without their consent;
paying another person to do work for you.

Intermediate Versions of Work

You are required to submit intermediate versions of your assignment.

Every time you work on the assignment and make some progress you should copy your work to your CSE account and submit it using the give command above. It is fine if intermediate versions do not compile or otherwise fail submission tests. Only the final submitted version of your assignment will be marked.

Assignment Conditions

  • Joint work is not permitted on this assignment.

    This is an individual assignment. The work you submit must be entirely your own work: submission of work even partly written by any other person is not permitted.

    Do not request help from anyone other than the teaching staff of COMP1521 — for example, in the course forum, or in help sessions.

    Do not post your assignment code to the course forum. The teaching staff can view code you have recently submitted with give, or recently autotested.

    Assignment submissions are routinely examined both automatically and manually for work written by others.

    Rationale: this assignment is designed to develop the individual skills needed to produce an entire working program. Using code written by, or taken from, other people will stop you learning these skills. Other CSE courses focus on skills needed for working in a team.

  • The use of generative tools such as Github Copilot, ChatGPT, Google Gemini is not permitted on this assignment.

    Rationale: this assignment is designed to develop your understanding of basic concepts. Using synthesis tools will stop you learning these fundamental concepts, which will significantly impact your ability to complete future courses.

  • Sharing, publishing, or distributing your assignment work is not permitted.

    Do not provide or show your assignment work to any other person, other than the teaching staff of COMP1521. For example, do not message your work to friends.

    Do not publish your assignment code via the Internet. For example, do not place your assignment in a public GitHub repository.

    Rationale: by publishing or sharing your work, you are facilitating other students using your work. If other students find your assignment work and submit part or all of it as their own work, you may become involved in an academic integrity investigation.

  • Sharing, publishing, or distributing your assignment work after the completion of COMP1521 is not permitted.

    For example, do not place your assignment in a public GitHub repository after this offering of COMP1521 is over.

    Rationale: COMP1521 may reuse assignment themes covering similar concepts and content. If students in future terms find your assignment work and submit part or all of it as their own work, you may become involved in an academic integrity investigation.

Violation of any of the above conditions may result in an academic integrity investigation, with possible penalties up to and including a mark of 0 in COMP1521, and exclusion from future studies at UNSW. For more information, read the UNSW Student Code, or contact the course account.