数字水印的易损水印的java实现,网上找了好久都没找着。
易损水印: *
原理就是,将图片像素分割成8x8的子模块。每个子模块进行一次dct变换,
变换之后,根据信号,对比第62与63位。如果信号为1则 使得63>62,如果信号为0则使得62>63.
最后再进行一次反DCT变换,就完成了一个子模块的操作,将子模块的像素值与原原子模块交换。
一个信号就需要一个子模块,将所有信号操作完成后,信号就完全嵌入到了图片中了。
预先处理:
将需要嵌入图片的信息进行处理:
message------md5(数字摘要)-----sign(RSA)—(转换成二进制)------二进制签名信息
将图片进行处理:
图片-----提取像素----->BufferedImage bimage=ImageIO.read(file);
因为图片是分RGB三层,因此要选择一层来作为你的操作像素:
//path,图片的地址
public static int[][] getPixels(String path){
File file =new File(path);
int[] rgb=new int[3];
try {
BufferedImage bimage=ImageIO.read(file);
int[][] RPixels=new int[bimage.getWidth()][bimage.getHeight()];
int minX=bimage.getMinX();
int minY=bimage.getMinY();
for(int i=minX;i<bimage.getWidth();i++){
for(int j=minY;j<bimage.getHeight();j++){
int p=bimage.getRGB(i, j);
rgb[0] = (p & 0xff0000) >> 16;
rgb[1] = (p & 0xff00) >> 8;
rgb[2] = (p & 0xff);
RPixels[i][j]=rgb[0];
}
}
return RPixels;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
下面是易损水印的实现。
/**@param imagePath:需要处理的图片地址
* @param sign:转为二进制信号后的签名信息
* */
public void createWaterMark(String imagePath,int[][] sign){
int[][]data=getPixels(imagePath);
int tag=0;
int h=0;
double[][] data2=MathTool.intToDoubleMatrix(data);
double[][]result2=data2;
for(int i=0;i<sign.length;i++){
for(int j=0;j<sign[i].length;j++){
double[][] blk=new double[8][8];
if(tag*8+8>data[h].length){
tag=0;
h++;
}
for(int n=0;n<8;n++){ //将像素分割成8*8的子块
for(int m=0;m<8;m++){
blk[n][m]=data[8*h+n][8*tag+m];
}
}
double[][] fblk=FDct.fDctTransform(blk); //DCT变换
if(sign[i][j]==1&&fblk[7][5]<fblk[7][6]){
double temp=fblk[7][6];
fblk[7][6]=fblk[7][5];
fblk[7][5]=temp;
}else if(sign[i][j]==1&&fblk[7][5]==fblk[7][6]){
fblk[7][6]-=3;
}else if(sign[i][j]==0&&fblk[7][5]>fblk[7][6]){
double temp=fblk[7][6];
fblk[7][6]=fblk[7][5];
fblk[7][5]=temp;
}else if(sign[i][j]==0&&fblk[7][5]==fblk[7][6]){
fblk[7][6]+=3;
}
blk=IFDct.iFDctTransform(fblk);; //反 DCT变换
for(int n=0;n<8;n++){ //将嵌入信息后的子块放进原图
for(int m=0;m<8;m++){
result2[8*h+n][8*tag+m]=blk[n][m];
}
}
tag++;
}
}
File file =new File(imagePath);
int[] rgb=new int[3];
try {
BufferedImage bimage=ImageIO.read(file);
BufferedImage newImage=new BufferedImage(bimage.getWidth(),bimage.getHeight(),bimage.getType());
int minX=bimage.getMinX();
int minY=bimage.getMinY();
for(int i=minX;i<bimage.getWidth();i++){
for(int j=minY;j<bimage.getHeight();j++){
int p=bimage.getRGB(i, j);
rgb[0] = (p & 0xff0000) >> 16;
rgb[1] = (p & 0xff00) >> 8;
rgb[2] = (p & 0xff);
Color myColor=new Color((int)result2[i][j],rgb[1],rgb[2] );
int newRGB=myColor.getRGB();
newImage.setRGB(i, j, newRGB);
}
}
File file2=new File("D://sign.png");
ImageIO.write(newImage, "png", file2);
}catch (IOException e) {
e.printStackTrace();
}
}
最后:
经过DCT变换后可能会出现越界问题。因为像素取值在[0-255],dct变换会使得有些数超越255这个值或低于0.
因此可以使用归一法对数据进行归一到[0-255]这个区间 这是不可以的,归一法会有误差。
最后,我自己的解决是 在提取图片像素的时候,将处于[255-253] /[0-3]这个区间的数进行缩小和放大。这样一般不会出现越界。
像素越界一般出现在黑白图片里。因为黑色和白色就是0/255.
如果有人有更好解决这个问题,请告知,谢谢。