Opencv实现盲水印技术(三)——傅里叶变换算法及盲水印实现
2016-10-20 18:16
1906 查看
前言
之前的文章中,介绍了如何构建Java工程和Java Web工程,本文将简单介绍对傅里叶变换的理解,以及使用opencv实现盲水印。关于傅里叶变换,有篇博文介绍的很详细,链接地址为:看了此文你还不了解傅里叶变换,你就过来掐死我吧!。对图像傅里叶变换的理解
对图像进行傅里叶变换,起始是一个二维离散傅里叶变换,图像的频率是指图像灰度变换的强烈程度,将二维图像由空间域变为频域后,图像上的每个点的值都变成了复数,也就是所谓的复频域,通过复数的实部和虚部,可以计算出幅值和相位,计算幅值即对复数取模值,将取模值后的矩阵显示出来,即为其频谱图。但是问题来了,复数取模后,数字有可能变的很大,远大于255,如果数据超过255,则在显示图像的时候会都当做255来处理,图像就成了全白色。因此,一般会对模值再取对数,在在0~255的范围内进行归一化,这样才能够准确的反映到图像上,发现数据之间的差别,区分高频和低频分量,这也是进行傅里叶变换的意义。opencv实现盲水印
废话不多说,先上代码。public class DFTUtil { private static List<Mat> planes; private static Mat complexImage; private DFTUtil(){} private static final DFTUtil dftUtil = new DFTUtil(); public static DFTUtil getInstance () { planes = new ArrayList<>(); complexImage = new Mat(); return dftUtil; } public Mat transformImage(Mat image) { // planes数组中存的通道数若开始不为空,需清空. if (!planes.isEmpty()) { planes.clear(); } // optimize the dimension of the loaded image Mat padded = this.optimizeImageDim(image); padded.convertTo(padded, CvType.CV_32F); // prepare the image planes to obtain the complex image planes.add(padded); planes.add(Mat.zeros(padded.size(), CvType.CV_32F)); // prepare a complex image for performing the dft Core.merge(planes, complexImage); // dft Core.dft(complexImage, complexImage); // optimize the image resulting from the dft operation Mat magnitude = this.createOptimizedMagnitude(complexImage); planes.clear(); return magnitude; } public void transformImageWithText(Mat image, String watermarkText, Point point, Double fontSize, Scalar scalar) { // planes数组中存的通道数若开始不为空,需清空. if (!planes.isEmpty()) { planes.clear(); } // optimize the dimension of the loaded image //Mat padded = this.optimizeImageDim(image); Mat padded = image; padded.convertTo(padded, CvType.CV_32F); // prepare the image planes to obtain the complex image planes.add(padded); planes.add(Mat.zeros(padded.size(), CvType.CV_32F)); // prepare a complex image for performing the dft Core.merge(planes, complexImage); // dft Core.dft(complexImage, complexImage); // 频谱图上添加文本 Core.putText(complexImage, watermarkText, point, Core.FONT_HERSHEY_DUPLEX, fontSize, scalar,2); Core.flip(complexImage, complexImage, -1); Core.putText(complexImage, watermarkText, point, Core.FONT_HERSHEY_DUPLEX, fontSize, scalar,2); Core.flip(complexImage, complexImage, -1); planes.clear(); } public Mat antitransformImage() { 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); planes.clear(); return restoredImage; } /** * 为加快傅里叶变换的速度,对要处理的图片尺寸进行优化 * * @param image * the {@link Mat} to optimize * @return the image whose dimensions have been optimized */ private Mat optimizeImageDim(Mat image) { // init Mat padded = new Mat(); // get the optimal rows size for dft int addPixelRows = Core.getOptimalDFTSize(image.rows()); // get the optimal cols size for dft int addPixelCols = Core.getOptimalDFTSize(image.cols()); // apply the optimal cols and rows size to the image Imgproc.copyMakeBorder(image, padded, 0, addPixelRows - image.rows(), 0, addPixelCols - image.cols(), Imgproc.BORDER_CONSTANT, Scalar.all(0)); return padded; } /** * Optimize the magnitude of the complex image obtained from the DFT, to * improve its visualization * * @param complexImage * the complex image obtained from the DFT * @return the optimized image */ private Mat createOptimizedMagnitude(Mat complexImage) { // init List<Mat> newPlanes = new ArrayList<>(); Mat mag = new Mat(); // split the comples image in two planes Core.split(complexImage, newPlanes); // compute the magnitude Core.magnitude(newPlanes.get(0), newPlanes.get(1), mag); // move to a logarithmic scale Core.add(Mat.ones(mag.size(), CvType.CV_32F), mag, mag); Core.log(mag, mag); // optionally reorder the 4 quadrants of the magnitude image this.shiftDFT(mag); // normalize the magnitude image for the visualization since both JavaFX // and OpenCV need images with value between 0 and 255 // convert back to CV_8UC1 mag.convertTo(mag, CvType.CV_8UC1); Core.normalize(mag, mag, 0, 255, Core.NORM_MINMAX, CvType.CV_8UC1); return mag; } /** * Reorder the 4 quadrants of the image representing the magnitude, after * the DFT * * @param image * the {@link Mat} object whose quadrants are to reorder */ private 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); } }上述代码是使用opencv进行傅里叶变换的工具类,其流程大致是:
对图像进行傅里叶变换;
打水印文字,注意此处是中心对称的打水印,至于原因,可以去研究一下傅里叶变换,就知道为啥要对称了;
傅里叶逆变换,取得原图像。
代码里注释的都很清楚,就不一一讲解代码了,有一篇写用DFT进行图片文字旋转的帖子,写的很详细,可以看下。链接为:Opencv实现基于傅里叶变换的旋转文本校正
效果展示
原图,还是上大美女lean吧。加水印后的图
解水印的图,即拿加水印的图片再做傅里叶变换。水印文字为“TEST”。
小结
本文介绍了傅里叶变换并对实现盲水印的核心算法实现给出了源代码,希望对大家有所帮助,欢迎探讨。转载请注明文章出处。。。
相关文章推荐
- Opencv实现盲水印技术(二)——使用maven构建Java Web工程
- Opencv实现盲水印技术(一)——构建Java工程
- OpenCV下利用傅里叶变换和逆变换实现图像卷积算法,并附自己对于卷积核/模板核算子的理解!
- CamShift算法----OpenCV实现1 Back Projection
- CamShift算法----OpenCV实现2 Mean Shift算法
- OpenCV下车牌定位算法实现代码(一)
- CamShift算法,OpenCV实现2-Mean Shift算法
- CamShift算法,OpenCV实现(3):CamShift算法 (转载)
- OpenCV下车牌定位算法实现代码
- CamShift算法,OpenCV实现(3):CamShift算法 (转载)
- 即时战略游戏中寻径(Path-finding)算法的原理及实现技术
- opencv实现二值图像细化的算法
- CamShift算法,OpenCV实现2-Mean Shift算法
- 3D智能问答旅游咨询员讨论区--算法与实现技术
- opencv实现二值图像细化的算法
- CamShift算法----OpenCV实现3 CamShift算法
- OpenCV下车牌定位算法实现代码(一)
- JAVA技术实现上传图片水印效果
- OpenCV学习——眼部识别算法实现
- OpenCV下车牌定位算法实现代码