可逆信息隐藏

Python实现可逆信息隐藏

简介

对如下参考文献进行代码实现,用于实现图片的可逆信息隐藏。

[1]赵彦涛, 李志全, and 董宇青. “基于排序和直方图修改的可逆信息隐藏方法.” 光电子.激光 21.1(2010):5.

算法介绍

  1. 将图像分为图像块,并且得出每个图像块的像素点。
  2. 将图像块序列从小到大进行排序,并且按照排序后的中间值灰度作为参考。
  3. 将排序后的其余像素与中间值灰度进行差值序列生成。
  4. 找到插值绝对值直方图的峰值最大点。
  5. 利用直方图修改的方法进行信息隐藏。
  6. 恢复出隐藏的信息和原始图片。
  7. 对该方法的抗攻击性进行验证。

代码实现

导入模块

按照如下要求导入相关的模块:

1
2
3
4
5
6
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import cv2
import random
from PIL.ImageFilter import FIND_EDGES, EDGE_ENHANCE, EDGE_ENHANCE_MORE, SHARPEN

导入图片

导入图片,由于方法限制,仅限于导入正方形图片。

1
2
3
4
5
6
7
8
9
10
11
# 导入图片
def importImg(imgLoc):
img = Image.open(imgLoc)
if img.size[0] == img.size[1]:
img = img.convert('L')
imgLen = img.size[0]
img = np.array(img)
return img, imgLen
else:
print("请输入正方形的图片")
quit()

信息隐藏函数

根据如下代码进行二进制数据的信息隐藏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 对图像进行信息隐藏
def processBlock(blockX, blockY):
global curr_index
left_top_point = (blockX * blockLen, blockY * blockLen)

# 像素排序并且获取中间值
pixelList = []
x0 = left_top_point[0]
y0 = left_top_point[1]
for i in range(blockLen):
for j in range(blockLen):
x = x0 + i
y = y0 + j
value = img[x][y]
pixelList.append((value, x, y))
pixelList.sort(key=getValue)
middle_value = pixelList[pixelInBlock // 2][0] # 获取中间值

# 生成峰值最大点
histogram = [0 for _ in range(260)]
for i in range(pixelInBlock):
diff = abs(int(pixelList[i][0]) - int(middle_value))
histogram[diff] += 1
summit_diff = histogram.index(max(histogram)) # 峰值最大点位置
summit[blockX][blockY] = summit_diff

# 更改像素值
for i in range(pixelInBlock):
if curr_index == len(data):
return
diff = int(pixelList[i][0]) - int(middle_value)
x = pixelList[i][1]
y = pixelList[i][2]
if i < pixelInBlock // 2:
if diff < -summit_diff:
img[x][y] -= 1
elif diff == -summit_diff:
if data[curr_index] == '1':
img[x][y] -= 1
curr_index += 1
elif i > pixelInBlock // 2:
if diff > summit_diff:
img[x][y] += 1
elif diff == summit_diff:
if data[curr_index] == '1':
img[x][y] += 1
curr_index += 1

信息提取函数

根据如下代码进行二进制数据的信息可逆提取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 信息提取
def recoverBlock(blockX, blockY, summitDiff):
leftTopPoint = (blockX * blockLen, blockY * blockLen)
pixelList = []
x0 = leftTopPoint[0]
y0 = leftTopPoint[1]
for i in range(blockLen):
for j in range(blockLen):
x = x0 + i
y = y0 + j
value = imgWithData[x][y]
pixelList.append((value, x, y))
pixelList.sort(key=getValue)
middle_value = pixelList[pixelInBlock // 2][0]
count = 0

# 排序并改变像素
for i in range(pixelInBlock):
diff = int(pixelList[i][0]) - int(middle_value)
x = pixelList[i][1]
y = pixelList[i][2]
if i < pixelInBlock // 2:
if diff == - summitDiff - 1:
pixelList[i] = (pixelList[i][0]+1, x, y)
count += 1
elif i > pixelInBlock // 2:
if diff == summitDiff + 1:
pixelList[i] = (pixelList[i][0]-1, x, y)
count += 1
pixelList.sort(key=getValue)

# 恢复提取数据
for i in range(pixelInBlock):
if len(data_extracted) == len(data):
return
x = pixelList[i][1]
y = pixelList[i][2]
diff = int(imgWithData[x][y]) - int(middle_value)
if i < pixelInBlock // 2:
if diff < - summitDiff - 1:
imgWithData[x][y] += 1
elif diff == - summitDiff - 1:
imgWithData[x][y] += 1
data_extracted.append((x, y, '1'))
elif diff == - summitDiff:
data_extracted.append((x, y, '0'))
elif i > pixelInBlock // 2:
if diff > summitDiff + 1:
imgWithData[x][y] -= 1
elif diff == summitDiff + 1:
imgWithData[x][y] -= 1
data_extracted.append((x, y, '1'))
elif diff == summitDiff:
data_extracted.append((x, y, '0'))

运行结果

二进制数据集为1101101101101110110110110110110110110110110,隐藏的图片为锤科的”OneStep一步“图标。

原始图片

onestep

隐藏后的图片

onestep_with_data

还原后的图片

onestep_recovered

恢复的数据集为:1101101101101110110110110110110110110110110。成功提取出数据,并且PSNR为100,实现可逆信息隐藏。

鲁棒性测试

对于该方法进行鲁棒性测试,主要针对于:

  • 高斯噪声
  • 椒盐噪声
  • 低通滤波
  • 图片旋转
  • 图片JPG压缩
  • 图片锐化

测试结果为:

类型 无处理 高斯噪声 椒盐噪声 低通滤波 锐化 旋转 压缩
PSNR 100 26.122 21.312 27.17 13.71 12.64 59.87
误码率(%) 0 7 41 39 22 49 1
是否抵抗 NULL