空间域隐藏是基于位图分解的思想产生的,其主要方法是将秘密信息存储到载体不那么重要的部分中,这样即使改变了载体的部分信息后,载体从肉眼来看也不会有太大区别。这种隐藏方法的优点是容量大,肉眼很难识别;但是缺点是直接隐藏了秘密信息,而且存储信息区域过于连续,容易造成载体统计特性的改变,而且因为信息存储在载体的最低有效位,在传输过程中很容易被一些压缩算法破坏。针对上述问题,研究者尝试将信息放在相对重要的的部位中,在不影响载体完整性的前提下尽可能将信息隐藏到低频系数中去。
变换域替换是将秘密信息存储至载体相对重要的区域来规避压缩算法和一些破坏行为,变换域替换包括DCT,傅里叶算法和小波变换,本文将主要介绍DCT算法。
其算法比较复杂,涉及的数学知识也比较多,就也不做详细介绍。有一点值得分享一下。傅里叶算法是高数中的一个知识点,其思想是将一个波分解为多个正余弦波的合,听起来好像没什么意思,但是这种算法我们在生活中每时每刻都在使用。比如我们到菜市场,有人的喧闹声,车声,叫卖声,我们接收的是杂乱无序的的音波,我们是如何分辨出他们的呢?
这就是傅里叶变换的应用,接收这些声波后,我们的大脑作快速傅里叶变换,将杂乱无序的波分解为多个波之和,大脑直接过滤掉不需要的波后,我们就能接收到想要的信息了,很神奇的方法不是吗。
学计算机越久,越怀疑世界的真实性,万物皆可编程的时代,我们看似一些神奇和自然的现象都可以用数学和计算知识来解释。程序世界的运转依赖与程序员设定的各项常量变量,三十万千米每秒,6.2607015×10-34 J·s的普朗克常数,G=6.67259×10-11N·m²/kg²的引力常量,这些又是谁设置的常量呢。我们的一切感知都是大脑告诉我们的,现在的科学已经能通过电信号来模拟大脑的一些活动,那我们又如何确定自己所处的是真实的世界呢,或许真实的世界中我们只是一个个培养瓶中的大脑,不断接受外部信号刺激来给我们造成各种经历的感受 黑客帝国既视感:),感觉科学的尽头是神学还是有一点道理。
言归正传,目前的主流隐藏方法,且兼顾效率与隐蔽性的算法是基于离散余弦变换的信息隐藏算法(DCT),该算法涉及比较复杂的数学知识,只能简要说下其原理:离散余弦变换是将图像由空间域转化到频率域上,将图像计算为二维余弦波,从效果来说是将图像计算为一个DCT系数矩阵,该矩阵代表图像的频率分布,频率低的为图像中相对重要的主体信息,高频则是图像的一些细节,但该矩阵中的像素与图像像素不存在一一对应关系。 然后将秘密信息根据自己的算法隐藏到DCT系数中即可,隐藏位置多选择中频区域,是在透明性和鲁棒性之间做了折中的选择。
本文设计的算法是利用dct系数的相对变化存储10信息(可用该10信息代表黑白,即可嵌入图片)。
引用一张觉得很不错关于域的分解图
变换域隐藏借助python的实现很简单,只需一句代码
img_dct=cv2.dct('打开的文件‘)
使用需要注意两点,打开的文件需要以灰度图像打开,而且需要将其转为float类型。示例如下:
img=cv2.imread(’文件路径',cv2.IMREAD_GRAYSCALE)
img1=img.astype('float')
变换域的思想在压缩领域应用相当广泛,下载的专辑可能一首歌要几十M,但是同样的mp3格式就可能只要3-4M,而且大部分人可能很难听出其中区别,这就是利用了变换域的方法,压缩掉细节和不那么重要的高频部分。
压缩代码示例如下:
def zip(num1,num2):#参数为保留范围
recor_temp = img_dct[num1:num2, num1:num2]
recor_temp2 = np.zeros(img.shape)
print(img.shape)
recor_temp2[num1:num2, num1:num2] = recor_temp
img_recor1 = cv2.idct(recor_temp2)
plt.figure('zip')
plt.subplot(121),plt.title('origin')
plt.imshow(img)
plt.subplot(122),plt.title('zip')
plt.imshow(img_recor1)
plt.show()
效果对比如下:
保留像素0-300
保留像素100-400
可以看到保留相同数量的像素,仅仅是范围后移了100就有如此大的差别,同时也验证了图像主要部分存储在低频系数内,而300像素点就能基本保存一张1920*1080图片的主题,也说明了基于变换域的压缩算法效率是相当高的。
DCT信息隐藏的手段主要是将秘密信息以一定方式隐藏到DCT系数中,方法不限于直接嵌入到float的最后几位,还有通过系数的奇偶存储,比较特定位置上两个系数的大小等,变换域信息隐藏是我最喜欢的信息隐藏方法,没有之一,因为这中隐写方法的高明之处在于不把信息直接隐藏到载体中,而是通过自行约定的一种算法,并且秘密信息在载体中没有丝毫体现。这个特性使得检测者即使拿到载体图片,甚至分析出了其中含有秘密信息也很难破解其中的具体信息。DCT信息隐藏中,信息隐写,隐写算法,明文加密,三者同时保证了秘密信息的安全性。
但是在信息嵌入的时候有几个点需要注意:
1.嵌入位置: DCT信息隐藏的思想是将秘密信息存储到相对重要的区域,防止被一些针对高频的图像压缩算法破坏,所以信息具体的隐藏位置出于不同目的会有不同选择。
2.嵌入算法: 由于DCT信息隐藏是将秘密信息存储到图像的DCT系数中,那么系数的变化规则应当本着对载体修改尽可能小,秘密信息尽可能隐蔽的原则来实现。
3.抗分析: 虽然信息隐写学科发展时间较短,但是针对目前隐写算法也有了很多行之有效的分析方法。其中对于DCT隐写分析威胁最大的应该就是卡方检测了,基于统计分析实现简单,导致其门槛较低而应用广泛,准确率又相对较高。所以如何有效对抗统计分析是DCT信息隐写面临比较严峻的问题。
1.信息嵌入位置:
如果修改低频部分,容易看出变化,隐蔽性较差;如果修改高频部分,虽然边缘轮廓替换不容易被识别,但是容易被处理高频的压缩算法破坏,鲁棒性比较差。所以最好采用折中方案—修改中频系数。而针对不同的用途,修改系数位置的细节还可以有一定不同,例如数字水印主要目的是保证嵌入信息的健壮性,所以可以在较中频系数略低的位置隐写,而信息隐藏更注重保证信息的隐蔽性,修改频率过低的可能会导致视觉效果变化,所以可以在较中频系数较高的位置隐写。
2.信息隐藏算法:
信息如何隐藏到dct系数中有很多选择,首次实验中采取比较两个特定位置的dct系数大小来隐藏信息,左边较大表示1,右边较大表示0,虽然看似只能存储1和0,但是应用可以更加广泛,比如存储灰度图像。提取过程就只需将载体图片dct变换后读取特定位置的dct系数即可。但是dct系数可以一直读取下去,所以按照读取原则读的话可以一直读取到信息,这样传输时需要提前商定秘密信息长度,这在实际传输中是比较不科学的。针对此问题,在后续实验中实现了一种方法—将秘密信息长度也隐藏在载体中。在第一个提取位置存储秘密信息长度,再根据长度提取之后的秘密信息。而在隐写过程中对DCT系数修改时也发现,修改值的大小虽然在隐藏信息提取时没什么影响,但是对于载体图像可能会造成一定破坏。如果添加的值过大可能会造成载体图像出现白点等现象,所以在对系数的更改时简便起见可以统一修改0.01,以保证载体图像视觉上的完整性。
3.隐写分析抵抗:
我认为DCT信息隐藏方法在抗分析上有先天优势,主流有效的分析方法大部分都是基于统计分析的,而DCT隐藏虽然修改了系数,但是根据算法和修改程度的不同可以有效规避统计分析检测。目前基于卡方统计的破解隐写方法对于隐写内容多,修改大的隐藏方式效果较好,但若是仅修改了少数DCT系数,就很难识别。上面实现的算法修改也相对较大,如果左右系数的值相差较大可能会造成某个值修改明显的情况。对于这一点可以优化算法进行解决,实现中DCT系数是以float类型存储,我们可以判断系数最后一个小数位的奇偶来存储1或0,相对于前一种算法修改值就大大下降了。
而且隐写分析使用的统计分析方法在隐写时同样可以使用,我们可以在保证不被分析出的情况下存储秘密信息。上面实现的算法算是比较浪费的一种,如果要存储n个字符需要2n个像素点,但是存储五个字符也只占用了十个像素点,这在一张1920*1080的图片中想探测出来,我认为是相当困难的。卡方检测最终要算出可能存在隐写的概率P,如果P等于1或者0就很容易确定是否有信息隐写,但是当p等于0.5时是否还容易判断呢,我把这种情况的系数叫做混淆系数,我们在隐写过程中如果要嵌入大量信息,如果能将卡方计算出的P控制在混淆系数左右或者以下,对于检测者来说辨别的难度更是增加了不少。
import cv2
import numpy as np
import matplotlib.pyplot as plt
def zip(num1,num2):
recor_temp = img_dct[num1:num2, num1:num2]
recor_temp2 = np.zeros(img.shape)
print(img.shape)
recor_temp2[num1:num2, num1:num2] = recor_temp
img_recor1 = cv2.idct(recor_temp2)
plt.figure('zip')
plt.subplot(121),plt.title('origin')
plt.imshow(img)
plt.subplot(122),plt.title('zip')
plt.imshow(img_recor1)
plt.show()
img=cv2.imread('D:\earth.jpg',cv2.IMREAD_GRAYSCALE)
img1=img.astype('float')
img_dct=cv2.dct(img1)
mes="10010"
for i in range(0,len(mes)):#嵌入
if mes[i]=='1':
if img_dct[i][1917-i]>img_dct[i][1918-i]:pass
else:
img_dct[i][1917-i]=img_dct[i][1918-i]+0.1
elif mes[i]=='0':
if img_dct[i][1917-i] img_dct[i][1918 - i]:
getmsg=getmsg+"1"
else:
getmsg=getmsg+"0"
print("Deliverd message is: ",mes)
print("Ranger message is: ",getmsg)
plt.imshow(img_recover)
plt.savefig('D:/earth_insert.jpg')
plt.figure('DCT')
plt.subplot(121),plt.title('origin')
plt.imshow(img)
plt.subplot(312),plt.title('DCT')
plt.imshow(img_dct)
plt.subplot(122)
plt.imshow(img_recover),plt.title('recover')
plt.show()