I am currently working on a design of steganography system that detects a certain area in an image using several techniques (K-means, Canny edge detection) using Python and OpenCV library. I am facing a huge problem updating the image pixel values to contain my secret data in the least significant bit.
I started with finding the threshold after several calculations.
thresh = cv.adaptiveThreshold(imgray,100,cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY_INV,11,2)
And to test the area I did the following:
Testim=np.copy(originalImage)
Testim[thresh==0]=[0,255,0]
plt.imshow(Testim)
plt.show()
and it showed this image which indicates the area I want to loop over:
Image showing after the threshold Isolation in green
After that I went through a loop that I will show you a snippet of it, it iterates over the RGB values and changes each least significant bit with a bit from the secret data. I would like to note that the original image shape is (1024,1024,3) and the shape of the image[thresh==0] is (863843, 3) :
for i in range(image[thresh==0].shape[0]):
# convert RGB values to binary format
r, g, b = to_bin(image[thresh==0][i])
# modify the least significant bit only if there is still data to store
if data_index < data_len:
# least significant red pixel bit
image[thresh==0][i][0] =int (r[:-1] + binary_secret_data[data_index], 2)
data_index += 1
if data_index < data_len:
# least significant green pixel bit
image[thresh==0][i][1] = int (g[:-1] + binary_secret_data[data_index], 2)
data_index += 1
if data_index < data_len:
# least significant blue pixel bit
image[thresh==0][i][2] = int (b[:-1] + binary_secret_data[data_index], 2)
data_index += 1
# if data is encoded, just break out of the loop
if data_index >= data_len:
plt.imshow(image)
break
return image,thresh
The problem is that the values of RGB are not changing in and out of the loop at all, I added some print statements, and it keeps showing zero, I also tried to assign 1 explicitly, and it didn't work either.
I want to note that this is part of the encode function only
I would appreciate your help with this
Your issue is that advanced indexing returns copies of arrays, so by doing image[thresh==0][i][j]
all the time you modify freshly created copies which are then discarded.
Let's start with some fake data
import cv2
import numpy as np
np.random.seed(0)
original = np.random.randint(0, 255, (1024, 1024, 3)).astype(np.uint8)
gray = cv2.cvtColor(original, cv2.COLOR_RGB2GRAY)
thresh = cv2.adaptiveThreshold(gray, 100, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)
copy = np.copy(original)
copy[thresh==0] = [0, 255, 0]
And now see what happens
>>> copy[thresh==0][0]
array([ 0, 255, 0], dtype=uint8)
>>> copy[thresh==0][0] = [1, 2, 3]
>>> copy[thresh==0][0]
array([ 0, 255, 0], dtype=uint8)
In order for the results to stick, you should use numpy.where
to get the individual indices where your threshold is 0.
idx = np.where(thresh==0)
for row, col in zip(*idx):
r, g, b = to_bin(copy[row,col])
copy[row,col,0] = modified_red_pixel
# etc
I would personally suggest the following so you avoid python loop and constant checks to see if you still have data to embed.
binary_secret_data = '01010000101010010101010001001100'
binary_secret_data = np.array(list(map(int, binary_secret_data)))
secret_len = len(binary_secret_data)
# The result is a tuple of (array of all row coords, array of all column coords)
idx = np.where(thresh==0)
# Make 3 copies of each pixel coordinate and add 0, 1, 2 repeatedly for the RGB of each pixel
idx = (np.repeat(idx[0], 3), np.repeat(idx[1], 3), np.array([0, 1, 2] * len(idx[0])))
# Only keep as many pixels as data you have to embed
idx = (idx[0][:secret_len], idx[1][:secret_len], idx[2][:secret_len])
# Boom, power of vectorisation
copy[idx] = copy[idx] & 0xfe | binary_secret_data
And now you can see the first 32 pixels have been successfully modified
>>> copy[thresh==0][:15]
array([[ 0, 255, 0],
[ 1, 254, 0],
[ 0, 254, 1],
[ 0, 255, 0],
[ 1, 254, 0],
[ 1, 254, 1],
[ 0, 255, 0],
[ 1, 254, 0],
[ 0, 255, 0],
[ 0, 255, 1],
[ 0, 254, 0],
[ 0, 255, 0],
[ 0, 255, 0],
[ 0, 255, 0],
[ 0, 255, 0]], dtype=uint8)