Skip to content

LSBHidePNGinWAV

stegobox.codec.LSBHidePNGinWAV

Bases: BaseCodec

This module implements simple LSB steganography to hide image in WAV audios. with code shamelessly taken from https://daniellerch.me/stego/lab/intro/lsb-en/#lsb-steganography-in-wav-audio-files

Source code in stegobox/codec/hide_png_in_wav_lsb.py
class LSBHidePNGinWAV(BaseCodec):
    """
    This module implements simple LSB steganography to hide image in WAV audios.
    with code shamelessly taken from
    https://daniellerch.me/stego/lab/intro/lsb-en/#lsb-steganography-in-wav-audio-files
    """

    def __init__(self) -> None:
        super().__init__()

    def encode(
        self, carrier: wave.Wave_read, payload: np.ndarray
    ) -> tuple[bytes, wave._wave_params, tuple[int, int, int]]:
        """Encoder requires the carrier audio to be WAV and the payload to be a image.

        Args:
            carrier: Carrier audio in format WAV. Read with `stegobox.io.audio.read()`.
            payload: Payload (secret image) to be encoded. Read with
                `stegobox.io.image.read_cv2()`.

        Returns:
            bytes: Frame information in bytes of the audio with the payload embedded.
            wave._wave_params: Parameters for audio with the payload embedded.
            tuple[int, int, int]: the size of secret image.
        """
        image_size = payload.shape
        payload = payload.reshape(-1)
        message_bits = np.unpackbits(payload)

        # Read audio frame information
        frames = bytearray(carrier.readframes(carrier.getnframes()))
        if len(frames) < len(message_bits):
            raise Exception("the size of image is larger than audio length")
        # LSB matching
        for i in range(len(message_bits)):
            if frames[i] % 2 != message_bits[i]:
                if frames[i] == 255:
                    s = -1
                elif frames[i] == 0:
                    s = +1
                else:
                    s = random.choice([-1, +1])
                frames[i] = frames[i] + s

        return (
            bytes(frames),
            carrier.getparams(),
            (image_size[0], image_size[1], image_size[2]),
        )

    def decode(self, _):
        raise NotImplementedError("Use decode_with_size() instead.")

    def decode_with_size(
        self, carrier: wave.Wave_read, image_size: tuple[int, int, int]
    ) -> np.ndarray:
        """Decode the secret image from the carrier audio.

        Args:
            carrier: Carrier audio in format WAV. Read with `stegobox.io.audio.read()`.
            image_size: The size of secret image, i.e., `(100, 100, 3)`.

        Returns:
            The decoded payload (secret image).
        """
        # Read audio frame information
        payload_length = image_size[0] * image_size[1] * image_size[2] * 8
        frames = bytearray(carrier.readframes(carrier.getnframes()))

        # Get the least significant bits of each frame of audio
        message_bits = [int(f) % 2 for f in frames][:payload_length]

        payload = np.packbits(message_bits)
        payload = payload.reshape(image_size)
        return payload

encode(carrier, payload)

Encoder requires the carrier audio to be WAV and the payload to be a image.

Parameters:

Name Type Description Default
carrier Wave_read

Carrier audio in format WAV. Read with stegobox.io.audio.read().

required
payload ndarray

Payload (secret image) to be encoded. Read with stegobox.io.image.read_cv2().

required

Returns:

Name Type Description
bytes bytes

Frame information in bytes of the audio with the payload embedded.

_wave_params

wave._wave_params: Parameters for audio with the payload embedded.

tuple[int, int, int]

tuple[int, int, int]: the size of secret image.

Source code in stegobox/codec/hide_png_in_wav_lsb.py
def encode(
    self, carrier: wave.Wave_read, payload: np.ndarray
) -> tuple[bytes, wave._wave_params, tuple[int, int, int]]:
    """Encoder requires the carrier audio to be WAV and the payload to be a image.

    Args:
        carrier: Carrier audio in format WAV. Read with `stegobox.io.audio.read()`.
        payload: Payload (secret image) to be encoded. Read with
            `stegobox.io.image.read_cv2()`.

    Returns:
        bytes: Frame information in bytes of the audio with the payload embedded.
        wave._wave_params: Parameters for audio with the payload embedded.
        tuple[int, int, int]: the size of secret image.
    """
    image_size = payload.shape
    payload = payload.reshape(-1)
    message_bits = np.unpackbits(payload)

    # Read audio frame information
    frames = bytearray(carrier.readframes(carrier.getnframes()))
    if len(frames) < len(message_bits):
        raise Exception("the size of image is larger than audio length")
    # LSB matching
    for i in range(len(message_bits)):
        if frames[i] % 2 != message_bits[i]:
            if frames[i] == 255:
                s = -1
            elif frames[i] == 0:
                s = +1
            else:
                s = random.choice([-1, +1])
            frames[i] = frames[i] + s

    return (
        bytes(frames),
        carrier.getparams(),
        (image_size[0], image_size[1], image_size[2]),
    )

decode_with_size(carrier, image_size)

Decode the secret image from the carrier audio.

Parameters:

Name Type Description Default
carrier Wave_read

Carrier audio in format WAV. Read with stegobox.io.audio.read().

required
image_size tuple[int, int, int]

The size of secret image, i.e., (100, 100, 3).

required

Returns:

Type Description
ndarray

The decoded payload (secret image).

Source code in stegobox/codec/hide_png_in_wav_lsb.py
def decode_with_size(
    self, carrier: wave.Wave_read, image_size: tuple[int, int, int]
) -> np.ndarray:
    """Decode the secret image from the carrier audio.

    Args:
        carrier: Carrier audio in format WAV. Read with `stegobox.io.audio.read()`.
        image_size: The size of secret image, i.e., `(100, 100, 3)`.

    Returns:
        The decoded payload (secret image).
    """
    # Read audio frame information
    payload_length = image_size[0] * image_size[1] * image_size[2] * 8
    frames = bytearray(carrier.readframes(carrier.getnframes()))

    # Get the least significant bits of each frame of audio
    message_bits = [int(f) % 2 for f in frames][:payload_length]

    payload = np.packbits(message_bits)
    payload = payload.reshape(image_size)
    return payload