圖片加數字盲水印


本文通過一個的實驗,簡要介紹 頻域 手段添加 數字盲水印 的方法,並進一步驗證其 抗攻擊性 。在上述實驗的基礎上,總結躲避數字盲水印的方法。( 多圖預警

本文分為五個部分,第一部分綜述;第二部分頻域數字盲水印制作原理介紹;第三部分盲水印攻擊性實驗;第四部分總結;第五部分附錄(源代碼)。

一、綜述
本文提供的一種實現“阿里通過肉眼無法識別的標識碼追蹤員工”的技術手段。通過看其他答主的分析,阿里可能還沒用到頻域加水印的技術。

相對於空域方法,頻域加盲水印的方法隱匿性更強,抵抗攻擊能力更強。這類算法解水印困難,你不知道水印加在那個頻段,而且受到攻擊往往會破壞圖像原本內容。本文簡要科普通過頻域手段添加數字盲水印。對於web,可以添加一個背景圖片,來追蹤截圖者。

所謂盲水印,是指人感知不到的水印,包括看不到聽不見(沒錯,數字盲水印也能夠用於音頻)。其主要應用於音像作品、數字圖書等,目的是,在不破壞原始作品的情況下,實現版權的防護與追蹤。

添加數字盲水印的方法簡單可分為空域方法和頻域方法,這兩種方法添加了冗余信息,但在編碼和壓縮情況不變的情況下,不會使原始圖像大小產生變化(原來是10MB添加盲水印之后還是10MB)。

空域是指空間域,我們日常所見的圖像就是空域。空域添加數字水印的方法是在空間域直接對圖像操作(之所以說的這么繞,是因為不僅僅原圖是空域,原圖的差分等等也是空域),比如將水印直接疊加在圖像上。

我們常說一個音有多高,這個音高是指頻率;同樣,圖像灰度變化強烈的情況,也可以視為圖像的頻率。頻域添加數字水印的方法,是指通過某種變換手段(傅里葉變換,離散余弦變換,小波變換等)將圖像變換到頻域(小波域),在頻域對圖像添加水印,再通過逆變換,將圖像轉換為空間域。相對於空域手段,頻域手段隱匿性更強,抗攻擊性更高

所謂對水印的攻擊,是指破壞水印,包括塗抹,剪切,放縮,旋轉,壓縮,加噪,濾波等。數字盲水印不僅僅要敏捷性高(不被人抓到),也要防御性強(抗打)。就像Dota的敏捷英雄往往是脆皮,數字盲水印的隱匿性和魯棒性是互斥的。(魯棒性是抗攻擊性的學術名字)

二、頻域制作數字盲水印的方法
信號是有頻率的,一個信號可以看做是無數個不同階的正弦信號的的疊加。
F(\omega)=\int_{-\infty }^{+\infty }  f(t)e^{-i\omega t}dt
上式為傅里葉變換公式,f(t)是指時域信號(對於信號我們說時域,因為是與時間有關的,而圖像我們往往說空域,與空間有關),\omega是指頻率。想要對傅里葉變換有深入了解的同學,建議看一下《信號與系統》或者《數字信號處理》的教材,里面系統介紹了傅里葉變換、快速傅里葉變換、拉普拉斯變換、z變換等。

簡而言之,我們有方法將時域信號轉換成為頻域,同樣,我們也能將二維信號(圖像)轉換為頻域。在上文中提到,圖像的頻率是指圖像灰度變換的強烈情況。關於此方面更系統的知識,參見岡薩雷斯的《圖像處理》。

下面以傅里葉變換為例,介紹通過頻域給圖像添加數字盲水印的方法。注意,因為圖像是離散信號,我們實際用的是離散傅里葉變換,在本文采用的都是二維快速傅里葉變換,快速傅里葉變換與離散時間傅里葉變換等價,通過蝶型歸並的手段,速度更快。下文中傅里葉變換均為二維快速傅里葉變換。

上圖為疊加數字盲水印的基本流程。編碼的目的有二,一是對水印加密,二控制水印能量的分布。以下是疊加數字盲水印的實驗。

這是原圖像,尺寸300*240 (不要問我為什么不用Lena,那是我前女友),


之后進行傅里葉變換,下圖變換后的頻域圖像,

這是我想加的水印,尺寸200*100,

這是我編碼后的水印,編碼方式采用隨機序列編碼,通過編碼,水印分布到隨機分布到各個頻率,並且對水印進行了加密,

將上圖與原圖的頻譜疊加,可見圖像的頻譜已經發生了巨大的變化,

之后,將疊加水印的頻譜進行傅里葉逆變換,得到疊加數字水印后的圖像,

肉眼幾乎看不出疊加水印后的圖像與原圖的差異,這樣,數字盲水印已經疊加到圖像中去。
實際上,我們是把水印以噪聲的形式添加到原圖像中。
下圖是在空域上的加水印圖與原圖的殘差(調整了對比度,不然殘差調小看不見),

可以看出,實際上上述方法是通過頻域添加冗余信息(像噪聲一樣)。這些噪聲遍布全圖,在空域上並不容易破壞。
最終,均方誤差(MSE)為0.0244
信噪比(PSNR)為64.2dB

那么,為什么頻譜發生了巨大的變化,而在空域卻變化如此小呢?這是因為我們避開了圖像的主要頻率。下圖是原圖頻譜豎過來的樣子,其能量主要集中在低頻。

水印提取是水印疊加的逆過程,

經提取后,我們得到如下水印,問:為什么水印要對稱呢?嘿嘿,大家想想看。

三、攻擊性實驗
本部分進行攻擊性實驗,來驗證通過頻域手段疊加數字盲水印的魯棒性。
1.進行塗抹攻擊,這是攻擊后的圖片:

再進行水印提取:

2.進行剪切攻擊,就是網上經常用的截圖截取一部分的情況:

進行循環補全:

提取水印:

3.伸縮攻擊(這個實驗明碼做的,水印能量較高,隱匿性不強):

提取水印(水印加的不好,混頻挺嚴重的):

4.旋轉攻擊(明碼):

提取水印:

5.JPEG壓縮后(這個實驗我好像是拿明碼做的,能量主要加在了高頻):

提取結果:

6.PS 4像素馬賽克/均值濾波等,攻擊后圖像(這是我女朋友嗎?丑死了):

提取水印后圖像:

7.截屏,
截屏后我手動摳出要測試的圖像區域,並且抽樣或者插值到原圖尺寸:

測試結果:

8. 亮度調節(明碼):

水印提取:

9.色相調節(明碼):

水印提取:

10.飽和度調節(明碼):

水印:

11.對比度(明碼):

水印:

12.評論區用waifu2x去噪后圖片:


解水印:

13.美圖秀秀,我對我女票一鍵美顏,美白,磨皮,加腮紅,加唇彩(有一種很羞恥的感覺,捂臉):

提取水印:

14.對於背景純色的圖其實也是無所謂的
能量系數為10時加水印圖片:覺得太顯噪就把能量系數調低,不過水印的隱秘性和魯棒性是互斥的

最終提取出的水印:

15.我用將RGB>600的像素設置成為(0,255,0)來模擬PS魔術手,

提取水印為:

16.屏攝,好吧,這個實驗我做哭了
屏攝圖:

實驗結果:

我把水印能量系數調整到2000都沒有用。
屏攝之后與原圖信噪比為4dB左右,我用多抽樣濾波的方式試過,濾不掉屏攝引入的噪聲。屏攝不僅引入了椒鹽噪聲,乘性噪聲,還有有規律的雪花紋理(摩爾紋)。

四、總結
基於頻域的盲水印方法隱藏性強,魯棒性高,能夠抵御大部分攻擊。但是,對於盲水印算法,魯棒性和隱匿性是互斥的。

本文方法針對屏攝不行,我多次實驗沒有成功,哪位大神可以做一下或者討論討論。還有二值化不行,這是我想當然的,覺得肯定不行所以沒做實驗。其他的我試了試,用給出的方法調整一下能量系數都可以。

我想大家最關心的是什么最安全,不會被追蹤。
不涉及圖像的都安全,比如拿筆記下來。
涉及圖像的屏攝最安全
截屏十分不安全。

=====彩蛋====

我在上圖明碼寫入了信息。為了抵抗jpg壓縮,我水印能量較高,並且因為沒有編碼,能量分布不均。圖中規律性紋路,就是你懂的。嘿嘿,你懂的,解開看看吧。
 
 
五、附錄
 
%%傅里葉變換加水印源代碼
%% 運行環境Matlab2010a 
clc;clear;close all;
alpha = 1;

%% read data
im = double(imread('gl1.jpg'))/255;
mark = double(imread('watermark.jpg'))/255;
figure, imshow(im),title('original image');
figure, imshow(mark),title('watermark');

%% encode mark
imsize = size(im);
%random
TH=zeros(imsize(1)*0.5,imsize(2),imsize(3));
TH1 = TH;
TH1(1:size(mark,1),1:size(mark,2),:) = mark;
M=randperm(0.5*imsize(1));
N=randperm(imsize(2));
save('encode.mat','M','N');
for i=1:imsize(1)*0.5
    for j=1:imsize(2)
        TH(i,j,:)=TH1(M(i),N(j),:);
    end
end
% symmetric
mark_ = zeros(imsize(1),imsize(2),imsize(3));
mark_(1:imsize(1)*0.5,1:imsize(2),:)=TH;
for i=1:imsize(1)*0.5
    for j=1:imsize(2)
        mark_(imsize(1)+1-i,imsize(2)+1-j,:)=TH(i,j,:);
    end
end
figure,imshow(mark_),title('encoded watermark');
%imwrite(mark_,'encoded watermark.jpg');

%% add watermark
FA=fft2(im);
figure,imshow(FA);title('spectrum of original image');
FB=FA+alpha*double(mark_);
figure,imshow(FB); title('spectrum of watermarked image');
FAO=ifft2(FB);
figure,imshow(FAO); title('watermarked image');
%imwrite(uint8(FAO),'watermarked image.jpg');
RI = FAO-double(im);
figure,imshow(uint8(RI)); title('residual');
%imwrite(uint8(RI),'residual.jpg');
xl = 1:imsize(2);
yl = 1:imsize(1);
[xx,yy] = meshgrid(xl,yl);
figure, plot3(xx,yy,FA(:,:,1).^2+FA(:,:,2).^2+FA(:,:,3).^2),title('spectrum of original image');
figure, plot3(xx,yy,FB(:,:,1).^2+FB(:,:,2).^2+FB(:,:,3).^2),title('spectrum of watermarked image');
figure, plot3(xx,yy,FB(:,:,1).^2+FB(:,:,2).^2+FB(:,:,3).^2-FA(:,:,1).^2+FA(:,:,2).^2+FA(:,:,3).^2),title('spectrum of watermark');

%% extract watermark
FA2=fft2(FAO);
G=(FA2-FA)/alpha;
GG=G;
for i=1:imsize(1)*0.5
    for j=1:imsize(2)
        GG(M(i),N(j),:)=G(i,j,:);
    end
end
for i=1:imsize(1)*0.5
    for j=1:imsize(2)
        GG(imsize(1)+1-i,imsize(2)+1-j,:)=GG(i,j,:);
    end
end
figure,imshow(GG);title('extracted watermark');
%imwrite(uint8(GG),'extracted watermark.jpg');

%% MSE and PSNR
C=double(im);
RC=double(FAO);
MSE=0; PSNR=0;
for i=1:imsize(1)
    for j=1:imsize(2)
        MSE=MSE+(C(i,j)-RC(i,j)).^2;
    end
end
MSE=MSE/360.^2;
PSNR=20*log10(255/sqrt(MSE));
MSE
PSNR

%% attack test
%% attack by smearing
%A = double(imread('gl1.jpg'));
%B = double(imread('attacked image.jpg'));
attack = 1-double(imread('attack.jpg'))/255;
figure,imshow(attack);
FAO_ = FAO;
for i=1:imsize(1)
    for j=1:imsize(2)
        if attack(i,j,1)+attack(i,j,2)+attack(i,j,3)>0.5
            FAO_(i,j,:) = attack(i,j,:);
        end
    end
end
figure,imshow(FAO_);
%extract watermark
FA2=fft2(FAO_);
G=(FA2-FA)*2;
GG=G;
for i=1:imsize(1)*0.5
    for j=1:imsize(2)
        GG(M(i),N(j),:)=G(i,j,:);
    end
end
for i=1:imsize(1)*0.5
    for j=1:imsize(2)
        GG(imsize(1)+1-i,imsize(2)+1-j,:)=GG(i,j,:);
    end
end
figure,imshow(GG);title('extracted watermark');

%% attack by cutting
s2 = 0.8;
FAO_ = FAO;
FAO_(:,s2*imsize(2)+1:imsize(2),:) = FAO_(:,1:int32((1-s2)*imsize(2)),:);
figure,imshow(FAO_);
%extract watermark
FA2=fft2(FAO_);
G=(FA2-FA)*2;
GG=G;
for i=1:imsize(1)*0.5
    for j=1:imsize(2)
        GG(M(i),N(j),:)=G(i,j,:);
    end
end
for i=1:imsize(1)*0.5
    for j=1:imsize(2)
        GG(imsize(1)+1-i,imsize(2)+1-j,:)=GG(i,j,:);
    end
end
figure,imshow(GG);title('extracted watermark');


%%小波變換加水印,解水印大家按照加的思路逆過來就好
clc;clear;close all;
%% read data
im = double(imread('gl1.jpg'))/255;
mark = double(imread('watermark.jpg'))/255;
figure, imshow(im),title('original image');
figure, imshow(mark),title('watermark');
%% RGB division
im=double(im); 
mark=double(mark); 
imr=im(:,:,1); 
markr=mark(:,:,1); 
img=im(:,:,2); 
markg=mark(:,:,2); 
imb=im(:,:,3); 
markb=mark(:,:,3); 
%% parameter
r=0.04; 
g = 0.04; 
b = 0.04;
%% wavelet tranform and add watermark
% for red
[Cwr,Swr]=wavedec2(markr,1,'haar'); 
[Cr,Sr]=wavedec2(imr,2,'haar'); 
% add watermark
Cr(1:size(Cwr,2)/16)=... 
Cr(1:size(Cwr,2)/16)+r*Cwr(1:size(Cwr,2)/16); 
k=0; 
while k<=size(Cr,2)/size(Cwr,2)-1 
Cr(1+size(Cr,2)/4+k*size(Cwr,2)/4:size(Cr,2)/4+... 
(k+1)*size(Cwr,2)/4)=Cr(1+size(Cr,2)/4+... 
k*size(Cwr,2)/4:size(Cr,2)/4+(k+1)*size(Cwr,2)/4)+... 
r*Cwr(1+size(Cwr,2)/4:size(Cwr,2)/2); 
Cr(1+size(Cr,2)/2+k*size(Cwr,2)/4:size(Cr,2)/2+... 
(k+1)*size(Cwr,2)/4)=Cr(1+size(Cr,2)/2+... 
k*size(Cwr,2)/4:size(Cr,2)/2+(k+1)*size(Cwr,2)/4)+... 
r*Cwr(1+size(Cwr,2)/2:3*size(Cwr,2)/4); 
Cr(1+3*size(Cwr,2)/4+k*size(Cwr,2)/4:3*size(Cwr,2)/4+... 
(k+1)*size(Cwr,2)/4)=Cr(1+3*size(Cr,2)/4+... 
k*size(Cwr,2)/4:3*size(Cr,2)/4+(k+1)*size(Cwr,2)/4)+... 
r*Cwr(1+3*size(Cwr,2)/4:size(Cwr,2)); 
k=k+1; 
end; 
Cr(1:size(Cwr,2)/4)=Cr(1:size(Cwr,2)/4)+r*Cwr(1:size(Cwr,2)/4); 

% for green
[Cwg,Swg]=WAVEDEC2(markg,1,'haar'); 
[Cg,Sg]=WAVEDEC2(img,2,'haar'); 
Cg(1:size(Cwg,2)/16)=... 
Cg(1:size(Cwg,2)/16)+g*Cwg(1:size(Cwg,2)/16); 
k=0; 
while k<=size(Cg,2)/size(Cwg,2)-1 
Cg(1+size(Cg,2)/4+k*size(Cwg,2)/4:size(Cg,2)/4+... 
(k+1)*size(Cwg,2)/4)=Cg(1+size(Cg,2)/4+... 
k*size(Cwg,2)/4:size(Cg,2)/4+(k+1)*size(Cwg,2)/4)+... 
g*Cwg(1+size(Cwg,2)/4:size(Cwg,2)/2); 
Cg(1+size(Cg,2)/2+k*size(Cwg,2)/4:size(Cg,2)/2+... 
(k+1)*size(Cwg,2)/4)=Cg(1+size(Cg,2)/2+... 
k*size(Cwg,2)/4:size(Cg,2)/2+(k+1)*size(Cwg,2)/4)+... 
g*Cwg(1+size(Cwg,2)/2:3*size(Cwg,2)/4); 
Cg(1+3*size(Cg,2)/4+k*size(Cwg,2)/4:3*size(Cg,2)/4+... 
(k+1)*size(Cwg,2)/4)=Cg(1+3*size(Cg,2)/4+... 
k*size(Cwg,2)/4:3*size(Cg,2)/4+(k+1)*size(Cwg,2)/4)+... 
g*Cwg(1+3*size(Cwg,2)/4:size(Cwg,2)); 
k=k+1; 
end; 
Cg(1:size(Cwg,2)/4)=Cg(1:size(Cwg,2)/4)+g*Cwg(1:size(Cwg,2)/4); 

% for blue
[Cwb,Swb]=WAVEDEC2(markb,1,'haar'); 
[Cb,Sb]=WAVEDEC2(imb,2,'haar'); 
Cb(1:size(Cwb,2)/16)+b*Cwb(1:size(Cwb,2)/16); 
k=0; 
while k<=size(Cb,2)/size(Cwb,2)-1 
Cb(1+size(Cb,2)/4+k*size(Cwb,2)/4:size(Cb,2)/4+... 
(k+1)*size(Cwb,2)/4)=Cb(1+size(Cb,2)/4+... 
k*size(Cwb,2)/4:size(Cb,2)/4+(k+1)*size(Cwb,2)/4)+... 
g*Cwb(1+size(Cwb,2)/4:size(Cwb,2)/2); 
Cb(1+size(Cb,2)/2+k*size(Cwb,2)/4:size(Cb,2)/2+... 
(k+1)*size(Cwb,2)/4)=Cb(1+size(Cb,2)/2+... 
k*size(Cwb,2)/4:size(Cb,2)/2+(k+1)*size(Cwb,2)/4)+... 
b*Cwb(1+size(Cwb,2)/2:3*size(Cwb,2)/4); 
Cb(1+3*size(Cb,2)/4+k*size(Cwb,2)/4:3*size(Cb,2)/4+... 
(k+1)*size(Cwb,2)/4)=Cb(1+3*size(Cb,2)/4+... 
k*size(Cwb,2)/4:3*size(Cb,2)/4+(k+1)*size(Cwb,2)/4)+... 
b*Cwb(1+3*size(Cwb,2)/4:size(Cwb,2)); 
k=k+1; 
end; 
Cb(1:size(Cwb,2)/4)=Cb(1:size(Cwb,2)/4)+b*Cwb(1:size(Cwb,2)/4); 
%% image reconstruction
imr=WAVEREC2(Cr,Sr,'haar'); 
img=WAVEREC2(Cg,Sg,'haar'); 
imb=WAVEREC2(Cb,Sb,'haar'); 
imsize=size(imr); 
FAO=zeros(imsize(1),imsize(2),3); 
for i=1:imsize(1); 
for j=1:imsize(2); 
FAO(i,j,1)=imr(i,j); 
FAO(i,j,2)=img(i,j); 
FAO(i,j,3)=imb(i,j); 
end 
end 
figure, imshow(FAO); title('watermarked image');
View Code

 

 
 
======================================================

在知乎玩兒wargame也是有創意,不過師兄

你放種圖就不應該了啊(大霧)。
解碼思路大概是這樣的:先找到原圖,然后和原圖在頻域處理一下,就可以得到下面的**鏈接了























才怪嘞!師兄可是個守法的知乎青年。送給大家一個勵志良言:
 
 
 
參考:
鏈接:https://www.zhihu.com/question/50735753/answer/122593277
鏈接:https://www.zhihu.com/question/50735753/answer/122898864




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM