直方图平移与预测误差扩展的可逆水印技术
这是一种高容量、低失真的可逆水印嵌入技术,适用于需要无损恢复原始载体的领域。
一、核心原理
1.1 技术框架
原始图像 → 像素预测 → 预测误差直方图 → 平移扩展 → 嵌入水印 → 含水印图像
提取过程:含水印图像 → 相同预测 → 预测误差分析 → 提取水印 + 恢复原始图像
1.2 关键概念
- 直方图平移:移动预测误差直方图的特定部分,为水印嵌入创造空间
- 预测误差扩展:利用像素间的相关性,将预测误差扩展以嵌入更多信息
- 可逆性:嵌入水印后能够完全恢复原始图像
二、MATLAB实现代码
2.1 主程序框架
%% 主程序:基于直方图平移和预测误差扩展的可逆水印嵌入
clc; clear; close all;
%% 1. 参数设置
img_path = 'lena.bmp'; % 原始图像
watermark = '秘密信息:这是一段需要隐藏的水印信息!';
block_size = 8; % 分块大小
threshold_T = 3; % 直方图平移阈值
use_optimized_prediction = true; % 使用优化预测器
%% 2. 加载和处理图像
original_img = imread(img_path);
if size(original_img, 3) == 3
original_img = rgb2gray(original_img);
end
original_img = double(original_img);
[M, N] = size(original_img);
fprintf('图像尺寸: %d × %d\n', M, N);
fprintf('水印信息: %s\n', watermark);
fprintf('水印长度: %d bits\n', length(watermark)*8);
%% 3. 水印预处理
watermark_bits = watermark_to_bits(watermark);
fprintf('水印转换为二进制: %d bits\n', length(watermark_bits));
%% 4. 可逆水印嵌入
[watermarked_img, peak_point, zero_point, location_map] = ...
embed_watermark_PEE(original_img, watermark_bits, threshold_T, use_optimized_prediction);
%% 5. 水印提取和恢复
[extracted_watermark, recovered_img] = ...
extract_watermark_PEE(watermarked_img, threshold_T, peak_point, zero_point, location_map, use_optimized_prediction);
%% 6. 评估性能
evaluate_performance(original_img, watermarked_img, recovered_img, watermark, extracted_watermark);
2.2 核心嵌入函数
function [watermarked_img, peak_point, zero_point, location_map] = ...
embed_watermark_PEE(original_img, watermark_bits, T, optimized)
% 基于直方图平移和预测误差扩展的水印嵌入
% 输入:
% original_img - 原始图像
% watermark_bits - 水印比特序列
% T - 阈值
% optimized - 是否使用优化预测器
% 输出:
% watermarked_img - 含水印图像
% peak_point - 峰值点
% zero_point - 零点
% location_map - 位置图(记录不可嵌入像素)
[M, N] = size(original_img);
watermarked_img = original_img;
% 步骤1: 计算预测误差
[prediction_errors, prediction_map] = compute_prediction_errors(original_img, optimized);
% 步骤2: 构建预测误差直方图
[error_hist, bin_centers] = compute_error_histogram(prediction_errors);
% 步骤3: 寻找峰值点和零点
[peak_point, zero_point] = find_peak_zero_points(error_hist, bin_centers, T);
fprintf('峰值点: %d, 零点: %d\n', peak_point, zero_point);
% 步骤4: 生成位置图(标记不可嵌入像素)
location_map = create_location_map(original_img, prediction_errors, peak_point, zero_point, T);
% 步骤5: 直方图平移
watermarked_img = histogram_shifting(original_img, prediction_errors, peak_point, zero_point, T);
% 步骤6: 水印嵌入
watermark_idx = 1;
watermark_len = length(watermark_bits);
for i = 2:M-1
for j = 2:N-1
% 跳过边界像素
if location_map(i, j) == 1
continue; % 不可嵌入像素
end
% 计算当前像素的预测误差
if optimized
pred = optimized_predictor(watermarked_img, i, j);
else
pred = median_predictor(watermarked_img, i, j);
end
current_error = watermarked_img(i, j) - pred;
% 检查是否在嵌入范围内
if current_error == peak_point
if watermark_idx <= watermark_len
% 嵌入水印
b = watermark_bits(watermark_idx);
if b == 1
watermarked_img(i, j) = watermarked_img(i, j) + 1;
end
watermark_idx = watermark_idx + 1;
if watermark_idx > watermark_len
break;
end
end
end
if watermark_idx > watermark_len
break;
end
end
if watermark_idx > watermark_len
break;
end
end
% 检查是否所有水印都已嵌入
if watermark_idx <= watermark_len
warning('水印未完全嵌入!可能需要调整阈值T');
end
fprintf('嵌入水印比特数: %d/%d\n', watermark_idx-1, watermark_len);
end
2.3 预测误差计算
function [prediction_errors, prediction_map] = compute_prediction_errors(img, optimized)
% 计算预测误差
[M, N] = size(img);
prediction_errors = zeros(M, N);
prediction_map = zeros(M, N);
% 使用不同的预测器
for i = 2:M-1
for j = 2:N-1
if optimized
% 优化预测器(GAP: Gradient Adjusted Predictor)
pred = optimized_predictor(img, i, j);
else
% 中值预测器
pred = median_predictor(img, i, j);
end
prediction_errors(i, j) = img(i, j) - pred;
prediction_map(i, j) = pred;
end
end
end
function pred = median_predictor(img, i, j)
% 中值预测器
neighbors = [img(i-1, j), img(i, j-1), img(i-1, j-1)];
pred = median(neighbors);
end
function pred = optimized_predictor(img, i, j)
% 优化预测器(GAP)
% 考虑梯度信息,提高预测精度
I_n = img(i-1, j);
I_w = img(i, j-1);
I_nw = img(i-1, j-1);
% 计算梯度
dh = abs(I_w - I_nw);
dv = abs(I_n - I_nw);
if dh - dv > 80
% 垂直边缘
pred = I_n;
elseif dv - dh > 80
% 水平边缘
pred = I_w;
else
% 平滑区域
pred = I_w + I_n - I_nw;
if pred > 255
pred = 255;
elseif pred < 0
pred = 0;
end
end
end
2.4 直方图分析与处理
function [peak_point, zero_point] = find_peak_zero_points(error_hist, bin_centers, T)
% 寻找峰值点和零点
% 峰值点:直方图中频率最高的点
% 零点:频率为0或最低的点
% 找到峰值
[max_freq, peak_idx] = max(error_hist);
peak_point = bin_centers(peak_idx);
% 在峰值点附近寻找零点
% 搜索范围:[-T, T]
search_range = -T:T;
% 从峰值点向两侧搜索
zero_candidates = [];
for k = 1:length(search_range)
test_point = peak_point + search_range(k);
% 检查是否在直方图范围内
idx = find(bin_centers == test_point, 1);
if ~isempty(idx) && error_hist(idx) == 0
zero_candidates = [zero_candidates, test_point];
end
end
% 如果没有找到零点,选择频率最低的点
if isempty(zero_candidates)
[~, min_idx] = min(error_hist);
zero_point = bin_centers(min_idx);
else
% 选择离峰值点最近的零点
distances = abs(zero_candidates - peak_point);
[~, min_dist_idx] = min(distances);
zero_point = zero_candidates(min_dist_idx);
end
end
function watermarked_img = histogram_shifting(img, prediction_errors, peak, zero, T)
% 执行直方图平移
[M, N] = size(img);
watermarked_img = img;
for i = 2:M-1
for j = 2:N-1
error = prediction_errors(i, j);
if zero > peak
% 零点在峰值右侧
if error > peak && error < zero
watermarked_img(i, j) = watermarked_img(i, j) + 1;
elseif error >= zero
watermarked_img(i, j) = watermarked_img(i, j) + 1;
end
else
% 零点在峰值左侧
if error < peak && error > zero
watermarked_img(i, j) = watermarked_img(i, j) - 1;
elseif error <= zero
watermarked_img(i, j) = watermarked_img(i, j) - 1;
end
end
% 确保像素值在有效范围内
if watermarked_img(i, j) > 255
watermarked_img(i, j) = 255;
elseif watermarked_img(i, j) < 0
watermarked_img(i, j) = 0;
end
end
end
end
2.5 水印提取和恢复
function [extracted_watermark, recovered_img] = ...
extract_watermark_PEE(watermarked_img, T, peak, zero, location_map, optimized)
% 提取水印并恢复原始图像
[M, N] = size(watermarked_img);
recovered_img = watermarked_img;
% 存储提取的水印比特
extracted_bits = [];
% 第一遍:提取水印
for i = 2:M-1
for j = 2:N-1
if location_map(i, j) == 1
continue;
end
% 计算预测值
if optimized
pred = optimized_predictor(watermarked_img, i, j);
else
pred = median_predictor(watermarked_img, i, j);
end
current_error = watermarked_img(i, j) - pred;
% 检查是否是嵌入位置
if current_error == peak || current_error == peak + 1
if current_error == peak
extracted_bits = [extracted_bits, 0];
else
extracted_bits = [extracted_bits, 1];
end
% 恢复原始像素值
if current_error == peak + 1
recovered_img(i, j) = recovered_img(i, j) - 1;
end
end
end
end
% 第二遍:逆直方图平移
for i = 2:M-1
for j = 2:N-1
if location_map(i, j) == 1
continue;
end
% 计算预测误差(使用恢复后的图像)
if optimized
pred = optimized_predictor(recovered_img, i, j);
else
pred = median_predictor(recovered_img, i, j);
end
error = recovered_img(i, j) - pred;
% 逆平移
if zero > peak
if error > peak + 1 && error <= zero
recovered_img(i, j) = recovered_img(i, j) - 1;
elseif error > zero
recovered_img(i, j) = recovered_img(i, j) - 1;
end
else
if error < peak - 1 && error >= zero
recovered_img(i, j) = recovered_img(i, j) + 1;
elseif error < zero
recovered_img(i, j) = recovered_img(i, j) + 1;
end
end
end
end
% 将提取的比特转换为水印信息
extracted_watermark = bits_to_watermark(extracted_bits);
fprintf('提取水印比特数: %d\n', length(extracted_bits));
end
2.6 辅助函数
function bits = watermark_to_bits(watermark_str)
% 将字符串转换为比特序列
bits = [];
for i = 1:length(watermark_str)
char_bits = dec2bin(double(watermark_str(i)), 8);
bits = [bits, char_bits - '0'];
end
end
function watermark_str = bits_to_watermark(bit_seq)
% 将比特序列转换为字符串
watermark_str = '';
% 确保比特数是8的倍数
num_bits = length(bit_seq);
num_chars = floor(num_bits / 8);
for i = 1:num_chars
start_idx = (i-1)*8 + 1;
end_idx = i*8;
if end_idx > num_bits
break;
end
char_bits = bit_seq(start_idx:end_idx);
char_code = bin2dec(num2str(char_bits));
watermark_str = [watermark_str, char(char_code)];
end
end
function location_map = create_location_map(img, prediction_errors, peak, zero, T)
% 创建位置图,标记不可嵌入像素
[M, N] = size(img);
location_map = zeros(M, N);
for i = 2:M-1
for j = 2:N-1
error = prediction_errors(i, j);
% 标记可能溢出的像素
if zero > peak
if img(i, j) == 255 && error >= zero
location_map(i, j) = 1;
end
else
if img(i, j) == 0 && error <= zero
location_map(i, j) = 1;
end
end
% 标记边界像素
if i == 1 || i == M || j == 1 || j == N
location_map(i, j) = 1;
end
end
end
end
2.7 性能评估
function evaluate_performance(original_img, watermarked_img, recovered_img, original_watermark, extracted_watermark)
% 评估算法性能
%% 1. 图像质量评估
% PSNR (峰值信噪比)
psnr_wm = psnr(uint8(watermarked_img), uint8(original_img));
psnr_rec = psnr(uint8(recovered_img), uint8(original_img));
% SSIM (结构相似性)
ssim_wm = ssim(uint8(watermarked_img), uint8(original_img));
ssim_rec = ssim(uint8(recovered_img), uint8(original_img));
% MSE (均方误差)
mse_wm = immse(uint8(watermarked_img), uint8(original_img));
mse_rec = immse(uint8(recovered_img), uint8(original_img));
%% 2. 水印正确性评估
watermark_correct = strcmp(original_watermark, extracted_watermark);
watermark_similarity = sum(original_watermark == extracted_watermark) / length(original_watermark);
%% 3. 容量评估
[M, N] = size(original_img);
total_pixels = M * N;
usable_pixels = sum(sum(original_img(2:end-1, 2:end-1) > 0 & original_img(2:end-1, 2:end-1) < 255));
%% 4. 显示结果
fprintf('\n========== 性能评估结果 ==========\n');
fprintf('图像质量指标:\n');
fprintf(' 含水印图像 PSNR: %.2f dB\n', psnr_wm);
fprintf(' 恢复图像 PSNR: %.2f dB\n', psnr_rec);
fprintf(' 含水印图像 SSIM: %.4f\n', ssim_wm);
fprintf(' 恢复图像 SSIM: %.4f\n', ssim_rec);
fprintf(' 含水印图像 MSE: %.4f\n', mse_wm);
fprintf(' 恢复图像 MSE: %.4f\n', mse_rec);
fprintf('\n水印正确性:\n');
fprintf(' 水印完全正确: %s\n', string(watermark_correct));
fprintf(' 水印相似度: %.2f%%\n', watermark_similarity*100);
fprintf('\n容量评估:\n');
fprintf(' 图像尺寸: %d × %d = %d 像素\n', M, N, total_pixels);
fprintf(' 可用像素: %d\n', usable_pixels);
fprintf(' 嵌入率: %.4f bpp (比特每像素)\n', length(original_watermark)*8 / total_pixels);
%% 5. 可视化结果
figure('Position', [100, 100, 1200, 800]);
% 子图1: 原始图像
subplot(2, 3, 1);
imshow(uint8(original_img));
title('原始图像');
% 子图2: 含水印图像
subplot(2, 3, 2);
imshow(uint8(watermarked_img));
title(sprintf('含水印图像\nPSNR=%.2fdB, SSIM=%.4f', psnr_wm, ssim_wm));
% 子图3: 恢复图像
subplot(2, 3, 3);
imshow(uint8(recovered_img));
title(sprintf('恢复图像\nPSNR=%.2fdB, SSIM=%.4f', psnr_rec, ssim_rec));
% 子图4: 差值图像1
subplot(2, 3, 4);
diff1 = abs(double(watermarked_img) - double(original_img));
imshow(uint8(diff1 * 10)); % 放大显示
title('含水印-原始图像差值(×10)');
colorbar;
% 子图5: 差值图像2
subplot(2, 3, 5);
diff2 = abs(double(recovered_img) - double(original_img));
imshow(uint8(diff2 * 10)); % 放大显示
title('恢复-原始图像差值(×10)');
colorbar;
% 子图6: 水印信息
subplot(2, 3, 6);
text(0.1, 0.5, sprintf('原始水印:\n%s\n\n提取水印:\n%s\n\n正确性: %s\n相似度: %.1f%%', ...
original_watermark, extracted_watermark, ...
string(watermark_correct), watermark_similarity*100), ...
'FontSize', 10, 'Interpreter', 'none');
axis off;
title('水印信息对比');
sgtitle('直方图平移与预测误差扩展水印算法性能评估');
end
2.8 预测误差直方图分析
function [error_hist, bin_centers] = compute_error_histogram(prediction_errors)
% 计算预测误差直方图
% 展平预测误差矩阵
errors_flat = prediction_errors(:);
% 移除零值(未计算的边界)
errors_flat = errors_flat(errors_flat ~= 0);
% 计算直方图
min_error = min(errors_flat);
max_error = max(errors_flat);
bin_edges = min_error-0.5:1:max_error+0.5;
error_hist = histcounts(errors_flat, bin_edges);
bin_centers = (bin_edges(1:end-1) + bin_edges(2:end)) / 2;
% 可视化直方图
figure('Position', [200, 200, 800, 400]);
subplot(1, 2, 1);
bar(bin_centers, error_hist, 'b');
xlabel('预测误差值');
ylabel('频率');
title('预测误差直方图');
grid on;
subplot(1, 2, 2);
% 显示峰值区域
[max_freq, peak_idx] = max(error_hist);
peak_value = bin_centers(peak_idx);
% 高亮显示峰值附近区域
hold on;
bar(bin_centers, error_hist, 'b');
% 标记峰值
plot(peak_value, max_freq, 'ro', 'MarkerSize', 10, 'LineWidth', 2);
text(peak_value, max_freq, sprintf('峰值: %d\n频率: %d', peak_value, max_freq), ...
'VerticalAlignment', 'bottom', 'HorizontalAlignment', 'center');
xlim([peak_value-10, peak_value+10]);
xlabel('预测误差值');
ylabel('频率');
title('峰值区域放大');
grid on;
sgtitle('预测误差分布分析');
end
参考代码 利用直方图平移和预测误差拓展的水印嵌入技术 www.youwenfan.com/contentcnu/54958.html
三、算法优化
3.1 自适应阈值选择
function optimal_T = find_optimal_threshold(img, target_capacity)
% 自适应寻找最优阈值T
[M, N] = size(img);
max_capacity = (M-2)*(N-2); % 最大可能容量
T_values = 1:10;
capacities = zeros(size(T_values));
for t_idx = 1:length(T_values)
T = T_values(t_idx);
% 计算预测误差
prediction_errors = compute_prediction_errors(img, true);
% 计算峰值点频率
[error_hist, bin_centers] = compute_error_histogram(prediction_errors);
[max_freq, peak_idx] = max(error_hist);
peak_point = bin_centers(peak_idx);
% 估计容量
capacities(t_idx) = max_freq;
end
% 寻找满足目标容量的最小T
optimal_T = find(capacities >= target_capacity, 1);
if isempty(optimal_T)
optimal_T = T_values(end);
end
% 可视化容量-T关系
figure;
plot(T_values, capacities, 'b-o', 'LineWidth', 2);
xlabel('阈值 T');
ylabel('估计容量 (bits)');
title('容量与阈值关系');
grid on;
hold on;
plot([optimal_T, optimal_T], [0, capacities(optimal_T)], 'r--');
text(optimal_T, capacities(optimal_T)/2, ...
sprintf('最优T=%d\n容量=%d bits', optimal_T, capacities(optimal_T)), ...
'VerticalAlignment', 'bottom');
end
3.2 多峰值嵌入
function [watermarked_img, peaks] = multi_peak_embedding(original_img, watermark_bits)
% 多峰值嵌入策略,提高容量
[M, N] = size(original_img);
% 计算预测误差直方图
prediction_errors = compute_prediction_errors(original_img, true);
[error_hist, bin_centers] = compute_error_histogram(prediction_errors);
% 寻找多个峰值
[sorted_freqs, sorted_idx] = sort(error_hist, 'descend');
num_peaks = min(5, length(sorted_idx)); % 使用前5个峰值
peaks = bin_centers(sorted_idx(1:num_peaks));
peak_freqs = sorted_freqs(1:num_peaks);
fprintf('找到 %d 个峰值点:\n', num_peaks);
for i = 1:num_peaks
fprintf(' 峰值%d: 值=%d, 频率=%d\n', i, peaks(i), peak_freqs(i));
end
% 为每个峰值分配水印比特
total_capacity = sum(peak_freqs);
watermark_alloc = round(watermark_bits * peak_freqs / total_capacity);
% 执行多峰值嵌入
watermarked_img = original_img;
watermark_idx = 1;
for p = 1:num_peaks
current_peak = peaks(p);
bits_to_embed = min(peak_freqs(p), watermark_alloc(p));
% 对该峰值进行嵌入
for i = 2:M-1
for j = 2:N-1
pred = optimized_predictor(watermarked_img, i, j);
current_error = watermarked_img(i, j) - pred;
if current_error == current_peak
if watermark_idx <= length(watermark_bits) && bits_to_embed > 0
b = watermark_bits(watermark_idx);
if b == 1
watermarked_img(i, j) = watermarked_img(i, j) + 1;
end
watermark_idx = watermark_idx + 1;
bits_to_embed = bits_to_embed - 1;
end
end
end
end
end
end
四、应用示例
4.1 医学图像应用
%% 医学图像可逆水印应用
function medical_image_watermarking()
% 加载医学图像
medical_img = imread('medical_image.dcm'); % DICOM格式
% 患者信息水印
patient_info = struct(...
'name', '张某某', ...
'id', '2023123456', ...
'date', '2024-01-15', ...
'doctor', '李医生', ...
'diagnosis', '胸部CT扫描正常' ...
);
% 将结构体转换为字符串
watermark_str = jsonencode(patient_info);
% 使用更高的T值以获得更大容量
T = 5;
% 嵌入水印
[watermarked_img, peak, zero, location_map] = ...
embed_watermark_PEE(double(medical_img), watermark_to_bits(watermark_str), T, true);
% 保存含水印图像
imwrite(uint8(watermarked_img), 'medical_image_watermarked.dcm');
fprintf('医学图像水印嵌入完成\n');
fprintf('患者信息已安全嵌入\n');
end
4.2 批量处理
%% 批量图像水印处理
function batch_watermarking(input_folder, output_folder, watermark)
% 批量处理文件夹中的所有图像
% 创建输出文件夹
if ~exist(output_folder, 'dir')
mkdir(output_folder);
end
% 获取所有图像文件
image_files = dir(fullfile(input_folder, '*.bmp'));
image_files = [image_files; dir(fullfile(input_folder, '*.png'))];
image_files = [image_files; dir(fullfile(input_folder, '*.jpg'))];
% 处理每张图像
for i = 1:length(image_files)
fprintf('处理第 %d/%d 张图像: %s\n', i, length(image_files), image_files(i).name);
% 读取图像
img_path = fullfile(input_folder, image_files(i).name);
img = imread(img_path);
if size(img, 3) == 3
img = rgb2gray(img);
end
% 嵌入水印
watermark_bits = watermark_to_bits(watermark);
T = 3; % 固定阈值
[watermarked_img, ~, ~, ~] = embed_watermark_PEE(double(img), watermark_bits, T, true);
% 保存结果
output_path = fullfile(output_folder, image_files(i).name);
imwrite(uint8(watermarked_img), output_path);
end
fprintf('批量处理完成!共处理 %d 张图像\n', length(image_files));
end
五、技术特点与优势
5.1 技术特点
- 完全可逆:能够无损恢复原始图像
- 高容量:利用预测误差扩展提高嵌入容量
- 低失真:PSNR通常高于40dB
- 安全性:水印信息不可感知
- 鲁棒性:对常规图像处理有一定抵抗能力
5.2 性能指标
- PSNR:40-50 dB(取决于嵌入容量)
- 嵌入率:0.1-2.0 bpp(比特每像素)
- 计算复杂度:O(N)线性复杂度
- 内存需求:需要存储位置图
5.3 适用场景
- 医学影像:嵌入患者信息,同时保证诊断质量
- 司法取证:嵌入时间戳和取证信息
- 军事图像:嵌入加密的元数据
- 数字版权:嵌入版权信息,支持所有权验证
六、使用说明
-
基本使用:
% 运行主程序 main_watermarking_PEE -
调整参数:
- 修改
threshold_T控制嵌入容量和质量 - 设置
use_optimized_prediction选择预测器 - 调整图像尺寸适应不同应用
- 修改
-
扩展功能:
- 使用多峰值嵌入提高容量
- 实现自适应阈值选择
- 添加加密增强安全性
这个实现提供了完整的直方图平移和预测误差扩展水印技术,适用于需要可逆水印的各种应用场景。