Java使用OpenCV基于离散傅里叶变换算法实现-----图片添加盲水印

xiaoxiao2025-10-15  7

前言

最近公司孵化一个项目,里面涉及到大量的原创照片及图片,目前市场上的明文水印,对用户体验有一定影响,并且这种水印也极其容易被破坏,之前了解到一种基于傅里叶变换实现的盲水印,这种盲水印技术相比普通水印,不但有更好的用户视觉体验,安全性也是拉了普通水印几条街 早在16年,阿里的内部泄密事件发生后,一位知乎大神在知乎上对这种技术已经给出了很详细的解释,帖子中也给出了相关理论和matlab代码及对其水印的安全测评 :阿里巴巴公司根据截图查到泄露信息的具体员工的技术是什么? - 知乎

整个过程大概如下

打水印 先将原图片进行 离散傅里叶变换 到频域,加上水印后再通过离散傅里叶逆变换到空间域恢复图片解水印 将打有水印的图片通过傅里叶变换到频域,提取出水印 本篇文章主要介绍 JAVA 结合OpenCV实现盲水印服务,并对其进行封装,供整个系统各个服务进行调用

搭建OpenCV开发环境,加载OpenCV动态库

环境:JDK1.8 + Maven3.x + IntelliJ IDEA 2018.2.5 + OpenCV2.4.13 + Windows

OpenCV2.4.13 下载地址

安装OpenCV 其实安装程序做的也就是把Opencv内容解压到你所选择的目录下面而已 新建一个Maven项目 File --> Project Strcture --> Project Settings --> Libraries 点击+号 把opencv-2413.jar引入

 

配置open动态库路径

配置完毕 点击 Apply 应用

 

创建工具类 ImgWatermarkUtil.java

import org.opencv.core.*; import org.opencv.imgproc.Imgproc; import java.util.ArrayList; import java.util.List; /** * @author yangxiaohui * @Date: Create by 2018-10-25 19:14 * @Description: 添加图片盲水印工具类 */ public class ImgWatermarkUtil { private static List<Mat> planes = new ArrayList<Mat>(); private static List<Mat> allPlanes = new ArrayList<Mat>(); /** * <pre> * 添加图片文字水印 * <pre> * @author Yangxiaohui * @date 2018-10-25 19:16 * @param image 图片对象 * @param watermarkText 水印文字 */ public static Mat addImageWatermarkWithText(Mat image, String watermarkText){ Mat complexImage = new Mat(); //优化图像的尺寸 //Mat padded = optimizeImageDim(image); Mat padded = splitSrc(image); padded.convertTo(padded, CvType.CV_32F); planes.add(padded); planes.add(Mat.zeros(padded.size(), CvType.CV_32F)); Core.merge(planes, complexImage); // dft Core.dft(complexImage, complexImage); // 添加文本水印 Scalar scalar = new Scalar(0, 0, 0); Point point = new Point(40, 40); Core.putText(complexImage, watermarkText, point, Core.FONT_HERSHEY_DUPLEX, 1D, scalar); Core.flip(complexImage, complexImage, -1); Core.putText(complexImage, watermarkText, point, Core.FONT_HERSHEY_DUPLEX, 1D, scalar); Core.flip(complexImage, complexImage, -1); return antitransformImage(complexImage, allPlanes); } /** * <pre> * 获取图片水印 * <pre> * @author Yangxiaohui * @date 2018-10-25 19:58 * @param image */ public static Mat getImageWatermarkWithText(Mat image){ List<Mat> planes = new ArrayList<Mat>(); Mat complexImage = new Mat(); Mat padded = splitSrc(image); padded.convertTo(padded, CvType.CV_32F); planes.add(padded); planes.add(Mat.zeros(padded.size(), CvType.CV_32F)); Core.merge(planes, complexImage); // dft Core.dft(complexImage, complexImage); Mat magnitude = createOptimizedMagnitude(complexImage); planes.clear(); return magnitude; } private static Mat splitSrc(Mat mat) { mat = optimizeImageDim(mat); Core.split(mat, allPlanes); Mat padded = new Mat(); if (allPlanes.size() > 1) { for (int i = 0; i < allPlanes.size(); i++) { if (i == 0) { padded = allPlanes.get(i); break; } } } else { padded = mat; } return padded; } private static Mat antitransformImage(Mat complexImage, List<Mat> allPlanes) { Mat invDFT = new Mat(); Core.idft(complexImage, invDFT, Core.DFT_SCALE | Core.DFT_REAL_OUTPUT, 0); Mat restoredImage = new Mat(); invDFT.convertTo(restoredImage, CvType.CV_8U); if (allPlanes.size() == 0) { allPlanes.add(restoredImage); } else { allPlanes.set(0, restoredImage); } Mat lastImage = new Mat(); Core.merge(allPlanes, lastImage); return lastImage; } /** * <pre> * 为加快傅里叶变换的速度,对要处理的图片尺寸进行优化 * <pre> * @author Yangxiaohui * @date 2018-10-25 19:33 * @param image * @return */ private static Mat optimizeImageDim(Mat image) { Mat padded = new Mat(); int addPixelRows = Core.getOptimalDFTSize(image.rows()); int addPixelCols = Core.getOptimalDFTSize(image.cols()); Imgproc.copyMakeBorder(image, padded, 0, addPixelRows - image.rows(), 0, addPixelCols - image.cols(), Imgproc.BORDER_CONSTANT, Scalar.all(0)); return padded; } private static Mat createOptimizedMagnitude(Mat complexImage) { List<Mat> newPlanes = new ArrayList<Mat>(); Mat mag = new Mat(); Core.split(complexImage, newPlanes); Core.magnitude(newPlanes.get(0), newPlanes.get(1), mag); Core.add(Mat.ones(mag.size(), CvType.CV_32F), mag, mag); Core.log(mag, mag); shiftDFT(mag); mag.convertTo(mag, CvType.CV_8UC1); Core.normalize(mag, mag, 0, 255, Core.NORM_MINMAX, CvType.CV_8UC1); return mag; } private static void shiftDFT(Mat image) { image = image.submat(new Rect(0, 0, image.cols() & -2, image.rows() & -2)); int cx = image.cols() / 2; int cy = image.rows() / 2; Mat q0 = new Mat(image, new Rect(0, 0, cx, cy)); Mat q1 = new Mat(image, new Rect(cx, 0, cx, cy)); Mat q2 = new Mat(image, new Rect(0, cy, cx, cy)); Mat q3 = new Mat(image, new Rect(cx, cy, cx, cy)); Mat tmp = new Mat(); q0.copyTo(tmp); q3.copyTo(q0); tmp.copyTo(q3); q1.copyTo(tmp); q2.copyTo(q1); tmp.copyTo(q2); } }

测试图片水印添加和读取

import org.opencv.core.Core; import org.opencv.core.Mat; import static org.opencv.highgui.Highgui.imread; import static org.opencv.highgui.Highgui.imwrite; /** * @author yangxiaohui * @Date: Create by 2018-10-25 19:42 * @Description: */ public class Main { static{ //加载opencv动态库 System.loadLibrary(Core.NATIVE_LIBRARY_NAME); } public static void main(String[] args){ Mat img = imread("stzz.jpg");//加载图片 Mat outImg = ImgWatermarkUtil.addImageWatermarkWithText(img,"testwatermark"); imwrite("stzz-out.jpg",outImg);//保存加过水印的图片 //读取图片水印 Mat watermarkImg = ImgWatermarkUtil.getImageWatermarkWithText(outImg); imwrite("stzz-watermark.jpg",watermarkImg);//保存获取到的水印 } }

测试结果:

加水印前

加水印后

读取水印结果

 

转载请注明原文地址: https://www.6miu.com/read-5037963.html

最新回复(0)