• 主页
  • 相册
  • 随笔
  • 目录
  • 存档
Total 244
Search AboutMe

  • 主页
  • 相册
  • 随笔
  • 目录
  • 存档

实验:Jsteg水印的PSNR~C水印攻击以及卡方分析

2019-11-25

内容:

  • 实现Jsteg水印算法以及psnr~C水印分析

1. 实验原理

  • 频域水印

    • 用秘密信息比特直接替换量化后的DCT系数的最低比特位(DCT系数的LSB嵌入)
  • 实现步骤

    • 对图像进行分块,并转化为DCT系数矩阵

    • 对DCT矩阵运用LSB的变换规则,对相邻DCT系数进行转化$P_{2i}\leftrightarrow P_{2i+1}$。特别的,若量化后DCT系数为0或者1,则不进行处理

  • 0与1不处理的原因

    Jsteg does not embed in coefficients equal to 0 or 1 because too many nonzero coefficients would appear in higher frequencies, which would lead to perceptible and statistically detectable artifacts

    • Quantitative Structural Steganalysis of Jsteg By Jan Kodovský and Jessica Fridrich, Member, IEEE
    • 由于DCT系数中有大量的0和1,若对其进行处理,将会导致更为明显的统计检测效果
  • 算法缺陷

    • Jsteg隐写使得DCT系数$P_{2i}$和$P_{2i+1}$的频率趋向一致。由于这种统计上的特性,很容易被卡方攻击检测出来

    • PSNR~C

      PSNR is most commonly used to measure the quality of reconstruction of lossy compression codecs (e.g., for image compression). The signal in this case is the original data, and the noise is the error introduced by compression. When comparing compression codecs, PSNR is an approximation to human perception of reconstruction quality.

      • Peak signal-to-noise ratio - Wikipedia

      嵌入数量越多,图片相当于图片噪声越大,因此可以通过PSNR~嵌入容量关系作为隐写分析

2. 实验内容

本实验利用python实现Jsteg水印算法以及psnr~C水印分析

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import cv2
import numpy as np
import math
import matplotlib.pyplot as plt
size = 8


def as_num(x):
y = round(x, 10) # .10f 保留10位小数
return y


def psnr(img1, img2):
mse = np.mean((img1/255. - img2/255.) ** 2)
if mse < 1.0e-10:
return 100
PIXEL_MAX = 1
return 20 * math.log10(PIXEL_MAX / math.sqrt(mse))


def curve(P, Q, img):
plt.subplot(121)
plt.title("PSNR~C")
plt.ylabel("PSNR")
plt.xlabel("C")
plt.plot(Q, P)
plt.subplot(122)
plt.title("stego-photo")
plt.imshow(img)
plt.savefig("result.png")
plt.show()


def dct(m):
m = m.astype(np.float32)
return cv2.dct(m)


def water(watermark):
binwtrl = ""
for i in watermark:
cc = ord(i) # 字符转化为整数
bincc = bin(cc)
bincc = bincc[2:]
while len(bincc) < 8:
bincc = "0" + bincc
binwtrl += bincc
return binwtrl


def re_shape(pblock, w):
block = pblock.copy()
for i in block[:]:
for j in i[:]:
j = int(j)
if j != 0 and j != 1:
if w % 2 == 1 and j % 2 == 0:
j += 1
elif w % 2 == 0 and j % 2 == 1:
j -= 1
return block


def embedWater(img, wstr):
binwtrl = water(wstr)
im_array = img[:, :, 0]
w, h = im_array.shape
w_size = w//size
cnth = 0
cntl = 0
for i in binwtrl:
block = np.zeros((size, size), int)
block[:size, :size] = im_array[cnth * size:(cnth + 1) * size, cntl * size:(cntl + 1) *
size]
block_pdct = dct(block)
block_dct = re_shape(block_pdct, int(i))
block_change = cv2.idct(block_dct)
block_idct = block_change.astype(np.uint8)
im_array[cnth * size:(cnth + 1) * size, cntl * size:(cntl + 1) *
size] = block_idct
cntl += 1
if (cntl == w_size):
cnth += 1
cntl = 0
im = img.copy()
im[:, :, 0] = im_array
return im


def attackWater(im1, im2, P, Q, i):
P.append(as_num(psnr(im1, im2)))
Q.append(i)


if __name__ == "__main__":
path = ""
img = cv2.imread(path)
wstr = ""
P = []
Q = []
for i in range(20):
wstr += ""
im = embedWater(img.copy(), wstr)
attackWater(img.copy(), P, Q, len(wstr))
curve(P, Q, im)

简要解释

  • embedWater函数
    • 将图像分为size * size的块block,并将其转化为DCT系数矩阵block_dct,尔后嵌入水印
  • re_shape函数
    • 对block_dct进行DCT的LSB替换
  • wstr变量
    • 初始水印,并通过循环增长,以达到增加嵌入容量的目的
  • 注意
    • 为了省事每次循环都得从头开始嵌入,但你可以 很轻易地 改为更效率的代码

2.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
from scipy import stats
import cv2
import numpy as np


def grayHist(img):

grayDict = {}
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
for key in range(256):
grayDict[key] = 0
for i in range(img_gray.shape[0]):
for j in range(img_gray.shape[1]):
grayDict[img_gray[i][j]] += 1
'''
grayDict=cv2.calcHist([img],[0],None,[256],[0,256])
'''
return grayDict


def as_num(x):
y = '{:.10f}'.format(x) # .10f 保留10位小数
return y


if __name__ == "__main__":
rpath = "result.bmp"
rim = cv2.imread(rpath)
rim_grey = grayHist(rim)
tp = []
for i in range(256):
tp.append(rim_grey[i])
i = 0
exp = []
obs = []
while(i <= 127):
if(((tp[2*i]+tp[2*i+1])//2) == 0):
i += 1
continue
exp.append((tp[2*i]+tp[2*i+1])/2)
obs.append(tp[2*i])
i += 1
x = stats.chisquare(obs, f_exp=exp)
print(as_num(x[1]))

简要说明

  • 卡方分析

    • 比较理论频数和实际频数的吻合程度或拟合优度问题
    • 卡方分布:$r=\sum_i^{127}\frac{h_{2i}-h_{2i}^{}}{h_{2i}^{}}$
      • $h_{2i}^{*}=\frac{h_{2i}+h_{2i+1}}{2}$
    • 卡方分布概率密度函数(隐写率):$1-P(x^2 \leq r)$
  • as_num

    • 轮子,科学计数法转化为浮点型数据
  • stats.chisquare

    Calculate a one-way chi square test.

    The chi square test tests the null hypothesis that the categorical data has the given frequencies.

    • obs: 观测值
    • exp: 理论值
    • p_value:置信度。一般p大于95%,则可认为“观测到的值和预期值是相近的”

3. 实验结果展示

分块大小对结果的影响

  • 控制水印相同:水印初始长度相同、水印增长循环次数相同

  • 控制块为不同的大小

    • $block=8*8$

    • $block=10*10$

  • $8\times8$分块大约下降1dB,$10\times10$分块大约下降9dB。可以看到:随着分块的大小的增加,实验效果更为显著。(实质上是嵌入容量不同)

4. 参考资料

Peak signal-to-noise ratio - Wikipedia

Quantitative Structural Steganalysis of Jsteg - Jan Kodovský and Jessica Fridrich, Member, IEEE

  • Lab
  • Computer Graphics
  • Lab
实验:无线网络嗅探基础
图像水印算法
© 2024 何决云 载入天数...