Assignment Chef icon Assignment Chef
All English tutorials

Programming lesson

Run-Length Encoding in Python: Build an Image Compression Tool (2026 Edition)

Learn run-length encoding (RLE) in Python by building functions to encode, decode, and display pixel data. This step-by-step tutorial covers loops, lists, and string manipulation with real image data examples.

run length encoding python RLE image compression python image encoding tutorial COP3502C homework 3 and 4 encode_rle python decode_rle python pixel art compression lossless compression python python list manipulation hex string to list python RLE string format python loops and lists practice retro game programming data compression algorithms python programming assignment help image data encoding techniques

Introduction: Why RLE Still Matters in 2026

In an era of 4K streaming and AI-generated art, you might wonder why we still teach run-length encoding (RLE). The answer is simple: RLE is the backbone of many retro-style games, pixel art editors, and even some modern compression pipelines. Think about the viral pixel art revival on social media or the low-poly aesthetic in indie games like Stardew Valley or Celeste—they all rely on efficient storage of repeated pixel values. In this tutorial, you'll implement RLE in Python, just like the pros do for game assets and image formats.

Understanding Run-Length Encoding (RLE)

RLE is a lossless compression technique that replaces sequences of identical data values with a single pair: a count and the value. For example, the flat data [0, 0, 2, 2, 2] becomes [2, 0, 3, 2] in RLE: two zeros followed by three twos. This is especially effective for images with large uniform areas, like the black background of a game sprite or the green grass in a pixel art gator.

Real-World Analogy: Grouping Homework Assignments

Imagine you're organizing a study group. Instead of listing each member's name individually ("Alice, Bob, Bob, Charlie, Charlie, Charlie"), you can say "1 Alice, 2 Bobs, 3 Charlies." That's RLE in a nutshell—saving space by grouping repeats.

Setting Up Your Python Environment

You don't need any external libraries. We'll work with standard Python lists and strings. Make sure you have Python 3.10+ installed. For color display in the console, you may use the provided ConsoleGfx module (simulated here).

Implementing Core RLE Functions

We'll build eight functions that form the backbone of your RLE toolkit. Each function has a clear purpose and can be tested independently.

1. to_hex_string(data) – Convert Data to Hex

This function translates a list of integers (0–15) into a hexadecimal string without delimiters. For example, [3, 15, 6, 4] becomes "3f64". Use Python's format(value, 'x') to convert each number to hex.

def to_hex_string(data):
    return ''.join(format(v, 'x') for v in data)

2. count_runs(flat_data) – Count Runs in Flat Data

Count how many runs (consecutive identical values) exist. For [15,15,15,4,4,4,4,4,4], there are 2 runs: one of 15s and one of 4s. Loop through the list and increment whenever 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 runs

3. encode_rle(flat_data) – Encode Flat Data to RLE

This is the heart of compression. Iterate through flat data, count consecutive identical values, and append [count, value] to the result list. Example: [15,15,15,4,4,4,4,4,4][3,15,6,4].

def encode_rle(flat_data):
    if not flat_data:
        return []
    encoded = []
    count = 1
    for i in range(1, len(flat_data)):
        if flat_data[i] == flat_data[i-1]:
            count += 1
        else:
            encoded.append(count)
            encoded.append(flat_data[i-1])
            count = 1
    encoded.append(count)
    encoded.append(flat_data[-1])
    return encoded

4. get_decoded_length(rle_data) – Get Decompressed Size

Given RLE data (pairs of count, value), return the total number of pixels after decompression. Sum all counts at even indices. Example: [3,15,6,4] → 3+6 = 9.

def get_decoded_length(rle_data):
    return sum(rle_data[i] for i in range(0, len(rle_data), 2))

5. decode_rle(rle_data) – Decode RLE to Flat Data

The inverse of encode_rle. Expand each (count, value) pair into a list of repeated values. Example: [3,15,6,4][15,15,15,4,4,4,4,4,4].

def decode_rle(rle_data):
    decoded = []
    for i in range(0, len(rle_data), 2):
        count = rle_data[i]
        value = rle_data[i+1]
        decoded.extend([value] * count)
    return decoded

6. string_to_data(data_string) – Hex String to List

Convert a hexadecimal string like "3f64" into a list of integers. Each character represents a nibble (0–15). Use int(char, 16).

def string_to_data(data_string):
    return [int(ch, 16) for ch in data_string]

7. to_rle_string(rle_data) – RLE Data to Readable String

Format RLE data as a colon-separated string where each run is count:value_hex. Example: [15,15,6,4]"15f:64". Note that count is decimal, value is hex.

def to_rle_string(rle_data):
    parts = []
    for i in range(0, len(rle_data), 2):
        count = rle_data[i]
        value = rle_data[i+1]
        parts.append(f"{count}:{format(value, 'x')}")
    return ':'.join(parts)

8. string_to_rle(rle_string) – Readable String to RLE Data

Parse a string like "15f:64" back into RLE list. Split by colon, then each part: decimal count before colon, hex value after.

def string_to_rle(rle_string):
    rle = []
    for part in rle_string.split(':'):
        count_str, value_hex = part.split(':')
        rle.append(int(count_str))
        rle.append(int(value_hex, 16))
    return rle

Putting It All Together: The Main Menu

Your program should present a menu with options to load data (from file, test image, RLE string, RLE hex, flat hex) and display it (image, RLE string, RLE hex, flat hex). Use a loop that continues until the user chooses to exit. Here's a simplified skeleton:

def main():
    print("Welcome to RLE Image Processor!")
    ConsoleGfx.test_rainbow()
    current_data = None  # flat data
    while True:
        print("\nMenu:")
        print("1. Load file")
        print("2. Load test image")
        print("3. Read RLE string")
        print("4. Read RLE hex string")
        print("5. Read flat hex string")
        print("6. Display image")
        print("7. Display RLE string")
        print("8. Display RLE hex")
        print("9. Display flat hex")
        print("0. Exit")
        choice = input("Select option: ")
        # handle each case

Testing with Real Pixel Art

Let's test with the "chubby smiley" example. The flat hex data is "880bbbbbb0bbbbbbbbbb0bb0bbbbbbbbbbbb0bb0bbbbb00bbbbbbbbbbb0bbbbbb0". Use string_to_data to convert to a list, then encode_rle to get the RLE representation. Your output should match the expected RLE hex: "28106b10ab102b10cb102b105b20bb106b10".

Common Pitfalls and Debugging Tips

  • Off-by-one errors in count_runs: Ensure you start counting from 1 and increment only when the value changes.
  • Hex case sensitivity: Use lowercase hex to match expected outputs.
  • Delimiter confusion: In to_rle_string, the delimiter is colon (:), not space or comma.
  • Data type mismatches: All pixel values are integers 0–15. Keep them as ints.

Going Further: RLE in the Real World

RLE is used in BMP image format, TIFF, and even PDF for certain types of data. In 2026, with the rise of AI-generated pixel art and retro game development, understanding RLE helps you optimize storage for large sprite sheets. You can also extend this project to handle 24-bit color images by encoding each color channel separately.

Conclusion

You've built a complete RLE image compression toolkit in Python. By implementing these eight functions, you've practiced loops, list manipulation, string formatting, and type conversion—all essential skills for any programmer. Now go ahead and compress some pixel art!