0%

Reversible Data Hiding in JPEG Images

论文地址

JPEG图像可逆数据隐藏
摘要

​ 在日常生活中使用的各种数字图像格式中,JPEG是最受欢迎的。因此JPEG图像的可逆数据隐藏(RDH)对于许多应用程序(如档案管理和图像认证)非常重要和有用。然而,JPEG图像中的RDH比未压缩图像要困难得多,因为JPEG图像中的信息冗余比未压缩图像要少,压缩域中的任何修改都可能导致宿主图像失真。此外除了未压缩图像必须考虑得嵌入容量和保真度(视觉质量)外,标记JPEG文件的存储大小也应考虑在内。在本文中,基于JPEG编码器背后的原理和离散余弦变换(DCT)系数的统计特性,我们对如何为RDH选择量化的DCT系数提出了一些基本见解。然后提出了一种新的基于直方图移位的方法。其中零系数保持不变,只有值为1和-1的系数被展开以携带消息位。此外提出了一种基于8*8块中零系数个数的块选择策略,可用于自适应地选择DCT系数进行数据隐藏。实验结果表明,该方法易于实现高嵌入容量和良好的视觉质量。主机JPEG文件的存储大小也可以很好保存。

引言

REVERSIBLE又称可逆或无损数据隐藏技术,它可以将数据在不知不觉中隐藏到数字图像中,更重要的是,在提取嵌入数据的同时,可以完全重建原始图像。可逆数据隐藏作为信息隐藏的一种特殊情况,具有广泛的应用前景。当需要真正的保真度时,其可逆性尤其可取,例如用于医疗和军事图像处理。

RDH的想法最早是在Barton申请的专利中提出的。此后,基于以下三种基本策略提出了许多RDH方案:无损压缩,差分扩展(DE)和直方图移动(HS)。

基于无损压缩的方法通过执行无损压缩来利用主机信号的统计冗余,从而可以为保存附加信息创建空闲空间。这一策略最近较少受到关注,因为一般来说,它不能提供大的嵌入容量,并可能导致视觉质量的严重退化。Tian首先提出了一种基于DE的算法,将宿主图像分成像素对,并将像素对中的两个像素的差值扩大为携带一个秘密消息位。从那时起,Tian的工作有了多方面的改进。Alattar将DE推广到三倍或四倍像素,以实现更大的嵌入容量。Thodi和Rodriguez建议利用预测误差进行扩展嵌入,因为它可以更好地利用相邻像素之间的局部相关性。这种算法也被称为预测误差扩展。Ni et al.首先提出了一种基于HS的算法,该算法利用图像直方图的峰值进行数据隐藏。由于每个像素值最多修改一个,因此可以保证标记后图像的视觉质量高于48 dB。基于hs的方法还可以应用于差异直方图和预测误差直方图,由于差异直方图和预测误差直方图可能具有相当高的峰值点,因此嵌入容量更大,视觉质量更好。

最近的一些研究表明,DE和HS策略还可以与排序和像素选择技术相结合,以获得更好的性能。

大多数RDH方法都是针对未压缩图像设计的,近年来在视觉质量和嵌入容量方面取得了非常成功的成就。一般来说,它们不能直接应用于JPEG图像。首先,数据隐藏的一个最重要的条件是宿主图像必须具有相当大的信息冗余。由于是压缩图像格式,JPEG图像的冗余比未压缩图像要少得多。其次,由于去量化过程,离散余弦变换(DCT)域的修改可能比空间域的修改引入更大的失真。第三,对于JPEG图像中的RDH,我们不仅需要考虑视觉质量,还需要考虑嵌入过程中可能增加的文件大小。因此,JPEG图像中的RDH比未压缩图像z xdasxz1 中的RDH要困难得多。

然而,JPEG目前是数码相机和其他摄影图像捕获设备使用的最常见的图像格式。JPEG图像除了在医疗和军事领域的潜在用途外,还广泛应用于我们的日常生活中。它是存储和传输摄影图像最常用的格式。JPEG图像中的RDH可能有许多应用,如档案管理和图像认证。在多媒体档案中,图像提供者可能不希望原始内容被扭曲,即使这种扭曲对大多数用户来说是难以察觉的,而且就存储空间而言,同时存储原始版本和标记版本的成本可能过高。对于图像身份验证,有时图像的所有者不希望对图像进行轻微的更改。在这种情况下,RDH应该是完美的选择,因为它可以定位被篡改的区域以及恢复原始图像内容。

近年来,JPEG图像中的RDH受到越来越多的关注,目前已经开发了四种JPEG图像 RDH方法。第一种是基于无损压缩的方法,该方法在[2]中首次提出。由于JPEG是一种压缩格式,基于无损压缩的方法的嵌入能力相当有限。此外,这种方法也可能导致低视觉质量和文件大小的大幅增加。第二种方法是基于对JPEG量化表的修改,该方法在[2]中首次提出,Wang等人进一步改进。这个想法很简单,也很有效。将量化表中的部分元素除以整数,将相应的量化后的DCT系数乘以相同的整数(如果需要,可以添加一个调整标志),可以为嵌入信息创建空间。这种方法易于获得大容量和高保真度。然而,正如我们所知,JPEG文件的原始量化表在文件大小和编码图像的感知质量之间提供了相当大的权衡。这种方法可能不可避免地破坏文件大小和宿主JPEG图像感知质量之间的平衡。一般来说,标记的JPEG文件的存储大小可能会显著增加。例如,在Wang等人的方法中,文件大小的增加与嵌入的消息大小不成比例。第三种方法是基于修改Huffman表,由Mobasseri et al.[19]提出,Qian and Zhang [20] and Hu et al进一步改进。这些方法既能保证jpeg图像的真实保真度,又能保持图像的存储大小。然而,嵌入能力是相当有限的,一般情况下,一些尺寸为512 × 512的标准测试图像只能嵌入数百位。此外,这种方法只能应用于用非优化的霍夫曼表编码的JPEG图像。

第四种方法是基于修正的量化DCT系数(注意,与第二种方法相比,这种方法保留了量化表)。这也是最受欢迎的方法,在过去的很多年已经得到了相当多的关注。例如,Chang等[22]提出了一种JPEG图像的RDH方案,采用每个块中某些分量连续两个零系数来隐藏秘密信息。考虑到[22]中图像中有部分区域没有使用,Lin和Shiu[23]提出了另一种称为layer-1数据嵌入策略的方法,以增强Chang等人方法的嵌入能力。Xuan等人[24]提出了一种基于直方图对的JPEG图像RDH方法。该方法利用最优搜索策略对量化后的DCT系数直方图进行移位,取得了较好的性能。为了使数据嵌入不可感知,在嵌入过程中只选择低频和中频DCT系数来嵌入数据。Sakai等[25]改进Xuan等人的方案,并通过使用一种新的自适应嵌入策略产生了更好的图像质量。Li et al.[26]倾向于将信息嵌入到属于某些选定频率的系数中,和Efimushkina等人[27]的目的是将消息嵌入到选定的较小的系数中(例如,值为0、±1和±2的系数),以确保数据隐藏可能会在宿主JPEG图像中引入很少的失真。

如前所述,在过去的几年中已经提出了许多用于JPEG图像的RDH方案。然而,与嵌入容量、视觉质量和文件大小保存相对应的基本问题迄今尚未解决。本文首先分析了如何选择DCT系数进行数据隐藏的一些新见解。然后,在这些分析的基础上,我们将提出一种新的基于hs的JPEG图像RDH方案。我们的方法与先前提出的方法的主要区别如下

1)零交流系数在嵌入过程中保持不变。

2)只有那些值为1和−1的ac系数才会展开以携带秘密消息位。

3)提出了一种基于每个8 × 8分块中0 AC系数数量的分块选择策略。

实验结果表明,该方法具有较高的嵌入容量和良好的视觉质量。同时,宿主JPEG图像的存储大小保存良好。

本文的其余部分组织如下,在第二节中,介绍了JPEG图像的RDH方案。第三节讨论了实验结果和对比研究。最后,我们在第四节中进行总结。

提出方案
A jpeg压缩概述

JPEG压缩过程[28]的关键步骤如图所示,这是灰度图像压缩的一个特例。彩色图像可以近似地认为是多个灰度图像

image-20230228143101489

JPEG编码器主要由DCT、量化器和熵编码器三部分组成。通过应用二维DCT到不重叠的8 × 8块,将原空间域信号转化为频域信号。然后将得到的DCT系数输入量化器,并使用预定的量化表进行量化,量化的DCT系数以锯齿扫描顺序排列,并使用直流(dc)系数上的差分脉冲编码调制(DPCM)和交流系数上的运行长度编码(RLE)预压缩。最后,对符号串进行霍夫曼编码,得到最终压缩的比特流。在添加头部之后,我们得到最终的JPEG文件。

B RDH系数选择

几乎所有最先进的基于hs的RDH算法都包含三个步骤。第一步是从主图像中生成一个熵较小的序列,这可以通过直接使用宿主图像的原始直方图、差值直方图或预测误差直方图来实现。第二步是将得到的直方图分成两个区域。一个叫内区,另一个叫外区。一般情况下,与峰值点相关联的一些箱子被划分到内部区域,其余的箱子被划分到外部区域。第三步是可逆地嵌入消息。也就是说,内部区域的箱子被扩展来携带数据,而外部区域的箱子被移动,这样在隐藏数据后,内部和外部区域仍然是分开的。请注意,箱子的展开和移动是通过修改宿主图像对应的像素/系数值来完成的。为了便于表示,在下面的讨论中,与内区域和外区域的元素对应的像素/系数分别称为内像素/系数和外像素/系数,对内、外像素/系数所做的修改分别称为展开和移动。

对于JPEG图像,可以很容易地获得量化后的DCT系数直方图,并采用(DCT系数)HS策略将信息直接嵌入到JPEG图像中。然而,由于去量化过程,任何对量化的DCT系数的修改都可能在空间域中引入相当大的失真,也可能显著增加JPEG文件的大小。在下面的段落中,我们将分析哪些类型的系数可以选择用于数据隐藏。为了便于解释,我们在讨论中使用了不同质量因子的标准512 × 512 Lena图像,这是目前最常用的用于测试不同RDH方案效率的图像。

1)关于DC直流系数:对于大多数JPEG图像,直流系数的统计值最好近似于高斯分布。直流系数的直方图峰值很低,意味着所能获得的嵌入容量很低。此外,如果使用直流系数进行数据隐藏,无论是使用直方图还是差分直方图,都需要移动大量的外部系数,这样在数据隐藏后DCT系数直方图的内外区域仍然可以相互分离。这些无效的移动会给宿主图像带来相当大的失真。因此,在本文提出的基于hs的RDH方法中,所有的直流系数在嵌入过程中保持不变。

2)关于AC交流系数:对于大多数JPEG图像,ac系数的统计值最好近似于拉普拉斯分布。

Lena图像的所有量化ac系数的直方图如图所示。如图所示,大部分量化ac系数的值为零,量化ac系数的直方图相当清晰。因此,HS策略可以很容易地应用于交流系数直方图。然而,对于JPEG图像中的RDH,我们不仅需要考虑嵌入容量和视觉质量,还需要考虑所获得的标记图像的存储大小。根据上述分析,如果在嵌入过程中修改一些零系数,图像文件的大小可能会显著增加,可能导致JPEG文件大小不成比例的增加。但是,如果只选择非零ac系数进行修改,中间符号的数量保持不变。通过选择合适的VLC编码策略,可以实现

但是,如果只选择非零ac系数进行修改,中间符号的数量保持不变。通过选择合适的VLC编码策略,JPEG文件的存储大小可以很好地保存,因此,在所提出的方法中,零交流系数在嵌入过程中保持不变。

从非零AC系数直方图中很容易发现位于1和- 1的两个峰值点。显然,我们可以将Bin 1和Bin−1分组到内部区域,将其余的Bin分组到外部区域。相应的内部系数的大小为1(即值为1和- 1),外部系数的大小大于1。对内系数进行扩展以携带信息位,对外系数进行移位,使系数直方图的内外区域在数据隐藏后仍然是分开的。属于高频的DCT系数对应较大的量化步长,在之前提出的大多数RDH算法中,作者倾向于选择属于低频或中频的系数来隐藏数据。在该算法中,选择所有频率的非零交流系数进行数据隐藏,原因如下:

1、可获得较高的嵌入容量;

2、由于量子化,大多数属于高频的非零系数一般具有+1或−1的值;因此,这些系数可以扩展到直接携带消息位,而不需要外部系数产生的无效移位;

3、如前所述,JPEG图像中的RDH也可以作为脆弱水印用于身份验证;因此,在属于所有频率的系数上传播消息位可以为潜在的攻击者提供更多的障碍。

C 嵌入提取和恢复算法

在不失一般性的前提下,非零ac系数用C = {C1,C2,…,CN),其中N表示JPEG图像中非零ac系数的总数。根据上述分析,非零交流系数直方图的峰值点一般位于点1和- 1。因此,在提出的算法中,Bin 1和 Bin−1被分组到内部区域,其余的Bin被分组到外部区域。所提出的RDH方案的嵌入算法描述为

image-20230228212715738

在(1)中,b∈{0,1}表示要嵌入的消息位,Ci~表示标记的JPEG图像中对应的ac系数,由图可见,该算法在嵌入过程中,所有系数最多只能修改1个。

信息提取和图像恢复可以描述为

image-20230228213513359

b’和C’i分别表示嵌入的信息位和恢复的ac系数。

D 块选择策略

如图所示,在本文提出的基于hs的RDH方案中,将幅值为1的内系数(即值为1和- 1的内系数)进行扩展以携带数据,而将幅值大于1的外系数进行移位,使系数直方图的内外区域在隐藏数据后仍然保持分离。显然,外部系数不携带任何信息,但它们需要移动;这种无效的移动可能会导致被标记图像的视觉质量变差和存储容量变大。因此,对于我们的块选择策略,主要目标是找到外部系数较少的8 × 8块,属于这些系数块的系数将优先用于数据隐藏。

此外,压缩域DCT系数的统计量与空间域像素的统计量有密切的关系,为了便于解释,下面将属于平坦区域的JPEG图像块称为平坦块,属于纹理区域的8 × 8块称为纹理块。一般情况下,经过量化后,平面块中零系数的个数要多于纹理块中零系数的个数。此外,在平坦块中,大多数非零系数的数值较小,如1。然而,在纹理块中,一些非零系数可能具有相对较大的值,如2和3。也就是说,在平面块体中,零系数多,外系数少。因此,将消息位嵌入到零系数较多的块中(即平面块)可以减少无效移动,从而获得更高的视觉质量

从BOSSbase图像数据集[31]中随机选取1000张图像,所对应的一些统计结果如表二所示,BOSSbase数据集中的所有图像都是未压缩的图像(.pgm),大小为512 × 512。我们使用独立JPEG组(IJG)工具箱[32]用不同的质量因子压缩它们。对于每个压缩图像,根据每个块中零系数的数量对4096个8 × 8块进行排序。第一部分包括2048个零系数较多的区块,第二部分包括其余零系数较少的区块。对于所有1000张图像,内系数NIn的平均值,外系数NOut的平均值,各部分的NOut/NIn值见表II。可以观察到,在第一部分中,内系数的数量要远远多于外系数的数量。但是,在第二部分中,外部系数的数量与内部系数的数量相当或相当多。

image-20230228223135010

也就是说,将消息位嵌入到第一部分零系数较多的系数块中,无效移动的概率会降低,因此,在所提出的方法中,属于零系数较多的块的系数将在嵌入过程中优先。需要注意的是,在所提出的算法中,在嵌入过程中,所有的零ac系数保持不变。接收者可以很容易地定位用于数据隐藏的块,然后提取嵌入的信息并恢复宿主图像。

E 嵌入 提取和恢复步骤

1)嵌入步骤

Step1:熵解码原始JPEG文件以获得量化的DCT系数,然后计算每个8 × 8块0 ac系数的数量以及大小为1的ac系数的数量(值位1的系数,对应1和−1)。

Step2:将8 × 8 DCT系数块分成两部分。第一部分包括ac系数零个数不小于阈值Tz(0≤Tz≤63)的所有块,第二部分包括其余块。假设在第一部分中,大小为1的ac系数的数量为S,则确定阈值Tz为

image-20230301093904211

式中0≤T≤63,L为消息嵌入比特数,l1表示表示L值所需的比特数,l2表示表示阈值Tz所需的比特数。由于消息长度一般小于2^24位,阈值Tz在[0,63]范围内,我们可以选择l1 = 24和l2 = 5,这对于几乎所有应用程序都是可以接受的。

Step3:用密钥洗牌所有非零ac系数的排列,并按顺序扫描洗牌系数。将消息长度L (l1位表示)和阈值Tz (l2位表示)嵌入到宿主图像中。

Step4:按照顺序将密文位依次嵌入到宿主图像中。注意,在这一步中,如果被访问的系数属于零系数不小于Tz的8×8块,按照(1)嵌入消息位。否则,什么都不做,继续按照扫描顺序访问下一个系数。

Step5:将所有L消息位嵌入后,将所有洗牌后的系数恢复到原来的位置,并对得到的系数进行熵编码,得到标记的JPEG文件。

2)提取和恢复步骤

Step1:对标记的JPEG文件进行熵解码,得到量化的DCT系数

Step2:使用与嵌入过程中使用的相同密钥洗牌所有非零ac系数的排列,并按顺序扫描洗牌后的系数。根据(3)从宿主图像中提取消息长度L(即前l1位)和阈值Tz(即以下l2位),并根据(4)恢复系数。

Step3:按照先后顺序,根据(3)从标记图像中依次提取L个消息位,并根据(4)恢复原始系数。注意,在这一步中,如果被访问的系数属于不小于Tz零系数的8×8块,按(3)提取消息位,按(4)恢复原始系数。否则,什么都不做,继续按照扫描顺序访问标记图像中的下一个系数。

Step4:在提取完所有秘密信息位并恢复所有标记系数后,对恢复的系数再次进行熵编码,得到原始的JPEG文件

注意,提出的方法的安全性依赖于加密密钥(通常要嵌入的消息首先加密)和shuffle密钥(在嵌入步骤3中)。如果没有这两个密钥,攻击者就无法获得原始的未加密消息并恢复宿主JPEG文件。

实验

本文块选择是优先选择零系数多的块进行嵌入,我们可以具体选择Nout/Nin小的块进行嵌入(或许根据其他的指标进行确认)。

想法

修改块选择策略,可以先嵌入低频系数,或者在每一个块中从后向前嵌入

怎么把每次的无效移动变成嵌入数据

dc系数直方图

dc系数怎么没有负数值呢

dc直方图 qf=50

image-20221121215459342

diff-dc直方图 qf=50

image-20221121215210120

diff-ac直方图 qf=50

image-20221121221941984

非零ac系数直方图

image-20221122110724095

附录代码
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
import cv2
import hashlib
import numpy as np
import matplotlib.pyplot as plt
from pyblake2 import blake2s
import base64

'''
jpeg压缩函数
data:要压缩的灰度图像数据流
quality_scale控制压缩质量(1-99),默认为50,值越小图像约清晰
return:得到压缩后的图像数据,为FFD9开头的jpeg格式字符串
'''


def compress(img_data, quality_scale=50):
# 获取图像数据流宽高
h, w = img_data.shape
# 标准亮度量化表
Qy = np.array([[16, 11, 10, 16, 24, 40, 51, 61],
[12, 12, 14, 19, 26, 58, 60, 55],
[14, 13, 16, 24, 40, 57, 69, 56],
[14, 17, 22, 29, 51, 87, 80, 62],
[18, 22, 37, 56, 68, 109, 103, 77],
[24, 35, 55, 64, 81, 104, 113, 92],
[49, 64, 78, 87, 103, 121, 120, 101],
[72, 92, 95, 98, 112, 100, 103, 99]], dtype=np.uint8)

# 根据压缩质量重新计算量化表
if quality_scale <= 0:
quality_scale = 1
elif quality_scale >= 100:
quality_scale = 99
for i in range(64):
tmp = int((Qy[int(i / 8)][i % 8] * quality_scale + 50) / 100)
if tmp <= 0:
tmp = 1
elif tmp > 255:
tmp = 255
Qy[int(i / 8)][i % 8] = tmp

# Qy=np.array([[2,1,1,1,1,1,2,1],
# [1,1,2,2,2,2,2,4],
# [3,2,2,2,2,5,4,4],
# [3,4,6,5,6,6,6,5],
# [6,6,6,7,9,8,6,7],
# [9,7,6,6,8,11,8,9],
# [10,10,10,10,10,6,8,11],
# [12,11,10,12,9,10,10,10]],dtype=np.uint8)

# Z字型
ZigZag = [
0, 1, 5, 6, 14, 15, 27, 28,
2, 4, 7, 13, 16, 26, 29, 42,
3, 8, 12, 17, 25, 30, 41, 43,
9, 11, 18, 24, 31, 40, 44, 53,
10, 19, 23, 32, 39, 45, 52, 54,
20, 22, 33, 38, 46, 51, 55, 60,
21, 34, 37, 47, 50, 56, 59, 61,
35, 36, 48, 49, 57, 58, 62, 63]

# DC哈夫曼编码表
standard_dc_nrcodes = [0, 0, 7, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
standard_dc_values = [4, 5, 3, 2, 6, 1, 0, 7, 8, 9, 10, 11]
# standard_dc_nrcodes=[0, 1,5,1,1,1,1,1,1,0,0,0,0,0,0,0]
# standard_dc_values=[0,1,2,3,4,5,6,7,8,9,10,11]
pos_in_table = 0;
code_value = 0;
dc_huffman_table = [0] * 16

for i in range(1, 9):
for j in range(1, standard_dc_nrcodes[i - 1] + 1):
dc_huffman_table[standard_dc_values[pos_in_table]] = bin(code_value)[2:].rjust(i, '0')
# ac_huffman_table[standard_ac_values[pos_in_table]].length=k
pos_in_table += 1
code_value += 1
code_value <<= 1

# AC哈夫曼编码表

standard_ac_nrcodes = [0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d]
standard_ac_values = [0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa]

pos_in_table = 0;
code_value = 0;
ac_huffman_table = [0] * 256

for i in range(1, 17):
for j in range(1, standard_ac_nrcodes[i - 1] + 1):
ac_huffman_table[standard_ac_values[pos_in_table]] = bin(code_value)[2:].rjust(i, '0')
pos_in_table += 1
code_value += 1
code_value <<= 1
# 第一次置乱
# PWLCM迭代3*w*h次,得到迭代序列ai
a0 = 0.26280656351622866
p0 = 0.37542877661550467
ai = []
for i in range(3 * w * h):
if 0 <= a0 < p0:
a0 = a0 / p0
elif a0 < 0.5:
a0 = (a0 - p0) * (0.5 - p0)
else:
a0 = 1 - a0
ai.append(a0)
# 转成float类型
img_data = img_data.astype(np.float64)
# 存储最后的哈夫曼编码
result = ''
prev = 0
# 分成8*8的块
for i in range(h // 8):
for j in range(w // 8):
block = img_data[i * 8:(i + 1) * 8, j * 8:(j + 1) * 8]
# 探索块内置乱对压缩效率的影响
# print(block)
# tmp=block.flatten(order='C')

# aa=ai[(i*8+j)*64:(i*8+j)*64+64]
# dic=list(zip(aa,tmp))
# dic.sort(key=lambda x:x[0])
# tmp=list(list(zip(*dic))[1])
# print(tmp)
# print(tmp)
# block=np.array(tmp).reshape((8,8),order='C')

# print(block)
# return

# print('原始8*8图像块')
# print(block)
block = cv2.dct(block)
# print('dct变换后')
# print(block)
# 数据量化
block[:] = np.round(block / Qy)
# print('量化后')
# print(block)
# return
# 把量化后的二维矩阵转成一维数组
arr = [0] * 64
notnull_num = 0
for k in range(64):
tmp = int(block[int(k / 8)][k % 8])
arr[ZigZag[k]] = tmp;
# 统计arr数组中有多少个非0元素
if tmp != 0:
notnull_num += 1

# print(arr)
# return 0
# RLE编码
# 标识连续0的个数
time = 0
# print(arr)
# 处理DC
if arr[0] != 0:
notnull_num -= 1
# if i==21 and j==45:
# print('ssfdafdsa',notnull_num)
data = arr[0] - prev
data2 = bin(np.abs(data))[2:]
data1 = len(data2)
if data < 0:
data2 = bin(np.abs(data) ^ (2 ** data1 - 1))[2:].rjust(data1, '0')
if data == 0:
data2 = ''
data1 = 0
result += dc_huffman_table[data1]
# if i==21 and j==46:
# print(prev,dc_huffman_table[data1],data2)
result += data2
prev = arr[0]
for k in range(1, 64):
# 有可能AC系数全为0 所以要先进行判断
if notnull_num == 0:
# 添加EOB
result += '1010'
break
if arr[k] == 0 and time < 15:
time += 1
else:
# BIT编码
# 处理括号中第二个数
data = arr[k]
data2 = bin(np.abs(data))[2:]
data1 = len(data2)
# print(type(data1))
if data < 0:
data2 = bin(np.abs(data) ^ (2 ** data1 - 1))[2:].rjust(data1, '0')
if data == 0:
data1 = 0
data2 = ''
# if arr[k]==0:
# data2=''
# 哈夫曼编码,序列化
# print(type(ac_huffman_table[6]))
# return
result += ac_huffman_table[time * 16 + data1]

result += data2
time = 0
# 判断是否要添加EOB
if int(arr[k]) != 0:
notnull_num -= 1
# AC系数没有非空
# if notnull_num==0 and k<63:
# #添加EOB
# result+='1010'
# break
# if i==21 and j==46:
# print(result)

# print(result)
# 补足为8的整数倍,以便编码成16进制数据
if len(result) % 8 != 0:
result = result.ljust(len(result) + 8 - len(result) % 8, '0')
# print(len(result))
# print(result[:40])
# result=hex(int(result,2))[2:]
res_data = ''
for i in range(0, len(result), 8):
temp = int(result[i:i + 8], 2)
res_data += hex(temp)[2:].rjust(2, '0').upper()
if temp == 255:
# print(11111)
res_data += '00'
# print(res_data)
# print(res_data[:10])
# print(len(res_data))
result = res_data
# print(result)
res = ''

# 添加jpeg文件头
# SOI(文件头),共89个字节
res += 'FFD8'
# APP0图像识别信息
res += 'FFE000104A46494600010100000100010000'
# DQT定义量化表
res += 'FFDB004300'
# 64字节的量化表

for i in range(64):
res += hex(Qy[int(i / 8)][i % 8])[2:].rjust(2, '0')
# SOF0图像基本信息,13个字节
res += 'FFC0000B08'
res += hex(h)[2:].rjust(4, '0')
res += hex(w)[2:].rjust(4, '0')
# res+='01012200'
# 采样系数好像都是1
res += '01011100'
# DHT定义huffman表,33个字节+183
# res+='FFC4001F0000010501010101010100000000000000'
res += 'FFC4001F00'
for i in standard_dc_nrcodes:
res += hex(i)[2:].rjust(2, '0')
for i in standard_dc_values:
res += hex(i)[2:].rjust(2, '0')
# res+='FFC400B5100002010303020403050504040000017D'
res += 'FFC400B510'
for i in standard_ac_nrcodes:
res += hex(i)[2:].rjust(2, '0')

for i in standard_ac_values:
res += hex(i)[2:].rjust(2, '0')

# SOS扫描行开始,10个字节
res += 'FFDA0008010100003F00'

# 压缩的图像数据(一个个扫描行),数据存放顺序是从左到右、从上到下
res += result
# print(len(result))
# EOI文件尾0
res += 'FFD9'
return res


'''
jpeg解压缩
img:解压缩的jpeg灰度图像文件
return:返回解压缩后的图像原数据,为多维数组形式
'''


def decompress(img):
# jpeg解码的所有参数都是从编码后的jpeg文件中读取的
with open(img, 'rb') as f:
img_data = f.read()
res = ''
for i in img_data:
res += hex(i)[2:].rjust(2, '0').upper()

ZigZag = [
0, 1, 5, 6, 14, 15, 27, 28,
2, 4, 7, 13, 16, 26, 29, 42,
3, 8, 12, 17, 25, 30, 41, 43,
9, 11, 18, 24, 31, 40, 44, 53,
10, 19, 23, 32, 39, 45, 52, 54,
20, 22, 33, 38, 46, 51, 55, 60,
21, 34, 37, 47, 50, 56, 59, 61,
35, 36, 48, 49, 57, 58, 62, 63]
# 获取亮度量化表
Qy = np.zeros((8, 8))
for i in range(64):
Qy[int(i / 8)][i % 8] = int(res[50 + i * 2:52 + i * 2], 16)
# 获取SOF0图像基本信息,图像的宽高
h = int(res[188:192], 16)
w = int(res[192:196], 16)
# 获取DHT定义huffman表
standard_dc_values = res[246:270]
# standard_dc_nrcodes=[0, 0, 7, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
standard_dc_nrcodes = [0] * 16
for i in range(16):
standard_dc_nrcodes[i] = int(res[214 + i * 2:216 + i * 2], 16)

standard_ac_values = res[312:636]
standard_ac_nrcodes = [0] * 16
for i in range(16):
standard_ac_nrcodes[i] = int(res[280 + i * 2:282 + i * 2], 16)
# standard_ac_nrcodes=[0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d]
# 生成dc哈夫曼表
pos_in_table = 0;
code_value = 0;
reverse_dc_huffman_table = {}

for i in range(1, 9):
for j in range(1, standard_dc_nrcodes[i - 1] + 1):
reverse_dc_huffman_table[bin(code_value)[2:].rjust(i, '0')] = standard_dc_values[
pos_in_table * 2:pos_in_table * 2 + 2]
pos_in_table += 1
code_value += 1
code_value <<= 1
# 生成ac哈夫曼表
pos_in_table = 0;
code_value = 0;
reverse_ac_huffman_table = {}

for i in range(1, 17):
for j in range(1, standard_ac_nrcodes[i - 1] + 1):
reverse_ac_huffman_table[bin(code_value)[2:].rjust(i, '0')] = standard_ac_values[
pos_in_table * 2:pos_in_table * 2 + 2]
pos_in_table += 1
code_value += 1
code_value <<= 1

# 获取压缩的图像数据

tmp_result = res[656:-4]
# print(len(tmp_result))
result = ''
# for i in range(0,len(tmp_result),2):
i = 0
while i < len(tmp_result):
tmp0 = tmp_result[i:i + 2]
result += tmp0
i += 2
if (tmp0 == 'FF'):
i += 2

# result=result.replace('FF00','FF')
# print(len(result))
# print(result)
# 得到哈夫曼编码后的01字符串
# result=result.ljust(len(result)+8-len(result)%8,'0')
result = bin(int(result, 16))[2:].rjust(len(result) * 4, '0')

# print(result)
# print(reverse_dc_huffman_table)
img_data = np.zeros((h, w))
pos = 0
prev = 0
tt = 0
for j in range(h // 8):
for k in range(w // 8):
# 逆dc哈夫曼编码
# 正向最大匹配
arr = [0]
# 计算EOB块中0的个数
num = 0
tt = pos
# if(j*(w//8)+k==1390):
# if j==21 and k==45:
# print(result[pos:pos+100])
# if(j==63 and k==60):
# print(result[pos:])
for i in range(8, 2, -1):
tmp = reverse_dc_huffman_table.get(result[pos:pos + i])
# 匹配成功
if (tmp):
pos += i
num += 1
# DC系数为0
# if j==21 and k==45:
# print(tmp,prev)
if tmp == '00':
# print(reverse_dc_huffman_table.get(result[pos-i:pos]),result[pos-i:pos])
# print(tmp,11111)
# 是差值编码 差点忘了加上prev
arr[0] = 0 + prev
prev = arr[0]
# pos+=i
# num+=1
break

time = 0
data1 = int(tmp[1], 16)
# pos+=i

data2 = result[pos:pos + data1]
# data2=data2 if data2 else '0'
# print(data1,data2)
# data=int(data2,2)
if data2[0] == '0':
# 负数
data = -(int(data2, 2) ^ (2 ** data1 - 1))
else:
data = int(data2, 2)
arr[0] = data + prev
prev = arr[0]
pos += data1
# print(arr[0])
# num+=1
break
# 逆ac哈夫曼编码
while (num < 64):
# AC系数编码长度是从16bits到2bits
for i in range(16, 1, -1):
tmp = reverse_ac_huffman_table.get(result[pos:pos + i])
if (tmp):
# if j==21 and k==45:
# print(tmp,result[pos:pos+i])
pos += i
if (tmp == '00'):
arr += ([0] * (64 - num))
num = 64
break
time = int(tmp[0], 16)
data1 = int(tmp[1], 16)
data2 = result[pos:pos + data1]
pos += data1
# data2为空,赋值为0,应对(15,0)这种情况
data2 = data2 if data2 else '0'

if data2[0] == '0':
# print(data2,data1)
# 负数,注意负号和异或运算的优先级
data = -(int(data2, 2) ^ (2 ** data1 - 1))
# print(data)
else:
data = int(data2, 2)
# data=int(data2,2)
num += time + 1
# time个0
arr += ([0] * time)
# 非零值或最后一个单元0
arr.append(data)
break
# print(arr)
# return 0
# 逆ZigZag扫描,得到block量化块
block = np.zeros((8, 8))

# print(arr)
for i in range(64):
block[int(i / 8)][i % 8] = arr[ZigZag[i]]
# 逆量化
block = block * Qy
# 逆DCT变换
block = cv2.idct(block)
img_data[j * 8:(j + 1) * 8, k * 8:(k + 1) * 8] = block
# print(j,k)
img_data = img_data.astype(np.uint8)
# print(img_data)
return img_data


def embed_message(img):
#L表示要嵌入消息的比特数
L=88
l1=24
l2=6
str="I love you!"
message3=''.join(format(ord(x), '08b') for x in str)
message1=bin(L)[2:].rjust(l1,'0')
# print("message1:",message1)


# jpeg解码的所有参数都是从编码后的jpeg文件中读取的
with open(img, 'rb') as f:
img_data = f.read()
res = ''
for i in img_data:
res += hex(i)[2:].rjust(2, '0').upper()

ZigZag = [
0, 1, 5, 6, 14, 15, 27, 28,
2, 4, 7, 13, 16, 26, 29, 42,
3, 8, 12, 17, 25, 30, 41, 43,
9, 11, 18, 24, 31, 40, 44, 53,
10, 19, 23, 32, 39, 45, 52, 54,
20, 22, 33, 38, 46, 51, 55, 60,
21, 34, 37, 47, 50, 56, 59, 61,
35, 36, 48, 49, 57, 58, 62, 63]
# 获取亮度量化表
Qy = np.zeros((8, 8))
for i in range(64):
Qy[int(i / 8)][i % 8] = int(res[50 + i * 2:52 + i * 2], 16)
# 获取SOF0图像基本信息,图像的宽高
h = int(res[188:192], 16)
w = int(res[192:196], 16)
# 获取DHT定义huffman表
standard_dc_values = res[246:270]
# standard_dc_nrcodes=[0, 0, 7, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
standard_dc_nrcodes = [0] * 16
for i in range(16):
standard_dc_nrcodes[i] = int(res[214 + i * 2:216 + i * 2], 16)

standard_ac_values = res[312:636]
standard_ac_nrcodes = [0] * 16
for i in range(16):
standard_ac_nrcodes[i] = int(res[280 + i * 2:282 + i * 2], 16)
# standard_ac_nrcodes=[0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d]
# 生成dc哈夫曼表
pos_in_table = 0;
code_value = 0;
reverse_dc_huffman_table = {}

for i in range(1, 9):
for j in range(1, standard_dc_nrcodes[i - 1] + 1):
reverse_dc_huffman_table[bin(code_value)[2:].rjust(i, '0')] = standard_dc_values[
pos_in_table * 2:pos_in_table * 2 + 2]
pos_in_table += 1
code_value += 1
code_value <<= 1
# 生成ac哈夫曼表
pos_in_table = 0;
code_value = 0;
reverse_ac_huffman_table = {}

for i in range(1, 17):
for j in range(1, standard_ac_nrcodes[i - 1] + 1):
reverse_ac_huffman_table[bin(code_value)[2:].rjust(i, '0')] = standard_ac_values[
pos_in_table * 2:pos_in_table * 2 + 2]
pos_in_table += 1
code_value += 1
code_value <<= 1

# DC哈夫曼编码表
standard_dc_nrcodes = [0, 0, 7, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
standard_dc_values = [4, 5, 3, 2, 6, 1, 0, 7, 8, 9, 10, 11]
# standard_dc_nrcodes=[0, 1,5,1,1,1,1,1,1,0,0,0,0,0,0,0]
# standard_dc_values=[0,1,2,3,4,5,6,7,8,9,10,11]
pos_in_table = 0;
code_value = 0;
dc_huffman_table = [0] * 16

for i in range(1, 9):
for j in range(1, standard_dc_nrcodes[i - 1] + 1):
dc_huffman_table[standard_dc_values[pos_in_table]] = bin(code_value)[2:].rjust(i, '0')
# ac_huffman_table[standard_ac_values[pos_in_table]].length=k
pos_in_table += 1
code_value += 1
code_value <<= 1

# AC哈夫曼编码表

standard_ac_nrcodes = [0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d]
standard_ac_values = [0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa]

pos_in_table = 0;
code_value = 0;
ac_huffman_table = [0] * 256

for i in range(1, 17):
for j in range(1, standard_ac_nrcodes[i - 1] + 1):
ac_huffman_table[standard_ac_values[pos_in_table]] = bin(code_value)[2:].rjust(i, '0')
pos_in_table += 1
code_value += 1
code_value <<= 1

# 获取压缩的图像数据
tmp_result = res[656:-4]
# print(len(tmp_result))
result = ''
# for i in range(0,len(tmp_result),2):
i = 0
while i < len(tmp_result):
tmp0 = tmp_result[i:i + 2]
result += tmp0
i += 2
if (tmp0 == 'FF'):
i += 2

# result=result.replace('FF00','FF')
# print(len(result))
# print(result)
# 得到哈夫曼编码后的01字符串
# result=result.ljust(len(result)+8-len(result)%8,'0')
result = bin(int(result, 16))[2:].rjust(len(result) * 4, '0')

# print(result)
# print(reverse_dc_huffman_table)
img_data = np.zeros((h, w))
#量化后的DCT系数块
dct_coefficient=np.zeros((h,w))
pos = 0
prev = 0
tt = 0
ac0_list=[0] * (h*w//64)
ac1_list=[0] *(h*w//64)
block_pos=0
for j in range(h // 8):
for k in range(w // 8):
# 逆dc哈夫曼编码
# 正向最大匹配
arr = [0]
# 计算EOB块中0的个数
num = 0
tt = pos
# if(j*(w//8)+k==1390):
# if j==21 and k==45:
# print(result[pos:pos+100])
# if(j==63 and k==60):
# print(result[pos:])
for i in range(8, 2, -1):
tmp = reverse_dc_huffman_table.get(result[pos:pos + i])
# 匹配成功
if (tmp):
pos += i
num += 1
# DC系数为0
# if j==21 and k==45:
# print(tmp,prev)
if tmp == '00':
# print(reverse_dc_huffman_table.get(result[pos-i:pos]),result[pos-i:pos])
# print(tmp,11111)
# 是差值编码 差点忘了加上prev
arr[0] = 0 + prev
prev = arr[0]
# pos+=i
# num+=1
break

time = 0
data1 = int(tmp[1], 16)
# pos+=i

data2 = result[pos:pos + data1]
# data2=data2 if data2 else '0'
# print(data1,data2)
# data=int(data2,2)
if data2[0] == '0':
# 负数
data = -(int(data2, 2) ^ (2 ** data1 - 1))
else:
data = int(data2, 2)
arr[0] = data + prev
prev = arr[0]
pos += data1
# print(arr[0])
# num+=1
break
# 逆ac哈夫曼编码
while (num < 64):
# AC系数编码长度是从16bits到2bits
for i in range(16, 1, -1):
tmp = reverse_ac_huffman_table.get(result[pos:pos + i])
if (tmp):
# if j==21 and k==45:
# print(tmp,result[pos:pos+i])
pos += i
if (tmp == '00'):
arr += ([0] * (64 - num))
num = 64
break
time = int(tmp[0], 16)
data1 = int(tmp[1], 16)
data2 = result[pos:pos + data1]
pos += data1
# data2为空,赋值为0,应对(15,0)这种情况
data2 = data2 if data2 else '0'

if data2[0] == '0':
# print(data2,data1)
# 负数,注意负号和异或运算的优先级
data = -(int(data2, 2) ^ (2 ** data1 - 1))
# print(data)
else:
data = int(data2, 2)
# data=int(data2,2)
num += time + 1
# time个0
arr += ([0] * time)
# 非零值或最后一个单元0
arr.append(data)
break
# print(arr)
# return 0
# 逆ZigZag扫描,得到block量化块
block = np.zeros((8, 8))

# print(arr)
for i in range(64):
block[int(i / 8)][i % 8] = arr[ZigZag[i]]

# 逆量化
# block = block * Qy
# 逆DCT变换
# block = cv2.idct(block)
dct_coefficient[j * 8:(j + 1) * 8, k * 8:(k + 1) * 8] = block

#每个8*8块 0 ac系数的数量
ac0_num=0
ac1_num=0
for i in range(64):
if arr[i]==0:
ac0_num+=1
elif arr[i]==1 or arr[i]==-1:
ac1_num+=1
ac0_list[block_pos]=ac0_num
ac1_list[block_pos]=ac1_num
block_pos+=1

# img_data[j * 8:(j + 1) * 8, k * 8:(k + 1) * 8] = block
# print(j,k)
# 求阈值Tz
Tz=0
s0=L+l1+l2
S=sum(ac1_list)

S_ac0=list(zip(ac0_list,ac1_list))
S_ac0.sort(key=lambda x:x[0])
new_ac1_list=list(list(zip(*S_ac0))[1])
new_ac0_list=list(list(zip(*S_ac0))[0])
tmp=0
for tz in new_ac0_list:
S=S-new_ac1_list[tmp]
tmp+=1
if S<s0:
break
Tz=tz
# print(Tz)
message2=bin(Tz)[2:].rjust(l2,'0')
# print("message2:",message2)
# print("message3:",message3)
block_pos=0
pos=0
message=message1+message2+message3
length=l1+l2+L
flag=True
result=""
prev=0
block_pos=0
for i in range(h // 8):
for j in range(w // 8):
block = dct_coefficient[i * 8:(i + 1) * 8, j * 8:(j + 1) * 8]

# 把量化后的二维矩阵转成一维数组
arr = [0] * 64
notnull_num = 0
for k in range(64):
tmp = int(block[int(k / 8)][k % 8])
arr[ZigZag[k]] = tmp;
# 统计arr数组中有多少个非0元素
if tmp != 0:
notnull_num += 1

# print(arr)
# print(ac1_list)

if pos<(l1+l2) or (flag and ac0_list[block_pos]>=Tz):
#嵌入消息
# print("块号:", block_pos)
for v in range(1,64):

if pos>=length:
flag=False
break
if arr[v]==1:
if message[pos]=="1":
arr[v]=2
pos+=1
elif arr[v]==-1:
if message[pos]=="1":
arr[v]=-2
pos+=1
elif arr[v]>1:
arr[v]+=1
elif arr[v]<-1:
arr[v]-=1
block_pos+=1
# print(arr)
# return 0
# RLE编码
# 标识连续0的个数
time = 0
# print(arr)
# 处理DC
if arr[0] != 0:
notnull_num -= 1
# if i==21 and j==45:
# print('ssfdafdsa',notnull_num)
data = arr[0] - prev
data2 = bin(np.abs(data))[2:]
data1 = len(data2)
if data < 0:
data2 = bin(np.abs(data) ^ (2 ** data1 - 1))[2:].rjust(data1, '0')
if data == 0:
data2 = ''
data1 = 0
result += dc_huffman_table[data1]
# if i==21 and j==46:
# print(prev,dc_huffman_table[data1],data2)
result += data2
prev = arr[0]
for k in range(1, 64):
# 有可能AC系数全为0 所以要先进行判断
if notnull_num == 0:
# 添加EOB
result += '1010'
break
if arr[k] == 0 and time < 15:
time += 1
else:
# BIT编码
# 处理括号中第二个数
data = arr[k]
data2 = bin(np.abs(data))[2:]
data1 = len(data2)
# print(type(data1))
if data < 0:
data2 = bin(np.abs(data) ^ (2 ** data1 - 1))[2:].rjust(data1, '0')
if data == 0:
data1 = 0
data2 = ''
# if arr[k]==0:
# data2=''
# 哈夫曼编码,序列化
# print(type(ac_huffman_table[6]))
# return
result += ac_huffman_table[time * 16 + data1]

result += data2
time = 0
# 判断是否要添加EOB
if int(arr[k]) != 0:
notnull_num -= 1
# AC系数没有非空
# if notnull_num==0 and k<63:
# #添加EOB
# result+='1010'
# break
# if i==21 and j==46:
# print(result)

# print(result)
# 补足为8的整数倍,以便编码成16进制数据
if len(result) % 8 != 0:
result = result.ljust(len(result) + 8 - len(result) % 8, '0')
# print(len(result))
# print(result[:40])
# result=hex(int(result,2))[2:]
# print(result)
res_data = ''
for i in range(0, len(result), 8):
temp = int(result[i:i + 8], 2)
res_data += hex(temp)[2:].rjust(2, '0').upper()
if temp == 255:
# print(11111)
res_data += '00'
# print(res_data)
# print(res_data[:10])
# print(len(res_data))
result = res_data
# print(result)
res = ''

# 添加jpeg文件头
# SOI(文件头),共89个字节
res += 'FFD8'
# APP0图像识别信息
res += 'FFE000104A46494600010100000100010000'
# DQT定义量化表
res += 'FFDB004300'
# 64字节的量化表
Qy=Qy.astype(np.uint8)
for i in range(64):
res += hex(Qy[int(i / 8)][i % 8])[2:].rjust(2, '0')
# SOF0图像基本信息,13个字节
res += 'FFC0000B08'
res += hex(h)[2:].rjust(4, '0')
res += hex(w)[2:].rjust(4, '0')
# res+='01012200'
# 采样系数好像都是1
res += '01011100'
# DHT定义huffman表,33个字节+183
# res+='FFC4001F0000010501010101010100000000000000'
res += 'FFC4001F00'
for i in standard_dc_nrcodes:
res += hex(i)[2:].rjust(2, '0')
for i in standard_dc_values:
res += hex(i)[2:].rjust(2, '0')
# res+='FFC400B5100002010303020403050504040000017D'
res += 'FFC400B510'
for i in standard_ac_nrcodes:
res += hex(i)[2:].rjust(2, '0')

for i in standard_ac_values:
res += hex(i)[2:].rjust(2, '0')

# SOS扫描行开始,10个字节
res += 'FFDA0008010100003F00'

# 压缩的图像数据(一个个扫描行),数据存放顺序是从左到右、从上到下
res += result
# print(len(result))
# EOI文件尾0
res += 'FFD9'
return res

#嵌入信息提取

def extract_message(img):
#L表示要嵌入消息的比特数
# L=88
l1=24
l2=6
# str="I love you!"
# message3=''.join(format(ord(x), '08b') for x in str)
# message1=bin(L)[2:].rjust(l1,'0')

# jpeg解码的所有参数都是从编码后的jpeg文件中读取的
with open(img, 'rb') as f:
img_data = f.read()
res = ''
for i in img_data:
res += hex(i)[2:].rjust(2, '0').upper()

ZigZag = [
0, 1, 5, 6, 14, 15, 27, 28,
2, 4, 7, 13, 16, 26, 29, 42,
3, 8, 12, 17, 25, 30, 41, 43,
9, 11, 18, 24, 31, 40, 44, 53,
10, 19, 23, 32, 39, 45, 52, 54,
20, 22, 33, 38, 46, 51, 55, 60,
21, 34, 37, 47, 50, 56, 59, 61,
35, 36, 48, 49, 57, 58, 62, 63]
# 获取亮度量化表
Qy = np.zeros((8, 8))
for i in range(64):
Qy[int(i / 8)][i % 8] = int(res[50 + i * 2:52 + i * 2], 16)
# 获取SOF0图像基本信息,图像的宽高
h = int(res[188:192], 16)
w = int(res[192:196], 16)
# 获取DHT定义huffman表
standard_dc_values = res[246:270]
# standard_dc_nrcodes=[0, 0, 7, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
standard_dc_nrcodes = [0] * 16
for i in range(16):
standard_dc_nrcodes[i] = int(res[214 + i * 2:216 + i * 2], 16)

standard_ac_values = res[312:636]
standard_ac_nrcodes = [0] * 16
for i in range(16):
standard_ac_nrcodes[i] = int(res[280 + i * 2:282 + i * 2], 16)
# standard_ac_nrcodes=[0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d]
# 生成dc哈夫曼表
pos_in_table = 0;
code_value = 0;
reverse_dc_huffman_table = {}

for i in range(1, 9):
for j in range(1, standard_dc_nrcodes[i - 1] + 1):
reverse_dc_huffman_table[bin(code_value)[2:].rjust(i, '0')] = standard_dc_values[
pos_in_table * 2:pos_in_table * 2 + 2]
pos_in_table += 1
code_value += 1
code_value <<= 1
# 生成ac哈夫曼表
pos_in_table = 0;
code_value = 0;
reverse_ac_huffman_table = {}

for i in range(1, 17):
for j in range(1, standard_ac_nrcodes[i - 1] + 1):
reverse_ac_huffman_table[bin(code_value)[2:].rjust(i, '0')] = standard_ac_values[
pos_in_table * 2:pos_in_table * 2 + 2]
pos_in_table += 1
code_value += 1
code_value <<= 1

# DC哈夫曼编码表
standard_dc_nrcodes = [0, 0, 7, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
standard_dc_values = [4, 5, 3, 2, 6, 1, 0, 7, 8, 9, 10, 11]
# standard_dc_nrcodes=[0, 1,5,1,1,1,1,1,1,0,0,0,0,0,0,0]
# standard_dc_values=[0,1,2,3,4,5,6,7,8,9,10,11]
pos_in_table = 0;
code_value = 0;
dc_huffman_table = [0] * 16

for i in range(1, 9):
for j in range(1, standard_dc_nrcodes[i - 1] + 1):
dc_huffman_table[standard_dc_values[pos_in_table]] = bin(code_value)[2:].rjust(i, '0')
# ac_huffman_table[standard_ac_values[pos_in_table]].length=k
pos_in_table += 1
code_value += 1
code_value <<= 1

# AC哈夫曼编码表

standard_ac_nrcodes = [0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d]
standard_ac_values = [0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa]

pos_in_table = 0;
code_value = 0;
ac_huffman_table = [0] * 256

for i in range(1, 17):
for j in range(1, standard_ac_nrcodes[i - 1] + 1):
ac_huffman_table[standard_ac_values[pos_in_table]] = bin(code_value)[2:].rjust(i, '0')
pos_in_table += 1
code_value += 1
code_value <<= 1

# 获取压缩的图像数据
tmp_result = res[656:-4]
# print(len(tmp_result))
result = ''
# for i in range(0,len(tmp_result),2):
i = 0
while i < len(tmp_result):
tmp0 = tmp_result[i:i + 2]
result += tmp0
i += 2
if (tmp0 == 'FF'):
i += 2

# result=result.replace('FF00','FF')
# print(len(result))
# print(result)
# 得到哈夫曼编码后的01字符串
# result=result.ljust(len(result)+8-len(result)%8,'0')
result = bin(int(result, 16))[2:].rjust(len(result) * 4, '0')

# print(result)
# print(reverse_dc_huffman_table)
img_data = np.zeros((h, w))
#量化后的DCT系数块
dct_coefficient=np.zeros((h,w))
pos = 0
prev = 0
tt = 0
ac0_list=[0] * (h*w//64)
ac1_list=[0] *(h*w//64)
block_pos=0
for j in range(h // 8):
for k in range(w // 8):
# 逆dc哈夫曼编码
# 正向最大匹配
arr = [0]
# 计算EOB块中0的个数
num = 0
tt = pos
# if(j*(w//8)+k==1390):
# if j==21 and k==45:
# print(result[pos:pos+100])
# if(j==63 and k==60):
# print(result[pos:])
for i in range(8, 2, -1):
tmp = reverse_dc_huffman_table.get(result[pos:pos + i])
# 匹配成功
if (tmp):
pos += i
num += 1
# DC系数为0
# if j==21 and k==45:
# print(tmp,prev)
if tmp == '00':
# print(reverse_dc_huffman_table.get(result[pos-i:pos]),result[pos-i:pos])
# print(tmp,11111)
# 是差值编码 差点忘了加上prev
arr[0] = 0 + prev
prev = arr[0]
# pos+=i
# num+=1
break

time = 0
data1 = int(tmp[1], 16)
# pos+=i

data2 = result[pos:pos + data1]
# data2=data2 if data2 else '0'
# print(data1,data2)
# data=int(data2,2)
if data2[0] == '0':
# 负数
data = -(int(data2, 2) ^ (2 ** data1 - 1))
else:
data = int(data2, 2)
arr[0] = data + prev
prev = arr[0]
pos += data1
# print(arr[0])
# num+=1
break
# 逆ac哈夫曼编码
while (num < 64):
# AC系数编码长度是从16bits到2bits
for i in range(16, 1, -1):
tmp = reverse_ac_huffman_table.get(result[pos:pos + i])
if (tmp):
# if j==21 and k==45:
# print(tmp,result[pos:pos+i])
pos += i
if (tmp == '00'):
arr += ([0] * (64 - num))
num = 64
break
time = int(tmp[0], 16)
data1 = int(tmp[1], 16)
data2 = result[pos:pos + data1]
pos += data1
# data2为空,赋值为0,应对(15,0)这种情况
data2 = data2 if data2 else '0'

if data2[0] == '0':
# print(data2,data1)
# 负数,注意负号和异或运算的优先级
data = -(int(data2, 2) ^ (2 ** data1 - 1))
# print(data)
else:
data = int(data2, 2)
# data=int(data2,2)
num += time + 1
# time个0
arr += ([0] * time)
# 非零值或最后一个单元0
arr.append(data)
break
# print(arr)
# return 0
# 逆ZigZag扫描,得到block量化块
block = np.zeros((8, 8))

# print(arr)
for i in range(64):
block[int(i / 8)][i % 8] = arr[ZigZag[i]]

# 逆量化
# block = block * Qy
# 逆DCT变换
# block = cv2.idct(block)
dct_coefficient[j * 8:(j + 1) * 8, k * 8:(k + 1) * 8] = block

#每个8*8块 0 ac系数的数量
ac0_num=0
ac1_num=0
for i in range(64):
if arr[i]==0:
ac0_num+=1
elif arr[i]==1 or arr[i]==-1:
ac1_num+=1
ac0_list[block_pos]=ac0_num
ac1_list[block_pos]=ac1_num
block_pos+=1

# img_data[j * 8:(j + 1) * 8, k * 8:(k + 1) * 8] = block
# print(j,k)
# # 求阈值Tz
Tz=0
# s0=L+l1+l2
# S=sum(ac1_list)
#
#
#
# S_ac0=list(zip(ac0_list,ac1_list))
# S_ac0.sort(key=lambda x:x[0])
# new_ac1_list=list(list(zip(*S_ac0))[1])
# new_ac0_list=list(list(zip(*S_ac0))[0])
# tmp=0
# for tz in new_ac0_list:
# S=S-new_ac1_list[tmp]
# tmp+=1
# if S<s0:
# break
# print(tz)
# message2=bin(Tz)[2:].rjust(l2,'0')
block_pos=0
pos=0
# message=message1+message2+message3
length=l1+l2
flag=True
message=""
L=0
result=""
# message1=""
# message2=""
prev=0
# tmp=0
# for i in range(len(ac0_list)):
# if ac0_list[i]>60:
# tmp+=ac1_list[i]
# print("tmp:",tmp)
block_pos=0
for i in range(h // 8):
for j in range(w // 8):
block = dct_coefficient[i * 8:(i + 1) * 8, j * 8:(j + 1) * 8]

# 把量化后的二维矩阵转成一维数组
arr = [0] * 64
notnull_num = 0
for k in range(64):
tmp = int(block[int(k / 8)][k % 8])
arr[ZigZag[k]] = tmp;
# 统计arr数组中有多少个非0元素
if tmp != 0:
notnull_num += 1

# print(arr)
# print(ac1_list)
if pos<(l1+l2) or (flag and ac0_list[block_pos]>=Tz):
# print("块号:", block_pos)
#提取消息
for v in range(1,64):
if pos==l1+l2 and L==0:
L=int(message[:l1],2)
Tz=int(message[l1:],2)
# print("message:",message)
# print("L:",L)
# print("l1:",l1," l2:",l2)
# print("Tz:",Tz)

length=length+L
# print("length:",length)
message=""
if pos>=length:
flag=False
# print(pos)
break
if arr[v]==1 or arr[v]==-1:
message+="0"
pos+=1
elif arr[v]==2:
message+="1"
arr[v]-=1
pos+=1
elif arr[v]==-2:
message+="1"
arr[v]+=1
pos+=1
elif arr[v]>2:
arr[v]-=1
elif arr[v]<-2:
arr[v]+=1
block_pos+=1
# return 0
# RLE编码
# 标识连续0的个数

time = 0
# print(arr)
# 处理DC
if arr[0] != 0:
notnull_num -= 1
# if i==21 and j==45:
# print('ssfdafdsa',notnull_num)
data = arr[0] - prev
data2 = bin(np.abs(data))[2:]
data1 = len(data2)
if data < 0:
data2 = bin(np.abs(data) ^ (2 ** data1 - 1))[2:].rjust(data1, '0')
if data == 0:
data2 = ''
data1 = 0
result += dc_huffman_table[data1]
# if i==21 and j==46:
# print(prev,dc_huffman_table[data1],data2)
result += data2
prev = arr[0]
for k in range(1, 64):
# 有可能AC系数全为0 所以要先进行判断
if notnull_num == 0:
# 添加EOB
result += '1010'
break
if arr[k] == 0 and time < 15:
time += 1
else:
# BIT编码
# 处理括号中第二个数
data = arr[k]
data2 = bin(np.abs(data))[2:]
data1 = len(data2)
# print(type(data1))
if data < 0:
data2 = bin(np.abs(data) ^ (2 ** data1 - 1))[2:].rjust(data1, '0')
if data == 0:
data1 = 0
data2 = ''
# if arr[k]==0:
# data2=''
# 哈夫曼编码,序列化
# print(type(ac_huffman_table[6]))
# return
result += ac_huffman_table[time * 16 + data1]

result += data2
time = 0
# 判断是否要添加EOB
if int(arr[k]) != 0:
notnull_num -= 1
# AC系数没有非空
# if notnull_num==0 and k<63:
# #添加EOB
# result+='1010'
# break
# if i==21 and j==46:
# print(result)
# print("提取的消息:",message)

# print(result)
# 补足为8的整数倍,以便编码成16进制数据
if len(result) % 8 != 0:
result = result.ljust(len(result) + 8 - len(result) % 8, '0')
# print(len(result))
# print(result[:40])
# result=hex(int(result,2))[2:]
res_data = ''
for i in range(0, len(result), 8):
temp = int(result[i:i + 8], 2)
res_data += hex(temp)[2:].rjust(2, '0').upper()
if temp == 255:
# print(11111)
res_data += '00'
# print(res_data)
# print(res_data[:10])
# print(len(res_data))
result = res_data
# print(result)
res = ''

# 添加jpeg文件头
# SOI(文件头),共89个字节
res += 'FFD8'
# APP0图像识别信息
res += 'FFE000104A46494600010100000100010000'
# DQT定义量化表
res += 'FFDB004300'
# 64字节的量化表
Qy=Qy.astype(np.uint8)
for i in range(64):
res += hex(Qy[int(i / 8)][i % 8])[2:].rjust(2, '0')
# SOF0图像基本信息,13个字节
res += 'FFC0000B08'
res += hex(h)[2:].rjust(4, '0')
res += hex(w)[2:].rjust(4, '0')
# res+='01012200'
# 采样系数好像都是1
res += '01011100'
# DHT定义huffman表,33个字节+183
# res+='FFC4001F0000010501010101010100000000000000'
res += 'FFC4001F00'
for i in standard_dc_nrcodes:
res += hex(i)[2:].rjust(2, '0')
for i in standard_dc_values:
res += hex(i)[2:].rjust(2, '0')
# res+='FFC400B5100002010303020403050504040000017D'
res += 'FFC400B510'
for i in standard_ac_nrcodes:
res += hex(i)[2:].rjust(2, '0')

for i in standard_ac_values:
res += hex(i)[2:].rjust(2, '0')

# SOS扫描行开始,10个字节
res += 'FFDA0008010100003F00'

# 压缩的图像数据(一个个扫描行),数据存放顺序是从左到右、从上到下
res += result
# print(len(result))
# EOI文件尾0
res += 'FFD9'
# print("extract:",message)
hex_str=""
for i in range(0,len(message),4):
hex_str+=hex(int(message[i:i+4],2))[2:]

return res,hex_str


def main():
# 原始图像路径,灰度图像
img_path = './lena.jpg'
# 读取原始图像,cv2.imread()默认是用color模式读取的,保持原样读取要加上第二个参数-1,即CV_LOAD_IMAGE_GRAYSCALE
# 得到图像原数据流
img_data = cv2.imread(img_path, -1)
# cv2.imwrite('./jpeg_compress.jpg', img_data)
# img0 = cv2.imread('./jpeg_compress.jpg', -1)
# data=np.ones((8,8))
# for i in range(8):
# for j in range(8):
# data[i][j]=i*j
# 得到压缩后图像数据
img_compress=compress(img_data)
img_compress_path = './img_compress.jpg'
with open(img_compress_path, 'wb') as f:
f.write(base64.b16decode(img_compress.upper()))
img_mark=embed_message(img_compress_path)
# 存储标记后的图像
img_mark_path = './img_mark.jpg'
with open(img_mark_path, 'wb') as f:
f.write(base64.b16decode(img_mark.upper()))
img_restore,message=extract_message(img_mark_path)
#存储提取后的图像
img_restore_path='./img_restore.jpg'
with open(img_restore_path, 'wb') as f:
f.write(base64.b16decode(img_restore.upper()))
message = base64.b16decode(message.upper())
# print("提取的信息为:", message)

# img_compress = compress(img_data, 50)
# print(img_compress)
# test=cv2.imread('./lena.bmp')
# cv2.imwrite('./lena_rgb.jpg',test)

# 存储压缩后的图像
# img_compress_path = './img_compress.jpg'
# with open(img_compress_path, 'wb') as f:
# f.write(base64.b16decode(img_compress.upper()))
# # jpeg图像解压缩测试
# img_decompress = decompress(img_compress_path)
# img1 = cv2.imread('./img_compress.jpg', -1)
# print(img1)

# 结果展示
# plt.rcParams['font.sans-serif'] = ['SimHei'] # 中文乱码
# # 子图1,原始图像
# plt.subplot(221)
# # imshow()对图像进行处理,画出图像,show()进行图像显示
# plt.imshow(img_data, cmap=plt.cm.gray)
# plt.title('原始图像')
# # 不显示坐标轴
# plt.axis('off')
#
# # 子图2,自己写的jpeg压缩后解码图像
# plt.subplot(222)
# plt.imshow(img_decompress, cmap=plt.cm.gray)
# plt.title('自写jpeg图像(自解压)')
# plt.axis('off')
#
# # 子图3,jpeg压缩后解码图像
# plt.subplot(223)
# plt.imshow(img0, cmap=plt.cm.gray)
# plt.title('官方jpeg图像')
# plt.axis('off')
#
# # 子图3,jpeg压缩后解码图像
# plt.subplot(224)
# plt.imshow(img1, cmap=plt.cm.gray)
# plt.title('自写jpeg图像(官方解压)')
# plt.axis('off')
# plt.show()

# print(img_encrypt)


if __name__ == '__main__':
main()


def Nin_Nout(img_data, quality_scale=50):
# 获取图像数据流宽高
h, w = img_data.shape
# 标准亮度量化表
Qy = np.array([[16, 11, 10, 16, 24, 40, 51, 61],
[12, 12, 14, 19, 26, 58, 60, 55],
[14, 13, 16, 24, 40, 57, 69, 56],
[14, 17, 22, 29, 51, 87, 80, 62],
[18, 22, 37, 56, 68, 109, 103, 77],
[24, 35, 55, 64, 81, 104, 113, 92],
[49, 64, 78, 87, 103, 121, 120, 101],
[72, 92, 95, 98, 112, 100, 103, 99]], dtype=np.uint8)

# 根据压缩质量重新计算量化表
if quality_scale <= 0:
quality_scale = 1
elif quality_scale >= 100:
quality_scale = 99
for i in range(64):
tmp = int((Qy[int(i / 8)][i % 8] * quality_scale + 50) / 100)
if tmp <= 0:
tmp = 1
elif tmp > 255:
tmp = 255
Qy[int(i / 8)][i % 8] = tmp

# Qy=np.array([[2,1,1,1,1,1,2,1],
# [1,1,2,2,2,2,2,4],
# [3,2,2,2,2,5,4,4],
# [3,4,6,5,6,6,6,5],
# [6,6,6,7,9,8,6,7],
# [9,7,6,6,8,11,8,9],
# [10,10,10,10,10,6,8,11],
# [12,11,10,12,9,10,10,10]],dtype=np.uint8)

# Z字型
ZigZag = [
0, 1, 5, 6, 14, 15, 27, 28,
2, 4, 7, 13, 16, 26, 29, 42,
3, 8, 12, 17, 25, 30, 41, 43,
9, 11, 18, 24, 31, 40, 44, 53,
10, 19, 23, 32, 39, 45, 52, 54,
20, 22, 33, 38, 46, 51, 55, 60,
21, 34, 37, 47, 50, 56, 59, 61,
35, 36, 48, 49, 57, 58, 62, 63]

# DC哈夫曼编码表
standard_dc_nrcodes = [0, 0, 7, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
standard_dc_values = [4, 5, 3, 2, 6, 1, 0, 7, 8, 9, 10, 11]
# standard_dc_nrcodes=[0, 1,5,1,1,1,1,1,1,0,0,0,0,0,0,0]
# standard_dc_values=[0,1,2,3,4,5,6,7,8,9,10,11]
pos_in_table = 0;
code_value = 0;
dc_huffman_table = [0] * 16

for i in range(1, 9):
for j in range(1, standard_dc_nrcodes[i - 1] + 1):
dc_huffman_table[standard_dc_values[pos_in_table]] = bin(code_value)[2:].rjust(i, '0')
# ac_huffman_table[standard_ac_values[pos_in_table]].length=k
pos_in_table += 1
code_value += 1
code_value <<= 1

# AC哈夫曼编码表

standard_ac_nrcodes = [0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d]
standard_ac_values = [0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa]

pos_in_table = 0;
code_value = 0;
ac_huffman_table = [0] * 256

for i in range(1, 17):
for j in range(1, standard_ac_nrcodes[i - 1] + 1):
ac_huffman_table[standard_ac_values[pos_in_table]] = bin(code_value)[2:].rjust(i, '0')
pos_in_table += 1
code_value += 1
code_value <<= 1
# 第一次置乱
# PWLCM迭代3*w*h次,得到迭代序列ai
a0 = 0.26280656351622866
p0 = 0.37542877661550467
ai = []
for i in range(3 * w * h):
if 0 <= a0 < p0:
a0 = a0 / p0
elif a0 < 0.5:
a0 = (a0 - p0) * (0.5 - p0)
else:
a0 = 1 - a0
ai.append(a0)
# 转成float类型
img_data = img_data.astype(np.float64)
# 存储最后的哈夫曼编码
result = ''
prev = 0
ac_list = [0] * (h * w // 64)
# 分成8*8的块
block_pos = 0
for i in range(h // 8):
for j in range(w // 8):
block = img_data[i * 8:(i + 1) * 8, j * 8:(j + 1) * 8]
block = cv2.dct(block)
block[:] = np.round(block / Qy)
arr = [0] * 64
ac0_num = 0
for k in range(64):
tmp = int(block[int(k / 8)][k % 8])
arr[ZigZag[k]] = tmp;
# 统计arr数组中有多少个非0元素
# if tmp!=0:
# notnull_num+=1
if tmp == 0:
ac0_num += 1
ac_list[block_pos] = ac0_num
block_pos += 1
tmp = list(ac_list)

tmp.sort()
Tz = tmp[2048]
part_list = [0] * (h * w // 64)
part1_num = 0;
for i in range(h * w // 64):

if ac_list[i] >= Tz and part1_num < h * w // 64 / 2:
part_list[i] = 1
else:
part_list[i] = 2
# print(ac_list[2048])
in_num1 = 0
out_num1 = 0
in_num2 = 0
out_num2 = 0
block_pos1 = 0
for i in range(h // 8):
for j in range(w // 8):
block = img_data[i * 8:(i + 1) * 8, j * 8:(j + 1) * 8]
block = cv2.dct(block)
block[:] = np.round(block / Qy)
arr = [0] * 64
ac0_num = 0
for k in range(64):
tmp = int(block[int(k / 8)][k % 8])
arr[ZigZag[k]] = tmp;
# 统计arr数组中有多少个非0元素
# if tmp!=0:
# notnull_num+=1
if part_list[block_pos1] == 1:
if tmp == -1 or tmp == 1:
in_num1 += 1
elif tmp != 0:
out_num1 += 1
elif tmp == -1 or tmp == 1:
in_num2 += 1
elif tmp != 0:
out_num2 += 1
block_pos1 += 1

print("Nout1/Nin1:", out_num1 / in_num1)
print("Nout2/Nin2:", out_num2 / in_num2)

return block_pos
------------- THE END! THANKS! -------------