Patchwork数字水印算法基础原理

Patchwork原意为一种用各种颜色和形状拼接成的布料。 Patchwork算法最早由麻省理工学院研发,在空间域上通过大量的模式冗余来实现鲁棒的数字水印技术,期初多用于打印票据的防伪。

基础原理Patchwork将水印信息隐藏在图像数据的亮度统计特性中,给出了一种原始的扩频调制机制。该算法根据给定的key随机选择N对像素点(ai, bi),然后将每个ai点的亮度值加δ(通常取256的1~5%),每个bi点的亮度值减δ,这样整个图像的平均亮度保持不变。 验证:根据验证用的key取得随机N对像素点,计算每一对像素点的亮度差并累加。如果key正确,最终的累加结果应该接近2 * N * δ;否则,最终累加结果会接近于0。

特点:

  1. 数据量较小,为了不破坏原始图像,一般只能存储1bit的数据(?存疑);

  2. 隐藏性好;

  3. 鲁棒性强,可以抵抗缩放、剪裁等的攻击;

改进Patchwork算法

借鉴:  甘霖,杨榆.基于变换域的Patchwork水印改进算法[J].成都信息工程大学学报,2017,32(06):623-627.  数字水印 改进的patchwork算法 实现_夏荷影的博客-CSDN博客_patchwork算法(MATLAB实现)

改进思路:

(1)离散余弦变换(Discrete Cosine Transform)

与离散傅里叶变换类似,可以将图像从空间域变换到频率域。经过DCT变换后的低频能量会集中在左上角。DCT变换比DFT变换后的能量更加集中。 在水印算法中引入图像的变换域用于嵌入水印,以此增强水印的透明性。

(2)人类视觉系统特性人类的视觉系统对不同颜色的敏感程度不同。

由亮度方程 y = 0.299R + 0.587G + 0.144B可知,人眼对绿色光最为敏感,对红色光的敏感程度次之,对于蓝色光最不敏感。人眼对红色光和蓝色光的敏感程度之和与对绿色光的敏感程度较为接近。 使用红色和蓝色光部分作为 Pacthwork 算法的 A 集合,绿色光作为 B 集合,两个集合在嵌入信息时使用相逆的操作,可以在一定程度上互相抵消嵌入信息引起的图像视觉上的变化,提高水印透明性。

算法描述——嵌入水印

载体图像 I 与水印图像W选取正方形的 RGB 图像,并将水印图像压缩到载体图像的边长为 1/8 。

将载体图像 I 的 3 个颜色通道分离到iBGR数组中,数组的三个元素分别为Bule、Greed、Red 3个颜色分量;

将水印图像W的 3 个颜色通道分离wBGR数组中,数组的三个元素分别为Bule、Greed、Red 3个颜色分量;

对水印图像的三个颜色分量wBGR分别进行Arnold变换得到wBGRA。

对载体图像iBGR中的三个颜色分量进行如下操作:各分量以8×8的大小为一个单位划分为若干子块,对每一个子块分别应用DCT 变换,最终得到三个颜色分量的分块DCT变换矩阵,放入iBGR_DCT中;

(上图为分块DCT变换后生成的图像,可以看到图中有明暗点阵,较亮的点隐约将原图形的轮廓勾勒了出来。)

然后对每个颜色分量的分块DCT变换矩阵取出其中的每一个子块左上角(0, 0)位置的直流分量组成一个新矩阵,该矩阵称为直流分量矩阵iBGRD,该矩阵的边长应为原图像边长的1/8;

在直流分量矩阵上嵌入水印,嵌入方法是增加/减去置乱的水印图像分量k倍的亮度。分量提取公式如下:

将直流分量矩阵还原回分块DCT矩阵的各直流分量处。之后对修改过的iBGR_DCT作逆DCT变换,获得三个颜色分量的矩阵。

合并三个颜色分量矩阵,即可得到嵌入数字水印后的图像。

算法描述——提取水印

ps:美中不足的是该算法不能仅仅依靠嵌有数字水印的图像来提取水印,还需要未嵌入水印的原图。

将含有数字水印的载体图像 P 和原始载体图像 I 的 3 个颜色通道分离;

对P和I的颜色分量矩阵都做DCT变换,并提取出直流分量矩阵pBGRD和iBGRD;

利用直流分量矩阵作差并除以嵌入时设定的系数k,提取水印图片的Arnold变换图像;

对Arnold置换图像作逆Arnold置换后再合并即可得到嵌入的水印图片。

实验

选取k=0.162、0.05、0.03、0.01作为系数,测试嵌入水印的效果如下:

可以看出,k值过大会影响嵌入后的图像以及水印图像的显示效果,k值过小只会影响提取出的水印图像的效果。最终发现k=0.03最为合适。

攻击测试

选取k=0.03时嵌入的图像,进行格式压缩、尺寸放大、尺寸缩小、剪裁的攻击方式。对比提取出的水印图像的效果。(除去剪裁之外,嵌入水印的图像均无明显变化)

其中可以看出,剪裁和缩小攻击对提取出的水印质量影响最大。

C++源码

#pragma once
#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
#include "DCTransform.hpp"
using namespace std;
​
/**** Edit by Atrix·M 2022.5.24* Improved patchwork digital watermark*/
namespace Patchwork
{static class PatchworkAlgorithm{private:static int Offset;static int NumberOfColor;static int NumberOfIterations;static int Sigma;
​static Mat DCTFunc(const Mat& img);static Mat IDCTFunc(const Mat& img);
​public:static Mat ArnoldReplace(Mat img);//static Mat ArnoldReplace(Mat img, int a=1, int b=1, int c=1, int d=2);//static Mat DeArnoldReplace(Mat img, int a = 2, int b = -1, int c = -1, int d = 1);static Mat DeArnoldReplace(Mat img);static bool SetWatermark(cv::Mat &picture, cv::Mat &watermark, float k = 0.1);static bool GetWatermark(cv::Mat &picture, cv::Mat &originImg, cv::Mat &watermark, float k = 0.1);};
​int PatchworkAlgorithm::Offset = 54;int PatchworkAlgorithm::NumberOfColor = 3;int PatchworkAlgorithm::NumberOfIterations = 10000;int PatchworkAlgorithm::Sigma = 3;
​//嵌入水印核心算法,k为系数bool PatchworkAlgorithm::SetWatermark(cv::Mat &picture, cv::Mat &watermark, float k){//对图片进行缩放,使载体图片和水印图片均为正方形,且载体图片的长宽等于水印图片长宽的8倍int pic_size = max(picture.rows, picture.cols);if (picture.rows != picture.cols){cv::resize(picture, picture, Size(pic_size, pic_size));}if (watermark.rows != watermark.cols || watermark.rows != (picture.rows / 8) || watermark.cols != (picture.cols / 8)){cv::resize(watermark, watermark, Size(pic_size / 8, pic_size / 8));}//分离RGB通道vector<Mat> iBGR, wBGR;cv::split(picture, iBGR);cv::split(watermark, wBGR);//DCT变换后的原矩阵vector<Mat> iBGR_DCT(3);vector<Mat> iBGRD(3), wBGRA(3);for (int i = 0; i < 3; i++){//对水印图像的三个分量分别作Arnold变换wBGR[i].convertTo(wBGR[i], CV_32FC1, 1 / 256.0);wBGRA[i] = ArnoldReplace(wBGR[i]);//对载体图像的三个分量分别作DCT变换iBGR[i].convertTo(iBGR[i], CV_32FC1, 1 / 256.0);iBGR_DCT[i] = DCTFunc(iBGR[i]);//取出三个载体图像DCT变换的直流分量iBGRD[i] = DCTransform::GetDirectComponent(iBGR_DCT[i]);}//在直流分量中插入水印图片的值iBGRD[0] = iBGRD[0] + k * wBGRA[0]; //BlueiBGRD[1] = iBGRD[1] - k * wBGRA[1]; //GreeniBGRD[2] = iBGRD[2] + k * wBGRA[2]; //Redfor (int i = 0; i < 3; i++){//将直流分量放回原DCT变换图中DCTransform::SetDirectComponent(iBGR_DCT[i], iBGRD[i]);//作DCT逆变换iBGR[i] = IDCTFunc(iBGR_DCT[i]);iBGR[i].convertTo(iBGR[i], CV_8UC1, 256.0);}picture = cv::Mat(Size(pic_size, pic_size), CV_8UC3, Scalar(0));//合并RGB分量cv::merge(iBGR, picture);return true;}
​//提取水印核心算法bool PatchworkAlgorithm::GetWatermark(cv::Mat &picture, cv::Mat &originImg, cv::Mat &watermark, float k){int pic_size = picture.rows;vector<Mat> wBGR(3), wBGRA(3);vector<Mat> pBGR, pBGR_DCT(3), pBGRD(3);vector<Mat> iBGR, iBGR_DCT(3), iBGRD(3);cv::split(picture, pBGR);cv::split(originImg, iBGR);for (int i = 0; i < 3; i++){pBGR[i].convertTo(pBGR[i], CV_32FC1, 1 / 256.0);iBGR[i].convertTo(iBGR[i], CV_32FC1, 1 / 256.0);//DCT变换pBGR_DCT[i] = DCTFunc(pBGR[i]);iBGR_DCT[i] = DCTFunc(iBGR[i]);//提取直流分量pBGRD[i] = DCTransform::GetDirectComponent(pBGR_DCT[i]);iBGRD[i] = DCTransform::GetDirectComponent(iBGR_DCT[i]);}//提取水印图片的Arnold变换图像wBGRA[0] = (pBGRD[0] - iBGRD[0]) / k;wBGRA[1] = (iBGRD[1] - pBGRD[1]) / k;wBGRA[2] = (pBGRD[2] - iBGRD[2]) / k;for (int i = 0; i < 3; i++){//进行Arnold逆变换wBGR[i] = DeArnoldReplace(wBGRA[i]);wBGR[i].convertTo(wBGR[i], CV_8UC1, 256.0);}watermark = Mat(Size(pic_size / 8, pic_size / 8), CV_8UC3, Scalar(0));cv::merge(wBGR, watermark);cv::resize(watermark, watermark, Size(pic_size, pic_size));return true;}
​//Arnold变换Mat PatchworkAlgorithm::ArnoldReplace(Mat img){int cols = img.cols;int rows = img.rows;//创建一个等大小的空白图Mat result(Size(rows, cols), CV_32FC1, Scalar(0));int a = 1;int b = 1;for (int i = 0; i < rows; i++){for(int j = 0; j < cols; j++){int x = (i + b * j) % rows;int y = (a * i + (a * b + 1) * j) % cols;result.at<float>(x, y) = img.at<float>(i, j);}}return result;}
​//Arnold反变换Mat PatchworkAlgorithm::DeArnoldReplace(Mat img){int cols = img.cols;int rows = img.rows;Mat result(Size(rows, cols), CV_32FC1, Scalar(0));int a = 1;int b = 1;for (int i = 0; i < rows; i++){for (int j = 0; j < cols; j++){int x = ((a * b + 1) * i - b * j) % rows;int y = (-a * i + j) % cols;x = x < 0 ? x + rows : x;y = y < 0 ? y + rows : y;result.at<float>(x, y) = img.at<float>(i, j);}}return result;}
​Mat PatchworkAlgorithm::DCTFunc(const Mat& img){float msk[8][8] = { {16,11,10,16,24,40,51,61},{12,12,14,19,26,58,60,55},{14,13,16,24,40,57,69,56},{14,17,22,29,51,87,80,62},{18,22,37,56,68,109,103,77},{24,35,55,64,81,104,113,92},{49,64,78,87,103,121,120,101},{72,92,95,98,112,100,103,99} };Mat mask(8, 8, CV_32FC1, msk);Mat A(8, 8, CV_32FC1);DCTransform::InitDctMat(A);return DCTransform::DCT(img, A, mask);}Mat PatchworkAlgorithm::IDCTFunc(const Mat& img){float msk[8][8] = {{16,11,10,16,24,40,51,61},{12,12,14,19,26,58,60,55},{14,13,16,24,40,57,69,56},{14,17,22,29,51,87,80,62},{18,22,37,56,68,109,103,77},{24,35,55,64,81,104,113,92},{49,64,78,87,103,121,120,101},{72,92,95,98,112,100,103,99}};Mat mask(8, 8, CV_32FC1, msk);Mat A(8, 8, CV_32FC1);DCTransform::InitDctMat(A);return DCTransform::IDCT(img, A, mask);}
}
#pragma once
#include <iostream>
#include <opencv2/opencv.hpp>
​
using namespace cv;
​
/**** Edit by Atrix·M 2022.5.24* Discrete Cosine Transform*/
namespace Patchwork
{class DCTransform{private:static const float PI;public:static void InitDctMat(Mat& A);static Mat DCT(const Mat& src, const Mat& A, const Mat& mask);  //求DCT变换后的矩阵static Mat IDCT(const Mat& src, const Mat& A, const Mat& mask); //求DCT逆变换后的矩阵static Mat GetDirectComponent(Mat &DCT);    //求直流分量static void SetDirectComponent(Mat &DCT, Mat &DirectComponent); //设置直流分量};
​const float DCTransform::PI = 3.1415926;
​void DCTransform::InitDctMat(Mat& A){for (int i = 0; i < 8; ++i){for (int j = 0; j < 8; ++j){float a;if (i == 0)a = sqrt(1.0 / 8.0);elsea = sqrt(2.0 / 8.0);A.ptr<float>(i)[j] = a * cos((j + 0.5) * PI * i / 8);}}}
​Mat DCTransform::DCT(const Mat& src, const Mat& A, const Mat& mask){int rows = src.rows;int cols = src.cols;
​Mat res = src.clone();for (int i = 0; i < rows; i += 8){for (int j = 0; j < cols; j += 8){//std::cout << "i:" << i << std::endl;//std::cout << "j:" << j << std::endl;res(Range(i, i + 8), Range(j, j + 8)) = A * res(Range(i, i + 8), Range(j, j + 8)) * A.t(); //dctres(Range(i, i + 8), Range(j, j + 8)) /= mask; //量化//std::cout << "直流通量:" << res(Range(i, i + 8), Range(j, j + 8)).at<float>(0, 0) << std::endl;}}return res;}
​Mat DCTransform::IDCT(const Mat& src, const Mat& A, const Mat& mask){int rows = src.rows;int cols = src.cols;
​Mat res = src.clone();for (int i = 0; i < rows; i += 8){for (int j = 0; j < cols; j += 8){res(Range(i, i + 8), Range(j, j + 8)) = res(Range(i, i + 8), Range(j, j + 8)).mul(mask);res(Range(i, i + 8), Range(j, j + 8)) = A.t() * res(Range(i, i + 8), Range(j, j + 8)) * A;}}return res;}
​Mat DCTransform::GetDirectComponent(Mat &DCT){int rows = DCT.rows;int cols = DCT.cols;Mat res(Size(rows / 8, cols/ 8), CV_32FC1);for (int i = 0; i < rows; i += 8){for (int j = 0; j < cols; j += 8){res.at<float>(i / 8, j / 8) = DCT(Range(i, i + 8), Range(j, j + 8)).at<float>(0, 0);}}return res;}
​void DCTransform::SetDirectComponent(Mat &DCT, Mat &DirectComponent){int rows = DCT.rows;int cols = DCT.cols;for (int i = 0; i < rows; i += 8){for (int j = 0; j < cols; j += 8){DCT(Range(i, i + 8), Range(j, j + 8)).at<float>(0, 0) = DirectComponent.at<float>(i / 8, j / 8);}}}
}

真男人当然要用C++,用什么MATLAB,用什么Python!弱爆了!C++功能强大,语法严格,最主要的是还能噎磨同学,可谓是一举三得!(bushi)

改进的Patchwork数字水印算法——C++实现相关推荐

  1. 基于DCT的数字水印算法

    基于MATLAB的离散余弦的数字水印系统 一.课题介绍 数字水印技术近年来得到了较大的发展,基于变换域的水印技术是目前研究的热点.数字水印是利用数字作品中普遍存在的冗余数据和随机性,把标识版权的水印信 ...

  2. MATLAB基于LSB的数字水印算法

    摘要-LSB是一种简单传统的信息隐藏算法,属于数字水印技术中的一种.本文首先介绍了LSB技术的原理和特点,然后讨论了基于LSB的数字水印算法.最后利用MATLAB 2010 b2对这一算法的加密过程进 ...

  3. 改进粒子群优化算法(IWAPSO)的MATLAB源程序

    改进粒子群优化算法(IWAPSO)的MATLAB源程序 作者:aaron8967    主页:http://aaron8967.blog.51cto.com 说明:粒子群优化算法(Particle S ...

  4. 【论文复现与改进】针对弱标注数据多标签矩阵恢复问题,改进后的MCWD算法,让你的弱标注多标签数据赢在起跑线上

    改进后的MCWD算法,让你的弱标注多标签数据赢在起跑线上 前言 MCWD算法 算法展示 算法改进 实现代码 实验结果 总结 前言 最近刷完了李航老师的<统计学习与方法>,手痒到又想复现几个 ...

  5. 改进粒子群优化算法(PURPSO)的MATLAB源程序

    改进粒子群优化算法(PURPSO)的MATLAB源程序 作者:aaron8967    主页:http://aaron8967.blog.51cto.com 说明:粒子群优化算法(Particle S ...

  6. 【优化算法】改进的灰狼优化算法(IGWO)【含Matlab源码 1349期】

    一.获取代码方式 获取代码方式1: 完整代码已上传我的资源:[优化算法]改进的灰狼优化算法(IGWO)[含Matlab源码 1349期] 点击上面蓝色字体,直接付费下载,即可. 获取代码方式2: 付费 ...

  7. 改进Zhang Suen细化算法的C#实现

    本文主要实现了改进Zhang Suen细化算法的C#实现,相关论文 :"牟少敏,杜海洋,苏平,查绪恒,陈光艺.一种改进的快速并行细化算法[J].微电子学与计算机,2013,(第1期)&quo ...

  8. 基于DCT的图像数字水印算法

    一.背景意义 信息革命使人类的社会与生活产生了深刻的变化.数字产品知识产权保护问题尤为突出,使用加密.数字签名等技术已经不能够有效的解决这一问题.本文结合隐蔽通信和数字水印两个主要的应用背景,对图像压 ...

  9. 基于改进二进制粒子群算法的配电网重构(matlab实现)

    目录 一.引言 1.问题背景 2.二进制粒子群算法 2.1简介 2.2 S i g m o i d Sigmoid Sigmoid函数 2.3二进制粒子群算法 2.4算法的改进 二.配电网重构模型 1 ...

最新文章

  1. atom编写python程序_如何进行Python程序的编写
  2. opencv 正脸和侧脸检测
  3. docker-compose部署thingsboard(docker部署thingsboard)
  4. pycharm如何安装pythoncard_pycharm的各种设置,配置
  5. Python break/continue - Python零基础入门教程
  6. Linux Hackers/Suspicious Account Detection
  7. 实现查询所有商品功能
  8. 目录-管壳式换热器的分析与计算
  9. [GIS教程] 6.1空间数据采集与处理具体步骤
  10. asp.net 旅游网站 源码+配置文档
  11. 用isPrime函数来判断一个数字是否为素数
  12. modbusx协议讲解
  13. Orserg WSPT WEB 安全项目组A-3项目训练文档
  14. poj 1655 树的重心
  15. html代码在线压缩成一行,HTML压缩 - Hito's Blog
  16. mysql 星座_mysql 查询年龄段,星座以及最近7天生日的sql
  17. Vue Dhtmlxgantt甘特图/横道图 baselines 含(计划、实际时间对比)树形实例实现及部分扩展
  18. “公正”与“公平”之辨
  19. 计算机主板 也叫系统版,计算机主板的型号是什么?
  20. 一种 低噪声放大器 电路结构

热门文章

  1. 奔腾cpu可以安装黑苹果吗_【2020】macOS黑苹果硬件主板CPU和显卡的支持列表和选购指南...
  2. .py 与 Cython 的 .pxd .pyx .pyd 文件格式之间的主要区别
  3. 广告营销核心干货——《我的营销心得》读书笔记2900字
  4. python修改Jenkins job的参数默认值
  5. 龟虽寿-曹操(东汉)
  6. 爬取中国空气质量在线监测分析平台
  7. Android系统启动流程 -- bootloader
  8. 场景调研 persona
  9. oracle绑定变量执行计划,绑定变量对执行计划的影响
  10. NodeJS中的异步编程经验