Project: send a picture

Capture a camera frame, make it 1-bit, compress it with RLE and send it by radio.

F8.10Data representationGCSE25 min

Do this lesson in the simulator

A team of robots is exploring. One robot sees something interesting and wants to show the others, but the radio carries at most 200 characters in a message, and a picture is hundreds of pixels. In this project BugBot captures what it sees, turns it into a black and white picture, compresses it with run length encoding until it fits, and sends it. Every idea in F8 is in here: pixels, colour depth, file size, and compression.

The brief

The robot captures a 32 × 24 camera image and converts it to 1-bit black and white: # for a pixel whose average of red, green and blue is below 128, . otherwise. It compresses each row with RLE, joins the rows with /, and checks the whole message fits in 200 characters. If it fits, it sends it with send(); if not, it prints too big and sends nothing. It reports the uncompressed and compressed sizes, and proves the compression is lossless by decoding the message back into the picture.

Step 1: capture and convert

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

img = camera_image(32, 24)
picture = []
for row in img:
    line = ""
    for r, g, b in row:
        line = line + ("#" if (r + g + b) / 3 < 128 else ".")
    picture.append(line)

for line in picture:
    print(line)
print("uncompressed:", 32 * 24, "bits")

Run this in the simulator

The picture is now a list of 24 strings, one per row: a 1-bit image, stored as characters.

Step 2: compress it

Use rle_encode from lesson F8.9 on every row, and join them with / so the receiver knows where each row ends. The / is the metadata the receiver needs to rebuild the rows.

message = "/".join(rle_encode(line) for line in picture)

"/".join(...) puts a / between the encoded rows. Print the message and its len. A plain view of the mat compresses very well, because most rows are one long run.

Step 3: decode and check

The receiver splits the message at / and decodes each part. If the decoded picture is exactly the original, the compression was lossless:

rows = [rle_decode(part) for part in message.split("/")]
print("lossless:", rows == picture)

Step 4: send it

if len(message) <= 200:
    send(message)
else:
    print("too big")

Try turning the robot to face something busier before capturing, such as both balls and the wall. The message grows, and past 200 characters it will not fit: the more detail in a picture, the less RLE can squeeze it.

Task: send a picture

Build the program from the brief. Print the 24 picture lines, then uncompressed: 768 bits (calculated), compressed: <n> characters, and lossless: True. Send the message if it has 200 characters or fewer.

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

img = camera_image(32, 24)

Challenges

  1. Compress the whole picture as one string instead of row by row. Which is shorter, and why?
  2. Send a 64 × 48 picture by splitting its message into several messages of 200 characters each, numbered so they can be put back in order.
  3. Invent a way to make the message shorter still, for example by writing a row that repeats the one above it as =.