Python实现音频数字水印方法

数字水印技术可以将隐藏信息嵌入到音频文件中而不明显影响音频质量。下面我将介绍几种在Python中实现音频数字水印的方法。

方法一:LSB (最低有效位) 水印

import numpy as np

from scipy.io import wavfile

def embed_watermark_lsb(audio_path, watermark, output_path):

读取音频文件

sample_rate, audio_data = wavfile.read(audio_path)

确保是立体声,如果是单声道则转换为立体声

if len(audio_data.shape) == 1:

audio_data = np.column_stack((audio_data, audio_data))

将水印转换为二进制

watermark_bin = ''.join(format(ord(c), '08b') for c in watermark)

watermark_bin += '00000000' # 添加结束标记

检查水印是否适合音频

if len(watermark_bin) > audio_data.size:

raise ValueError("水印太大,无法嵌入到音频中")

嵌入水印到最低有效位

watermark_index = 0

for i in range(len(audio_data)):

for j in range(len(audio_data[i])):

if watermark_index < len(watermark_bin):

替换最低有效位

audio_data[i][j] = (audio_data[i][j] & 0xFE) | int(watermark_bin[watermark_index])

watermark_index += 1

else:

break

保存带水印的音频

wavfile.write(output_path, sample_rate, audio_data)

def extract_watermark_lsb(audio_path, watermark_length):

读取音频文件

sample_rate, audio_data = wavfile.read(audio_path)

提取最低有效位

watermark_bits = []

for i in range(len(audio_data)):

for j in range(len(audio_data[i])):

watermark_bits.append(str(audio_data[i][j] & 1))

将比特转换为字节

watermark = ''

for i in range(0, len(watermark_bits), 8):

byte = ''.join(watermark_bits[i:i+8])

if byte == '00000000': # 遇到结束标记

break

watermark += chr(int(byte, 2))

return watermark[:watermark_length]

使用示例

embed_watermark_lsb('original.wav', '秘密消息', 'watermarked.wav')

extracted = extract_watermark_lsb('watermarked.wav', 4)

print("提取的水印:", extracted)

方法二:频域水印 (DCT变换)

import numpy as np

from scipy.fftpack import dct, idct

from scipy.io import wavfile

def embed_watermark_dct(audio_path, watermark, output_path, alpha=0.01):

读取音频

sample_rate, audio_data = wavfile.read(audio_path)

如果是立体声,只使用一个声道

if len(audio_data.shape) > 1:

audio_data = audio_data[:, 0]

将水印转换为二进制

watermark_bin = ''.join(format(ord(c), '08b') for c in watermark)

watermark_bin = [int(b) for b in watermark_bin]

分段处理音频

segment_size = 1024

num_segments = len(audio_data) // segment_size

watermark_length = len(watermark_bin)

if num_segments < watermark_length:

raise ValueError("音频太短,无法嵌入水印")

嵌入水印

watermarked_audio = np.copy(audio_data)

for i in range(watermark_length):

start = i * segment_size

end = start + segment_size

segment = audio_data[start:end]

dct_coeffs = dct(segment, norm='ortho')

修改中频系数嵌入水印

coeff_index = 100 # 选择一个中频系数

if watermark_bin[i] == 1:

dct_coeffs[coeff_index] += alpha * np.abs(dct_coeffs[coeff_index])

else:

dct_coeffs[coeff_index] -= alpha * np.abs(dct_coeffs[coeff_index])

逆DCT变换

watermarked_segment = idct(dct_coeffs, norm='ortho')

watermarked_audio[start:end] = watermarked_segment

保存带水印的音频

wavfile.write(output_path, sample_rate, watermarked_audio.astype(np.int16))

def extract_watermark_dct(audio_path, original_path, watermark_length):

读取带水印音频和原始音频

sample_rate, watermarked = wavfile.read(audio_path)

_, original = wavfile.read(original_path)

如果是立体声,只使用一个声道

if len(watermarked.shape) > 1:

watermarked = watermarked[:, 0]

original = original[:, 0]

segment_size = 1024

watermark_bits = []

for i in range(watermark_length):

start = i * segment_size

end = start + segment_size

wm_segment = watermarked[start:end]

orig_segment = original[start:end]

wm_dct = dct(wm_segment, norm='ortho')

orig_dct = dct(orig_segment, norm='ortho')

coeff_index = 100

if wm_dct[coeff_index] > orig_dct[coeff_index]:

watermark_bits.append('1')

else:

watermark_bits.append('0')

将比特转换为字符串

watermark = ''

for i in range(0, len(watermark_bits), 8):

byte = ''.join(watermark_bits[i:i+8])

watermark += chr(int(byte, 2))

return watermark

使用示例

embed_watermark_dct('original.wav', '秘密', 'watermarked_dct.wav', 0.02)

extracted = extract_watermark_dct('watermarked_dct.wav', 'original.wav', 16)

print("提取的水印:", extracted)

方法三:扩频水印

import numpy as np

from scipy.io import wavfile

def generate_pn_sequence(length, seed=42):

np.random.seed(seed)

return np.random.choice([-1, 1], size=length)

def embed_watermark_spread_spectrum(audio_path, watermark, output_path, alpha=0.01):

读取音频

sample_rate, audio_data = wavfile.read(audio_path)

如果是立体声,只使用一个声道

if len(audio_data.shape) > 1:

audio_data = audio_data[:, 0]

将水印转换为二进制

watermark_bin = ''.join(format(ord(c), '08b') for c in watermark)

watermark_bits = np.array([int(b) for b in watermark_bin])

watermark_bits = 2 * watermark_bits - 1 # 转换为±1

生成伪随机序列

pn_length = len(audio_data) // len(watermark_bits)

pn_sequence = generate_pn_sequence(pn_length)

创建扩频水印

spread_watermark = np.repeat(watermark_bits, pn_length)

spread_watermark = spread_watermark[:len(audio_data)] * pn_sequence[:len(audio_data)]

嵌入水印

watermarked_audio = audio_data + alpha * spread_watermark * np.abs(audio_data)

watermarked_audio = np.clip(watermarked_audio, -32768, 32767) # 确保在16位范围内

保存带水印的音频

wavfile.write(output_path, sample_rate, watermarked_audio.astype(np.int16))

def extract_watermark_spread_spectrum(audio_path, original_path, watermark_length, pn_length):

读取音频

sample_rate, watermarked = wavfile.read(audio_path)

_, original = wavfile.read(original_path)

如果是立体声,只使用一个声道

if len(watermarked.shape) > 1:

watermarked = watermarked[:, 0]

original = original[:, 0]

计算差异

diff = watermarked - original

生成相同的伪随机序列

num_bits = watermark_length * 8

pn_sequence = generate_pn_sequence(pn_length)

extracted_bits = []

for i in range(num_bits):

start = i * pn_length

end = start + pn_length

segment_diff = diff[start:end]

segment_pn = pn_sequence[:len(segment_diff)]

correlation = np.sum(segment_diff * segment_pn)

extracted_bits.append('1' if correlation > 0 else '0')

将比特转换为字符串

watermark = ''

for i in range(0, len(extracted_bits), 8):

byte = ''.join(extracted_bits[i:i+8])

watermark += chr(int(byte, 2))

return watermark

使用示例

embed_watermark_spread_spectrum('original.wav', '秘密', 'watermarked_ss.wav', 0.01)

extracted = extract_watermark_spread_spectrum('watermarked_ss.wav', 'original.wav', 2, 1000)

print("提取的水印:", extracted)

注意事项

  1. **音频质量**:水印嵌入会影响音频质量,需要平衡水印强度和音频质量。

  2. **鲁棒性**:不同方法对音频处理的抵抗能力不同:

  • LSB方法脆弱但容量大

  • DCT方法对压缩有一定抵抗能力

  • 扩频方法鲁棒性最强但容量小

  1. **安全性**:可以考虑加密水印内容提高安全性

  2. **格式支持**:示例中使用WAV格式,因其是无损格式,其他格式可能需要先解码

扩展建议

  1. 添加错误校正码提高水印提取的可靠性

  2. 实现盲水印提取(不需要原始音频)

  3. 添加同步信号提高对裁剪、时间拉伸的抵抗能力

  4. 结合多种技术提高水印的鲁棒性和隐蔽

这些方法可以根据具体需求进行调整和组合,以实现不同场景下的音频数字水印需求。

相关推荐
极梦网络无忧9 小时前
OpenClaw 基础使用说明(中文版)
python
codeJinger9 小时前
【Python】操作Excel文件
python·excel
XLYcmy9 小时前
一个针对医疗RAG系统的数据窃取攻击工具
python·网络安全·ai·llm·agent·rag·ai安全
Islucas10 小时前
Claude code入门保姆级教程
python·bash·claude
萝卜白菜。10 小时前
TongWeb7.0相同的类指明加载顺序
开发语言·python·pycharm
赵钰老师10 小时前
【ADCIRC】基于“python+”潮汐、风驱动循环、风暴潮等海洋水动力模拟实践技术应用
python·信息可视化·数据分析
爬山算法10 小时前
MongoDB(80)如何在MongoDB中使用多文档事务?
数据库·python·mongodb
YuanDaima204811 小时前
基于 LangChain 1.0 的检索增强生成(RAG)实战
人工智能·笔记·python·langchain·个人开发·langgraph
愚公搬代码11 小时前
【愚公系列】《剪映+DeepSeek+即梦:短视频制作》046-转场:短视频一气呵成的秘密(什么是转场)
人工智能·音视频