MY WEBCAM TEST GUIDE

How to Write a Python Image Steganography Project Report

A practical guide to structuring and writing a Python image steganography project report, including local scripts, expected output checks, format limitations, and common fixes.

Before you start

A report on image steganography in Python normally describes a local script. The program opens a carrier image, changes selected pixel values, and writes a new image file. You do not need an online service, a microphone, or a camera capture routine unless the report explicitly includes a capture module. Prepare the carrier image as a separate file, choose where to save the output image, and keep a copy of the Python code and its dependency list.

Check which image file format you are documenting. A lossless format such as PNG is easier to describe for bit-level hiding because the saved pixels are not changed by compression in the way they can be with JPEG. If the report uses JPEG output, state that compression may alter the modification and that results should be verified from the actual saved file.

Have the following details available before writing: the Python version, the image library used, the exact embedding method, the carrier image name, its pixel dimensions, and the output file name. These facts make the procedure reproducible without inventing measurements.

Step-by-step instructions

Start by describing the setup. State the Python interpreter version and the required packages. If the code uses Pillow, note that it provides image opening, pixel access, and saving. If the code uses another library, describe that library with the same level of precision.

Next, explain how the carrier image is loaded. Mention whether the image is opened in RGB mode and whether the script keeps the original aspect ratio and pixel dimensions. If the output must match the input dimensions, say that clearly. If the script resizes images, document the resize operation instead of implying that resolution is always preserved.

Describe the hiding procedure step by step. A common approach is least significant bit substitution: convert each character of the message into bits, then place those bits into the lowest bit of selected pixel channels. Give the order of processing, such as rows before columns or red, green, and blue channels in sequence.

Show the core code in a code block. Include loading, checking capacity, embedding, and saving operations. Keep the sample short enough to be readable and use comments to name each part.

from PIL import Image

carrier = Image.open("carrier.png").convert("RGB")
message = "hidden"
bits = "".join(format(ord(ch), "08b") for ch in message)

def set_lsb(value, bit):
    return (value & 0xFE) | int(bit)

pixels = carrier.load()
width, height = carrier.size
index = 0

for y in range(height):
    for x in range(width):
        if index >= len(bits):
            break
        r, g, b = pixels[x, y]
        if index < len(bits):
            r = set_lsb(r, bits[index])
            index += 1
        if index < len(bits):
            g = set_lsb(g, bits[index])
            index += 1
        if index < len(bits):
            b = set_lsb(b, bits[index])
            index += 1
        pixels[x, y] = (r, g, b)

carrier.save("output.png")
print("Saved output.png")

The loop stops when all message bits have been placed. The capacity check should be described before the loop: enough pixels must exist for the message length plus any required terminator. If the script does not implement a terminator or length header, state that the reader must know the message length in advance.

Explain the extraction part next. The reading function walks the same pixel order and rebuilds every eight collected bits into a character. Emphasize that extraction must use the same order and channel sequence as embedding.

def get_lsb(value):
    return value & 1

def extract_text(path, bit_count):
    image = Image.open(path).convert("RGB")
    pixels = image.load()
    width, height = image.size
    bits = []

    for y in range(height):
        for x in range(width):
            r, g, b = pixels[x, y]
            bits.append(str(get_lsb(r)))
            bits.append(str(get_lsb(g)))
            bits.append(str(get_lsb(b)))
            if len(bits) >= bit_count:
                chars = []
                for i in range(0, bit_count, 8):
                    byte = "".join(bits[i:i + 8])
                    chars.append(chr(int(byte, 2)))
                return "".join(chars)
    return ""

print(extract_text("output.png", len("hidden") * 8))

Finish the method section with the save step. For PNG output, the saved file should be the exact file tested in the report. For JPEG output, add a sentence that compression is not bit-exact and that the report treats successful recovery only as something observed for the specific test image and settings.

Check the output

Open the output image and compare its visible appearance with the carrier. If the carrier and output have the same pixel dimensions and file format, most viewers will show the same composition. Visual similarity alone does not prove that extraction works, so run the extraction function and compare the recovered message with the original.

If the image is displayed at a different aspect ratio or size in the report, distinguish the stored pixel dimensions from the screen preview. A preview may scale the image, but that does not change the canvas dimensions saved in the file.

For a lossless PNG, verify that the output file exists and can be reopened by the script. A simple test message of a few characters is enough to demonstrate the pipeline. If transparency is present, check whether the image mode preserves or discards the alpha channel; naive conversion to RGB can remove transparency.

Common problems and next steps

A frequent failure is message truncation. If the reader stops early or adds extra characters, check that the bit count and character count are consistent. One character requires eight bits, so a five-character message requires forty bits. When no terminator is used, the reader must be called with the correct number of bits.

Capacity errors occur when the message is larger than the available pixel storage. For an RGB image, a simple bit-per-channel calculation uses pixel width multiplied by height multiplied by three bits. However, a report should present this as a calculation rule, not as a tested limit for an unlisted image.

If extraction returns unreadable characters, confirm that embedding and extraction step through the same channels in the same order. A mismatch in row direction, channel order, or image mode can make the recovered bits unusable even when the output image looks correct.

A JPEG output may lose the hidden data. That is not necessarily a defect in the Python code if the bit changes are not preserved through compression. The report should note JPEG compression as a known limitation and recommend PNG for reliable bit-level demonstration.

Keep the report testable. Include the carrier image, the message, the exact command or function calls, and the expected recovered output. If you cannot verify a claim or measurement, describe it as a limitation or as something to confirm rather than presenting it as a measured result.