Steganography


In [1]:
import numpy as np
import imageio
import matplotlib.pyplot as plt

Load images


In [2]:
original = np.asarray(imageio.imread("rogerinho.png", as_gray=False, pilmode="RGB"))
secret = np.asarray(imageio.imread("renan.png", as_gray=False, pilmode="RGB"))

Let's take a look at our images.

Left: original

Right: image to be hidden


In [3]:
plt.axis('off')
plt.subplot(1,2,1)
plt.imshow(original)
plt.axis('off')
plt.subplot(1,2,2)
plt.imshow(secret)
plt.axis('off')
plt.show()


Initialize output image


In [4]:
merged = np.copy(original)

Let's merge the images


In [5]:
# merge images
for i in range(original.shape[0]):
    for j in range(original.shape[1]):
        # get binary representation of pixel (original)
        # we're only interested in RGB
        r, g, b = original[i][j][0:3]
        rgb1 = ('{0:08b}'.format(r), '{0:08b}'.format(g), '{0:08b}'.format(b))
        # get binary representation of pixel (secret)
        r, g, b = secret[i][j][0:3]
        rgb2 = ('{0:08b}'.format(r), '{0:08b}'.format(g), '{0:08b}'.format(b))
        # now, we merge them
        # rg1 and rgb2 are string tuples, let's break them
        r1, g1, b1 = rgb1
        r2, g2, b2 = rgb2
        # now we can add the least significant bit of each one
        rgb = (r1[:4] + r2[:4], g1[:4] + g2[:4], b1[:4] + b2[:4])
        # we now have the new pixel for the output image (binary)
        # let's convert it back to int tuple (R,G,B)
        r, g, b = rgb
        merged[i][j] = [int(r, 2), int(g, 2), int(b, 2)]

Let's see if that worked


In [7]:
plt.axis('off')
plt.subplot(1,3,1)
plt.imshow(original)
plt.axis('off')
plt.subplot(1,3,2)
plt.imshow(secret)
plt.axis('off')
plt.subplot(1,3,3)
plt.imshow(merged)
plt.axis('off')
plt.show()


It's clear we lost some quality, but didn't affect image comprehension. You could sense something is off tho...

Let's try to unmerge the images


In [10]:
# unmerge images
unmerged = np.copy(merged)

for i in range(merged.shape[0]):
    for j in range(merged.shape[1]):
        # get RGB
        r, g, b = merged[i][j][0:3]
        r, g, b = ('{0:08b}'.format(r), '{0:08b}'.format(g), '{0:08b}'.format(b))
        
        # extract last 4 bits (assuming we know it's 4 bits)
        # concatenate remaining bits as 0
        rgb = (r[4:] + "0000", g[4:] + "0000", b[4:] + "0000")
        
        # convert it back to int
        r, g, b = rgb
        unmerged[i][j] = [int(r, 2), int(g, 2), int(b, 2)]

In [11]:
plt.axis('off')
plt.subplot(1,4,1)
plt.imshow(original)
plt.axis('off')
plt.subplot(1,4,2)
plt.imshow(secret)
plt.axis('off')
plt.subplot(1,4,3)
plt.imshow(merged)
plt.axis('off')
plt.subplot(1,4,4)
plt.imshow(unmerged)
plt.axis('off')
plt.show()