0%

A Content-Adaptive Joint Image Compression and Encryption Scheme

论文地址

一种内容自适应联合图像压缩和加密方案
摘要

​ 对于联合的图像压缩和加密方案,加密能力和压缩效率通常是两个相互矛盾的问题。本文提出了一种新的基于有损JPEG标准的图像压缩与加密联合方案,旨在在保持JPEG压缩效率的前提下,提高加密能力。提出的方案是图像内容自适应的,因为加密密钥是对明文图像使用BLAKE2散列算法生成的。该方案包含三种加密操作:交替新正交变换、直流系数加密和交流系数加密。

​ 为了节省当明文图像发生变化时,每次向解码器传输不同的加密密钥进行解密的成本,我们提出将密钥嵌入到某些ac系数的熵编码比特流中,整个嵌入过程由另一个称为嵌入密钥的密钥控制。大量的实验表明,我们的加密方案是JPEG友好的,并具有良好的混淆和扩散特性。详细的安全性分析也说明了该方案对各种密码分析策略的健壮性。

引言

​ 近年来,由于计算机网络和信息技术的迅速发展,大量的多媒体数据已经通过各种类型的社交网络平台进行传输,比如Facebook和Instagram。在这些交换数据的主要部分(图像、视频或音频)中,无论它们是机密的还是私有的,都需要安全机制提供不同程度的保护。保护数字数据不被窃听和截取的常用解决方案是加密消息或加密通道,这两者都需要一定的密码学知识。由于图像在通信中起着至关重要的作用,而原始视频也可以看作是一组静止图像(帧)序列的构建,因此图像数据的加密越来越受到人们的关注。

​ 自20世纪70年代以来,许多密码学技术得到了发展,其中一些比如数据加密标准(DES)和高级加密标准(AES),已被广泛采用作为标准化的加密算法。然而,使用这些传统算法进行图像加密时,由于图像数据量大,计算成本高。此外,在执行DES/AES之后,输出的密码图像通常是随机的,因此用于压缩目的的空间很小。为了加快加密速度,简化加密操作,人们提出了多种纯排列图像加密算法。这种加密方案易于实现,在空间域和频率域都可用,但仍有一些固有的局限性。例如,由于像素值没有被修改,纯排列方法不能改变像素的频率分布,因此容易受到统计攻击。此外,当明文图像的大小较小时,像素/块的排列排列数量将小于密钥空间,使用选择明文攻击猜测排列映射可能会破坏密码系统。因为我们看到的大多数互联网图像都被压缩了,为了减少图像处理和通信中的加解密时间,图像保护的研究重点转向了图像压缩与加密的结合。

​ JPEG和JPEG2000是联合摄影专家组委员会制定的两种图像压缩标准,其中,JPEG2000是在2000年提出的,在JPEG之后,目的是取代它们最初的基于DCT的JPEG标准。JPEG2000 (JPSEC)安全,作为JPEG2000标准的安全部分,它为JPEG2000图像的安全解决方案提供了框架。JPSEC被设计成一个开放的框架,以便在未来可以加入新的工具。然而,截至2017年,各种社交网络平台和数码相机使用的大多数图像格式仍然是有损的JPEG。鉴于此,JPEG委员会最近发起了一项名为JPEG Privacy & Security的新活动,旨在开发有损JPEG的各种安全功能,以实现图像信息的安全共享。因此,我们的安全机制设计主要集中在有损的JPEG标准上。在下图中给出了JPEG的基线编码过程,从中我们可以看到主要包含四个阶段:DCT变换,量化,锯齿扫描,熵编码。在JPEG解码过程中,所包含的阶段正好与编码的四个阶段相反。

li1-2786860-small

​ JPEG是用于图像存储和传输的通用图像压缩标准,大大节省了存储空间和传输带宽。因此,研究JPEG图像联合压缩与加密具有重要的理论和实用价值,引起了许多研究者的关注。现有的JPEG图像联合压缩和加密方案主要是通过将加密技术引入JPEG标准的一个阶段或多个阶段来实现的。在[12]中,AuYeung等人提出了在使用多个8x8转换的H.264和MPEG-4的转换阶段实现感知视频加密,也可用于JPEG图像加密。提出了三种不同转换的加密算法,并证明算法-3在加密和压缩性能之间具有最佳的平衡。他们的方案可以在不危及底层视频压缩标准压缩性能的前提下实现可控的加密程度,但是8 × 8块的独立加密技术扩散性能差,容易受到差分攻击。在[13]和[14]中,他们提出了JPEG量化阶段的加密方法,而这一阶段的加密技术往往伴随着其他阶段的加密技术,以提供更高的保护能力。后来,Qian等人在[15]中指出[13]中的算法不够安全,因为当对手使用所有霍夫曼编码并将所有附加位置零时,他/她可以重建平面图像的轮廓。为了提高保护能力,[15]建议首先随机选择JPEG的比特流片段的一部分,然后使用流密码加密所有的附加位,DC和AC霍夫曼编码在这些选定的片段内,最后得到较小尺寸的JPEG密码图像。因此,在有限的霍夫曼编码下,对手无法恢复平面图像的轮廓。对于在JPEG的熵编码阶段实现的加密,Wu等人[16]对不同的输入符号使用不同的Huffman表来实现JPEG图像加密。该方案可以在不牺牲压缩性能的情况下获得非常高的视觉退化,但它的格式不符合JPEG标准,因为解码器需要知道用于编码的Huffman表才能正确解压缩加密的比特流[17]。此外,[18]和[19]指出[16]的加密方案易受选择明文/已知明文攻击。

     在[20]中,Zhang等人指出一些联合图像压缩和加密密码系统存在安全缺陷,如[21]、[22]中的方法。他们说,密钥控制的间隔分配加密操作相当于没有扩散的混淆,这导致了一些经典攻击的漏洞,如选择明文/已知明文攻击。为了克服这一缺陷,他们认为加密方案不仅应该依赖于混淆扩散体系结构,而且还应该具有与明文相关的各种加密参数。

​ 在我们的工作中,我们提出了一个联合的图像压缩和加密方案,具有良好的扩散特性和良好的压缩效率。整个密码系统基于有损的JPEG标准,并且明文图像内容自适应,因为我们用于加密的密钥依赖于明文图像。为了生成明文图像敏感的密钥,我们使用以明文图像为输入的BLAKE2哈希算法,并输出256位随机散列值,表示为Key1,命名为加密密钥,用于控制以下加密操作。BLAKE2对输入发生的变化非常敏感,因此不同的输入明文图像会产生不同的Key1值,这些值必须对译码器可用以恢复密文图像。为了节省每次向解码器传输不同Key1的成本,我们将Key1通过一些嵌入规则嵌入到某个熵编码比特流中,嵌入位置由另一个256位密钥Key2决定,命名为嵌入密钥,它是已知的,且仅为编码器和解码器所知。密钥2可以在一段时间内保持不变,这是由编码器和解码器决定的,即使用于压缩和加密的明文图像发生了变化。我们的加密操作包括新的正交变换、直流系数加密和交流系数加密。实验结果表明,整个加密方案是有效的,具有良好的压缩和安全特性。

​ 我们工作的主要贡献可以总结如下。

  • 从明文图像生成加密密钥的机制可以极大地改善密码系统的扩散特性,从而提高方案对差分攻击的防护性能,这是许多联合压缩加密方案所缺乏的。
  • 通过在8阶DCT- ii流图中引入旋转角度,提出了一种编码效率与DCT相似的8阶DCT- ii正交变换,并将其用于由密钥交替控制的8 × 8块变换。
  • 在此基础上,提出了一种数据隐藏技术不破坏最终加密比特流的格式信息。
  • 整个方案具有JPEG压缩友好性,可以在加密能力和压缩效率之间达到很好的平衡。
加密操作实现

​ 详细的加密操作实现包括新的正交变换、块排列和异或运算的DC加密和数据嵌入的AC加密。它们的设计原则是实现三个目标:(a)抑制比特流大小增量,(b)格式兼容加密和(c)增强混淆和扩散性能。

​ 给定一个大小为H*W的明文图像,其中H和W为平面图像的高度和宽度,以像素为单位。我们将其作为BLAKE2算法的输入,产生一个256位的随机值Key1来控制加解密操作。另外一个256位的密钥Key2是预定义的,只有编码器和解码器知道。为了从Key1和Key2生成伪随机密钥流,我们还使用

BLAKE2算法以Key1和Key2为初始种子密钥,计算公式如下:

其中,H(·)是BLAKE2哈希函数,$K_0$是Key1或Key2。我们将这两个伪随机密钥流表示为 $K_{enc-1}$和 $K_{enc-2}$。

新的正交变换

在JPEG中,8 × 8 type-II DCT用于块的变换,因为当像素间[24]存在高相关性(这是大多数自然图像普遍存在的现象)时,它能提供高效的能量压缩性能,并具有可分性。在我们的工作中,我们修改了JPEG的转换阶段,允许更多的8 × 8块正交变换,而不仅仅是DCT。为了保持JPEG的优良压缩性能,我们新生成的变换的编码效率应该与DCT相似或略有下降。

DC系数加密

为了使加密后的图像变得更加混乱,在JPEG量化过程后,我们提出使用由Kenc−1控制的Fisher-Yates shuffle生成的置换向量置换所有的8阶量化块。

为了混淆一个n个元素的数组S,Fisher-Yates混淆如下:

for i <- n to 2 do

     `j <- random integer with 1 <= j <= i`

exchange S[j] and S[i]

end for

当在我们的加密方案中应用这个混淆算法时,S是 8*8块的原始顺序,n是8*8块的数量,每个循环中的整数从$K_{enc-1}$获得,可以描述为如下:

随机整数生成

+++

1)选择一个整数$r$满足 $2^r\geq n$​;

2)从 $K_{enc-1}$获取r位然后把他们转成一个数x,$0\leq x<2^r$

3)计算$t=\lfloor\frac{x}{n}\rfloor$,然后输出 $x-tn$作为随机整数x


在用S对所有的8*8量化块进行块排列操作后,我们用下式对每个8*8排列块改变DC系数去更好地提高扩散混淆特性

其中 $dc_i$​是第i个 8*8排列块的DC系数,i=1,2,…,(H*W)/64。为了恢复这些混淆的DC系数,可以用下面的式子

其中j从(H*W)/64开始,到2结束。DC系数混淆操作可以提高密码系统对差分攻击的抵抗能力,但可能会导致加密图像出现不规则的线条/条纹。

AC系数加密

为了节省明文图像发生变化时传输不同加密密钥的开销,我们将256位的Key1嵌入游程为0的非零交流系数中。选择这种非零的交流系数的原因是为了控制最终比特流的大小,因为在非零的交流系数之前存在更多的零意味着更长Huffman码来表示这个系数。

为了确保嵌入的Key1无法被攻击者使用,我们使用Kenc−2来决定这些256bit数据的嵌入位置。在我们的嵌入策略中,每一个合格的非零AC系数都携带了一位密钥比特。设对所有交流系数采用变长码(VLC)编码后,符合条件的非零交流系数个数为L (L > 256),我们使用Kenc−2控制Fisher-Yates洗牌来打乱这L个系数的位置,然后将前256个排列系数的位置保存到数组Index中,用于控制数据嵌入。整个操作下图所示。

li3-2786860-large

对于数据嵌入策略,当出现一个run为0的非零AC系数时,首先检查该系数的位置是否在数组Index中。如果不在则不改变该系数的Huffman编码结果。如果在,将系数的类别(即AC系数的二进制长度)增加1,然后查找JPEG的Huffman表为了得到它对应的Huffman代码,将更改后的Huffman代码与这个AC系数附加的比特连接起来产生新的熵编码Q。然后将Key1的一位数据追加到Q的最终位置,并生成Q∗。Q∗是最终被压缩和加密的比特流数据,将被传输给解码器进行解密和解压缩。下图给出了一个例子来更好地解释我们的数据嵌入策略。

li4-2786860-large

为了正确地从Q∗中提取嵌入的256位Key1,解码器需要知道Key2来产生Kenc−2并进行上图的操作,并得到256个嵌入位置数组的索引。给定一个交流系数的熵编码位,解码器首先通过查找交流系数的霍夫曼表来检查游程是否为0。如果游程为0,且符合条件的AC系数的位置在array Index中,则通过该非零AC系数的Huffman表所表示的类别获得附加的位,附加位中的最后一位是Key1的1位数据。下图给出了一个数据提取的例子,它是上图的反过程。

li5-2786860-large

值得注意的是,我们的数据嵌入方案不能适用于大小为10位的交流系数,这可能出现在QF大于或等于93时,否则,由于类别升级过程,8 × 8块中的系数超过64个的问题将会产生,从而导致非格式兼容问题。为了解决这一问题,我们在不同的QF值下采用不同的交流系数选择策略。当QF < 93时,考虑块内所有AC系数进行数据嵌入当QF≥93时,首先Z型扫描QF标度量化表,计算值“1”上次出现的位置,记为LastPos,因为经过DCT后,交流系数的范围为(−1024,1024),量化表中只有步长为’ 1 ‘才能产生10位量化AC系数。然后,我们从每个8 × 8量化块(LastP os + 1)的第一个系数开始,根据之字形顺序选择合格的交流系数。93到100之间的QF值对应的LastP os值分别为6、6、10、13、21、25、54和64。尽管如此,10位量化的交流系数出现在每个8 × 8块右下位置的概率非常低,因为在锯齿扫描顺序下,JPEG量化表中元素的值会增加。因此,在我们的实验中,当QF = 99和QF = 100时,我们设置LastP os = 30,所有的1857张图像都没有问题。

加密解密算法

本文提出的密码系统的实现过程如下图所示

li6-2786860-large

加解密算法描述如下。

+++

算法1:加密算法

+++

1: Load image I, and embedding key Key2

2: Key1 ← BLAKE2(I)

3: Kenc−1 ← BLAKE2(Key1 )

4: Kenc−2 ← BLAKE2(Key2 )

5: Raster scan image into non-overlapping 8 × 8 blocks

6: for each 8 × 8 block do

7: Pick 63 bits from Kenc−1

8: Select one horizontal transform and eight vertical transforms from TS1

9: Transform this 8 × 8 block using selected transforms, and quantize it

10: end for

11: Perform 8 × 8 blocks’ permutation, controlled by Kenc−1

12: Confuse DC coefficients using (5)

13: Embed Key1 into AC coefficients, controlled by Kenc−2

14: Entropy encoding all DC and AC coefficients, and produce encrypted bitstream Q∗

+++

+++

算法2:解密算法

+++

1: Kenc−2 ← BLAKE2(Key2 )

2: Entropy decoding Q∗, and extract Key1 using Kenc−2

3: Kenc−1 ← BLAKE2(Key1 )

4: Use (6) to recover confused DC coefficients

5: Put permuted 8 × 8 blocks’ back to their original positions, controlled by Kenc−1

6: for each 8 × 8 block do

7: Perform de-quantization on this block

8: Pick 63 bits from Kenc−1

9: Select one horizontal transform and eight vertical transforms from TS1

10: Inverse-transform the 8 × 8 block using selected transforms

11: end for

12: Produce the decrypted image

+++

实验代码
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
import cv2
import hashlib
import numpy as np
import matplotlib.pyplot as plt
from pyblake2 import blake2s
import base64


#计算DCT矩阵
def fast_DCT(num):
#计算A8
P8=np.array(
[[1,0,0,0,0,0,0,0],
[0,0,0,0,1,0,0,0],
[0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,1,0],
[0,1,0,0,0,0,0,0],
[0,0,0,0,0,1,0,0],
[0,0,0,1,0,0,0,0],
[0,0,0,0,0,0,0,1]])
P4=np.array(
[[1,0,0,0],
[0,0,1,0],
[0,1,0,0],
[0,0,0,1]])

M1=np.array(
[[np.sin(np.pi/16),0,0,np.cos(np.pi/16)],
[0,np.sin(5*np.pi/16),np.cos(5*np.pi/16),0],
[0,-np.sin(3*np.pi/16),np.cos(3*np.pi/16),0],
[-np.sin(7*np.pi/16),0,0,np.cos(7*np.pi/16)]])
M2=np.sqrt(2)/2*np.array(
[[1,1,0,0],
[1,-1,0,0],
[0,0,-1,1],
[0,0,1,1]])
M3=np.array(
[[1,0,0,0],
[0,-np.cos(np.pi/4),np.cos(np.pi/4),0],
[0,np.cos(np.pi/4),np.cos(np.pi/4),0],
[0,0,0,1]])
B2=np.sqrt(2)/2*np.array(
[[1,1],
[1,-1]])
R1=0
A1=1/np.sqrt(2)

# print(M3)
R4=M1.dot(M2).dot(M3)
B8=np.sqrt(2)/2*np.array(
[[1,0,0,0,0,0,0,1],
[0,1,0,0,0,0,1,0],
[0,0,1,0,0,1,0,0],
[0,0,0,1,1,0,0,0],
[0,0,0,1,-1,0,0,0],
[0,0,1,0,0,-1,0,0],
[0,1,0,0,0,0,-1,0],
[1,0,0,0,0,0,0,-1]])
#计算A4
P2=np.array([[1,0],[0,1]])
A2=np.sqrt(2)/2*np.array([[1,1],[1,-1]])
R2=np.array([[np.sin(np.pi/8),np.cos(np.pi/8)],[-np.sin(3*np.pi/8),np.cos(3*np.pi/8)]])
B4=np.sqrt(2)/2*np.array([[1,0,0,1],[0,1,1,0],[0,1,-1,0],[1,0,0,-1]])

tmp1=np.concatenate((P2.T.dot(A2),np.zeros((2,2))),axis=1)
tmp2=np.concatenate((np.zeros((2,2)),R2),axis=1)
tmp=np.concatenate((tmp1,tmp2))
A4=P4.dot(tmp).dot(B4)
set1=[0,np.pi]
set2=[0,np.pi/3,2*np.pi/3,np.pi]
a1=set1[int(num[0],2)]
a2=set2[int(num[1:3],2)]
a3=set2[int(num[3:5],2)]
a4=set2[int(num[5:7],2)]
T1=np.diag([np.cos(a1),np.cos(a1),np.cos(a2),np.cos(a2),np.cos(a3),np.cos(a4),np.cos(a4),np.cos(a3)])


tmp1=np.concatenate((P4.T.dot(A4),np.zeros((4,4))),axis=1)
tmp2=np.concatenate((np.zeros((4,4)),R4),axis=1)
tmp=np.concatenate((tmp1,tmp2))
A8=P8.dot(T1).dot(tmp).dot(B8)

return A8

'''
算法1加密函数
img:加密图像路径
key:密钥列表,大小为6(1、2、3为Logistic初始条件和参数;4、5为加密轮数和扩散轮数,6为异或扩散时的参数)
return:返回加密后的图像
'''
def encrypt(img,Key2):
#读取原始图像,cv2.imread()默认是用color模式读取的,保持原样读取要加上第二个参数-1,即CV_LOAD_IMAGE_GRAYSCALE
# im=cv2.imread(img,cv2.IMREAD_GRAYSCALE)
im=cv2.imread(img,-1)
#获取图像宽高和通道数
h,w=im.shape


#明文图像作为输入,生成一个256bit的Key1
with open(img,'rb') as f:

Key1=blake2s(f.read()).hexdigest().upper()
Key1=base64.b16decode(Key1)
#第二种方式
# h=blake2s()
# info=f.read()
# h.update(info)
# img_blake2=h.hexdigest()
#Key2是预定义的256bit的密钥,这里先假定与Key1相同
# Key2=Key1
# print(Key2)

#生成伪随机密钥流
K_enc1=base64.b16decode(blake2s(Key1).hexdigest().upper())
K_enc2=base64.b16decode(blake2s(Key2).hexdigest().upper())
#32bytes的key转成256bit的字符串
K_bit1=''
K_bit2=''
for i in range(len(K_enc1)):
K_bit1+=bin(K_enc1[i])[2:].rjust(8,'0')
K_bit2+=bin(K_enc2[i])[2:].rjust(8,'0')
# print(K_bit2)
#DCT的快速计算算法
#生成TS1集
TS1=[]

for i in range(9):
num=K_bit1[i*7:(i+1)*7]
A8=fast_DCT(num)
TS1.append(A8)
#标准亮度量化表
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]])
#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]
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')
pos_in_table+=1
code_value+=1
code_value <<=1
#AC哈夫曼编码表
# print(dc_huffman_table)
# return 0

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

#转成float类型
im=im.astype(np.float64)
#存储最后的哈夫曼编码
result=''
#分成8*8的块
for i in range(h//8):
for j in range(w//8):
block=im[i*8:(i+1)*8,j*8:(j+1)*8]
# print(block)
# return 0
#对每一个图像block块做正交变换
#每行采用相同的正交矩阵
for k in range(8):
block[k]=TS1[0].dot(block[k]).dot(TS1[0].T)
#每列采用不同的正交矩阵
for k in range(8):
block[:,k]=TS1[k+1].dot(block[:,k]).dot(TS1[k+1].T)
#数据量化
#同时也修改了im的值
# block[:]=np.round(block/Qy)
block=np.round(block/Qy)
im[i*8:(i+1)*8,j*8:(j+1)*8]=block
#量化块置乱
#量化块的数量im[0,...,n-1]
# print(im[0:8,0:8])
n=h*w//64
pos=0
for i in range(n-1,1,-1):
#交换S[i]与S[j]
# if i==n-1:
# print(pos)

#生成随机整数
#选择r,满足2**r>=i,r>=log(i)
r=int(np.ceil(np.log2(i)))
#从Kenc-1获取r位(循环获取)
if pos%256+r>255:
x=int(K_bit1[pos%256:]+K_bit1[r+pos%256-256],2)
else:
# print(r,pos%256,pos%256+r)
x=int(K_bit1[pos%256:pos%256+r],2)
pos+=1
j=x-(x//i)*i
# print(im.shape)
#交换S[i]和S[j]
# print(i,j,i//64*8,i//64*8+8)
# print(im[i//64*8:i//64*8+8,i%64*8:i%64*8+8],im[j//64*8:j//64*8+8,j%64*8:j%64*8+8])
tmp1=np.zeros((8,8))
tmp2=np.zeros((8,8))
# print(tmp1.shape)
tmp1[:]=im[i//64*8:i//64*8+8,i%64*8:i%64*8+8]
tmp2[:]=im[j//64*8:j//64*8+8,j%64*8:j%64*8+8]
im[j//64*8:j//64*8+8,j%64*8:j%64*8+8]=tmp1[:]
im[i//64*8:i//64*8+8,i%64*8:i%64*8+8]=tmp2[:]
# im[i//64*8:i//64*8+8,i%64*8:i%64*8+8],im[j//64*8:j//64*8+8,j%64*8:j%64*8+8]=im[j//64*8:j//64*8+8,j%64*8:j%64*8+8],im[i//64*8:i//64*8+8,i%64*8:i%64*8+8]
# print(im[i//64*8:i//64*8+8,i%64*8:i%64*8+8],im[j//64*8:j//64*8+8,j%64*8:j%64*8+8])
# return 0
# print(im)
# print(im[8:16,8:16])
#DC系数异或
for i in range(1,n):
# print(im[i//64*8][i%64*8])
im[i//64*8][i%64*8]=int(im[i//64*8][i%64*8])^int(im[(i-1)//64*8][(i-1)%64*8])
# print(im[i//64*8][i%64*8])
# return 0

# print(im)
# print(im[8:16,8:16])
#Z型扫描
#把256bit Key1嵌入AC系数
arr_sum=[]
a=0
for i in range(h//8):
for j in range(w//8):
block=im[i*8:(i+1)*8,j*8:(j+1)*8]
arr=[0]*64
for k in range(64):
tmp=int(block[int(k/8)][k%8])
arr[ZigZag[k]]=tmp;
arr_sum+=arr
# print(arr_sum)
# print(arr[0])
# print(arr_sum)
# print(h,w,len(arr_sum))
# return 0

#存储满足游程为0的非0AC系数在arr_sum中的索引
qualified_AC=[]
for i in range(h*w//64):
arr=arr_sum[i*64:(i+1)*64]
#统计arr数组中非零元素
notnull_num=0
for j in arr:
if j!=0:
notnull_num+=1
#RLE编码
#标识连续0的个数
time=0
#AC系数嵌入Key1
#得到符合条件的L个非零AC系数
for k in range(1,64):
if arr[k]==0 and time<15:
time+=1
else:
#run为0的非零AC系数
if time==0:
data=arr[k]
qualified_AC.append(i*64+k)
time=0

L=len(qualified_AC)
pos=0
# print(L)
# print(qualified_AC)
# return 0
#使用Kenc-2控制Fisher-Yates洗牌
for i in range(L-1,1,-1):
#交换arr_sum[i]与arr_sum[j]
#生成随机整数
#选择r,满足2**r>=i,r>=log(i)
r=int(np.ceil(np.log2(i)))
# print(r)
#从Kenc-2获取r位(循环获取)
if pos%256+r>255:
x=int(K_bit2[pos%256:]+K_bit2[r+pos%256-256],2)
else:
# print(r,pos%256,pos%256+r)
x=int(K_bit2[pos%256:pos%256+r],2)
#上面也要改不是n是i

pos+=1

j=x-(x//i)*i
# print(i,j)
#交换arr_sum[i]和arr_sum[j]
# print(arr_sum[i],arr_sum[j])
qualified_AC[i],qualified_AC[j]=qualified_AC[j],qualified_AC[i]
#arr_sum中非零AC系数交换相应的位置,这一步应该没有,否则无法逆排列导致无法解密
# arr_sum[qualified_AC[i]],arr_sum[qualified_AC[j]]=arr_sum[qualified_AC[j]],arr_sum[qualified_AC[i]]
# print(arr_sum[i],arr_sum[j])
#Fisher-Yates洗牌后的顺序
# print(qualified_AC)
#取qualified_AC前256个元素,得到数组嵌入索引index数组,大小为256
index_arr=qualified_AC[:256]

index_dict={}
for i in range(256):
index_dict[qualified_AC[i]]=i
# dc_num=0
# return arr_sum
#huffman编码前的数据
# print('加密过程,huffman编码前的数据:',arr_sum)
#*****************测试************
# return arr_sum
#huffman编码
for i in range(h*w//64):
arr=arr_sum[i*64:(i+1)*64]
#统计arr数组中非零元素
notnull_num=0
for j in arr:
if j!=0:
notnull_num+=1
#RLE编码
#标识连续0的个数
time=0

for k in range(64):
if arr[k]==0 and time<15:
#判断是否是DC系数
if k==0:
result+=dc_huffman_table[0]
# dc_num+=1
else:
time+=1
else:
#BIT编码
#处理括号中第二个数
data=arr[k]
data2=bin(np.abs(data))[2:]
data1=len(data2)
if data<0:
data2=bin(np.abs(data)^(2**data1-1))[2:].rjust(data1,'0')
elif data==0:
data1=0
data2=''

#判断是否需要嵌入K-enc2
if time==0 and (index_dict.get(i*64+k) or index_dict.get(i*64+k)==0):
# print(index_dict.get(i*64+k))
data1+=1
data2+=K_bit2[index_dict.get(i*64+k)]

#哈夫曼编码,序列化
# print(time,data1,data2)
#直流
if k==0:
result+=dc_huffman_table[time*16+data1]
# print(dc_huffman_table[time*16+data1],data2)
else:
result+=ac_huffman_table[time*16+data1]
# print(ac_huffman_table[time*16+data1],data2)
result+=data2


time=0
#判断是否要添加EOB
if int(arr[k])!=0:
notnull_num-=1
if notnull_num==0 and k<63:
#添加EOB
result+='1010'
break
# print('加密过程,huffman编码后的数据:')
# print(result[:100])
# print('dc_num:',dc_num)
#补足为8的整数倍
if len(result)%8!=0:
result=result.ljust(len(result)+8-len(result)%8,'0')
result=hex(int(result,2))[2:]
res=''
# print(result)

#添加jpeg文件头
#SOI(文件头)
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图像基本信息
res+='FFC0000B08'
res+=hex(h)[2:].rjust(4,'0')
res+=hex(w)[2:].rjust(4,'0')
res+='01012200'
#DHT定义huffman表
res+='FFC4001F0000010501010101010100000000000000'
for i in standard_dc_values:
res+=hex(i)[2:].rjust(2,'0')
res+='FFC400B5100002010303020403050504040000017D'
for i in standard_ac_values:
res+=hex(i)[2:].rjust(2,'0')
#SOS扫描行开始
res+='FFDA0008010100003F00'
#压缩的图像数据(一个个扫描行),数据存放顺序是从左到右、从上到下
res+=result
# print(result[:8])

#EOI文件尾
res+='FFD9'
# print(len(res))
# print(res)
return res

'''
jpeg解码
img:加密图像路径
key:密钥列表,大小为6(1、2、3为Logistic初始条件和参数;4、5为加密轮数和扩散轮数,6为异或扩散时的参数)
return:返回解密后的图像
'''

def decrypt(img,Key2):
#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()


#huffman后拼接的图像16进制数据
# print(res)
#生成伪随机密钥流
K_enc2=base64.b16decode(blake2s(Key2).hexdigest().upper())
#32bytes的key转成256bit的字符串
K_bit2=''
for i in range(len(K_enc2)):
K_bit2+=bin(K_enc2[i])[2:].rjust(8,'0')
# print(K_bit2)

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_ac_values=res[312:636]
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


#获取压缩的图像数据
result=res[656:-4]
# print(result)
#得到哈夫曼编码后的01字符串,注意左边用0进行补齐
result=bin(int(result,16))[2:].rjust(len(result)*4,'0')

# print('解密过程huffman编码后的数据',result[:100])
img_data=np.zeros((h,w))
pos=0
arr_sum=[]
for j in range(h//8):
for k in range(w//8):
#逆dc哈夫曼编码
#正向最大匹配
arr=[0]
#计算EOB块中0的个数
num=0
for i in range(8,2,-1):
tmp=reverse_dc_huffman_table.get(result[pos:pos+i])
#匹配成功
if(tmp):
#DC系数为0
if tmp=='00':
arr[0]=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=-1*(int(data2,2)^(2**data1-1))
else:
data=int(data2,2)
arr[0]=data
pos+=data1
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):
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':
#负数
data=-1*(int(data2,2)^(2**data1-1))
else:
data=int(data2,2)
num+=time+1
#time个0
arr+=([0]*time)
#非零值或最后一个单元0
arr.append(data)

break

arr_sum+=arr
# print(len(arr_sum))
# return 0
# huffman编码前的数据,应该有256个不同的
# print('解密过程huffman编码前的数据:',arr_sum)
#*****************测试
# return arr_sum

#存储满足游程为0的非0AC系数在arr_sum中的索引
qualified_AC=[]
for i in range(h*w//64):
arr=arr_sum[i*64:(i+1)*64]
#RLE编码
#标识连续0的个数
time=0
#AC系数嵌入Key1
#得到符合条件的L个非零AC系数
for k in range(1,64):
if arr[k]==0 and time<15:
time+=1
else:
#run为0的非零AC系数
if time==0:
data=arr[k]
qualified_AC.append(i*64+k)
time=0

L=len(qualified_AC)
# print(L)
#得到非零AC系数洗牌前的顺序
# print(qualified_AC)
pos=0
# print(L)
# print(qualified_AC)
# return 0
#使用Kenc-2控制Fisher-Yates洗牌
for i in range(L-1,1,-1):
#交换arr_sum[i]与arr_sum[j]
#生成随机整数
#选择r,满足2**r>=i,r>=log(i)
r=int(np.ceil(np.log2(i)))
# print(r)
#从Kenc-2获取r位(循环获取)
if pos%256+r>255:
x=int(K_bit2[pos%256:]+K_bit2[r+pos%256-256],2)
else:
# print(r,pos%256,pos%256+r)
x=int(K_bit2[pos%256:pos%256+r],2)
#上面也要改不是n是i

pos+=1

j=x-(x//i)*i
# print(i,j)
#交换arr_sum[i]和arr_sum[j]
# print(arr_sum[i],arr_sum[j])
qualified_AC[i],qualified_AC[j]=qualified_AC[j],qualified_AC[i]
#arr_sum中非零AC系数交换相应的位置,这一步应该是没有的
# arr_sum[qualified_AC[i]],arr_sum[qualified_AC[j]]=arr_sum[qualified_AC[j]],arr_sum[qualified_AC[i]]
# print(arr_sum[i],arr_sum[j])
#Fisher-Yates洗牌后的顺序
# print(qualified_AC)
index_arr=qualified_AC[:256]

index_dict={}
for i in range(256):
index_dict[qualified_AC[i]]=i

#得到嵌入的256位的K_bit1
K_bit1=''
for i in range(256):
#获取K_bit1的第i位,嵌入到arr_num中的索引是qualified_AC[i]
data=arr_sum[qualified_AC[i]]
if data<0:
#取反
K_bit1+=str(int(bin(np.abs(data))[-1])^1)
else:
K_bit1+=bin(data)[-1]
# print(K_bit1)

# if K_bit1==K_bit2:
# print('yes')

#逆Z型扫描
n=h*w//64
# print('解密中z型扫描之后:',arr_sum)
img_data=np.zeros((h,w),dtype=np.float64)
for i in range(n):
arr=arr_sum[i*64:(i+1)*64]

block=np.zeros((8,8))
for j in range(64):
block[int(j/8)][j%8]=arr[ZigZag[j]]
img_data[i//(w//8)*8:i//(w//8)*8+8,i%(w//8)*8:i%(w//8)*8+8]=block

# print(img_data)
# print(img_data[8:16,8:16])
#逆DC系数异或,倒叙要设置步长
for i in range(n-1,0,-1):
# print(img_data[i//64*8][i%64*8])
img_data[i//64*8][i%64*8]=int(img_data[i//64*8][i%64*8])^int(img_data[(i-1)//64*8][(i-1)%64*8])

# print(img_data[8:16,8:16])

#逆量化块置乱
#计算出从前往后时pos开始的位置
pos=n-3
for i in range(2,n):
#交换S[i]与S[j]
# if i==n-1:
# print(pos)

#生成随机整数
#选择r,满足2**r>=i,r>=log(i)
r=int(np.ceil(np.log2(i)))
#从Kenc-1获取r位(循环获取)
if pos%256+r>255:
x=int(K_bit1[pos%256:]+K_bit1[r+pos%256-256],2)
else:
# print(r,pos%256,pos%256+r)
x=int(K_bit1[pos%256:pos%256+r],2)
pos-=1
j=x-(x//i)*i
# print(im.shape)
#交换S[i]和S[j]
# print(i,j,i//64*8,i//64*8+8)
# print(im[i//64*8:i//64*8+8,i%64*8:i%64*8+8],im[j//64*8:j//64*8+8,j%64*8:j%64*8+8])
tmp1=np.zeros((8,8))
tmp2=np.zeros((8,8))
# print(tmp1.shape)
tmp1[:]=img_data[i//64*8:i//64*8+8,i%64*8:i%64*8+8]
tmp2[:]=img_data[j//64*8:j//64*8+8,j%64*8:j%64*8+8]
img_data[j//64*8:j//64*8+8,j%64*8:j%64*8+8]=tmp1[:]
img_data[i//64*8:i//64*8+8,i%64*8:i%64*8+8]=tmp2[:]
# im[i//64*8:i//64*8+8,i%64*8:i%64*8+8],im[j//64*8:j//64*8+8,j%64*8:j%64*8+8]=im[j//64*8:j//64*8+8,j%64*8:j%64*8+8],im[i//64*8:i//64*8+8,i%64*8:i%64*8+8]
# print(im[i//64*8:i//64*8+8,i%64*8:i%64*8+8],im[j//64*8:j//64*8+8,j%64*8:j%64*8+8])
# return 0

# print(img_data[8:16,8:16])
TS1=[]
for i in range(9):
num=K_bit1[i*7:(i+1)*7]
A8=fast_DCT(num)
TS1.append(A8)
#得到量化块
#分成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)
block=block*Qy


#对每一个图像block块做逆正交变换
#每列采用不同的正交矩阵

for k in range(8):
block[:,k]=(TS1[k+1].T).dot(block[:,k]).dot(TS1[k+1])

#每行采用相同的正交矩阵
for k in range(8):
block[k]=(TS1[0].T).dot(block[k]).dot(TS1[0])

img_data[i*8:(i+1)*8,j*8:(j+1)*8]=block
# print(img_data[i*8:(i+1)*8,j*8:(j+1)*8])
# return 0




# dc_num=0
#熵编码之前
# print(arr_sum)
# return arr_sum
# print(len(arr_sum))
# #逆ZigZag扫描,得到block量化块
# block=np.zeros((8,8))
# 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
return img_data



def main():
#原始图像路径,灰度图像
img_path='./lena.jpg'
# img_path='f:/black.jpg'
#读取原始图像,cv2.imread()默认是用color模式读取的,保持原样读取要加上第二个参数-1
img=cv2.imread(img_path,-1)
# print(img)
key=b'\xba0s\x8a\xcd\xe6j\xb8\x1eL"\xf4\xa1{lvC\xe9xsM\x01t\x05\x08\x00;\xff\x97\xf6\x8cW'

# print(key)
# 加密图像
img_encrypt=encrypt(img_path,key)
# print(len(img_encrypt))
# print(img_encrypt.upper()[:8])
img_encrypt_path='./img_encrypt.jpg'
#存储加密后的图像
with open(img_encrypt_path,'wb') as f:
f.write(base64.b16decode(img_encrypt.upper()))
#解密图像
img_decrypt=decrypt(img_encrypt_path,key)
# diff_sum=0
# print(img_decrypt)
# for i in range(len(img_encrypt)):
# if img_encrypt[i]!=img_decrypt[i]:
# diff_sum+=1
# print(diff_sum)


#结果展示
plt.rcParams['font.sans-serif'] = ['SimHei'] # 中文乱码
#子图1,原始图像
plt.subplot(121)
#imshow()对图像进行处理,画出图像,show()进行图像显示
plt.imshow(img,cmap=plt.cm.gray)
plt.title('原始图像')
#不显示坐标轴
plt.axis('off')

#子图2,加密后图像
plt.subplot(122)
plt.imshow(img_decrypt,cmap=plt.cm.gray)
plt.title('解密图像')
plt.axis('off')
plt.show()



# print(img_encrypt)

if __name__ == '__main__':
main()

实验结果

result

改进代码
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
import cv2
import hashlib
import numpy as np
import matplotlib.pyplot as plt
from pyblake2 import blake2s
import base64


#计算DCT矩阵
def fast_DCT(num):
#计算A8
P8=np.array(
[[1,0,0,0,0,0,0,0],
[0,0,0,0,1,0,0,0],
[0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,1,0],
[0,1,0,0,0,0,0,0],
[0,0,0,0,0,1,0,0],
[0,0,0,1,0,0,0,0],
[0,0,0,0,0,0,0,1]])
P4=np.array(
[[1,0,0,0],
[0,0,1,0],
[0,1,0,0],
[0,0,0,1]])

M1=np.array(
[[np.sin(np.pi/16),0,0,np.cos(np.pi/16)],
[0,np.sin(5*np.pi/16),np.cos(5*np.pi/16),0],
[0,-np.sin(3*np.pi/16),np.cos(3*np.pi/16),0],
[-np.sin(7*np.pi/16),0,0,np.cos(7*np.pi/16)]])
M2=np.sqrt(2)/2*np.array(
[[1,1,0,0],
[1,-1,0,0],
[0,0,-1,1],
[0,0,1,1]])
M3=np.array(
[[1,0,0,0],
[0,-np.cos(np.pi/4),np.cos(np.pi/4),0],
[0,np.cos(np.pi/4),np.cos(np.pi/4),0],
[0,0,0,1]])
B2=np.sqrt(2)/2*np.array(
[[1,1],
[1,-1]])
R1=0
A1=1/np.sqrt(2)

# print(M3)
R4=M1.dot(M2).dot(M3)
B8=np.sqrt(2)/2*np.array(
[[1,0,0,0,0,0,0,1],
[0,1,0,0,0,0,1,0],
[0,0,1,0,0,1,0,0],
[0,0,0,1,1,0,0,0],
[0,0,0,1,-1,0,0,0],
[0,0,1,0,0,-1,0,0],
[0,1,0,0,0,0,-1,0],
[1,0,0,0,0,0,0,-1]])
#计算A4
P2=np.array([[1,0],[0,1]])
A2=np.sqrt(2)/2*np.array([[1,1],[1,-1]])
R2=np.array([[np.sin(np.pi/8),np.cos(np.pi/8)],[-np.sin(3*np.pi/8),np.cos(3*np.pi/8)]])
B4=np.sqrt(2)/2*np.array([[1,0,0,1],[0,1,1,0],[0,1,-1,0],[1,0,0,-1]])

tmp1=np.concatenate((P2.T.dot(A2),np.zeros((2,2))),axis=1)
tmp2=np.concatenate((np.zeros((2,2)),R2),axis=1)
tmp=np.concatenate((tmp1,tmp2))
A4=P4.dot(tmp).dot(B4)
set1=[0,np.pi]
set2=[0,np.pi/3,2*np.pi/3,np.pi]
a1=set1[int(num[0],2)]
a2=set2[int(num[1:3],2)]
a3=set2[int(num[3:5],2)]
a4=set2[int(num[5:7],2)]
T1=np.diag([np.cos(a1),np.cos(a1),np.cos(a2),np.cos(a2),np.cos(a3),np.cos(a4),np.cos(a4),np.cos(a3)])


tmp1=np.concatenate((P4.T.dot(A4),np.zeros((4,4))),axis=1)
tmp2=np.concatenate((np.zeros((4,4)),R4),axis=1)
tmp=np.concatenate((tmp1,tmp2))
A8=P8.dot(T1).dot(tmp).dot(B8)

return A8

'''
算法1加密函数
img:加密图像路径
key:密钥列表,大小为6(1、2、3为Logistic初始条件和参数;4、5为加密轮数和扩散轮数,6为异或扩散时的参数)
return:返回加密后的图像
'''
def encrypt(img,Key2):
#读取原始图像,cv2.imread()默认是用color模式读取的,保持原样读取要加上第二个参数-1,即CV_LOAD_IMAGE_GRAYSCALE
# im=cv2.imread(img,cv2.IMREAD_GRAYSCALE)
im=cv2.imread(img,-1)
#获取图像宽高和通道数
h,w=im.shape


#明文图像作为输入,生成一个256bit的Key1
with open(img,'rb') as f:
Key1=blake2s(f.read()).hexdigest().upper()
Key1=base64.b16decode(Key1)
#第二种方式
# h=blake2s()
# info=f.read()
# h.update(info)
# img_blake2=h.hexdigest()
#Key2是预定义的256bit的密钥,这里先假定与Key1相同
# Key2=Key1
# print(Key2)

#生成伪随机密钥流
K_enc1=base64.b16decode(blake2s(Key1).hexdigest().upper())
K_enc2=base64.b16decode(blake2s(Key2).hexdigest().upper())
#32bytes的key转成256bit的字符串
K_bit1=''
K_bit2=''
for i in range(len(K_enc1)):
K_bit1+=bin(K_enc1[i])[2:].rjust(8,'0')
K_bit2+=bin(K_enc2[i])[2:].rjust(8,'0')
# print(K_bit2)
#DCT的快速计算算法
#生成TS1集
TS1=[]

for i in range(9):
num=K_bit1[i*7:(i+1)*7]
A8=fast_DCT(num)
TS1.append(A8)
#标准亮度量化表
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]])
#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]
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')
pos_in_table+=1
code_value+=1
code_value <<=1
#AC哈夫曼编码表
# print(dc_huffman_table)
# return 0

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

#转成float类型
im=im.astype(np.float64)
#存储最后的哈夫曼编码
result=''
#分成8*8的块
for i in range(h//8):
for j in range(w//8):
block=im[i*8:(i+1)*8,j*8:(j+1)*8]
# print(block)
# return 0
#对每一个图像block块做正交变换
#每行采用相同的正交矩阵
# block=cv2.dct(block)
# block=TS1[0].dot(block).dot(TS1[0].T)
for k in range(8):
block[k]=TS1[0].dot(block[k]).dot(TS1[0].T)
#每列采用不同的正交矩阵
for k in range(8):
block[:,k]=TS1[k+1].dot(block[:,k]).dot(TS1[k+1].T)
#数据量化
#同时也修改了im的值
# block[:]=np.round(block/Qy)
block=np.round(block/Qy)
im[i*8:(i+1)*8,j*8:(j+1)*8]=block
#量化块置乱
#量化块的数量im[0,...,n-1]
# print(im[0:8,0:8])
# print('********************')
n=h*w//64
pos=0
for i in range(n-1,1,-1):
#交换S[i]与S[j]
# if i==n-1:
# print(pos)

#生成随机整数
#选择r,满足2**r>=i,r>=log(i)
r=int(np.ceil(np.log2(i)))
#从Kenc-1获取r位(循环获取)
if pos%256+r>255:
x=int(K_bit1[pos%256:]+K_bit1[r+pos%256-256],2)
else:
# print(r,pos%256,pos%256+r)
x=int(K_bit1[pos%256:pos%256+r],2)
pos+=1
j=x-(x//i)*i
# print(im.shape)
#交换S[i]和S[j]
# print(i,j,i//64*8,i//64*8+8)
# print(im[i//64*8:i//64*8+8,i%64*8:i%64*8+8],im[j//64*8:j//64*8+8,j%64*8:j%64*8+8])
tmp1=np.zeros((8,8))
tmp2=np.zeros((8,8))
# print(tmp1.shape)
tmp1[:]=im[i//64*8:i//64*8+8,i%64*8:i%64*8+8]
tmp2[:]=im[j//64*8:j//64*8+8,j%64*8:j%64*8+8]
im[j//64*8:j//64*8+8,j%64*8:j%64*8+8]=tmp1[:]
im[i//64*8:i//64*8+8,i%64*8:i%64*8+8]=tmp2[:]
# im[i//64*8:i//64*8+8,i%64*8:i%64*8+8],im[j//64*8:j//64*8+8,j%64*8:j%64*8+8]=im[j//64*8:j//64*8+8,j%64*8:j%64*8+8],im[i//64*8:i//64*8+8,i%64*8:i%64*8+8]
# print(im[i//64*8:i//64*8+8,i%64*8:i%64*8+8],im[j//64*8:j//64*8+8,j%64*8:j%64*8+8])
# return 0
# print(im)
# print(im[8:16,8:16])
#DC系数异或
for i in range(1,n):
# print(im[i//64*8][i%64*8])
im[i//64*8][i%64*8]=int(im[i//64*8][i%64*8])^int(im[(i-1)//64*8][(i-1)%64*8])
# print(im[i//64*8][i%64*8])
# return 0

# print(im)
# print(im[8:16,8:16])
#Z型扫描
#把256bit Key1嵌入AC系数


arr_sum=[]
a=0
for i in range(h//8):
for j in range(w//8):
block=im[i*8:(i+1)*8,j*8:(j+1)*8]
arr=[0]*64
for k in range(64):
tmp=int(block[int(k/8)][k%8])
arr[ZigZag[k]]=tmp;
arr_sum+=arr
# print(arr_sum)
# print(arr[0])
# print(arr_sum)
# print(h,w,len(arr_sum))
# return 0

#存储满足游程为0的非0AC系数在arr_sum中的索引
qualified_AC=[]
for i in range(h*w//64):
arr=arr_sum[i*64:(i+1)*64]
#统计arr数组中非零元素
notnull_num=0
for j in arr:
if j!=0:
notnull_num+=1
#RLE编码
#标识连续0的个数
time=0
#AC系数嵌入Key1
#得到符合条件的L个非零AC系数
for k in range(1,64):
if arr[k]==0 and time<15:
time+=1
else:
#run为0的非零AC系数
if time==0:
data=arr[k]
qualified_AC.append(i*64+k)
time=0

L=len(qualified_AC)
pos=0
# print(L)
# print(qualified_AC)
# return 0
#使用Kenc-2控制Fisher-Yates洗牌
for i in range(L-1,1,-1):
#交换arr_sum[i]与arr_sum[j]
#生成随机整数
#选择r,满足2**r>=i,r>=log(i)
r=int(np.ceil(np.log2(i)))
# print(r)
#从Kenc-2获取r位(循环获取)
if pos%256+r>255:
x=int(K_bit2[pos%256:]+K_bit2[r+pos%256-256],2)
else:
# print(r,pos%256,pos%256+r)
x=int(K_bit2[pos%256:pos%256+r],2)
#上面也要改不是n是i

pos+=1

j=x-(x//i)*i
# print(i,j)
#交换arr_sum[i]和arr_sum[j]
# print(arr_sum[i],arr_sum[j])
qualified_AC[i],qualified_AC[j]=qualified_AC[j],qualified_AC[i]
#arr_sum中非零AC系数交换相应的位置,这一步应该没有,否则无法逆排列导致无法解密
# arr_sum[qualified_AC[i]],arr_sum[qualified_AC[j]]=arr_sum[qualified_AC[j]],arr_sum[qualified_AC[i]]
# print(arr_sum[i],arr_sum[j])
#Fisher-Yates洗牌后的顺序
# print(qualified_AC)
#取qualified_AC前256个元素,得到数组嵌入索引index数组,大小为256
index_arr=qualified_AC[:256]

index_dict={}
for i in range(256):
index_dict[qualified_AC[i]]=i
# dc_num=0
# return arr_sum
#huffman编码前的数据
# print('加密过程,huffman编码前的数据:',arr_sum)
#*****************测试************
# return arr_sum

#huffman编码
prev=0
# print('加密中huffman编码前的一维64大小的块')
for i in range(h*w//64):
arr=arr_sum[i*64:(i+1)*64]
# print(arr)
#统计arr数组中非零元素
notnull_num=0
for j in arr:
if j!=0:
notnull_num+=1
#RLE编码
#标识连续0的个数
time=0
#处理DC
if arr[0]!=0:
notnull_num-=1
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]
result+=data2
prev=arr[0]
for k in range(1,64):
if notnull_num==0:
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)
if data<0:
data2=bin(np.abs(data)^(2**data1-1))[2:].rjust(data1,'0')
elif data==0:
data1=0
data2=''

#判断是否需要嵌入K-enc1
if time==0 and (index_dict.get(i*64+k) or index_dict.get(i*64+k)==0):
# print(index_dict.get(i*64+k))
data1+=1
data2+=K_bit1[index_dict.get(i*64+k)]

#哈夫曼编码,序列化
# print(time,data1,data2)
result+=ac_huffman_table[time*16+data1]
# print(ac_huffman_table[time*16+data1],data2)
result+=data2
time=0
#判断是否要添加EOB
if int(arr[k])!=0:
notnull_num-=1

# print('加密过程,huffman编码后的数据:')
# print(result[:100])
# print('dc_num:',dc_num)

#补足为8的整数倍
if len(result)%8!=0:
result=result.ljust(len(result)+8-len(result)%8,'0')
# print('加密中huffman编码后的01串')
# print('长度',len(result))
# print('前100',result[:100])
# print('后100',result[-100:])
# test0=hex(int(result,2))[2:]
# print(len(test0))

# print('加密过程huffman编码后的数据',result[-100:])
# print('前100',result[:100])
# print('后100',result[-100:])
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
res=''
# print(result)

#添加jpeg文件头
#SOI(文件头)
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图像基本信息
res+='FFC0000B08'
res+=hex(h)[2:].rjust(4,'0')
res+=hex(w)[2:].rjust(4,'0')
res+='01011100'
#DHT定义huffman表
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扫描行开始
res+='FFDA0008010100003F00'
#压缩的图像数据(一个个扫描行),数据存放顺序是从左到右、从上到下
res+=result
# print(result[:8])

#EOI文件尾
res+='FFD9'
# print(len(res))
# print(res)
return res

'''
jpeg解码
img:加密图像路径
key:密钥列表,大小为6(1、2、3为Logistic初始条件和参数;4、5为加密轮数和扩散轮数,6为异或扩散时的参数)
return:返回解密后的图像
'''

def decrypt(img,Key2):
#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()

#huffman后拼接的图像16进制数据
# print(res)
#生成伪随机密钥流
K_enc2=base64.b16decode(blake2s(Key2).hexdigest().upper())
#32bytes的key转成256bit的字符串
K_bit2=''
for i in range(len(K_enc2)):
K_bit2+=bin(K_enc2[i])[2:].rjust(8,'0')
# print(K_bit2)

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,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d]
standard_ac_nrcodes=[0]*16
for i in range(16):
standard_ac_nrcodes[i]=int(res[280+i*2:282+i*2],16)
#生成逆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

#生成ac哈夫曼表
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[(int)(standard_ac_values[pos_in_table*2:pos_in_table*2+2],16)]=bin(code_value)[2:].rjust(i,'0')
pos_in_table+=1
code_value+=1
code_value <<=1
#获取压缩的图像数据
# result=res[656:-4]
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

# print(len(result))

# print('解密过程huffman编码后的数据')
# print('前100',result[:100])
# print('后100',result[-100:])
# print(result)
#得到哈夫曼编码后的01字符串,注意左边用0进行补齐
result=bin(int(result,16))[2:].rjust(len(result)*4,'0')
# print('解密中huffman编码后的01串')
# print('长度',len(result))
# print('前100',result[:100])
# print('后100',result[-100:])


#到这一步都是没问题的,忘了考虑嵌入密钥这个事情了,啊啊啊啊
img_data=np.zeros((h,w))
pos=0
prev=0
tt=0
arr_sum=[]
# print('hahahahhahah')
# print(h//8)
# print(result[168086:])
# print('解密中huffman编码前的一维64大小的块')
for j in range(h//8):
for k in range(w//8):
#逆dc哈夫曼编码
#正向最大匹配
arr=[0]
start=pos
#计算EOB块中0的个数
num=0
tt=pos
# print(h//8*j+k,k)
for i in range(8,2,-1):
tmp=reverse_dc_huffman_table.get(result[pos:pos+i])
#匹配成功
# if(j==0 and k==60):
# print(pos)
# print(result[pos:200],tmp)
if(tmp):
pos+=i
num+=1
#DC系数为0
if tmp=='00':
arr[0]=0+prev
prev=arr[0]
break

time=0
# num+=
data1=int(tmp[1],16)
data2=result[pos:pos+data1]
# if(j==24 and k==26):
# print(data2)
data2=data2 if data2 else '0'
# print(data1,data2)
# data=int(data2,2)

if data2[0]=='0':
#负数
data=-1*(int(data2,2)^(2**data1-1))
else:
data=int(data2,2)
arr[0]=data+prev
prev=arr[0]
pos+=data1
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==63 and k==61):
# print(pos)
# print(i,result[pos:pos+100],tmp)
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':
#负数
data=-1*(int(data2,2)^(2**data1-1))
else:
data=int(data2,2)
num+=time+1
#time个0
arr+=([0]*time)
#非零值或最后一个单元0
arr.append(data)
break
# print(j*64+k,pos-start)
# print(result[start:pos])
# print('解密中huffman编码前的一维64大小的块')
# print(arr)
arr_sum+=arr
# print(j,k)
# if(j<2 and k<61):

# print('*********************',len(arr))
# print(arr_sum)
# print(arr_sum[:])
# pass


# print(len(arr_sum))
# print()
# return 0
# huffman编码前的数据,应该有256个不同的
# print('解密过程huffman编码前的数据:',arr_sum)
#*****************测试
# return arr_sum
# print('hahahahhahah')
#存储满足游程为0的非0AC系数在arr_sum中的索引
# print('加密过程嵌入后的arr_sum',arr_sum[:100])
qualified_AC=[]
for i in range(h*w//64):
arr=arr_sum[i*64:(i+1)*64]
#RLE编码
#标识连续0的个数
time=0
#AC系数嵌入Key1
#得到符合条件的L个非零AC系数
for k in range(1,64):
if arr[k]==0 and time<15:
time+=1
else:
#run为0的非零AC系数
if time==0:
data=arr[k]
qualified_AC.append(i*64+k)
time=0

L=len(qualified_AC)
# print(L)
#得到非零AC系数洗牌前的顺序
# print(qualified_AC)
pos=0
# print(L)
# print(qualified_AC)
# return 0
#使用Kenc-2控制Fisher-Yates洗牌
for i in range(L-1,1,-1):
#交换arr_sum[i]与arr_sum[j]
#生成随机整数
#选择r,满足2**r>=i,r>=log(i)
r=int(np.ceil(np.log2(i)))
# print(r)
#从Kenc-2获取r位(循环获取)
if pos%256+r>255:
x=int(K_bit2[pos%256:]+K_bit2[r+pos%256-256],2)
else:
# print(r,pos%256,pos%256+r)
x=int(K_bit2[pos%256:pos%256+r],2)
#上面也要改不是n是i

pos+=1

j=x-(x//i)*i
# print(i,j)
#交换arr_sum[i]和arr_sum[j]
# print(arr_sum[i],arr_sum[j])
qualified_AC[i],qualified_AC[j]=qualified_AC[j],qualified_AC[i]
#arr_sum中非零AC系数交换相应的位置,这一步应该是没有的
# arr_sum[qualified_AC[i]],arr_sum[qualified_AC[j]]=arr_sum[qualified_AC[j]],arr_sum[qualified_AC[i]]
# print(arr_sum[i],arr_sum[j])
#Fisher-Yates洗牌后的顺序
# print(qualified_AC)
index_arr=qualified_AC[:256]

index_dict={}
for i in range(256):
index_dict[qualified_AC[i]]=i

#得到嵌入的256位的K_bit1
K_bit1=''
# print(ac_huffman_table)
# print(qualified_AC[:10])
for i in range(256):

#获取K_bit1的第i位,嵌入到arr_num中的索引是qualified_AC[i]
data=arr_sum[qualified_AC[i]]
# print(qualified_AC[i],i,data)
for i in range(256):

#获取K_bit1的第i位,嵌入到arr_num中的索引是qualified_AC[i]
data=arr_sum[qualified_AC[i]]
data2=bin(np.abs(data))[2:]
data1=len(data2)
if data<0:
data2=bin(np.abs(data)^(2**data1-1))[2:].rjust(data1,'0')
data2=data2[:-1]
data1=len(data2)


# print(11111,type(data),data,ac_huffman_table[data][:-1])
# print(type(ac_huffman_table[data]),ac_huffman_table[data])
# data2=ac_huffman_table[data][:-1]


if data2[0]=='0':
arr_sum[qualified_AC[i]]=-(int(data2,2)^(2**data1-1))
else:
arr_sum[qualified_AC[i]]=int(data2,2)
# print(22222,data,data1,data2,arr_sum[qualified_AC[i]])

# arr_sum[qualified_AC[i]]=reverse_ac_huffman_table.get(ac_huffman_table[data][:-1])
# print(arr_sum[qualified_AC[i]])
if data<0:
#取反
K_bit1+=str(int(bin(np.abs(data))[-1])^1)
else:
K_bit1+=bin(data)[-1]
# print(K_bit1)
# print('加密嵌入前的arr_sum',arr_sum[:200])

# if K_bit1==K_bit2:
# print('yes')

#逆Z型扫描
n=h*w//64
# print('解密中z型扫描之后:',arr_sum)
img_data=np.zeros((h,w),dtype=np.float64)
for i in range(n):
arr=arr_sum[i*64:(i+1)*64]

block=np.zeros((8,8))
for j in range(64):
block[int(j/8)][j%8]=arr[ZigZag[j]]
img_data[i//(w//8)*8:i//(w//8)*8+8,i%(w//8)*8:i%(w//8)*8+8]=block

# print(img_data)
# print(img_data[8:16,8:16])
#逆DC系数异或,倒叙要设置步长
for i in range(n-1,0,-1):
# print(img_data[i//64*8][i%64*8])
img_data[i//64*8][i%64*8]=int(img_data[i//64*8][i%64*8])^int(img_data[(i-1)//64*8][(i-1)%64*8])

# print(img_data[8:16,8:16])

#逆量化块置乱
#计算出从前往后时pos开始的位置
pos=n-3
for i in range(2,n):
#交换S[i]与S[j]
# if i==n-1:
# print(pos)

#生成随机整数
#选择r,满足2**r>=i,r>=log(i)
r=int(np.ceil(np.log2(i)))
#从Kenc-1获取r位(循环获取)
if pos%256+r>255:
x=int(K_bit1[pos%256:]+K_bit1[r+pos%256-256],2)
else:
# print(r,pos%256,pos%256+r)
x=int(K_bit1[pos%256:pos%256+r],2)
pos-=1
j=x-(x//i)*i
# print(im.shape)
#交换S[i]和S[j]
# print(i,j,i//64*8,i//64*8+8)
# print(im[i//64*8:i//64*8+8,i%64*8:i%64*8+8],im[j//64*8:j//64*8+8,j%64*8:j%64*8+8])
tmp1=np.zeros((8,8))
tmp2=np.zeros((8,8))
# print(tmp1.shape)
tmp1[:]=img_data[i//64*8:i//64*8+8,i%64*8:i%64*8+8]
tmp2[:]=img_data[j//64*8:j//64*8+8,j%64*8:j%64*8+8]
img_data[j//64*8:j//64*8+8,j%64*8:j%64*8+8]=tmp1[:]
img_data[i//64*8:i//64*8+8,i%64*8:i%64*8+8]=tmp2[:]
# im[i//64*8:i//64*8+8,i%64*8:i%64*8+8],im[j//64*8:j//64*8+8,j%64*8:j%64*8+8]=im[j//64*8:j//64*8+8,j%64*8:j%64*8+8],im[i//64*8:i//64*8+8,i%64*8:i%64*8+8]
# print(im[i//64*8:i//64*8+8,i%64*8:i%64*8+8],im[j//64*8:j//64*8+8,j%64*8:j%64*8+8])
# return 0

# print(img_data[8:16,8:16])
TS1=[]
for i in range(9):
num=K_bit1[i*7:(i+1)*7]
A8=fast_DCT(num)
TS1.append(A8)
#得到量化块
#分成8*8的块
# print(img_data[0:8,0:8])
# print(Qy)
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)
block=block*Qy
# if(i==0 and j==0):
# print('量化之前',block)


#对每一个图像block块做逆正交变换
#每列采用不同的正交矩阵
block=cv2.idct(block)
# block=(TS1[0].T).dot(block).dot(TS1[0])
# for k in range(8):
# block[:,k]=(TS1[k+1].T).dot(block[:,k]).dot(TS1[k+1])
# # if(i==0 and j==0):
# # print('列正交变换之前',block)

# #每行采用相同的正交矩阵
# for k in range(8):
# block[k]=(TS1[0].T).dot(block[k]).dot(TS1[0])
# if(i==0 and j==0):
# print('正交变换之前',block)

img_data[i*8:(i+1)*8,j*8:(j+1)*8]=block
# print(img_data[i*8:(i+1)*8,j*8:(j+1)*8])
# return 0




# dc_num=0
#熵编码之前
# print(arr_sum)
# return arr_sum
# print(len(arr_sum))
# #逆ZigZag扫描,得到block量化块
# block=np.zeros((8,8))
# 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
return img_data

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

#获取图像数据流宽高
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是0的情况
# 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 main():
#原始图像路径,灰度图像
# img_path='./gray_fei.jpg'
img_path='./lena_gray.bmp'
# img_path='f:/black.jpg'
#读取原始图像,cv2.imread()默认是用color模式读取的,保持原样读取要加上第二个参数-1
img=cv2.imread(img_path,-1)
# print(img)
key=b'\xba0s\x8a\xcd\xe6j\xb8\x1eL"\xf4\xa1{lvC\xe9xsM\x01t\x05\x08\x00;\xff\x97\xf6\x8cW'

# print(key)
# 加密图像
img_encrypt=encrypt(img_path,key)
img_compress=compress(img)
with open('./lena_compress.jpg','wb') as f:
f.write(base64.b16decode(img_compress.upper()))
# print(len(img_encrypt))
# print(img_encrypt.upper()[:8])
img_encrypt_path='./lena_encrypt.jpg'
#存储加密后的图像
with open(img_encrypt_path,'wb') as f:
f.write(base64.b16decode(img_encrypt.upper()))
# img_encrypt=decompress(img_encrypt_path)
# img_encrypt = cv2.imread('./img_encrypt.jpg',-1)
img_encrypt = decompress('./lena_encrypt.jpg')
#解密图像
img_decrypt=decrypt(img_encrypt_path,key)
# diff_sum=0
# print(img_decrypt)
# for i in range(len(img_encrypt)):
# if img_encrypt[i]!=img_decrypt[i]:
# diff_sum+=1
# print(diff_sum)
#结果展示
plt.rcParams['font.sans-serif'] = ['SimHei'] # 中文乱码
#子图1,原始图像
plt.subplot(131)
#imshow()对图像进行处理,画出图像,show()进行图像显示
plt.imshow(img,cmap=plt.cm.gray)
plt.title('原始图像')
#不显示坐标轴
plt.axis('off')
plt.subplot(132)
#imshow()对图像进行处理,画出图像,show()进行图像显示
plt.imshow(img_encrypt,cmap=plt.cm.gray)
plt.title('加密图像')
#不显示坐标轴
plt.axis('off')

#子图2,加密后图像
plt.subplot(133)
plt.imshow(img_decrypt,cmap=plt.cm.gray)
plt.title('解密图像')
plt.axis('off')
plt.show()

# plt.rcParams['font.sans-serif'] = ['SimHei'] # 中文乱码
# #子图1,原始图像
# plt.subplot(121)
# #imshow()对图像进行处理,画出图像,show()进行图像显示
# plt.imshow(img,cmap=plt.cm.gray)
# plt.title('原始图像')
# #不显示坐标轴
# plt.axis('off')

# print(img_encrypt)

if __name__ == '__main__':
main()

安全分析
参考

[17] W.-H. Chen, C. Harrison, S. Fralick, A fast computational algorithm for the discrete cosine transform, IEEE Trans. Commun. 25 (1977) 1004–1011.

------------- THE END! THANKS! -------------