草庐IT

信息隐藏与数字水印实验2-WAV 音频的 LSB 信息隐藏

Champhoenix 2024-05-14 原文

一、 实验目的
了解WAV文件格式,掌握利用WAV格式音频文件实现LSB信息隐藏原理,设计并实现一种基于WAV文件的LSB信息隐藏算法,同时自行练习设计实现一种次第有效位的音频隐写算法。
了解归一化相关系数NC的原理,利用NC对嵌入的水印图像和提取的图像水印进行比较。

二、 实验环境
(1)Windows或Linux操作系统
(2)Python3环境
(3)Python的wave、numpy、matplotlib、opencv-python库
(4)wave 音频文件和二值水印图像文件

三、 实验要求
(1)利用载体音频Carrier.wav隐藏嵌入秘密信息bupt.bmp图像,采取次低有效位嵌入,不是在最低有效,而是在倒数第二有效位嵌入。
(2)在一行两列中输出原始音频和携密音频的波形图。
(3)在没有任何攻击的情况下,从嵌入秘密信息后的音频中提取水印图像,在一行两列中输出原始图像水印和提取的水印图像。比较原始水印图像和提取水印图像的NC的值,这个NC的值应为为1。
(4)撰写实验报告,并提交实验报告。

四、 实验步骤和结果

原理简介
WAV是Microsoft Windows本身提供的音频格式,该格式通常都保存一些没有压缩的音频。对于数字音频,其最低比特位或者最低几个比特位的改变,对于整个声音没有明显的影响,因此替换掉这些不重要的部分,可以隐藏秘密信息。
如果在音频信号中嵌入的水印为图像,则来定义评价提取的水印与原始水印的相似性,可采用归一化相关系数(NC)作为评价标准,其定义为

(式4.1.1)式中:W为原始水印,W’为提取的水印,它们的大小为M1×M2。

作业:

(1)利用载体音频Carrier.wav隐藏嵌入秘密信息bupt.bmp图像,采取次低有效位嵌入,不是在最低有效,而是在倒数第二有效位嵌入。

实验步骤:

(1)读取载体音频carrier.wav,随后以字节方式读取该音频里的数据,以灰度图模式读取水印图像bupt64.bmp。
(2)利用flatten函数将二维矩阵降维(转为一维)并二值化,利用frombuffer函数将字节数据转换为nmupy数组。
(3)低次位LSB嵌入水印,用writeframes函数写入音频数据。
(4)展示原音频和嵌入音频波形对比。

代码:

# 导入wave音频文件处理库
import wave
# 导入图像处理库
import cv2
# 导入数学计算库
import numpy as np
# 导入绘图库
import matplotlib.pyplot as plt

# 读取载体音频
wav = wave.open('carrier.wav', 'rb')
nchannels, sampwidth, framerate, nframes, comptype, compname = wav.getparams()
time = nframes / framerate

# 以字节方式读取载体音频的数据
frames = wav.readframes(nframes)

# 以灰度图模式读取水印图像
wm = cv2.imread('bupt64.bmp', cv2.IMREAD_GRAYSCALE)

# 从二维矩阵转为一维并二值化
wm = wm.flatten() > 128

wav_embedded = wave.open('embedded.wav', 'wb')
wav_embedded.setparams(wav.getparams())

# 将字节数据转换为numpy数组
data = np.frombuffer(frames, dtype=np.uint8)

# # LSB嵌入水印
# data_embedded = data.copy()
# for i in range(len(wm)):
#     data_embedded[i] -= data_embedded[i] % 2
#     data_embedded[i] += wm[i]

# 次低位嵌入水印
data_embedded = data.copy()
for i in range(len(wm)):
    data_embedded[i] -= data_embedded[i] % 4 -  data_embedded[i] % 2
    data_embedded[i] += wm[i] * 2

# 写入音频数据
wav_embedded.writeframes(data_embedded)

# 展示原音频和嵌入音频的波形
plt.figure(figsize=(14, 6))

plt.subplot(121)
plt.plot(data)
plt.title('Original Audio')
plt.xticks([]), plt.yticks([])

plt.subplot(122)
plt.plot(data_embedded)
plt.title('Embedded Audio')
plt.xticks([]), plt.yticks([])

plt.show()

(2)在一行两列中输出原始音频和携密音频的波形图。

作业(1)的实验结果:

完成LSB嵌入之后,首先对LSB嵌入前后的音频文件进行听觉上的区分,二者靠人耳听不出任何差别。下图是原始音频波形图和携秘音频波形图,从这两幅图的对比中可以看出,LSB信息隐藏后,对原始音频的波形影响也非常小,几乎看不出任何差别。LSB隐藏的音频透明性非常好。


(3)在没有任何攻击的情况下,从嵌入秘密信息后的音频中提取水印图像,在一行两列中输出原始图像水印和提取的水印图像。比较原始水印图像和提取水印图像的NC的值,这个NC的值应为为1。

实验步骤:

(1)设置水印图像的宽高wm_width和wm_height,读取携密音频embedded.wav,随后用readframes函数以字节方式读取该音频的数据。
(2)用frombuffer函数将字节数据转换为numpy数组,随后LSB提取水印。
(3)用reshape函数将一维转为二维矩阵,imread函数以灰度图模式读取水印图像,编写函数计算NC值。
(4)用imwrite函数保存输出提取出的水印图像,随后战术嵌入图像、水印原图像和提取出的水印图像。

代码:

# 导入wave音频文件处理库
import wave
# 导入图像处理库
import cv2
# 导入数学计算库
import numpy as np
# 导入绘图库
import matplotlib.pyplot as plt

# 计算NC值的函数
def NC(template, img):
    template = template.astype(np.uint8)
    img = img.astype(np.uint8)
    return cv2.matchTemplate(img, template, cv2.TM_CCORR_NORMED)[0][0]


# 设置水印图像的宽高
wm_height = 64
wm_width = 64

# 读取携密音频
wav = wave.open('embedded.wav', 'rb')
nchannels, sampwidth, framerate, nframes, comptype, compname = wav.getparams()
time = nframes / framerate

# 以字节方式读取携密音频的数据
frames = wav.readframes(nframes)

# 将字节数据转换为numpy数组
data = np.frombuffer(frames, dtype=np.uint8)

# # LSB提取水印
# wm = np.zeros(wm_height * wm_width, dtype=np.uint8)
# for i in range(len(wm)):
#     wm[i] = data[i] % 2 * 255

wm = np.zeros(wm_height * wm_width, dtype=np.uint8)
for i in range(len(wm)):
    wm[i] = (data[i] % 4 - data[i] % 2) /2*255

# 从一维转为二维矩阵
wm = np.reshape(wm, (wm_height, wm_width))

# 以灰度图模式读取水印图像
wm_original = cv2.imread('bupt64.bmp', cv2.IMREAD_GRAYSCALE)

# 计算NC值
nc = NC(wm_original, wm)
print(f'NC = {nc * 100} %')

# 保存提取出的水印图像
cv2.imwrite('wm.bmp', wm)

# 展示嵌入图像、水印原图像和提取出的水印图像
plt.figure(figsize=(15, 6))

plt.subplot(131)
plt.plot(data)
plt.title('Embedded Audio')
plt.xticks([]), plt.yticks([])

plt.subplot(132)
plt.imshow(wm_original, 'gray')
plt.title('Original Watermark')
plt.xticks([]), plt.yticks([])

plt.subplot(133)
plt.imshow(wm, 'gray')
plt.title('Extracted Watermark')
plt.xticks([]), plt.yticks([])

plt.show()

实验结果:


图像水印提取后和原始水印进行归一化比较。在本例中,携密音频未发生任何变化,也就是说未对携密音频进行任何形式的攻击。因此提取出来的水印信息和原始的水印信息完全相同,归一化函数的值为1。
归一化函数的函数 NC 如下:

def NC(template, img): 
  template = template.astype(np.uint8) 
  img = img.astype(np.uint8) 
  return cv2.matchTemplate(img, template, cv2.TM_CCORR_NORMED)[0][0]


五、 实验心得体会
本次实验在进行图像水印隐藏实验的时候,遇到了cv2包的报错,虽然我上次实验已经安装完这个包了,不知道为什么这个实验又遇到了同样的问题,不得不又安装了一次。
在进行图像水印提取的实验的时候,遇到了生成的wm.bmp是图片一片漆黑的情况,在和老师给的pdf实验指导书里的代码比对后,发现在LSB提取水印那几行代码中的最后一行循环里的语句没有*255,修改后成功显示。

有关信息隐藏与数字水印实验2-WAV 音频的 LSB 信息隐藏的更多相关文章

  1. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 查找字符串中的内容类型(数字、日期、时间、字符串等) - 2

    我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s

  4. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  5. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  6. ruby - 将n维数组的每个元素乘以Ruby中的数字 - 2

    在Ruby中,是否有一种简单的方法可以将n维数组中的每个元素乘以一个数字?这样:[1,2,3,4,5].multiplied_by2==[2,4,6,8,10]和[[1,2,3],[1,2,3]].multiplied_by2==[[2,4,6],[2,4,6]]?(很明显,我编写了multiplied_by函数以区别于*,它似乎连接了数组的多个副本,不幸的是这不是我需要的)。谢谢! 最佳答案 它的长格式等价物是:[1,2,3,4,5].collect{|n|n*2}其实并没有那么复杂。你总是可以使你的multiply_by方法:c

  7. ruby - what is - gets is a directory - 错误信息 - 2

    我遇到了这个奇怪的错误.../Users/gideon/Documents/ca_ruby/rubytactoe/lib/player.rb:13:in`gets':Isadirectory-spec(Errno::EISDIR)player_spec.rb:require_relative'../spec_helper'#theuniverseisvastandinfinite...itcontainsagame....butnoplayersdescribe"tictactoegame"docontext"theplayerclass"doit"musthaveahumanplay

  8. Ruby隐藏与覆盖 - 2

    我刚刚了解到,在Java中,覆盖和隐藏之间是有区别的(静态方法是隐藏的,而不是覆盖),这意味着Java使用早期绑定(bind)和后期绑定(bind)。是否有与方法隐藏类似的东西,或者它只是具有方法重写? 最佳答案 Java具有三种不同的“方法”:实例方法,静态方法和构造函数。Ruby只有一个:实例方法。在Java中,静态方法的行为必须不同于实例方法,因为类不是对象。它们没有类,因此也没有父类(superclass),因此没有要覆盖的内容。在Ruby中,类与其他任何对象一样都是对象,它们具有一个类,该类可以具有父类(superclas

  9. Ruby 的数字方法性能 - 2

    我正在使用Ruby解决一些ProjectEuler问题,特别是这里我要讨论的问题25(Fibonacci数列中包含1000位数字的第一项的索引是多少?)。起初,我使用的是Ruby2.2.3,我将问题编码为:number=3a=1b=2whileb.to_s.length但后来我发现2.4.2版本有一个名为digits的方法,这正是我需要的。我转换为代码:whileb.digits.length当我比较这两种方法时,digits慢得多。时间./025/problem025.rb0.13s用户0.02s系统80%cpu0.190总计./025/problem025.rb2.19s用户0.0

  10. ruby - 按数字(从大到大)然后按字母(字母顺序)对对象集合进行排序 - 2

    我正在构建一个小部件来显示奥运会的奖牌数。我有一个“国家”对象的集合,其中每个对象都有一个“名称”属性,以及奖牌计数的“金”、“银”、“铜”。列表应该排序:1.首先是奖牌总数2.如果奖牌相同,按类型分割(金>银>铜,即2金>1金+1银)3.如果奖牌和类型相同,则按字母顺序子排序我正在用ruby​​做这件事,但我想语言并不重要。我确实找到了一个解决方案,但如果感觉必须有更优雅的方法来实现它。这是我做的:使用加权奖牌总数创建一个虚拟属性。因此,如果他们有2个金牌和1个银牌,加权总数将为“3.020100”。1金1银1铜为“3.010101”由于我们希望将奖牌数排序为最高的,因此列表按降序排

随机推荐