Zhao-Koch的隐写算法实现(提取问题)
Zhao-Koch's steganography algorithm realization (extraction issue)
编辑 24.03.2017:我决定拒绝 JPEG 和 YCBCR 格式。我正在使用 bmp 图像和 RGB,但是问题仍然存在。
我正在尝试实现 Zhao-Koch 的隐写算法,但是提取的消息与被阻止的消息不对应,我似乎无法理解,是什么原因造成的。
代码如下:
实施:
from PIL import Image
from sklearn.feature_extraction import image
import numpy as np
from scipy.fftpack import dct
from scipy.fftpack import idct
pic = Image.open('lama.bmp') # container, 400x400 bmp picture
pic_size = pic.size #picture size
(r, g, b) = pic.split() #splitting the colour channels
u1 = 4 # coordinates for the DCT coefficients to change. [u1][v1] and [u2][v2]
v1 = 5
u2 = 5
v2 = 4
P = 25 # Threshold value to compare the difference of the coefficients with
cvz = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1] # test message
i = 0 #
acb = np.asarray(b, dtype='int64') # colour channel as array. int64 because absolute difference may go out of the [0,255] boundaries.
patches = image.extract_patches_2d(acb, (8, 8)) # dividing array to 8x8 blocks
for patch in patches: # Applying
dct(patch, overwrite_x = True)
while (i < len(cvz)): # going through message bits
patch = patches[i] # take block
K1 = patch[u1][v1] # first coefficient
K2 = patch[u2][v2] # second coefficient
K = abs(K1) - abs(K2) # difference of absolute values
cur_bit = cvz[i] # take one bit of the message
if (cur_bit == 1) & (K >= -P): # Implementation works the following way: if message bit is 0 than K must be more than P. If it's 1, K must be less than -P. If the requirements are not met, the coefficients change.
i = i +1
while (K >= -P): # changing coefficient
K1 = K1 - 1
print(K1)
K2 = K2 + 1
print(K2)
K = abs(K1) - abs(K2)
patch[u1][v1] = K1 # applying new values
patch[u2][v2] = K2 # applying new values
elif (cur_bit == 0) & (K <= P): # changing coefficient
i = i + 1
while (K <= P):
K1 = K1 + 1
print(K1)
K2 = K2 - 1
print(K2)
K = abs(K1) - abs(K2)
patch[u1][v1] = K1 # applying new values
patch[u2][v2] = K2 # applying new values
else: # requirements are met and there is no need to change coefficients
i = i + 1
for patch in patches: # applying IDCT to blocks
idct(patch, overwrite_x = True)
acb2 = image.reconstruct_from_patches_2d(patches, (400,400)) # reconstructing colour channel
acb2 = acb2.astype(np.uint8) # converting
b_n = Image.fromarray(acb2, 'L') # converting colour channel array to image
changed_image = Image.merge('RGB', (r,g,b_n)) # merging channels to create new image
changed_image.save("stego.bmp") # saving image
提取:
from PIL import Image
from sklearn.feature_extraction import image
import numpy as np
from scipy.fftpack import dct
from scipy.fftpack import idct
pic = Image.open('stego.bmp')
(r, g, b) = pic.split()
u1 = 4
v1 = 5
u2 = 5
v2 = 4
length = 13
i = 0
cvz = []
acb = np.asarray(b, dtype='int64')
patches = image.extract_patches_2d(acb, (8, 8))
for patch in patches:
dct(patch,overwrite_x = True)
while (i < length): # extracting message. If absolute of coefficient 1 is more than absolute of coefficient 2 than message bit is 0. Otherwise it's 1
patch = patches[i]
print (patch[u1][v1])
print (patch[u2][v2])
K1 = abs(patch[u1][v1])
K2 = abs(patch[u2][v2])
if (K1 > K2):
cvz.append(0)
i = i + 1
else:
cvz.append(1)
i = i + 1
print(cvz)
但是提取的消息是错误的:
原始消息:
[1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1]
提取的消息:
[1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1]
我猜我在系数变化方面做错了。
有人可以帮我吗?
更新:似乎更改后的 DCT 系数未保存,因为如果我尝试专门查找它们,我无法在更改后的图片中找到它们。
您的代码有几个问题,即 8x8 块重叠,DCT 仅应用于图像的一个维度,系数更改的方式(K1 = K1 - 1
和 K2 = K2 + 1
)不能保证满足阈值条件等。为了解决所有这些问题,我提出了以下实现:
导入必要的模块,设置参数并定义一些有用的函数
import numpy as np
from skimage import io
from skimage.util import view_as_blocks
from scipy.fftpack import dct, idct
u1, v1 = 4, 5
u2, v2 = 5, 4
n = 8
P = 25
def double_to_byte(arr):
return np.uint8(np.round(np.clip(arr, 0, 255), 0))
def increment_abs(x):
return x + 1 if x >= 0 else x - 1
def decrement_abs(x):
if np.abs(x) <= 1:
return 0
else:
return x - 1 if x >= 0 else x + 1
改变DCT系数的函数
def abs_diff_coefs(transform):
return abs(transform[u1, v1]) - abs(transform[u2, v2])
def valid_coefficients(transform, bit, threshold):
difference = abs_diff_coefs(transform)
if (bit == 0) and (difference > threshold):
return True
elif (bit == 1) and (difference < -threshold):
return True
else:
return False
def change_coefficients(transform, bit):
coefs = transform.copy()
if bit == 0:
coefs[u1, v1] = increment_abs(coefs[u1, v1])
coefs[u2, v2] = decrement_abs(coefs[u2, v2])
elif bit == 1:
coefs[u1, v1] = decrement_abs(coefs[u1, v1])
coefs[u2, v2] = increment_abs(coefs[u2, v2])
return coefs
正在将消息插入图像
def embed_bit(block, bit):
patch = block.copy()
coefs = dct(dct(patch, axis=0), axis=1)
while not valid_coefficients(coefs, bit, P) or (bit != retrieve_bit(patch)):
coefs = change_coefficients(coefs, bit)
print coefs[u1, v1], coefs[u2, v2]
patch = double_to_byte(idct(idct(coefs, axis=0), axis=1)/(2*n)**2)
return patch
def embed_message(orig, msg):
changed = orig.copy()
blue = changed[:, :, 2]
blocks = view_as_blocks(blue, block_shape=(n, n))
h = blocks.shape[1]
for index, bit in enumerate(msg):
print 'index=%d, bit=%d' % (index, bit)
i = index // h
j = index % h
block = blocks[i, j]
blue[i*n: (i+1)*n, j*n: (j+1)*n] = embed_bit(block, bit)
changed[:, :, 2] = blue
return changed
提取隐藏消息
def retrieve_bit(block):
transform = dct(dct(block, axis=0), axis=1)
return 0 if abs_diff_coefs(transform) > 0 else 1
def retrieve_message(img, length):
blocks = view_as_blocks(img[:, :, 2], block_shape=(n, n))
h = blocks.shape[1]
return [retrieve_bit(blocks[index//h, index%h]) for index in range(length)]
演示
In [291]: original = io.imread('https://i.stack.imgur.com/TUV0V.png')
In [292]: test_message = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1]
In [293]: changed = embed_message(original, test_message)
In [294]: retrieve_message(changed, len(test_message))
Out[294]: [1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1]
In [295]: io.imshow(np.hstack((original, changed)))
Out[295]: <matplotlib.image.AxesImage at 0x106c7c18>
结果:原始图像(左)和隐藏消息的图像(右)
In [296]: np.random.seed(0)
In [297]: long_message = np.random.randint(0, 2, 300)
In [298]: long_message
Out[298]:
array([0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1,
0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1,
1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0,
0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1,
0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0,
0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0,
0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0,
0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0,
1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1,
0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1,
1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1,
0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0,
1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1,
1])
In [299]: changed2 = embed_message(original, long_message)
In [300]: np.all(long_message == retrieve_message(changed2, len(long_message)))
Out[300]: True
编辑 24.03.2017:我决定拒绝 JPEG 和 YCBCR 格式。我正在使用 bmp 图像和 RGB,但是问题仍然存在。
我正在尝试实现 Zhao-Koch 的隐写算法,但是提取的消息与被阻止的消息不对应,我似乎无法理解,是什么原因造成的。
代码如下:
实施:
from PIL import Image
from sklearn.feature_extraction import image
import numpy as np
from scipy.fftpack import dct
from scipy.fftpack import idct
pic = Image.open('lama.bmp') # container, 400x400 bmp picture
pic_size = pic.size #picture size
(r, g, b) = pic.split() #splitting the colour channels
u1 = 4 # coordinates for the DCT coefficients to change. [u1][v1] and [u2][v2]
v1 = 5
u2 = 5
v2 = 4
P = 25 # Threshold value to compare the difference of the coefficients with
cvz = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1] # test message
i = 0 #
acb = np.asarray(b, dtype='int64') # colour channel as array. int64 because absolute difference may go out of the [0,255] boundaries.
patches = image.extract_patches_2d(acb, (8, 8)) # dividing array to 8x8 blocks
for patch in patches: # Applying
dct(patch, overwrite_x = True)
while (i < len(cvz)): # going through message bits
patch = patches[i] # take block
K1 = patch[u1][v1] # first coefficient
K2 = patch[u2][v2] # second coefficient
K = abs(K1) - abs(K2) # difference of absolute values
cur_bit = cvz[i] # take one bit of the message
if (cur_bit == 1) & (K >= -P): # Implementation works the following way: if message bit is 0 than K must be more than P. If it's 1, K must be less than -P. If the requirements are not met, the coefficients change.
i = i +1
while (K >= -P): # changing coefficient
K1 = K1 - 1
print(K1)
K2 = K2 + 1
print(K2)
K = abs(K1) - abs(K2)
patch[u1][v1] = K1 # applying new values
patch[u2][v2] = K2 # applying new values
elif (cur_bit == 0) & (K <= P): # changing coefficient
i = i + 1
while (K <= P):
K1 = K1 + 1
print(K1)
K2 = K2 - 1
print(K2)
K = abs(K1) - abs(K2)
patch[u1][v1] = K1 # applying new values
patch[u2][v2] = K2 # applying new values
else: # requirements are met and there is no need to change coefficients
i = i + 1
for patch in patches: # applying IDCT to blocks
idct(patch, overwrite_x = True)
acb2 = image.reconstruct_from_patches_2d(patches, (400,400)) # reconstructing colour channel
acb2 = acb2.astype(np.uint8) # converting
b_n = Image.fromarray(acb2, 'L') # converting colour channel array to image
changed_image = Image.merge('RGB', (r,g,b_n)) # merging channels to create new image
changed_image.save("stego.bmp") # saving image
提取:
from PIL import Image
from sklearn.feature_extraction import image
import numpy as np
from scipy.fftpack import dct
from scipy.fftpack import idct
pic = Image.open('stego.bmp')
(r, g, b) = pic.split()
u1 = 4
v1 = 5
u2 = 5
v2 = 4
length = 13
i = 0
cvz = []
acb = np.asarray(b, dtype='int64')
patches = image.extract_patches_2d(acb, (8, 8))
for patch in patches:
dct(patch,overwrite_x = True)
while (i < length): # extracting message. If absolute of coefficient 1 is more than absolute of coefficient 2 than message bit is 0. Otherwise it's 1
patch = patches[i]
print (patch[u1][v1])
print (patch[u2][v2])
K1 = abs(patch[u1][v1])
K2 = abs(patch[u2][v2])
if (K1 > K2):
cvz.append(0)
i = i + 1
else:
cvz.append(1)
i = i + 1
print(cvz)
但是提取的消息是错误的:
原始消息:
[1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1]
提取的消息:
[1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1]
我猜我在系数变化方面做错了。
有人可以帮我吗?
更新:似乎更改后的 DCT 系数未保存,因为如果我尝试专门查找它们,我无法在更改后的图片中找到它们。
您的代码有几个问题,即 8x8 块重叠,DCT 仅应用于图像的一个维度,系数更改的方式(K1 = K1 - 1
和 K2 = K2 + 1
)不能保证满足阈值条件等。为了解决所有这些问题,我提出了以下实现:
导入必要的模块,设置参数并定义一些有用的函数
import numpy as np
from skimage import io
from skimage.util import view_as_blocks
from scipy.fftpack import dct, idct
u1, v1 = 4, 5
u2, v2 = 5, 4
n = 8
P = 25
def double_to_byte(arr):
return np.uint8(np.round(np.clip(arr, 0, 255), 0))
def increment_abs(x):
return x + 1 if x >= 0 else x - 1
def decrement_abs(x):
if np.abs(x) <= 1:
return 0
else:
return x - 1 if x >= 0 else x + 1
改变DCT系数的函数
def abs_diff_coefs(transform):
return abs(transform[u1, v1]) - abs(transform[u2, v2])
def valid_coefficients(transform, bit, threshold):
difference = abs_diff_coefs(transform)
if (bit == 0) and (difference > threshold):
return True
elif (bit == 1) and (difference < -threshold):
return True
else:
return False
def change_coefficients(transform, bit):
coefs = transform.copy()
if bit == 0:
coefs[u1, v1] = increment_abs(coefs[u1, v1])
coefs[u2, v2] = decrement_abs(coefs[u2, v2])
elif bit == 1:
coefs[u1, v1] = decrement_abs(coefs[u1, v1])
coefs[u2, v2] = increment_abs(coefs[u2, v2])
return coefs
正在将消息插入图像
def embed_bit(block, bit):
patch = block.copy()
coefs = dct(dct(patch, axis=0), axis=1)
while not valid_coefficients(coefs, bit, P) or (bit != retrieve_bit(patch)):
coefs = change_coefficients(coefs, bit)
print coefs[u1, v1], coefs[u2, v2]
patch = double_to_byte(idct(idct(coefs, axis=0), axis=1)/(2*n)**2)
return patch
def embed_message(orig, msg):
changed = orig.copy()
blue = changed[:, :, 2]
blocks = view_as_blocks(blue, block_shape=(n, n))
h = blocks.shape[1]
for index, bit in enumerate(msg):
print 'index=%d, bit=%d' % (index, bit)
i = index // h
j = index % h
block = blocks[i, j]
blue[i*n: (i+1)*n, j*n: (j+1)*n] = embed_bit(block, bit)
changed[:, :, 2] = blue
return changed
提取隐藏消息
def retrieve_bit(block):
transform = dct(dct(block, axis=0), axis=1)
return 0 if abs_diff_coefs(transform) > 0 else 1
def retrieve_message(img, length):
blocks = view_as_blocks(img[:, :, 2], block_shape=(n, n))
h = blocks.shape[1]
return [retrieve_bit(blocks[index//h, index%h]) for index in range(length)]
演示
In [291]: original = io.imread('https://i.stack.imgur.com/TUV0V.png')
In [292]: test_message = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1]
In [293]: changed = embed_message(original, test_message)
In [294]: retrieve_message(changed, len(test_message))
Out[294]: [1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1]
In [295]: io.imshow(np.hstack((original, changed)))
Out[295]: <matplotlib.image.AxesImage at 0x106c7c18>
结果:原始图像(左)和隐藏消息的图像(右)
In [296]: np.random.seed(0)
In [297]: long_message = np.random.randint(0, 2, 300)
In [298]: long_message
Out[298]:
array([0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1,
0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1,
1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0,
0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1,
0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0,
0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0,
0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0,
0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0,
1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1,
0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1,
1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1,
0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0,
1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1,
1])
In [299]: changed2 = embed_message(original, long_message)
In [300]: np.all(long_message == retrieve_message(changed2, len(long_message)))
Out[300]: True