Digital watermarking technology can embed hidden information into audio files without significantly affecting audio quality. Below I will introduce several methods to implement digital watermarking of audio in Python.
Method 1: LSB (lowest significant bit) watermark
import numpy as np from import wavfile def embed_watermark_lsb(audio_path, watermark, output_path): # Read audio files sample_rate, audio_data = (audio_path) # Make sure it is stereo, if it is mono, it will be converted to stereo if len(audio_data.shape) == 1: audio_data = np.column_stack((audio_data, audio_data)) # Convert watermark to binary watermark_bin = ''.join(format(ord(c), '08b') for c in watermark) watermark_bin += '00000000' # Add an end tag # Check whether the watermark is suitable for the audio if len(watermark_bin) > audio_data.size: raise ValueError("The watermark is too big to be embedded in the audio") # Embed the watermark to the lowest significant bit watermark_index = 0 for i in range(len(audio_data)): for j in range(len(audio_data[i])): if watermark_index < len(watermark_bin): # Replace the least significant bit audio_data[i][j] = (audio_data[i][j] & 0xFE) | int(watermark_bin[watermark_index]) watermark_index += 1 else: break # Save audio with watermark (output_path, sample_rate, audio_data) def extract_watermark_lsb(audio_path, watermark_length): # Read audio files sample_rate, audio_data = (audio_path) # Extract the least significant bit 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)) # Convert bits to bytes watermark = '' for i in range(0, len(watermark_bits), 8): byte = ''.join(watermark_bits[i:i+8]) if byte == '00000000': # End tag encountered break watermark += chr(int(byte, 2)) return watermark[:watermark_length] #User Exampleembed_watermark_lsb('', 'Secret news', '') extracted = extract_watermark_lsb('', 4) print("Extracted watermark:", extracted)
Method 2: Frequency Domain Watermark (DCT Transformation)
import numpy as np from import dct, idct from import wavfile def embed_watermark_dct(audio_path, watermark, output_path, alpha=0.01): # Read audio sample_rate, audio_data = (audio_path) # If it is stereo, use only one channel if len(audio_data.shape) > 1: audio_data = audio_data[:, 0] # Convert watermark to binary watermark_bin = ''.join(format(ord(c), '08b') for c in watermark) watermark_bin = [int(b) for b in watermark_bin] # Segmented audio processing segment_size = 1024 num_segments = len(audio_data) // segment_size watermark_length = len(watermark_bin) if num_segments < watermark_length: raise ValueError("The audio is too short to embed the watermark") # Embed watermark watermarked_audio = (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') # Modify the IF coefficient to embed the watermark coeff_index = 100 # Select an intermediate frequency coefficient if watermark_bin[i] == 1: dct_coeffs[coeff_index] += alpha * (dct_coeffs[coeff_index]) else: dct_coeffs[coeff_index] -= alpha * (dct_coeffs[coeff_index]) # Inverse DCT Transformation watermarked_segment = idct(dct_coeffs, norm='ortho') watermarked_audio[start:end] = watermarked_segment # Save audio with watermark (output_path, sample_rate, watermarked_audio.astype(np.int16)) def extract_watermark_dct(audio_path, original_path, watermark_length): # Read watermarked audio and original audio sample_rate, watermarked = (audio_path) _, original = (original_path) # If it is stereo, use only one channel if len() > 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') # Convert bits to string watermark = '' for i in range(0, len(watermark_bits), 8): byte = ''.join(watermark_bits[i:i+8]) watermark += chr(int(byte, 2)) return watermark #User Exampleembed_watermark_dct('', 'secret', 'watermarked_dct.wav', 0.02) extracted = extract_watermark_dct('watermarked_dct.wav', '', 16) print("Extracted watermark:", extracted)
Method 3: Spread spectrum watermark
import numpy as np from import wavfile def generate_pn_sequence(length, seed=42): (seed) return ([-1, 1], size=length) def embed_watermark_spread_spectrum(audio_path, watermark, output_path, alpha=0.01): # Read audio sample_rate, audio_data = (audio_path) # If it is stereo, use only one channel if len(audio_data.shape) > 1: audio_data = audio_data[:, 0] # Convert watermark to binary watermark_bin = ''.join(format(ord(c), '08b') for c in watermark) watermark_bits = ([int(b) for b in watermark_bin]) watermark_bits = 2 * watermark_bits - 1 # Convert to ±1 # Generate pseudo-random sequences pn_length = len(audio_data) // len(watermark_bits) pn_sequence = generate_pn_sequence(pn_length) # Create a spread spectrum watermark spread_watermark = (watermark_bits, pn_length) spread_watermark = spread_watermark[:len(audio_data)] * pn_sequence[:len(audio_data)] # Embed watermark watermarked_audio = audio_data + alpha * spread_watermark * (audio_data) watermarked_audio = (watermarked_audio, -32768, 32767) # Make sure to be in the 16-bit range # Save audio with watermark (output_path, sample_rate, watermarked_audio.astype(np.int16)) def extract_watermark_spread_spectrum(audio_path, original_path, watermark_length, pn_length): # Read audio sample_rate, watermarked = (audio_path) _, original = (original_path) # If it is stereo, use only one channel if len() > 1: watermarked = watermarked[:, 0] original = original[:, 0] # Calculate the difference diff = watermarked - original # Generate the same pseudo-random sequence 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 = (segment_diff * segment_pn) extracted_bits.append('1' if correlation > 0 else '0') # Convert bits to string watermark = '' for i in range(0, len(extracted_bits), 8): byte = ''.join(extracted_bits[i:i+8]) watermark += chr(int(byte, 2)) return watermark #User Exampleembed_watermark_spread_spectrum('', 'secret', 'watermarked_ss.wav', 0.01) extracted = extract_watermark_spread_spectrum('watermarked_ss.wav', '', 2, 1000) print("Extracted watermark:", extracted)
Things to note
1. **Audio Quality**: Watermark embedding will affect the audio quality and need to balance the watermark intensity and audio quality.
2. **Roominess**: Different methods have different resistance to audio processing:
- LSB method is fragile but has a large capacity
- DCT method has certain resistance to compression
- Spread spectrum method has the strongest robustness but small capacity
3. **Security**: You can consider encrypting watermark content to improve security
4. **Format support**: The WAV format is used in the example, because it is a lossless format, other formats may need to be decoded first.
Extension suggestions
1. Add error correction code to improve the reliability of watermark extraction
2. Implement blind watermark extraction (no original audio required)
3. Add synchronization signals to improve resistance to cropping and time stretching
4. Combining multiple technologies to improve the robustness and concealment of watermarks
These methods can be adjusted and combined according to specific needs to achieve audio digital watermarking requirements in different scenarios.
The above is a detailed explanation of the example of adding digital watermarks for Python audio. For more information about adding digital watermarks for Python audio, please pay attention to my other related articles!