Skip to content

HideJPEGinJPEGLSB

stegobox.codec.HideJPEGinJPEGLSB

Bases: BaseCodec

This steganography method is named: 'HideJPEGinJPEGLSB'

  • Created by: Ruinan Ma
  • Created time: 2022/09/30
Description

Hide one JPEG image into another JPEG image using 4-bits LSB method.

Source code in stegobox/codec/hide_jpeginjpeg_lsb.py
class HideJPEGinJPEGLSB(BaseCodec):
    """
    This steganography method is named: 'HideJPEGinJPEGLSB'

    * Created by: Ruinan Ma
    * Created time: 2022/09/30

    Description:
        Hide one JPEG image into another JPEG image using 4-bits LSB method.
    """

    def __init__(self):
        self.BLACK_PIXEL = (0, 0, 0)

    def _int_to_bin(self, rgb):
        """Convert an integer tuple to a binary (string) tuple.

        Args:
            rgb: An integer tuple like (220, 110, 96)

        Returns:
            A string tuple: like ("00101010", "11101011", "00010110")
        """
        r, g, b = rgb
        return f"{r:08b}", f"{g:08b}", f"{b:08b}"

    def _bin_to_int(self, rgb):
        """Convert a binary (string) tuple to an integer tuple.

        Args:
            rgb: A string tuple like ("00101010", "11101011", "00010110")

        Returns:
            An int tuple: like (220, 110, 96)
        """
        r, g, b = rgb
        return int(r, 2), int(g, 2), int(b, 2)

    def _merge_rgb(self, rgb1, rgb2):
        """Merge two RGB tuples.

        Args:
            rgb1: An integer tuple like (220, 110, 96)
            rgb2: An integer tuple like (240, 95, 105)

        Returns:
            An integer tuple: An integer tuple with the two RGB values merged.
        """
        r1, g1, b1 = self._int_to_bin(rgb=rgb1)
        r2, g2, b2 = self._int_to_bin(rgb=rgb2)
        rgb = r1[:4] + r2[:4], g1[:4] + g2[:4], b1[:4] + b2[:4]
        return self._bin_to_int(rgb)

    def _unmerge_rgb(self, rgb):
        """Unmerge RGB.

        Args:
            rgb: An integer tuple like (220, 110, 96)

        Returns:
            An integer tuple: An integer tuple with the two RGB values merged.
        """
        r, g, b = self._int_to_bin(rgb)
        # Extract the last 4 bits (corresponding to the hidden image)
        # Concatenate 4 zero bits because we are working with 8 bit
        new_rgb = r[4:] + "0000", g[4:] + "0000", b[4:] + "0000"
        return self._bin_to_int(new_rgb)

    def encode(self, carrier: Image.Image, payload: Image.Image) -> Image.Image:
        """Merge secret_img into host_img.

        Args:
            carrier: host image
            payload: secret image

        Raises:
            ValueError: carrier.size() < payload.size()

        Returns:
            Image: container image.
        """
        # Check the images dimensions. carrier.size() has to be '>=' payload.size()
        if payload.size[0] > carrier.size[0] or payload.size[1] > carrier.size[1]:
            raise ValueError("Sorry, secret image should be smaller than host image.")

        # Get the pixel map of the two images
        map1 = carrier.load()  # type: ignore
        map2 = payload.load()  # type: ignore

        # Create a new image that will be outputted
        new_image = Image.new(carrier.mode, carrier.size)
        new_map = new_image.load()  # type: ignore

        for i in range(carrier.size[0]):
            for j in range(carrier.size[1]):
                is_valid = i < payload.size[0] and j < payload.size[1]
                rgb1 = map1[i, j]  # type: ignore
                rgb2 = map2[i, j] if is_valid else self.BLACK_PIXEL  # type: ignore
                new_map[i, j] = self._merge_rgb(rgb1, rgb2)  # type: ignore

        return new_image

    def decode(self, carrier: Image.Image) -> Image.Image:
        """Unmerge an image.

        Args:
           carrier: Input container image.

        Returns:
            The unmerged/extracted secret image.
        """
        pixel_map = carrier.load()  # type: ignore

        # Create the new image and load the pixel map
        new_image = Image.new(carrier.mode, carrier.size)
        new_map = new_image.load()  # type: ignore

        for i in range(carrier.size[0]):
            for j in range(carrier.size[1]):
                new_map[i, j] = self._unmerge_rgb(pixel_map[i, j])  # type: ignore

        return new_image

encode(carrier, payload)

Merge secret_img into host_img.

Parameters:

Name Type Description Default
carrier Image

host image

required
payload Image

secret image

required

Raises:

Type Description
ValueError

carrier.size() < payload.size()

Returns:

Name Type Description
Image Image

container image.

Source code in stegobox/codec/hide_jpeginjpeg_lsb.py
def encode(self, carrier: Image.Image, payload: Image.Image) -> Image.Image:
    """Merge secret_img into host_img.

    Args:
        carrier: host image
        payload: secret image

    Raises:
        ValueError: carrier.size() < payload.size()

    Returns:
        Image: container image.
    """
    # Check the images dimensions. carrier.size() has to be '>=' payload.size()
    if payload.size[0] > carrier.size[0] or payload.size[1] > carrier.size[1]:
        raise ValueError("Sorry, secret image should be smaller than host image.")

    # Get the pixel map of the two images
    map1 = carrier.load()  # type: ignore
    map2 = payload.load()  # type: ignore

    # Create a new image that will be outputted
    new_image = Image.new(carrier.mode, carrier.size)
    new_map = new_image.load()  # type: ignore

    for i in range(carrier.size[0]):
        for j in range(carrier.size[1]):
            is_valid = i < payload.size[0] and j < payload.size[1]
            rgb1 = map1[i, j]  # type: ignore
            rgb2 = map2[i, j] if is_valid else self.BLACK_PIXEL  # type: ignore
            new_map[i, j] = self._merge_rgb(rgb1, rgb2)  # type: ignore

    return new_image

decode(carrier)

Unmerge an image.

Parameters:

Name Type Description Default
carrier Image

Input container image.

required

Returns:

Type Description
Image

The unmerged/extracted secret image.

Source code in stegobox/codec/hide_jpeginjpeg_lsb.py
def decode(self, carrier: Image.Image) -> Image.Image:
    """Unmerge an image.

    Args:
       carrier: Input container image.

    Returns:
        The unmerged/extracted secret image.
    """
    pixel_map = carrier.load()  # type: ignore

    # Create the new image and load the pixel map
    new_image = Image.new(carrier.mode, carrier.size)
    new_map = new_image.load()  # type: ignore

    for i in range(carrier.size[0]):
        for j in range(carrier.size[1]):
            new_map[i, j] = self._unmerge_rgb(pixel_map[i, j])  # type: ignore

    return new_image