一、 实验目的
了解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,修改后成功显示。

Logo

更多推荐