Programming lesson
Mastering Run-Length Encoding in Python: A Hands-On Tutorial for Image Compression
Learn run-length encoding (RLE) in Python through a practical image compression project. Covers encoding/decoding, hex conversion, and menu-driven programs with real-world examples.
Introduction to Run-Length Encoding (RLE)
Run-length encoding (RLE) is a simple form of lossless data compression that's widely used in image processing, especially for pixel art and simple graphics. In this tutorial, you'll learn how to implement RLE in Python as part of a homework assignment (like COP3502C HW 3 and 4). You'll build functions to encode and decode raw pixel data, convert between data and strings, and create a menu-driven program. By the end, you'll have a solid understanding of loops, lists, string manipulation, and type casting in Python.
RLE works by replacing consecutive identical values (runs) with a count and the value. For example, the flat data [15, 15, 15, 4, 4, 4, 4, 4, 4] becomes [3, 15, 6, 4] in RLE. This technique is used in everything from retro video games to modern AI image compression.
Understanding the Assignment
Your task is to implement several functions that handle RLE encoding and decoding. You'll also create a standalone program with a menu that lets users load data (from files, test images, or user input) and display it in various formats. The project simulates real-world image compression workflows.
Key Functions to Implement
- to_hex_string(data): Converts a list of integers (0-15) to a hexadecimal string without delimiters. Example:
[3, 15, 6, 4]→"3f64". - count_runs(flat_data): Counts the number of runs in flat data. Example:
[15,15,15,4,4,4,4,4,4]→2. - encode_rle(flat_data): Encodes flat data into RLE format. Example:
[15,15,15,4,4,4,4,4,4]→[3,15,6,4]. - get_decoded_length(rle_data): Returns the length of the decoded data from RLE. Example:
[3,15,6,4]→9. - decode_rle(rle_data): Decodes RLE data back to flat data. Example:
[3,15,6,4]→[15,15,15,4,4,4,4,4,4]. - string_to_data(data_string): Converts a hex string to a list of integers. Example:
"3f64"→[3,15,6,4]. - to_rle_string(rle_data): Converts RLE data to a human-readable string with colons. Example:
[15,15,6,4]→"15f:64". - string_to_rle(rle_string): Inverse of the above. Example:
"15f:64"→[15,15,6,4].
Step-by-Step Implementation
1. to_hex_string
This function takes a list of integers (each 0-15) and returns a hexadecimal string. Use Python's hex() or a custom mapping. For example, 15 becomes 'f'.
def to_hex_string(data):
return ''.join(hex(val)[2:] for val in data)2. count_runs
Iterate through the flat data and count how many times the value changes.
def count_runs(flat_data):
if not flat_data:
return 0
runs = 1
for i in range(1, len(flat_data)):
if flat_data[i] != flat_data[i-1]:
runs += 1
return runs3. encode_rle
Build the RLE list by counting consecutive identical values.
def encode_rle(flat_data):
if not flat_data:
return []
rle = []
count = 1
for i in range(1, len(flat_data)):
if flat_data[i] == flat_data[i-1]:
count += 1
else:
rle.append(count)
rle.append(flat_data[i-1])
count = 1
rle.append(count)
rle.append(flat_data[-1])
return rle4. get_decoded_length
Sum the counts (every other element starting from index 0).
def get_decoded_length(rle_data):
return sum(rle_data[i] for i in range(0, len(rle_data), 2))5. decode_rle
Expand each run into the flat list.
def decode_rle(rle_data):
flat = []
for i in range(0, len(rle_data), 2):
count = rle_data[i]
value = rle_data[i+1]
flat.extend([value] * count)
return flat6. string_to_data
Convert each hex character to an integer.
def string_to_data(data_string):
return [int(ch, 16) for ch in data_string]7. to_rle_string
Format each run as count in decimal and value in hex, separated by colons.
def to_rle_string(rle_data):
parts = []
for i in range(0, len(rle_data), 2):
parts.append(f"{rle_data[i]}{hex(rle_data[i+1])[2:]}")
return ':'.join(parts)8. string_to_rle
Parse the colon-separated string back to RLE list.
def string_to_rle(rle_string):
rle = []
for part in rle_string.split(':'):
count = int(part[:-1])
value = int(part[-1], 16)
rle.append(count)
rle.append(value)
return rleBuilding the Menu-Driven Program
Your main() function should display a welcome message, show a color test, then present a menu with options to load data and display results. Use a loop to keep the program running until the user chooses to exit. Here's a skeleton:
def main():
print("Welcome to RLE Image Processor!")
ConsoleGfx.test_rainbow()
image_data = None
while True:
print_menu()
choice = input("Select a Menu Option: ")
if choice == '1':
filename = input("Enter name of file to load: ")
image_data = ConsoleGfx.load_file(filename)
elif choice == '6':
if image_data:
ConsoleGfx.display_image(image_data)
else:
print("No image data loaded.")
# ... other options
elif choice == '0':
breakReal-World Applications and Trends
RLE isn't just for homework—it's used in modern technologies. For instance, AI image generators often use compression techniques like RLE to reduce file sizes when training on pixel art datasets. In gaming, retro-style indie games use RLE to store sprite data efficiently. Even in finance, RLE can be applied to compress time-series data where values repeat. As of 2026, with the rise of edge AI and IoT devices, lightweight compression algorithms like RLE are becoming essential for real-time data transmission.
"RLE is a foundational concept that bridges simple programming exercises and real-world data compression."
Testing Your Implementation
Test each function with the provided examples. For instance:
print(to_hex_string([3, 15, 6, 4])) # Expected: "3f64"
print(count_runs([15,15,15,4,4,4,4,4,4])) # Expected: 2
print(encode_rle([15,15,15,4,4,4,4,4,4])) # Expected: [3,15,6,4]
print(get_decoded_length([3,15,6,4])) # Expected: 9
print(decode_rle([3,15,6,4])) # Expected: [15,15,15,4,4,4,4,4,4]
print(string_to_data("3f64")) # Expected: [3,15,6,4]
print(to_rle_string([15,15,6,4])) # Expected: "15f:64"
print(string_to_rle("15f:64")) # Expected: [15,15,6,4]Common Pitfalls
- Forgetting that RLE data always has an even number of elements (count, value pairs).
- Mixing up decimal and hexadecimal representations: counts are decimal, values are hex.
- Not handling empty input gracefully.
- Off-by-one errors in loops.
Conclusion
By completing this tutorial, you've built a complete RLE image compression toolkit in Python. You've practiced essential programming concepts like loops, lists, string formatting, and modular design. These skills are directly applicable to more advanced topics in data compression, image processing, and even AI. As you move forward, consider exploring how RLE compares to other compression methods like Huffman coding or LZW. Happy coding!