Java实现【快速离散余弦变换FDCT】参考:《图像数字水印的JAVA实现 刘剑鸣 著》

原始图片:


水印图片:


嵌入水印后的图片:


提取出来的水印图片:


工具类:ImageUtil.java    MathTool.java

package com.zeph.watermark.util;

public class MathTool {
	public static double[][] intToDoubleMatrix(int[][] input) {
		int height = input.length;
		int width = input[0].length;
		double[][] output = new double[height][width];
		for (int i = 0; i < height; i++) {
			// 列
			for (int j = 0; j < width; j++) {
				// 行
				output[i][j] = Double.valueOf(String.valueOf(input[i][j]));
				System.out.print(output[i][j]);
			}
			System.out.println();
		}
		return output;
	}

	public static double[] intToDoubleArray(int[] input) {
		int length = input.length;
		double[] output = new double[length];
		for (int i = 0; i < length; i++)
			output[i] = Double.valueOf(String.valueOf(input[i]));
		return output;
	}

	public static void main(String[] args) {
		int[][] test = { { 4, 5, 6 }, { 1, 2, 3 } };
		MathTool.intToDoubleMatrix(test);
	}
}

package com.zeph.watermark.util;

import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

public class ImageUtil {
	/**
	 * 获取图片
	 * 
	 * @param filepath
	 * @return
	 */
	public static BufferedImage getImage(String filepath) {
		BufferedImage image = null;
		File file = new File(filepath);
		try {
			image = ImageIO.read(file);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return image;
	}

	/**
	 * 获取图像文件的像素(图片转换为像素)
	 * 
	 * @param filepath
	 * @param format
	 */
	public int[] getImagePixels(String filepath) {
		BufferedImage image = null;
		File file = new File(filepath);
		try {
			image = ImageIO.read(file);
		} catch (IOException e) {
			e.printStackTrace();
		}
		WritableRaster raster = image.getRaster();
		// 得到图像的宽度
		int width = raster.getWidth();
		// 得到图像的高度
		int height = raster.getHeight();
		// RGB格式图像文件每一个点的颜色由红、绿、兰三种颜色构成,即实际图像可为3层,
		// 分别为R,G,B层,因此分解后的文件象素是实际坐标高度和宽度的三倍。
		int[] pixels = new int[3 * width * height];
		// 读取坐标的范围是从(0,0)坐标开始宽width,高height
		raster.getPixels(0, 0, width, height, pixels);
		return pixels;
	}

	/**
	 * 像素转换成图像文件
	 * 
	 * @param result
	 * @param width
	 * @param height
	 * @param filepath
	 * @param format
	 */
	public static void setImage(double[] result, int width, int height,
			String filepath, String format, int type) {
		BufferedImage outImage = new BufferedImage(width, height, type);
		WritableRaster outRaster = outImage.getRaster();
		outRaster.setPixels(0, 0, width, height, result);
		// 图像文件的写入
		File outFile = new File(filepath);
		try {
			ImageIO.write(outImage, format, outFile);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 一维数组转为二维数组
	 * 
	 * @param m
	 * @param width
	 * @param height
	 * @return
	 */
	public static int[][] arrayToMatrix(int[] m, int width, int height) {
		int[][] result = new int[height][width];
		for (int i = 0; i < height; i++) {
			for (int j = 0; j < width; j++) {
				int p = j * height + i;
				result[i][j] = m[p];
			}
		}
		return result;
	}

	/**
	 * 一维数组转换为三维数组
	 * 
	 * @param pixels
	 * @param width
	 * @param height
	 * @return
	 */
	public static int[][][] getRGBArrayToMatrix(int[] pixels, int width,
			int height) {
		// 已知有3个二维数组组成分别代表RGB
		int[][][] result = new int[3][height][width];
		int[][] temp = new int[3][width * height];
		for (int i = 0; i < pixels.length; i++) {
			int m = i / 3;
			int n = i % 3;
			temp[n][m] = pixels[i];
		}
		result[0] = arrayToMatrix(temp[0], width, height);
		result[1] = arrayToMatrix(temp[1], width, height);
		result[2] = arrayToMatrix(temp[2], width, height);
		return result;
	}

	/**
	 * 二维数组转为一维数组
	 * 
	 * @param m
	 * @return
	 */
	public static double[] matrixToArray(double[][] m) {
		int p = m.length * m[0].length;
		double[] result = new double[p];
		for (int i = 0; i < m.length; i++) {
			for (int j = 0; j < m[i].length; j++) {
				int q = j * m.length + i;
				result[q] = m[i][j];
			}
		}
		return result;
	}

	/**
	 * 三维数组转为一维数组
	 * 
	 * @param m
	 * @return
	 */
	public static double[] getRGBMatrixToArray(double[][][] m) {
		int width = m[0].length;
		int height = m[0][0].length;
		int len = width * height;
		double[] result = new double[3 * len];
		double[][] temp = new double[3][len];
		temp[0] = matrixToArray(m[0]);
		temp[1] = matrixToArray(m[1]);
		temp[2] = matrixToArray(m[2]);
		for (int i = 0; i < 3; i++) {
			for (int j = 0; j < temp[i].length; j++)
				result[3 * j + i] = temp[i][j];
		}
		return result;
	}
}

实现类:Dct.java    FDct.java    IFDct.java    AddWatermark.java    ExtractWatermark.java

package com.zeph.watermark.fdct;

public interface Dct {
	static double C1 = 0.98078528, C2 = 0.923879532, C3 = 0.831469612,
			C4 = 0.707106781, C5 = 0.555570233, C6 = 0.382683432,
			C7 = 0.195090322;
}

package com.zeph.watermark.fdct;

public class FDct implements Dct {
	public static double[][] fDctTransform(double[][] ablk) {
		double[][] blk = new double[8][8];
		for (int i = 0; i < 8; i++) {
			for (int j = 0; j < 8; j++) {
				blk[i][j] = ablk[i][j];
			}
		}
		// 对行变换
		for (int i = 0; i <= 7; i++) {
			double S07, S16, S25, S34, S0734, S1625;
			double D07, D16, D25, D34, D0734, D1625;
			S07 = blk[i][0] + blk[i][7];
			S16 = blk[i][1] + blk[i][6];
			S25 = blk[i][2] + blk[i][5];
			S34 = blk[i][3] + blk[i][4];
			S0734 = S07 + S34;
			S1625 = S16 + S25;
			D07 = blk[i][0] - blk[i][7];
			D16 = blk[i][1] - blk[i][6];
			D25 = blk[i][2] - blk[i][5];
			D34 = blk[i][3] - blk[i][4];
			D0734 = S07 - S34;
			D1625 = S16 - S25;
			blk[i][0] = 0.5 * (C4 * (S0734 + S1625));
			blk[i][1] = 0.5 * (C1 * D07 + C3 * D16 + C5 * D25 + C7 * D34);
			blk[i][2] = 0.5 * (C2 * D0734 + C6 * D1625);
			blk[i][3] = 0.5 * (C3 * D07 - C7 * D16 - C1 * D25 - C5 * D34);
			blk[i][4] = 0.5 * (C4 * (S0734 - S1625));
			blk[i][5] = 0.5 * (C5 * D07 - C1 * D16 + C7 * D25 + C3 * D34);
			blk[i][6] = 0.5 * (C6 * D0734 - C2 * D1625);
			blk[i][7] = 0.5 * (C7 * D07 - C5 * D16 + C3 * D25 - C1 * D34);
		}
		// 对列变换
		for (int j = 0; j <= 7; j++) {
			double S07, S16, S25, S34, S0734, S1625;
			double D07, D16, D25, D34, D0734, D1625;
			S07 = blk[0][j] + blk[7][j];
			S16 = blk[1][j] + blk[6][j];
			S25 = blk[2][j] + blk[5][j];
			S34 = blk[3][j] + blk[4][j];
			S0734 = S07 + S34;
			S1625 = S16 + S25;
			D07 = blk[0][j] - blk[7][j];
			D16 = blk[1][j] - blk[6][j];
			D25 = blk[2][j] - blk[5][j];
			D34 = blk[3][j] - blk[4][j];
			D0734 = S07 - S34;
			D1625 = S16 - S25;
			blk[0][j] = 0.5 * (C4 * (S0734 + S1625));
			blk[1][j] = 0.5 * (C1 * D07 + C3 * D16 + C5 * D25 + C7 * D34);
			blk[2][j] = 0.5 * (C2 * D0734 + C6 * D1625);
			blk[3][j] = 0.5 * (C3 * D07 - C7 * D16 - C1 * D25 - C5 * D34);
			blk[4][j] = 0.5 * (C4 * (S0734 - S1625));
			blk[5][j] = 0.5 * (C5 * D07 - C1 * D16 + C7 * D25 + C3 * D34);
			blk[6][j] = 0.5 * (C6 * D0734 - C2 * D1625);
			blk[7][j] = 0.5 * (C7 * D07 - C5 * D16 + C3 * D25 - C1 * D34);
		}
		return blk;
	}
}

package com.zeph.watermark.fdct;

public class IFDct implements Dct {
	public static double[][] iFDctTransform(double[][] ablk) {
		double[][] blk = new double[8][8];
		for (int i = 0; i < 8; i++) {
			for (int j = 0; j < 8; j++) {
				blk[i][j] = ablk[i][j];
			}
		}
		// 对列做IDCT
		for (int j = 0; j <= 7; j++) {
			double[] tmp = new double[16];
			// first step
			tmp[0] = blk[0][j] * C4 + blk[2][j] * C2;
			tmp[1] = blk[4][j] * C4 + blk[6][j] * C6;
			tmp[2] = blk[0][j] * C4 + blk[2][j] * C6;
			tmp[3] = -blk[4][j] * C4 - blk[6][j] * C2;
			tmp[4] = blk[0][j] * C4 - blk[2][j] * C6;
			tmp[5] = -blk[4][j] * C4 + blk[6][j] * C2;
			tmp[6] = blk[0][j] * C4 - blk[2][j] * C2;
			tmp[7] = blk[4][j] * C4 - blk[6][j] * C6;
			tmp[8] = blk[1][j] * C7 - blk[3][j] * C5;
			tmp[9] = blk[5][j] * C3 - blk[7][j] * C1;
			tmp[10] = blk[1][j] * C5 - blk[3][j] * C1;
			tmp[11] = blk[5][j] * C7 + blk[7][j] * C3;
			tmp[12] = blk[1][j] * C3 - blk[3][j] * C7;
			tmp[13] = -blk[5][j] * C1 - blk[7][j] * C5;
			tmp[14] = blk[1][j] * C1 + blk[3][j] * C3;
			tmp[15] = blk[5][j] * C5 + blk[7][j] * C7;
			// second step
			tmp[0] = 0.5 * (tmp[0] + tmp[1]);
			tmp[1] = 0.5 * (tmp[2] + tmp[3]);
			tmp[2] = 0.5 * (tmp[4] + tmp[5]);
			tmp[3] = 0.5 * (tmp[6] + tmp[7]);
			tmp[4] = 0.5 * (tmp[8] + tmp[9]);
			tmp[5] = 0.5 * (tmp[10] + tmp[11]);
			tmp[6] = 0.5 * (tmp[12] + tmp[13]);
			tmp[7] = 0.5 * (tmp[14] + tmp[15]);
			// third step
			blk[0][j] = tmp[0] + tmp[7];
			blk[1][j] = tmp[1] + tmp[6];
			blk[2][j] = tmp[2] + tmp[5];
			blk[3][j] = tmp[3] + tmp[4];
			blk[4][j] = tmp[3] - tmp[4];
			blk[5][j] = tmp[2] - tmp[5];
			blk[6][j] = tmp[1] - tmp[6];
			blk[7][j] = tmp[0] - tmp[7];
		}
		// 对行做IDCT
		for (int i = 0; i <= 7; i++) {
			double[] tmp = new double[16];
			// first step
			tmp[0] = blk[i][0] * C4 + blk[i][2] * C2;
			tmp[1] = blk[i][4] * C4 + blk[i][6] * C6;
			tmp[2] = blk[i][0] * C4 + blk[i][2] * C6;
			tmp[3] = -blk[i][4] * C4 - blk[i][6] * C2;
			tmp[4] = blk[i][0] * C4 - blk[i][2] * C6;
			tmp[5] = -blk[i][4] * C4 + blk[i][6] * C2;
			tmp[6] = blk[i][0] * C4 - blk[i][2] * C2;
			tmp[7] = blk[i][4] * C4 - blk[i][6] * C6;
			tmp[8] = blk[i][1] * C7 - blk[i][3] * C5;
			tmp[9] = blk[i][5] * C3 - blk[i][7] * C1;
			tmp[10] = blk[i][1] * C5 - blk[i][3] * C1;
			tmp[11] = blk[i][5] * C7 + blk[i][7] * C3;
			tmp[12] = blk[i][1] * C3 - blk[i][3] * C7;
			tmp[13] = -blk[i][5] * C1 - blk[i][7] * C5;
			tmp[14] = blk[i][1] * C1 + blk[i][3] * C3;
			tmp[15] = blk[i][5] * C5 + blk[i][7] * C7;
			// second step
			tmp[0] = 0.5 * (tmp[0] + tmp[1]);
			tmp[1] = 0.5 * (tmp[2] + tmp[3]);
			tmp[2] = 0.5 * (tmp[4] + tmp[5]);
			tmp[3] = 0.5 * (tmp[6] + tmp[7]);
			tmp[4] = 0.5 * (tmp[8] + tmp[9]);
			tmp[5] = 0.5 * (tmp[10] + tmp[11]);
			tmp[6] = 0.5 * (tmp[12] + tmp[13]);
			tmp[7] = 0.5 * (tmp[14] + tmp[15]);
			// third step
			blk[i][0] = tmp[0] + tmp[7];
			blk[i][1] = tmp[1] + tmp[6];
			blk[i][2] = tmp[2] + tmp[5];
			blk[i][3] = tmp[3] + tmp[4];
			blk[i][4] = tmp[3] - tmp[4];
			blk[i][5] = tmp[2] - tmp[5];
			blk[i][6] = tmp[1] - tmp[6];
			blk[i][7] = tmp[0] - tmp[7];
		}
		return blk;
	}

}

package com.zeph.watermark.fdct;

import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;

import com.zeph.watermark.util.ImageUtil;
import com.zeph.watermark.util.MathTool;


public class AddWatermark {
	private static final int d = 5;

	public static void main(String[] args) {
		AddWatermark embed = new AddWatermark();
		embed.start();
	}

	public void start() {
		BufferedImage oImage = ImageUtil.getImage("D://lena.jpg");
		BufferedImage wImage = ImageUtil.getImage("D://zhong.bmp");
		int type = oImage.getType();
		WritableRaster oRaster = oImage.getRaster();
		WritableRaster wRaster = wImage.getRaster();
		int oWidth = oRaster.getWidth();
		int oHeight = oRaster.getHeight();
		int wWidth = wRaster.getWidth();
		int wHeight = wRaster.getHeight();
		int[] oPixels = new int[3 * oWidth * oHeight];
		int[] wPixels = new int[wWidth * wHeight];
		oRaster.getPixels(0, 0, oWidth, oHeight, oPixels);
		wRaster.getPixels(0, 0, wWidth, wHeight, wPixels);
		int[][][] RGBPixels = ImageUtil.getRGBArrayToMatrix(oPixels, oWidth,
				oHeight);
		// 得到RGB图像的三层矩阵表示
		double[][] rPixels = MathTool.intToDoubleMatrix(RGBPixels[2]);
		int[][] wDMatrix = ImageUtil.arrayToMatrix(wPixels, wWidth, wHeight);
		double[][] result = rPixels;
		// 嵌入算法
		for (int i = 0; i < wWidth; i++) {
			for (int j = 0; j < wHeight; j++) {
				double[][] blk = new double[8][8];
				// 对原始图像8 * 8 分块
				for (int m = 0; m < 8; m++) {
					for (int n = 0; n < 8; n++) {
						blk[m][n] = rPixels[8 * i + m][8 * j + n];
					}
				}
				double[][] dBlk = FDct.fDctTransform(blk);
				if (wDMatrix[i][j] == 0) {
					dBlk[3][3] = dBlk[3][3] - d;
					dBlk[3][4] = dBlk[3][4] - d;
					dBlk[3][5] = dBlk[3][5] - d;
					dBlk[4][3] = dBlk[4][3] - d;
					dBlk[5][3] = dBlk[5][3] - d;
				} else {
					dBlk[3][3] = dBlk[3][3] + d;
					dBlk[3][4] = dBlk[3][4] + d;
					dBlk[3][5] = dBlk[3][5] + d;
					dBlk[4][3] = dBlk[4][3] + d;
					dBlk[5][3] = dBlk[5][3] + d;
				}
				blk = IFDct.iFDctTransform(dBlk);
				for (int m = 0; m < 8; m++) {
					for (int n = 0; n < 8; n++) {
						result[8 * i + m][8 * j + n] = blk[m][n];
					}
				}
			}
		}
		double[][][] temp = new double[3][oWidth][oHeight];
		temp[0] = MathTool.intToDoubleMatrix(RGBPixels[0]);
		temp[1] = MathTool.intToDoubleMatrix(RGBPixels[1]);
		temp[2] = result;
		double[] rgbResult = ImageUtil.getRGBMatrixToArray(temp);
		// 将BufferedImage对象写入磁盘
		ImageUtil.setImage(rgbResult, oWidth, oHeight, "D://result.bmp",
				"bmp", type);
	}

}

package com.zeph.watermark.fdct;

import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;

import com.zeph.watermark.util.ImageUtil;
import com.zeph.watermark.util.MathTool;


public class ExtractWatermark {
	public static void main(String[] args) {
		ExtractWatermark distill = new ExtractWatermark();
		distill.start(32, 32);
	}

	public void start(int wWidth, int wHeight) {
		// mImage是嵌入水印后的图像
		BufferedImage mImage = ImageUtil.getImage("D://result.bmp");
		// 原始图像
		BufferedImage oImage = ImageUtil.getImage("D://lena.jpg");
		WritableRaster oRaster = oImage.getRaster();
		WritableRaster mRaster = mImage.getRaster();
		int oWidth = oRaster.getWidth();
		int oHeight = oRaster.getHeight();
		int[] oPixels = new int[3 * oWidth * oHeight];
		int[] mPixels = new int[3 * oWidth * oHeight];
		oRaster.getPixels(0, 0, oWidth, oHeight, oPixels);
		mRaster.getPixels(0, 0, oWidth, oHeight, mPixels);
		// 得rgb图像三层矩阵,mRgbPixels[0]表示b层分量
		int[][][] mRgbPixels = ImageUtil.getRGBArrayToMatrix(mPixels, oWidth,
				oHeight);
		int[][][] oRgbPixels = ImageUtil.getRGBArrayToMatrix(oPixels, oWidth,
				oHeight);
		double[][] oDPixels = MathTool.intToDoubleMatrix(mRgbPixels[2]);
		double[][] mDPixels = MathTool.intToDoubleMatrix(oRgbPixels[2]);
		double[][] result = new double[wWidth][wHeight];
		for (int i = 0; i < wWidth; i++) {
			for (int j = 0; j < wHeight; j++) {
				double[][] oBlk = new double[8][8];
				double[][] mBlk = new double[8][8];
				int d = 0;
				int f = 0;
				for (int m = 0; m < 8; m++) {
					for (int n = 0; n < 8; n++) {
						oBlk[m][n] = oDPixels[8 * i + m][8 * j + n];
						mBlk[m][n] = mDPixels[8 * i + m][8 * j + n];
					}
				}
				double[][] dOBlk = FDct.fDctTransform(oBlk);
				double[][] dMBlk = FDct.fDctTransform(mBlk);
				if (dOBlk[3][3] > dMBlk[3][3]) {
					d++;
				} else {
					f++;
				}
				if (dOBlk[3][4] > dMBlk[3][4]) {
					d++;
				} else {
					f++;
				}
				if (dOBlk[3][5] > dMBlk[3][5]) {
					d++;
				} else {
					f++;
				}
				if (dOBlk[4][3] > dMBlk[4][3]) {
					d++;
				} else {
					f++;
				}
				if (dOBlk[5][3] > dMBlk[5][3]) {
					d++;
				} else {
					f++;
				}
				if (d < f) {
					result[i][j] = 0;
				} else {
					result[i][j] = 1;
				}
			}
		}
		double[] outResult = ImageUtil.matrixToArray(result);
		// 把嵌入水印的结果写到BufferedImage对象
		ImageUtil.setImage(outResult, wWidth, wHeight, "D://mark.bmp", "bmp",
				BufferedImage.TYPE_BYTE_BINARY);
	}

}

程序的实现参考:刘剑鸣  著 《图像数字水印的JAVA实现》

书中没有给出完整的程序示例,在这里,我将它补充和修改,将程序调试成功。

程序算法是针对指定的图片,也就是非盲性图片的水印算法。









  • 8
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 16
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值