算法思想来自《彩色数字水印技术研究》一文
数字水印技术就是将特定的信息嵌入到数字信息的内容中,以保护所有者的权益。一个有效的数字水印应至少满足下面三个特性:不可觉察性:水印的存在不应明显干扰被保护的数据;安全性:水印的存在应该是安全的、可靠的,水印图像必须唯一标志原始图像的相关信息,任何第三者都不能伪造他人的水印图像;鲁棒性:给定一个水印文档,非授权的个人或团体在使文档可用的情况下无法剔除水印。可能看出水印的鲁棒性和不可觉察性是相互矛盾。对于一个特定的水印算法,必须折衷考虑这两个属性。
用的数字水印算法包含两个基本方面:水印的嵌入和水印的提取或检测。水印可由多种模型构成,如随机数字序列、数字标识、文本以及图像等。从鲁棒性和安全性考虑,常常要对水印进行随机化以及加密处理。
水印的嵌入过程:设有算法E,原始图像I和水印 W~,那么水印图像可表示如下:
数字水印的一般原理也可应用于彩色水印的嵌入和提取。但由于彩色水印含有比较多的信息,它比起一般水印信息来又有自己的特点。有文献利用静态图像的压缩编码技术,基于原始图像的DCT系数之间的关系,将彩色数字水印编码为一系列二值ID数字序列,直接作为掩码信息实现水印的嵌入。虽然水印的提取不需要原始图像,但水印只能抵抗JPEG有损压缩操作,对其它的图像操作的抵抗性很弱。基于这种情况,这篇文章提出了一种基于小波变换(DWT)的双彩色图印方案。实验证明,此方案不仅能抵抗JPEG有损压缩,还能抵抗剪切、放缩和平滑等操作。
彩色水印图像的详细嵌入过程如图3所示。基本步骤如下:
具体实践上有几点细节:
1.水印图像由[0 255]归一化到[-1 1],这应该是为了达到零均值,实际叠加时会乘以一个系数,文章认为α=4是不可察觉性和鲁棒性的一个很好的平衡
2.小波变换在YUV空间进行,文章说这是因为YUV是符合人视觉系统的一套颜色空间,而本人试了一下其他的色彩空间,确实YUV是效果最好的
3.加入水印分量是尽量往大的中频系数上加,大的系数意味着较强的能量,加一个小的扰动不会在视觉上变化太明显。
提取水印图像则是加水印的逆过程,基本步骤如图4所示
下面是MATLAB上的实现,与文章所述有一些变化:
1.只使用了二级小波变换,而不是四级,鲁棒性会有所下降,但水印尺寸得以提升
2.文章认为应向中频系数上添加水印,但低频分量能量和抗干扰性能比中频更强,所以优先向低频分量添加水印,如果低频分量系数个数不够再向中频分量添加
3.颜色空间使用的是YCbCr,跟YUV没有本质区别
function [Imgout] = Hide_3(OriImg,Print)
% 功能 将Print中的携带的信息通过图像处理技术隐藏在OriImg中
% OriImg 输入参数,RGB彩色图像,uint8格式,大小为512*512*3,作为隐
% 藏信息的载体
% Print 输入参数,RGB彩色图像,uint8格式,大小为140*200*3,信息本
% 身所在
% MarkedImg 输出参数,RGB彩色图像,uint8格式,大小为512*512*3,经过
% 信息隐藏处理后的图像
OriImg = rgb2ycbcr(OriImg);
MarkedImg = zeros(size(OriImg));
alpha = 4;
for i=1:3
Orilayer = OriImg(:,:,i);
Printlayer = Print(:,:,i);
Print_ori = Printlayer;
Printlayer = (double(Printlayer)-128)/128; %归一到-1~1
[C,S] = wavedec2(Orilayer,2,'db2');
[rows, cols] = size(Printlayer);
totallength = rows*cols;
cA2 = appcoef2(C,S,'db2',2);
cH2 = detcoef2('h',C,S,2);
[cA2_r, cA2_c] = size(cA2);
[cH2_r, cH2_c] = size(cH2);
part1length = cA2_r*cA2_c;
part2length = cH2_r*cH2_c;
flattenpri = reshape(Printlayer, 1, []);
C(1:part1length) = C(1:part1length)+alpha*flattenpri(1:part1length);
leftlength = totallength-part1length;
cH2 = C(part1length+1:part1length+part2length);
[~, IH] = sort(abs(cH2),'descend');
IH = IH(1:leftlength);
cH2(IH) = cH2(IH)+alpha*flattenpri(part1length+1:totallength);
C(part1length+1:part1length+part2length) = cH2;
Outlayer = waverec2(C,S,'db2');
MarkedImg(:,:,i) = uint8(Outlayer);
end
Imgout = ycbcr2rgb(uint8(MarkedImg));
end
function Print = Recover_3(MarkedImg, OriImg)
% 功能 将含有隐藏信息的图像MarkedImg中的信息标记(图像)分离到
%Print图像之中
% MarkedImg 输入参数,RGB彩色图像,uint8格式,大小为512*512*3,可能
%含有隐藏信息的图像
% OriImg 输入参数,RGB彩色图像,uint8格式,大小为512*512*3,不含隐
%藏信息的原始图像载体
% Print 输出参数,RGB彩色图像,uint8格式,大小为140*200*3,信息本
%身所在
rec_row = 140; %此处为水印图片的高度和宽度
rec_col = 200;
OriImg = rgb2ycbcr(OriImg);
MarkedImg = rgb2ycbcr(MarkedImg);
outImg = zeros(rec_row, rec_col, 3);
alpha = 4;
for i=1:3
Orilayer = OriImg(:,:,i);
Marklayer = MarkedImg(:,:,i);
[Co,So] = wavedec2(Orilayer,2,'db2');
coA2 = appcoef2(Co,So,'db2',2);
[cA2_r, cA2_c] = size(coA2);
part1length = cA2_r*cA2_c;
coA2 = Co(1:part1length);
coH2 = detcoef2('h',Co,So,2);
[cH2_r, cH2_c] = size(coH2);
part2length = cH2_r*cH2_c;
coH2 = Co(part1length+1:part1length+part2length);
[~, IHo] = sort(abs(coH2),'descend');
%IHo = 1:length(IHo);
leftlength = rec_row*rec_col-part1length;
IHo = IHo(1:leftlength);
[Cm,Sm] = wavedec2(Marklayer,2,'db2');
cmA2 = Cm(1:part1length);
cmH2 = Cm(part1length+1:part1length+part2length);
flat_print = zeros(1,rec_row*rec_col);
flat_print(1:part1length) = cmA2-coA2;
flat_print(part1length+1:rec_row*rec_col) = cmH2(IHo)-coH2(IHo);
Print = reshape(flat_print, rec_row, rec_col)/alpha;
Print = (Print*128)+128;
outImg(:,:,i) = Print;
end
Print = uint8(outImg);
end
实验效果
原图
水印
加水印后
恢复结果
对加水印图进行JPEG压缩攻击后的恢复结果
进行白色条形污损后的恢复结果
应该说,效果还是不错的