First, I read the cover image as greyscale image.
coverImage = cv2.imread(coverImagePath, 0)
Then I pass the cover image to another function to create another smaller image and that smaller image is my secret image.
def makeSecretImage(coverImage):
copyImage = coverImage.copy()
secretImage = cv2.resize(copyImage, (100,100))
return secretImage
After that I pass the two images to my embedding function to embed the secret image into the cover image. My intention is to embed every bit of every pixel of the secret image to the LSB of the cover image and the LSB of the cover image is selected randomly. The final output of this function is a stego image which will looks exactly same with the cover image as it only change the LSB of the pixel.
def embedding(coverImage,secretImage, password):
indices = permute_indices(coverImage.shape, password)
for i in range (secretImage.shape[0]):
for j in range(secretImage.shape[1]):
x,y = next(indices)
#convert pixel to binary
pixels_cover = format(coverImage[x][y], '08b')
pixels_hide = format(secretImage[i][j], '08b')
binary = list(pixels_hide)
#error : keep overwriting the last bit
for dataIndex in range(0,8):
stegoImage = pixels_cover[:7] + binary[dataIndex]
dataIndex += 1
coverImage[x][y] = int(stegoImage, 2)
cv2.imwrite('StegoImage.png', coverImage)
Then the stego image is read and pass to the extracting function. Here I'm getting the last bit of the selected pixel and form back to pixel. Then those pixel values placed back to the black image. But I'm getting error here as it read extra pixels from the stego image.
def extracting(stgimg,secretImage, password):
Swidth, Sheight = stgimg.shape
Ewidth, Eheight = secretImage.shape
newPixel = []
pixelList = []
# create 2 blank images
OriImg = np.zeros((Swidth, Sheight, 1), np.uint8)
ExtractedImg = np.zeros((Ewidth, Eheight, 1), np.uint8)
indices = permute_indices(stgimg.shape, password)
for x in range(Swidth):
for y in range(Sheight):
a, b = next(indices)
stegopixel = format(stgimg[a][b], '08b')
bitValue = stegopixel[-1]
newPixel.append(bitValue)
for i in range(0, len(newPixel),8):
pixelBit = ''.join(newPixel[i:i+8])
pixelByte = int(pixelBit, 2)
pixelList.append(pixelByte)
img = np.array(pixelList, dtype=np.uint8).reshape(secretImage.shape)
cv2.imwrite('ExtractedImage.png', ExtractedImg)
First of all, you have an error in embedding
for dataIndex in range(0,8):
stegoImage = pixels_cover[:7] + binary[dataIndex]
dataIndex += 1
You hide 8 bits in the LSB of the same pixel, effectively overwriting them and keeping only the last one. You're supposed to hide each bit in a different pixel and so you need at least 8 times more pixels in your cover image than what your secret has. With that in mind
def embedding(coverImage, secretImage, password):
stegoImage = coverImage.copy()
indices = permute_indices(coverImage.shape, password)
for pixel in secretImage.flatten():
# no need for string "bit manipulation", straight up bitwise operations
for shift in range(7, -1, -1):
x, y = next(indices)
bit = (pixel >> shift) & 0x01
stegoImage[x,y] = (coverImage[x,y] & 0xfe) | bit
return stegoImage
And then
def extracting(stegoImage, password):
indices = permute_indices(stegoImage.shape, password)
pixels = []
for _ in range(secretImage.size):
pixel = 0
for _ in range(8):
x, y = next(indices)
bit = stegoImage[x,y] & 0x01
pixel = (pixel << 1) | bit
pixels.append(pixel)
pixels = np.array(pixels, dtype=np.uint8).reshape(secretImage.shape)
return pixels
Edit: I've modified the above functions to return the image arrays instead of saving them, so I can directly test it with the original input. This also assumes that permute_indices
is still the same as defined here.
password = 'password'
coverImage = cv2.imread('original.png', 0)
secretImage = makeSecretImage(coverImage)
stegoImage = embedding(coverImage, secretImage, password)
extractedImage = extracting(stegoImage, password)
diff = coverImage.astype(int) - stegoImage
print(diff.min(), diff.max()) # Differences should be in the [-1, 1] range
print(np.all(secretImage == extractedImage)) # True