工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

栏目: 编程工具 · 发布时间: 5年前

内容简介:阿里巴巴内网的不可见水印用的是什么算法?据说月饼事件截图的那位员工也被开除了?下面的只是简单的加一个很浅的水印,实现起来很容易。

一、简单水印(watermark-dom)

阿里巴巴内网的不可见水印用的是什么算法?

据说月饼事件截图的那位员工也被开除了?

下面的只是简单的加一个很浅的水印,实现起来很容易。

1、看看水印的效果

随便找一个网站,比如就找掘金的个人首页,

(1)F12检查模式,

(2)在console里粘贴下面的代码,

(function(watermark){window.watermarkdivs=[];var loadMark=function(settings){var defaultSettings={watermark_txt:"text",watermark_x:20,watermark_y:20,watermark_rows:0,watermark_cols:0,watermark_x_space:50,watermark_y_space:50,watermark_color:'#000000',watermark_alpha:0.005,watermark_fontsize:'18px',watermark_font:'微软雅黑',watermark_width:150,watermark_height:100,watermark_angle:15,watermark_bg_alpha:0.5};if(arguments.length===1&&typeof arguments[0]==="object"){var src=arguments[0]||{};for(key in src){if(src[key]&&defaultSettings[key]&&src[key]===defaultSettings[key])continue;else if(src[key])defaultSettings[key]=src[key]}}var oTemp=document.createDocumentFragment();if(window.watermarkdivs&&window.watermarkdivs.length>0){document.body.removeChild(document.getElementById("otdivid"));window.watermarkdivs=[]}var page_width=Math.max(document.body.scrollWidth,document.body.clientWidth);var page_height=Math.max(document.body.scrollHeight,document.body.clientHeight);var otdiv=document.getElementById("otdivid");if(defaultSettings.watermark_cols==0||(parseInt(defaultSettings.watermark_x+defaultSettings.watermark_width*defaultSettings.watermark_cols+defaultSettings.watermark_x_space*(defaultSettings.watermark_cols-1))>page_width)){defaultSettings.watermark_cols=parseInt((page_width-defaultSettings.watermark_x+defaultSettings.watermark_x_space)/(defaultSettings.watermark_width+defaultSettings.watermark_x_space));defaultSettings.watermark_x_space=parseInt((page_width-defaultSettings.watermark_x-defaultSettings.watermark_width*defaultSettings.watermark_cols)/(defaultSettings.watermark_cols-1))}if(defaultSettings.watermark_rows==0||(parseInt(defaultSettings.watermark_y+defaultSettings.watermark_height*defaultSettings.watermark_rows+defaultSettings.watermark_y_space*(defaultSettings.watermark_rows-1))>page_height)){defaultSettings.watermark_rows=parseInt((defaultSettings.watermark_y_space+page_height-defaultSettings.watermark_y)/(defaultSettings.watermark_height+defaultSettings.watermark_y_space));defaultSettings.watermark_y_space=parseInt(((page_height-defaultSettings.watermark_y)-defaultSettings.watermark_height*defaultSettings.watermark_rows)/(defaultSettings.watermark_rows-1))}var x;var y;for(var i=0;i<defaultSettings.watermark_rows;i++){y=defaultSettings.watermark_y+(defaultSettings.watermark_y_space+defaultSettings.watermark_height)*i;for(var j=0;j<defaultSettings.watermark_cols;j++){x=defaultSettings.watermark_x+(defaultSettings.watermark_width+defaultSettings.watermark_x_space)*j;var mask_div=document.createElement('div');mask_div.id='mask_div'+i+j;mask_div.appendChild(document.createTextNode(defaultSettings.watermark_txt));mask_div.style.webkitTransform="rotate(-"+defaultSettings.watermark_angle+"deg)";mask_div.style.MozTransform="rotate(-"+defaultSettings.watermark_angle+"deg)";mask_div.style.msTransform="rotate(-"+defaultSettings.watermark_angle+"deg)";mask_div.style.OTransform="rotate(-"+defaultSettings.watermark_angle+"deg)";mask_div.style.transform="rotate(-"+defaultSettings.watermark_angle+"deg)";mask_div.style.visibility="";mask_div.style.position="absolute";mask_div.style.left=x+'px';mask_div.style.top=y+'px';mask_div.style.overflow="hidden";mask_div.style.zIndex="9";mask_div.style.pointerEvents="none";mask_div.style.opacity=defaultSettings.watermark_alpha;mask_div.style.fontSize=defaultSettings.watermark_fontsize;mask_div.style.fontFamily=defaultSettings.watermark_font;mask_div.style.color=defaultSettings.watermark_color;mask_div.style.textAlign="center";mask_div.style.width=defaultSettings.watermark_width+'px';mask_div.style.height=defaultSettings.watermark_height+'px';mask_div.style.display="block";mask_div.style.fontWeight="900";oTemp.appendChild(mask_div)}};document.body.appendChild(oTemp)};watermark.load=function(settings){window.onload=function(){loadMark(settings)};window.onresize=function(){loadMark(settings)}};watermark.load({watermark_txt:"测试水印,saucxs,测试水印,songEagle,工号等"})})(window.watermark={});

(3)改变一下页面窗口大小,然后就可以看到我首页出现如下图的水印,一层浅浅的水印

工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

它的作用是在当前页面上增加了一个透明度只有0.005的很多的水印。水印内容“测试水印,saucxs,测试水印,songEagle,工号等”。

2、水印的优化

当然是需要使用者不知道这个页面有水印,保证一些信息的安全性以及泄露之后可以追踪到是谁在泄露机密信息,他没有发觉到有水印,所以需要将水印调成透明度很低,这样使用者看不到水印,但是一旦使用者截图将图片发布到互联网上,这时候只需要将图片进行一些简单操作就可以让水印重新显现出来。

只要使用者不知道有水印存在,这样就是从根本上加了水印,信息的源头上加了水印,确保信息的安全,以及泄露之后的追踪。

我们还是拿掘金的个人首页作为试验田。这回我们将水印的透明度调成0.004,水印字体颜色调成页面背景颜色(掘金的是#f4f5f5),然后截图(这回看不出来有水印吧)

工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

把图片放到PS,我使用的是ps cs6里面,建一个空白图层在上面,填充为黑色,操作:如图所示背景色选择黑色,然后按shift+f5,选择背景色进行填充。

工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

混合模式选择正片叠底这一类的(也就是让亮的更亮,暗的更暗),一个个试。当我试到“实色混合”和“颜色加深”的时候,水印就显示出来了。

哇,吓到我了,原来可以这么玩。

3、watermark-dom水印原理分析

通过js向html中一次性添加dom元素,所以我取名叫做 watermark-dom ,如果一旦知道有这个水印插件,使用者是可以手动将当前页面的水印dom删掉,这样也就是开发人员知道怎么弄,对于其他人员还是不知道如何去掉页面水印的。

3.1关键元素1--pointer-events:none

这个归功于css的属性,准确的说是css3的一个属性pointer-events,本来这个属性的而设计之初是为了--真正意义上的禁用元素,因为设置值为none的时候,这个元素是使用鼠标或者触摸感知不到的,可以称pointer-events为“元素虚化”。

支持浏览器:目前FireFox浏览器,Chrome都支持。Opera以及IE不支持。

该属性的缺点:

1、pointer-events:none影响触屏设备的滚动,如无线端页面等;

2、如果子元素设置了pointer-eventes: auto会导致滚动的时候页面闪动

3.2关键元素2--opacity属性

为什么这么说opacity属性,因为这个属性是对元素进行透明的设置,因为是水印,希望尽量不影响到正常页面的视觉体验。

支持浏览器:所有浏览器都支持 opacity 属性。注意:IE8以及更早的版本支持替代的filter属性。例如:filter:Alpha(opacity=50)。

在设置这个透明度的时候,经过测试发现,水印透明度,要求设置在大于等于0.005,因为比这个低的话,使用ps显现的时候效果不明显。

如果需要去尽量影藏水印,可以把水印的字体颜色和页面的背景颜色调成一致。

如果就要正常显示,尽量设置opactity的时候设置为大于等于0.005,一个性能和显示的平衡点。

4、附上watermark-dom完整代码

如果后续更新和优化的时候,源码请看这个 watermark-dom 里的 watermark.js 文件

5、一个具体的例子

引入这个 watermark.js 的代码。

html代码

<html>
    <meta charset="utf-8">
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <script type="text/javascript" src="watermark.js"></script>
    <script type="text/javascript">
        watermark.load({ watermark_txt: "测试水印,1021002301,测试水印,SDAHJDBJJdjsfsc" });
    </script>
    
    <body>
        <div style="width:300px;height:300px;background-color: red; opacity:0.98;" onclick="alert(1);">test</div>
        <div style="width:300px;height:300px;background-color: blue;  opacity:0.9;" onclick="alert(2);">test</div>
        <a href="www.test.com"> baidu</a>
    </body>
</html>

效果如下图:

工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

6、水印测试工具

写了插件,这个是测试地址

watermark-dom的测试工具

包括,测试,重置,显示,随机,四个部分。

特性:1、测试对水印参数属性,重置水印属性参数,显示此时的水印属性参数,随机产生水印属性参数;

2、水印按钮组是position值fixed,可以浮现在页面之上,不占字节。

3、对系统的各个部分页面进行水印的测试。

7、基础使用

7.1本地引入封装的js文件

只是简单的加一个很浅的水印,实现起来很容易。不需要引入jquery插件。

watermark.js 是必须要引进的组件

第一步:获取组件方式: git clone https://github.com/saucxs/watermark-dom.git

第二步:clone后,在需要加水印的相关页面引入水印文件"watermark.js":

script type="text/javascript" src="./watermark.js"></script>

第三步:在确保页面DOM加载完毕之后,调用watermark的load方法(手动加载):

<script>watermark.load({ watermark_txt: "测试水印,1021002301,测试水印,100101010111101" })</script>

注意:我们提供了init方法,用来初始化水印,添加load和resize事件

<script>watermark.init({ watermark_txt: "测试水印,1021002301,测试水印,100101010111101" })</script>

7.2本地引入封装的js文件

第一步:npm获取水印组件包:

npm install watermark-dom

第二步:引入水印模块:

import watermark from 'watermark-dom'
或者
var watermarkDom = require("watermark-dom")

第三步:在确保页面DOM加载完毕之后,调用watermark的load方法(手动加载):

<script>watermark.load({ watermark_txt: "测试水印,1021002301,测试水印,100101010111101" })</script>

注意:我们提供了init方法,用来初始化水印,添加load和resize事件

<script>watermark.init({ watermark_txt: "测试水印,1021002301,测试水印,100101010111101" })</script>

8、支持各种属性配置使用

watermark_id: 'wm_div_id',          //水印总体的id
watermark_prefix: 'mask_div_id',    //小水印的id前缀
watermark_txt:"测试水印",             //水印的内容
watermark_x:20,                     //水印起始位置x轴坐标
watermark_y:20,                     //水印起始位置Y轴坐标
watermark_rows:0,                   //水印行数
watermark_cols:0,                   //水印列数
watermark_x_space:100,              //水印x轴间隔
watermark_y_space:50,               //水印y轴间隔
watermark_font:'微软雅黑',           //水印字体
watermark_color:'black',            //水印字体颜色
watermark_fontsize:'18px',          //水印字体大小
watermark_alpha:0.15,               //水印透明度,要求设置在大于等于0.005
watermark_width:100,                //水印宽度
watermark_height:100,               //水印长度
watermark_angle:15,                 //水印倾斜度数
watermark_parent_width:0,      //水印的总体宽度(默认值:body的scrollWidth和clientWidth的较大值)
watermark_parent_height:0,     //水印的总体高度(默认值:body的scrollHeight和clientHeight的较大值)
watermark_parent_node:null     //水印插件挂载的父元素element,不输入则默认挂在body上

上面的属性都支持配置的,怎么使用呢?

基本山需要自己配置的属性: watermark_txt , watermark_color , watermark_fontsize , watermark_alpha , watermark_anglewatermark_width , watermark_height 这7个属性一般是经常用到的,其他属性一般用的偏少。需要用到的就设置一下,不需要用到的就可以不设置,插件内部会有默认值的。

watermark.load({ 
    watermark_txt:"测试水印,saucxs,测试水印,songEagle,工号等",  //水印的内容
    watermark_color:'#5579ee',            //水印字体颜色
    watermark_fontsize:'24px',          //水印字体大小
    watermark_alpha:0.5,               //水印透明度,要求设置在大于等于0.005
    watermark_angle:135,                 //水印倾斜度数
    watermark_width:200,                //水印宽度
    watermark_height:200,               //水印长度
});

这个设置之后后的页面如下图所示:

工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

所以一般先在 watermark-dom的测试工具 上,把需要配置的属性值,调试好之后在写入代码中,这样效率更高。

二、复杂(频域方式图片合并水印)

频域方式图片合并水印,意思就是说,从图片编码里进行水印的添加,这样从最根本上解决图片的水印,而且水印的形成之后的图片是用肉眼和PS显示不出来的,只有通过反向编码让水印显示,这样就算是开发人员也不会知道这个图片是否含有水印,只有开发这个系统的人员知道。

1、原理分析

工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

编码的目的有两个:

1、对水印加密,

2、二控制水印能量的分布。

以下是频域方式图片合并水印的实验。

(1)原图像。尺寸300*240 ,汉子一枚,

工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

(2)水印照片。

工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

(3)水印编码。编码方式采用随机序列编码,通过编码,水印分布到随机分布到各个频率,并且对水印进行了加密。

工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

(4)原图像频域。经历的是傅里叶变换,下图变换后的频域图像

工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

(5)水印图像频域。经历的是傅里叶变换,下图变换后的频域图像

工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

(6)合并水印和原图。之后,将叠加水印的频谱进行傅里叶逆变换,得到叠加数字水印后的图像,,将图像频域和水印编码进行合并。看不出来已经加了水印吧,

实际上,我们是把水印以噪声的形式添加到原图像中。

工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

(7)水印图与原图的残差(看不出来残差区别,需要调整对比度才能看得出来)

工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

(8)最终的均方差(MSE)和信噪比(PSNR)

工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

(9)下图是原图频谱竖过来的样子,其能量主要集中在低频。

那么,为什么频谱发生了巨大的变化,而在空域却变化如此小呢?这是因为我们避开了图像的主要频率。

工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

合并之后

工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

(10)水印提取是水印叠加的逆过程,

工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

(11)提取后,得到水印。

工具系列--简单水印(watermark-dom)和算法水印(频域方式图片合并)实现

2、附上可以还原实验的全部代码(matlab代码)

clc;clear;close all;
alpha = 1;

%% read data
im = double(imread('G:\2017学习\Work\图片水印\test.jpg'))/255;
mark = double(imread('G:\2017学习\Work\图片水印\watermark.png'))/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('G:\2017学习\Work\图片水印\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');

%% 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

三、总结

1、watermark-dom水印是一个简单的用于dom的水印,简单易上手,支持多选项配置。

2、频域方式图片合并水印是一个从图片的编码方式进行合成水印,水印不可见,直接反向编码才可以拿到水印,从最根本上解决了数据安全和数据泄露后的追踪问题。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

编程.建筑

编程.建筑

保罗·科茨 / 2012-9 / 45.00元

《编程•建筑》简单明了地介绍了计算机算法与程序用于建筑设计的历史,解释了基本的算法思想和计算机作为建筑设计工具的运用。作为计算机辅助设计的先驱,保罗·科茨通过多年讲授的计算、设计的教学内容和实例研究,向我们展示了算法思维。《编程•建筑》提供了详细、可操作的编码所需要的技术和哲学思想,给读者一些代码和算法例子的认识。一起来看看 《编程.建筑》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

在线进制转换器
在线进制转换器

各进制数互转换器

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具