前言
水印回挽,一般是指圖片上印有文字的水印,比如發(fā)表在微博或者CSDN的圖片都會(huì)自動(dòng)打上肉眼可見(jiàn)的文字水印搓侄。像這種一般用于標(biāo)識(shí)某張圖片的出處或者用于聲明版權(quán)牲剃。但是這樣做會(huì)破壞掉原圖,而且影響美觀清酥,比較粗暴扶镀。此處我想討論的是另一種水印--盲水印。盲水印不僅僅用于圖片焰轻,也可應(yīng)用于像音頻這種數(shù)據(jù)流臭觉。當(dāng)然這篇文章只討論圖片上的應(yīng)用。盲水印隱蔽性強(qiáng),給水印數(shù)據(jù)進(jìn)行編碼過(guò)后不易被破解出來(lái)蝠筑。這是它最大的優(yōu)勢(shì)狞膘。
盲水印原理
在前一篇文章里面我寫(xiě)了關(guān)于傅立葉的c語(yǔ)言實(shí)現(xiàn)方式,盲水印的實(shí)現(xiàn)就和傅立葉相關(guān)菱肖,傅立葉變換可以把數(shù)據(jù)從時(shí)域轉(zhuǎn)換到頻域客冈。而且過(guò)程可逆。盲水印就是在圖像的頻域上添加水印數(shù)據(jù)稳强。再通過(guò)逆變換轉(zhuǎn)回時(shí)域场仲。區(qū)別是圖片是二維數(shù)據(jù),要把圖片數(shù)據(jù)由時(shí)域轉(zhuǎn)到頻域需要用到二維傅立葉變換退疫。
水印添加流程圖如下:
圖像的二維傅立葉變換
和一維數(shù)據(jù)不同渠缕,要想對(duì)圖片進(jìn)行頻域轉(zhuǎn)換就要使用二維傅立葉變換。
其實(shí)褒繁,只要實(shí)現(xiàn)了一維傅立葉變換亦鳞,二維傅立葉變換就不會(huì)那么難:先對(duì)圖像矩陣數(shù)據(jù)每一行進(jìn)行傅立葉變換,再對(duì)每一列進(jìn)行傅立葉變換即完成二維傅立葉變換棒坏。這里還是用一張常見(jiàn)的美女lena. 對(duì)lena(256*256)進(jìn)行二維傅立葉變換如下圖:
對(duì)于分辨率很大的圖片燕差,DFT的效率很低,通常不會(huì)考慮坝冕,取而代之的是FFT徒探,但是,F(xiàn)FT的條件是要保證圖片的寬和高都是2的冪級(jí)數(shù)喂窟。但是通常生活中的圖片一般都不滿(mǎn)足這個(gè)條件测暗。這時(shí)候我們就需要對(duì)圖片的寬和高進(jìn)行補(bǔ)0擴(kuò)充直到滿(mǎn)足條件為止。
圖片頻域數(shù)據(jù)特征如下圖所示:
圖片中明亮的部分就是低頻部分磨澡,暗點(diǎn)的是高頻部分碗啄。
一般為了展示會(huì)把頻譜圖低頻的部分移到中心(上面手機(jī)拍的最右邊的圖)。頻譜圖是關(guān)于中心點(diǎn)對(duì)稱(chēng)的稳摄。
由于傅立葉變換是可逆的稚字,若一張圖片進(jìn)行補(bǔ)零擴(kuò)大后,進(jìn)行逆變換后再把它補(bǔ)零的部分進(jìn)行裁剪就可以得到原圖秩命。
水印數(shù)據(jù)嵌入
為了讓水印更加隱蔽尉共,需要將水印像素按照一定的順序打亂,再進(jìn)行二維傅立葉變化弃锐,最后再讓它疊加在需要加水印的圖片的頻域上袄友。為了提高水印的安全性,規(guī)定這種打亂的順序需要外界傳入一個(gè)密鑰霹菊,根據(jù)不同的密鑰生成不同亂序的規(guī)則剧蚣,而且過(guò)程是可逆的支竹。
-
亂序
- 水印的添加過(guò)程
水印的提取
因?yàn)樗〉奶砑邮窃谠瓐D的頻域上進(jìn)行疊加。我們只需要將原圖和加了水印的圖片分別進(jìn)行傅立葉變換鸠按,最后通過(guò)減法則可提取出加密過(guò)的水印數(shù)據(jù)礼搁。通過(guò)已知的密鑰可以逆推出打亂的規(guī)則,這樣當(dāng)我們從水印圖片中提取出水印像素時(shí)目尖,可以還原出水印像素的原序列馒吴。最后就可以得到原水印。
代碼實(shí)現(xiàn)
所有的OC實(shí)現(xiàn)方法都已經(jīng)寫(xiě)成框架LHWatermark瑟曲。
嵌入水右痢:
//初始化
LHWatermarkProcessor * processor = [[LHWatermarkProcessor alloc] initWidthImage:image config:[LHConfig defaultConfig]];
__weak typeof(self) weakSelf = self;
//把文字水印@"你的名字"添加到image中。 異步線程
[processor addMarkText:@"你的名字" result:^(UIImage *watermarkImage) {
// block中返回加了水印的圖片 主線程
__strong typeof(weakSelf) strongSelf = weakSelf;
strongSelf.topImgView.image = watermarkImage;
}];
提取水佣床Α:
UIImage *image = [UIImage imageNamed:ImageName];
__weak typeof(self) weakSelf = self;
// 分別傳入原圖像扯罐、加了水印的圖像 。異步線程
[LHWatermarkProcessor restoreImageWidthOriginImage:image watermarkImage:[UIImage imageWithContentsOfFile:_imagePath] config:[LHConfig defaultConfig] result:^(UIImage *markImage) {
__strong typeof(weakSelf) strongSelf = weakSelf;
// block中返回水印的圖片 主線程
strongSelf.bottomImgView.image = watermarkImage;
}];
具體代碼請(qǐng)轉(zhuǎn)到本人GitHub烦衣,如果覺(jué)得對(duì)你有幫助不要吝惜你的start歹河。如果哪位大神有更好的方案也請(qǐng)不必吝惜你的建議。
轉(zhuǎn)載請(qǐng)注明出處花吟。謝謝秸歧!
博客地址:here
參考資料:
知乎
圖書(shū)館查閱后不記得名字的某本書(shū)