《信息隐藏LSB算法实验报告(共17页).doc》由会员分享,可在线阅读,更多相关《信息隐藏LSB算法实验报告(共17页).doc(17页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、精选优质文档-倾情为你奉上本科生课程考试答题本 考生姓名考生学号专业班级 指导老师考试科目考试日期年月日 目录一、 实验任务和要求1.1实验任务 信息载体:每个人自己的一张外景照片; 水印信息:每个人将学号、姓名按上下两列写在白纸上,然后手机拍摄,转化为黑白图片,作为水印信息; 信息隐藏方法:LSB算法(空域或频域)。1.2实验要求 实验可采用matlab6.5以上版本(C+、Java等),程序分为嵌入与检测两部分,最好有友好的操作方式;程序代码需要注释,编码简洁可靠明了,易检查。 实验测试要求有: 需对信息处理进行鲁棒性测试; 对水印嵌入的有效性进行测试; 计算嵌入前后的PSNR值; 对水印
2、容量进行分析。二、实验算法LSB原理LSB是L.F.Turner和R.G.van Schyndel等人提出的一种典型的空间域信息隐藏算法。LSB 最低有效位法(Least Significant Bit;LSB)是运用人类视觉系统无法觉察细微变化之掩蔽效果,将秘密信息隐藏在图像像素的最低位,具有计算速度快且容易 秘密信息隐藏在图像像素的最低位,具有计算速度快且容易实现有点。通常最低位是表示图像细节信息,一般人眼不容易觉察,也因此容易成为一般信号处理和压缩时被抛弃的部分。本次主要是针对24位的BMP图片做处理,算法通过把信息嵌入到24位真彩色BMP图象中,从而达到隐藏的效果。通常BMP图像可以用
3、一个M*N的矩阵表示,矩阵的数值表示一个像素的色彩信息,一般用8位二进制数表示。每个像素对应的数值的每位bit其意义和作用不尽相同,我们可以把每个数据的每一位bit构成一个平面数据,称为位平面,其中由每一个像素最低比特位组成的称为第0个位平面,为最不重要为平面,相应的比特位称为最低有效位(LSB)。LSB算法原理就是通过修改最不重要的LSB后,图像的改变根本无法用肉眼识别,以此来实现以图像为载体的信息隐藏。三、实验环境和采用的工具 此次试验环境及工具见下表3.1所示。表3.1 实验环境及工具项目内容实验环境Windows 7(64位)开发工具Microsoft Visual Studio 20
4、12(C#)图像处理Matlab 2010 Ra辅助工具Photoshop/画图四、具体实现步骤实验主要包括三个方面,LSB算法水印嵌入、LSB算法水印提取以及针对LSB算法性能的测试。4.1 LSB算法水印嵌入LSB算法水印嵌入主要步骤是加载载体和水印图片、将图片文件流转换成二进制数组(设计两个转换函数:长整型转换成24位和1字节转换成8位,详见后面源码分析)、获得水印长度判定是否大于载体长度大约1/8(程序中有详细的图4.1 LSB水印嵌入流程图计算公式)、大于则重新选择水印;符合则进行水印长度嵌入(设计长度值嵌在载体BMP第55-77位字节(24位)的LSB处)、循环获得水印内容并嵌入(
5、内容从载体BMP的78位字节处开始,载体每8位嵌入1位字节水印)、最后保存隐写体、备份载体。该嵌入步骤说明下:(1)长度嵌入在载体第55-77字节,这是因为24位BMP图前54位存储的是图片本身信息(如果嵌入可能会破坏载体图片导致嵌入后的隐写体无法正确读取);选择24位字节是因为可以嵌入24位的二进制长度,即能够嵌入2的24方水印信息(此范围合适,当然最终能否嵌这么多还要看载体信息长度)。(2)备份载体,这是为后面进行嵌入效果的对比和PSNR值做准备。4.2 LSB算法水印提取LSB算法水印提取与嵌入基本相同,主要步骤是加载隐写体、选择提取水印的保存名字及位置、将图片文件流转换成二进制数组(同
6、嵌入)、提取隐写体第55-77位的水印信息长度、判定长度是否大于隐写体长度的大约1/8(程序中有详细的计算)、大于,则提示可能不包含水印(判定方式做的比较简单);小于,则开始从隐写体的78位逆置换提取水印、保存水印、选择原始水印进行比较。图4.2 LSB水印提取流程图说明:1.因为长度不满足,系统就不做提取(设计比较简单); 2.水印对比中,如有嵌入过程,则不需选择;直接提取需要选择原始水印。4.3 LSB算法实验测试测试这一模块主要有:鲁棒性测试、可行性分析、计算PSNR值以及水印容量分析等内容。这一部分的实验步骤主要按照它们的分析进行,具体见下。1鲁棒性检测主要是对嵌入水印后的隐写体进行各
7、种变换(比如旋转、裁剪、放大缩小等),转换后再进行隐写体的水印提取。其中变换可以借用Photoshop或者画图来简单实现。2LSB水印嵌入的可行性分析主要是针对嵌入是否合理、提取是否成功等进行分析。作为最简单常用的水印嵌入算法LSB,它的可行性分析具体见后面。3计算PSNR值,这个依据PSNR值计算公式,使用C#语言界面化实现。具体步骤就是加载隐写体和原始载体,计算PSNR值并显示。4水印容量分析,这个在设计实现LSB算法时已经做了初步定论,分析的实现可以简单计算出来或者利用matlab实现。五、源码分析5.1 LSB算法水印嵌入根据前面的原理和分析,我主要是利用visual studio 2
8、012结合C#语言进行LSB算法的实现。LSB水印嵌入主要是建立LSBEncrypt类来实现,其中有如下五个函数,具体功能见下表5.1所示。表5.1 LSB水印嵌入所用函数及功能函数名功能byte ConvertToBinaryArray(long x)将长整型数转换为24位二进制字节数组ConvertToBinaryArray(bytearray)将1个字节转换为8位二进制字节数组void HideInfoLength()在载体55-77字节LSB替换为水印的长度void HideInfoContent()将水印信息以每1个字节写入载体图像从第78字节开始的每8字节块的LSB中void Ex
9、ecuteEncrypt()执行LSB水印信息嵌入操作 根据上表5.1中的函数及功能,主要的源码分析见下面:namespace LSB_Algorithm class LSBEncrypt /原始载体图片路径 private string _originalPicPath = null; /水印信息路径 private string _hidingInfoPath = null; /原始载体图片的文件流 private FileStream _picStream = null; /水印信息的文件流 private FileStream _infoStream = null; / / LSBEn
10、crypt类的构造函数 / / 原始图片路径 / 隐藏信息的路径 public LSBEncrypt(string path1, string path2) _originalPicPath = path1; _hidingInfoPath = path2; / / 将长整型数转换为24位二进制表示的字节数组 / / 长整型数,数的不会超过2的24次方 / 二进制表示的字节数组 private byte ConvertToBinaryArray(long x) byte binaryArray = new byte24; for (int i = 0; i != 24; i+) binaryA
11、rray23- i = (byte)(x & 1); x = x 1; return binaryArray; / / 将1个字节的数组转换为8位二进制表示的字节数组 / / 长度为1的字节数组 / 二进制表示的字节数组 private byte ConvertToBinaryArray(byte array) byte binaryArray = new byte8; int a = array0; for (int i = 0; i != 8; i+) binaryArray7 - i = (byte)(a & 1); a = a 1; return binaryArray; / / 将图
12、像的第55至第77字节的LSB替换为水印信息文件的长度 / private void HideInfoLength() byte picBlock = new byte24; /读取原始载体图像的第55至第77字节的内容块 _picStream.Position = 54; _picStream.Read(picBlock, 0, picBlock.Length); byte lenArray = ConvertToBinaryArray(_infoStream.Length); /置入水印文件的长度信息 int index = 0; for (int i = 0; i 24; i+) pic
13、Blocki = (byte)(lenArrayindex+ = 0) ? (picBlocki & 254) : (picBlocki | 1); /将原始载体文件流重定位到第55字节处/并将已嵌入长度信息的24字节块写回 _picStream.Position = 54; _picStream.Write(picBlock, 0, picBlock.Length); _picStream.Flush(); / / 将水印信息以每1字节写入载体第78字节开始每8字节块LSB中 / private void HideInfoContent() int readCnt = 0; /计算循环处理的
14、次数 long infoLen = _infoStream.Length; _picStream.Position = 78; for (int i = 0; i infoLen; i+) /每次循环读取BMP图像的下一个8字节的内容 byte picBlock = new byte8; readCnt = _picStream.Read(picBlock, 0, picBlock.Length); /读取水印信息的下一个1字节内容 byte readBuffer = new byte1; _infoStream.Read(readBuffer, 0, readBuffer.Length);
15、byte infoBlock = ConvertToBinaryArray(readBuffer); /置位操作 int index = 0; for (int ii = 0; ii 8; ii+) picBlockii = (byte)(infoBlockindex+ = 0) ? (picBlockii & 254) : (picBlockii | 1); _picStream.Position -= readCnt; _picStream.Write(picBlock, 0, picBlock.Length); _picStream.Flush(); / / 执行LSB水印嵌入操作 /
16、public void ExecuteEncrypt() _picStream = new FileStream(_originalPicPath, FileMode.Open, FileAccess.ReadWrite);/读取载体图片信息流 _infoStream = new FileStream(_hidingInfoPath, FileMode.Open, FileAccess.Read); /读取水印信息流 HideInfoLength(); /嵌入水印信息长度 HideInfoContent(); /嵌入水印信息内容 _picStream.Close(); _infoStream.
17、Close(); LSB水印嵌入源码说明: 1。整体源码执行流程和前面的LSB嵌入流程图一致,各功能有具体的函数实现,算法思想简单传统,没有做很大的改进,整体代码设计简单易懂。5.2 LSB算法水提取根据前面的原理和分析,我主要是利用visual studio 2012结合C#语言进行LSB算法的实现。LSB水印嵌入主要是建立LSBDecrypt类来实现,其中有如下4个函数,具体功能见下表5.2所示。表5.2 LSB水印嵌入所用函数及功能函数名功能int GetInfoLength()调用长度提取函数来获取水印信息长度ExtractHidingLengthBits(byte arr)从24字节
18、中提取出3字节水印长度信息ExtractHidingBits(byte arr)从8字节块中提取出的1字节水印信息ExecuteDecrypt()执行LSB水印提取功能 根据上表5.2中的函数及功能,主要的源码分析见下面:namespace LSB_Algorithm class LSBDecrypt /隐写体图片的路径 private string _camouflagePicPath = null; /还原出的水印信息的保存路径 private string _infoSavePath = null; /隐写体图片的文件流 private FileStream _camouflageStr
19、eam = null; /还原出的水印信息的文件流 private FileStream _infoSaveStream = null; / / LSBDecrypt的构造函数 / / 隐写体图片的路径 / 还原出的水印信息的保存路径 public LSBDecrypt(string path1, string path2) _camouflagePicPath = path1; _infoSavePath = path2; / / 从隐写体的第55至第77字节中提取出水印信息的长度 / / 隐藏信息长度 private int GetInfoLength() _camouflageStrea
20、m = new FileStream(_camouflagePicPath, FileMode.Open, FileAccess.Read); /将文件流定位到第55个字节处 _camouflageStream.Position = 54; /读取包含水印信息长度的24个字节块 byte lengthBlock = new byte24; _camouflageStream.Read(lengthBlock, 0, lengthBlock.Length); /提取LSB byte lengthBitArray = ExtractHidingLengthBits(lengthBlock); in
21、t len = lengthBitArray0* 65536 + lengthBitArray1 * 256 + lengthBitArray2; /如果长度不正确表明该图片可能不含有隐藏信息(检测较简单) if (len (_camouflageStream.Length - 54) / 8 - 3) _camouflageStream.Close(); return -1; return len; / 长度为24的字节数组,含有隐藏信息 / 从24字节块中提取出的3字节隐藏信息 private byte ExtractHidingLengthBits(byte arr) /用于存放从24个
22、字节块中提取出来的3个字节的隐藏信息 byte buf = new byte3; /24个bit位的处理 buf0 = (byte)(arr0 & 1) = 0 ? (buf0 & 127) : (buf0 | 128); /a7 buf0 = (byte)(arr1 & 1) = 0 ? (buf0 & 191) : (buf0 | 64); /a6 buf0 = (byte)(arr2 & 1) = 0 ? (buf0 & 223) : (buf0 | 32); /a5 buf0 = (byte)(arr3 & 1) = 0 ? (buf0 & 239) : (buf0 | 16); /a
23、4 buf0 = (byte)(arr4 & 1) = 0 ? (buf0 & 247) : (buf0 | 8); /a3 buf0 = (byte)(arr5 & 1) = 0 ? (buf0 & 251) : (buf0 | 4); /a2 buf0 = (byte)(arr6 & 1) = 0 ? (buf0 & 253) : (buf0 | 2); /a1 buf0 = (byte)(arr7 & 1) = 0 ? (buf0 & 254) : (buf0 | 1); /a0 buf1 = (byte)(arr8 & 1) = 0 ? (buf1 & 127) : (buf1 | 1
24、28); /b7 buf1 = (byte)(arr9 & 1) = 0 ? (buf1 & 191) : (buf1 | 64); /b6 buf1 = (byte)(arr10 & 1) = 0 ? (buf1 & 223) : (buf1 | 32); /b5 buf1 = (byte)(arr11 & 1) = 0 ? (buf1 & 239) : (buf1 | 16); /b4 buf1 = (byte)(arr12 & 1) = 0 ? (buf1 & 247) : (buf1 | 8); /b3 buf1 = (byte)(arr13 & 1) = 0 ? (buf1 & 25
25、1) : (buf1 | 4); /b2 buf1 = (byte)(arr14 & 1) = 0 ? (buf1 & 253) : (buf1 | 2); /b1 buf1 = (byte)(arr15 & 1) = 0 ? (buf1 & 254) : (buf1 | 1); /b0 buf2 = (byte)(arr16 & 1) = 0 ? (buf2 & 127) : (buf2 | 128); /c7 buf2 = (byte)(arr17 & 1) = 0 ? (buf2 & 191) : (buf2 | 64); /c6 buf2 = (byte)(arr18 & 1) = 0
26、 ? (buf2 & 223) : (buf2 | 32); /c5 buf2 = (byte)(arr19 & 1) = 0 ? (buf2 & 239) : (buf2 | 16); /c4 buf2 = (byte)(arr20 & 1) = 0 ? (buf2 & 247) : (buf2 | 8); /c3 buf2 = (byte)(arr21 & 1) = 0 ? (buf2 & 251) : (buf2 | 4); /c2 buf2 = (byte)(arr22 & 1) = 0 ? (buf2 & 253) : (buf2 | 2); /c1 buf2 = (byte)(ar
27、r23 & 1) = 0 ? (buf2 & 254) : (buf2 | 1); /c0 return buf; / / 利用位操作提取隐写体文件流中每8字节的LSB位 / / 长度为8的字节数组,含有隐藏信息 / 从8字节块中提取出的1字节隐藏信息 private byte ExtractHidingBits(byte arr) /用于存放从8个字节块中提取出来的1个字节的隐藏信息 byte buf = new byte1; /8个bit位的处理 buf0 = (byte)(arr0 & 1) = 0 ? (buf0 & 127) : (buf0 | 128); /a7 buf0 = (b
28、yte)(arr1 & 1) = 0 ? (buf0 & 191) : (buf0 | 64); /a6 buf0 = (byte)(arr2 & 1) = 0 ? (buf0 & 223) : (buf0 | 32); /a5 buf0 = (byte)(arr3 & 1) = 0 ? (buf0 & 239) : (buf0 | 16); /a4 buf0 = (byte)(arr4 & 1) = 0 ? (buf0 & 247) : (buf0 | 8); /a3 buf0 = (byte)(arr5 & 1) = 0 ? (buf0 & 251) : (buf0 | 4); /a2 b
29、uf0 = (byte)(arr6 & 1) = 0 ? (buf0 & 253) : (buf0 | 2); /a1 buf0 = (byte)(arr7 & 1) = 0 ? (buf0 & 254) : (buf0 | 1); /a0 return buf; / / 执行信息提取操作 / / 执行成功返回true,失败返回false public bool ExecuteDecrypt() int infoLen = GetInfoLength(); if (infoLen = -1) return false; /判定长度是否合格,不合格退出 _infoSaveStream = new
30、 FileStream(_infoSavePath, FileMode.Create, FileAccess.Write); /置文件流位置 _camouflageStream.Position = 78; _infoSaveStream.Position = 0; /按8字节一组每次提取1个字节的隐藏信息并写入文件 for (int i = 0; i infoLen; i+) byte contentBlock = new byte8; _camouflageStream.Read(contentBlock, 0, contentBlock.Length); byte contentBitA
31、rray = ExtractHidingBits(contentBlock); _infoSaveStream.Write(contetBitArray, 0, contentBitArray.Leth); _infoSaveStream.Flush(); _camouflageStream.Close(); _infoSaveStream.Close(); return true; LSB水印提取源码说明: 1。整体源码执行流程和前面的LSB提取流程图一致,主要是嵌入过程的逆操作。其中有两个提取函数:一个是从24位字节中提取3字节的长度信息;一个是从8字节中提取1字节的水印内容,其实这两个函
32、数可以合在一起,但是为了更好地理解程序,故没有整合在一起。5.3 计算PSNR值 计算PSNR值,主要是根据其原理和计算公式见下面,利用C#做出程序实现。图5.3 PSNR值计算公式具体的实验程序代码分析见下面:public double countPSNR(string formerFpath, string laterFpath) Bitmap formerImage = new Bitmap(formerFpath); Bitmap laterImage = new Bitmap(laterFpath); double sumR = 0; double sumG = 0; double
33、sumB = 0; for (int i = 0; i formerImage.Width - 1; i+) for (int j = 0; j formerImage.Height - 1; j+) Color color1 = formerImage.GetPixel(i, j); Color color2 = laterImage.GetPixel(i, j); int retR = color1.R - color2.R; int retG = color1.G - color2.G; int retB = color1.B - color2.B; sumR = sumR + retR * retR; sumG = sumG + retG * retG; sumB = sumB + retB * retB; double sum = sumR + sumG + sumB; double MSE = (sum/(3.0 * formerImage.Width * formerImage.Height); /计算MSE值 double PSNR = 20 * Math.Log10(255 / Math.Sqrt(MSE); r
限制150内