Image Compression / Decompression (The OK Text Image Format)

Author: Ben Stephenson (ben.stephenson@ucalgary.ca)

Description:

Widely used image storage formats like PNG and JPEG use a variety of techniques to store image data compactly. While the small file sizes that these formats provide are desirable, this benefit comes at the cost of significant complexity in both the encoders and decoders. This makes them difficult to implement correctly and, at least in some cases, computationally intensive.

Dominic Szablewski created the Quite OK Image format which he first described in a blog post on November 24, 2021. The format that he created uses some relatively straightforward techniques to quickly compress images. While the resulting files are not quite as small as well-compressed PNG files, the lack of complexity and better runtime performance outweighs the file size penalty in some contexts. The Quite OK Image format also has the benefit that it is not encumbered by any patents, and the code for loading and saving images is distributed under a permissive license that allows it to be used in both personal and commercial projects without payment. A second blog post on December 20, 2021, described the finalized format and required only a single page to do so. The format has since been introduced into several popular image manipulation tools, as noted in its Wikipedia article, and implementations are available for Rust, Python, Java, C++ and C# (among other languages).

The Quite OK Image format is a binary file format. While binary files can be read and written with Python programs in much the same way that text files can, there are some additional details that need to be considered that aren't covered in many CS1 courses, including my own. Working with binary files can also be more challenging for students because it is more difficult to examine the contents of the file that is being loaded or created. As result, I created a text file format for storing image data that is a variant of the Quite OK Image format for use in this assignment. I will refer to it as the OK Text Image (OKTI) format. While text file image formats are rare, they do exist. For example, the portable pixel map (PPM), portable gray map (PGB), and portable bit map (PBM) formats have both binary and text file variants which store full color, gray scale, and pure black and white images respectively.

Pixels in the OKTI format are encoded in order from the upper left corner of the image to the lower right corner of the image, moving across each row in the image (from left to right) with the rows encoded from top to bottom. Each pixel can be represented in one of 4 ways:

This document describes two assignments that use the OKTI format. One asks students to create a decoder for the format, and to load and display an OKTI image. The other asks students to load a PNG or PPM file by calling an appropriate library function and then save the image data in the OKTI format. The first variant of the assignment was used with almost 300 students in a CS1 course for non-majors in the winter of 2025 (January to April). The second variant of the assignment was used with another group of non-major CS1 students in a compressed spring semester course in 2025 (May / June). The actual assignment handouts provided to students are available below:

Decoder: Word PDF
Encoder: Word PDF

Metadata

Summary This assignment asks students to create a program which either decodes or encodes image data stored in a text-based format. Compression is achieved using three elementary techniques.
Audience Each variation of this assignment was the final assignment in a programming intensive CS1 course (for non-majors) that takes an objects-late approach. It can be used whenever students are being introduced to file I/O and exceptions, and have previous experience with lists, strings, functions, loops and decision making. While this assignment was used successfully with non-majors, it may work even better with CS majors who likely have greater interest in image compression techniques.
Difficulty This assignment was relatively easy for students to start, but somewhat difficult to complete perfectly. Handling pixels represented directly as RGB values was generally straightforward for the students. Pixels encoded as a difference from the immediately preceding pixel, or as a copy of a prior pixel was more challenging, but completed successfully by most students. Students generally found run- length encoded pixels most challenging because one line in the OKTI file represented several pixels.

Contrived images were provided that used only a subset of the pixel types to help students focus on correctly implementing a particular pixel type, but these images did not consider every possible case. As a result, students sometimes had to perform additional debugging when they moved from working with the contrived images to the real-world images. I felt that this approrpiately balanced guiding students through the successful completion of the assignment and forcing them to consider some edge cases and debug moderately complex code.

Students had the flexibility to tackle three of the four pixel types in any order. This reduced the difficulty of the assignment because students could, if necessary, abandon one part of the assignment and tackle another while waiting for an opportunity to seek help. I gave the students between two and three weeks to complete the assignment during a standard 13-week winter term, and approximately 10 days to complete the assignment during the double speed spring term. My impression was that this worked well in the winter term, but was a little bit rushed in the compressed spring term.

Topics The assignment focuses on lists, strings and file I/O, and to a lesser extent, exceptions, and functions. Students also need to write loops and if statements which I assume they are comfortable with from prior assignments that focused on such.
Strengths Some relatively recent image compression work is brought into the classroom in an accessible manner, and students are introduced to some elementary data compression techniques in a fun way.

Students are able to see that they are making progress because each part of the assignment allows additional images to be processed and immediately displayed or additional results to be output. It is not an assignment where you don't really see much progress until the final piece is put into place allowing everything to work together. I believe that being able to see their progress as each part of the assignment is completed benefits students by providing a more immediate sense of accomplishment.

The assignment structure allows students to approach the three compressed pixel types independently, allowing them to move on to a different pixel type if they become stuck on one.

The assignment demonstrates a practical use of hexadecimal numbers. They are used in the encoding of each of the four pixel types to reduce the number of characters needed compared to using base 10.

Faculty members can inflict their vacation photos on their students by including them among the 'real' images.

Weaknesses The text-based format used for this assignment was created for it. As such, while the programming concepts and compression techniques taught by it are highly valuable, the specifics of the file format don't provide much lasting value to the students.
Dependencies Students need the ability to load and display an image, and access individual pixels within it.

My students created their solutions in Python. However, this assignment could easily be adapted to Java (or very likely any other language used to teach CS1 that has a suitable image library available).

Variants Students could be asked to compare the compressed and uncompressed data. This would help them better appreciate the impact each of the encoding approaches has on the size of the OKTI file.

My students were asked to acquire the input file name from the command line and to perform some minimal exception handling. These requirements could easily be removed so that this assignment could be used in courses that don't cover those topics.

The difficulty of the assignment could be increased by requiring students to handle errors in the data files such as blank or incorrectly formatted lines.

The Quite OK Image format uses hashing to copy a pixel that has appeared at an earlier point in the image. My OK Text Image format uses a queue (bounded to 256 elements) as I felt that hashing was too advanced for students in a first course for non-majors. One could revise the OKTI format to use hashing instead if there was a desire to make the assignment more difficult and/or assess students' ability to implementing hashing.

Teaching Notes When writing the OKTI decoder, some students wanted to write nested for loops that iterated over every possible pixel in the image instead of writing a loop that iterated over the lines in the input file. While this worked well enough for three of the pixel types where one line in the file corresponds to one pixel, it did not work well for runs of pixels because a single line in the file results in several pixels being added to the image. In the future, I'd provide more guidance to students about looping over the lines in the file and updating two variables that keep track on the current location within the image as each pixel is added to it.

Similarly, when writing the encoder, some students wanted to write nested for loops to iterate over every pixel location. While a nested loop approach is suitable, while loops are a better choice than for loops (at least in Python) so that students can treat a sequence of identical pixels as group and advance the loop control variable by an appropriate amount. This is something that I'd point out in the assignment handout (perhaps suggesting that they can start with nested for loops, but that they will likely need to rewrite their for loops as while loops once they start to consider run-length encoded pixels).

Most students found the run-length encoded pixels the most difficult to implement successfully.

Sample Solutions

Sample solutions for the decoding and encoding assignments are available in Python. Please contact the author if you need them.

Data Files and Expected Output

Images that use limited types of pixels: 'Real' Images: Data files for the A+ part of the assignment: