有意思的数字盲水印的简单的实现。

article/2025/11/7 13:41:23

  早期大约是10年前从一本数字图像处理上看到过数字水印的概念,觉得确实一种很有意思的东西,那个时候主要就是基于LSB的图像信息的隐藏,这种在空域里的方法有较大的缺陷,鲁棒性是比较差的。随便一个后期的都会造成水印的丢失,因此,虽然是一种盲水印,但是不具有很好的推广性。

  前段时间一个朋友给了我一段使用Opencv的盲水印代码,是基于FFT变换的, 抽空看了下,对其中部分的实现过程进行了替换和分解,也实现了一个最简单的基于频域的盲水印效果。

  我在寻找相关资料的时候在网络上看到有几个这方面的文章和工具,现在分享如下:

  https://blog.csdn.net/chenxiao_ji/article/details/52875199https://blog.csdn.net/chenxiao_ji/article/details/52875199

  https://www.sdbeta.com/wg/2018/0903/225358.html

       https://blog.csdn.net/weiyiweiyiweiyiyi/article/details/82847756

       https://blog.csdn.net/linyacool/article/details/71506638

  好像还有一个写的比较详细,而且有工具,在github上也有分享代码。

  但是似乎这些工具大部分只支持文字水印,而不支持图像水印,文字我不熟悉,因此我还是用图像做水印模板,核心的代码如下所示:

int IM_AddBlindWaterMark(unsigned char *Src, unsigned char *WaterMark, unsigned char *Dest, int Width, int Height, int Stride, int WidthW, int HeightW, int StrideW)
{int Channel = Stride / Width, ChannelW = StrideW / WidthW;if ((Src == NULL) || (Dest == NULL))                        return IM_STATUS_NULLREFRENCE;if ((Width <= 0) || (Height <= 0))                            return IM_STATUS_INVALIDPARAMETER;if ((Channel != 1) && (Channel != 3) && (Channel != 4))        return IM_STATUS_INVALIDPARAMETER;if ((ChannelW != 1) && (ChannelW != 3) && (ChannelW != 4))    return IM_STATUS_INVALIDPARAMETER;if ((WidthW >= Width / 4) || (HeightW >= Height / 4))        return IM_STATUS_INVALIDPARAMETER;        //    水印图不能大于原图尺寸的一半int Status = IM_STATUS_OK;int OptimalW = IM_GetOptimalDftSize(Width),    OptimalH = IM_GetOptimalDftSize(Height);int OffsetX = (OptimalW - Width) / 2,        OffsetY = (OptimalH - Height) / 2;int HalfW = OptimalW / 2,                    HalfH = OptimalH / 2;if (Channel == 1){Complex    *Data = (Complex *)malloc(OptimalW * OptimalH * sizeof(Complex));if ((Data == NULL))    return IM_STATUS_OUTOFMEMORY;for (int Y = 0; Y < Height; Y++)                                //    我们把数据居中布置,边缘用重复像素的方式
        {unsigned char *LinePS = Src + Y * Stride;Complex *LinePD = Data + (Y + OffsetY) * OptimalW;for (int X = 0; X < OffsetX; X++){LinePD[X].Real = LinePS[0];LinePD[X].Imag = 0;}for (int X = OffsetX; X < OffsetX + Width; X++){LinePD[X].Real = LinePS[X - OffsetX];            LinePD[X].Imag = 0;}for (int X = OffsetX + Width; X < OptimalW; X++){LinePD[X].Real = LinePS[Width - 1];LinePD[X].Imag = 0;}}for (int Y = 0; Y < OffsetY; Y++){memcpy(Data + Y * OptimalW, Data + OffsetY * OptimalW, OptimalW * sizeof(Complex));}for (int Y = OffsetY + Height; Y < OptimalH; Y++){memcpy(Data + Y * OptimalW, Data + (OffsetY + Height - 1) * OptimalW, OptimalW * sizeof(Complex));}IM_FFT2D(Data, Data, OptimalW, OptimalH, false, 0, 0);IM_FFTShift(Data, Data, OptimalW, OptimalH);                //    数据偏移到中心for (int Y = 0; Y < HeightW; Y++){Complex *LineLT = Data + (Y + OffsetY + Height / 16) * OptimalW + OffsetX + Width / 16;            //    确保在可见的范围内添加,左上角和右下角都镜像添加Complex *LineRB = Data + (OffsetY + Height - 1 - Height / 16 - Y) * OptimalW + OffsetX + Width - 1 - Width / 16;    //    再稍微往内部移动一点,可以适当增强抵抗变形的能力,但是越往中心其对最终结果的影响越大。unsigned char *LinePS = WaterMark + Y * StrideW;if (ChannelW == 1){for (int X = 0; X < WidthW; X++){float Cof = ((LinePS[X] * 4) >> 8) + 1;LineLT[X].Real *= Cof;    LineLT[X].Imag *= Cof;LineRB[-X].Real *= Cof;    LineRB[-X].Imag *= Cof;}}else if (ChannelW == 3){for (int X = 0; X < WidthW; X++){float Cof = ((((LinePS[0] + LinePS[1] + LinePS[1] + LinePS[2]) >> 2) * 4) >> 8) + 1;LineLT[X].Real *= Cof;    LineLT[X].Imag *= Cof;LineRB[-X].Real *= Cof;    LineRB[-X].Imag *= Cof;LinePS += 3;}}else if (ChannelW == 4){for (int X = 0; X < WidthW; X++){float Cof = ((IM_Div255(((LinePS[0] + LinePS[1] + LinePS[1] + LinePS[2]) >> 2) * LinePS[3]) * 4) >> 8) + 1;LineLT[X].Real *= Cof;    LineLT[X].Imag *= Cof;LineRB[-X].Real *= Cof;    LineRB[-X].Imag *= Cof;LinePS += 4;}}}IM_IFFTShift(Data, Data, OptimalW, OptimalH);IM_FFT2D(Data, Data, OptimalW, OptimalH, true, 0, 0);for (int Y = 0; Y < Height; Y++){Complex *LinePS = Data + (Y + OffsetY) * OptimalW + OffsetX;unsigned char *LinePD = Dest + Y * Stride;for (int X = 0; X < Width; X++){LinePD[X] = IM_ClampToByte(LinePS[X].Real);}}if (Data != NULL)        free(Data);return IM_STATUS_OK;}else{}
}

  首先,把图像变换到频域,这里采用了opencv的有关FFT计算的过程,使用IM_GetOptimalDftSize计算最佳的DFT算法的大小,然后将图像数据居中分布,周边的空白像素采用镜像填充方式填充,虚部数据填0。

        FFT变换完成后,对FFT数据进行移位,把高频数据放置到图像的中心,低频的数据放置到图像的边缘。为了将水印的图像嵌入到目标图像,我们在适当位置根据水印图像的强度或内容来修改这些频域值,为了不影响最终的目标图像的视觉效果,嵌入的数据放置到边缘的低频数据中(靠近边缘的部位),我这里也没有放置在最边缘,而是边缘靠中的部位。

  常用的水印图像可能是8位灰度、24位彩色或32位透明图,因此,我在程序里对不同位数的水印图都做了处理,如果是32位图,则把Alpha也考虑进去了,使用的嵌入方式就是最简单的更具水印图的颜色强度值将目标图像的频域系数放大。这里的放大程度我做了固定的设计,测试效果还比较好,如果过度放大,则最后处理的结果将会严重的失真,这就失去了算法本身的意义了。当然还有一种方式就是缩小系数,也可以去尝试下。

  之后,我们需要将平移后的数据再次进行移位,然后就是进行IFFT计算了,并将计算结果返回到图像域。

  本例只给出了针对灰度目标图像的代码,那么彩色图像其实是一样的过程,将他们分解成三个通道单独处理就OK了。 

  同时,为了保证水印对结果图不会造成太大的影响,我们程序对水印图大小做了限制,长和宽都不得大于目标图像的1/4。

  另外,从嵌入的代码可以看到,我们希望水印图像尽量是黑色的背景(8位或24位)或纯背景部位是透明的(32位),这样对目标图像的影响也比较小。

  我们来做一些测试,以下是一张原图(原图缩小显示了)及两个水印图进行测试:

   

  分别查看其结果图和频谱图:

 

 

    可见,添加水印后基本未对原始图像造成视觉上的损失,在处理后的图像的频谱上可以明显看到添加后的水印的样式。

  如果对添加水印后的图像进行一些处理,看看水印是否还能有效保存。

  一、乱七八糟的增强

 

2、有局部裁切的旋转

  

3、含有模糊性质的算法

  

  可见,这个时候水印信息就基本丢失了,这主要是因为我们的水印信息是加在图像的低频的,而模糊会对低频进行处理,所以就看不到水印了,但是如果是锐化算法就不成问题的。

  因此,这个盲水印的功能还是比较初级的,但是如果在自己的比较重要的图里隐藏个水印有的时候还是值得的,假如某个坏人是直接使用你的图而没有做任何更改呢。

  另外,还有一种基于FFT比较常见的水印技术,需要嵌入水印的图片以及未嵌入水印的原始图这样才可以获得水印,理论上讲这种应该不叫做盲水印了,但是他有个好处就是可以对水印进行加密,这样别人就比较难以知道你对图像是否嵌入了水印了。需要做的额外工具就是一定需要保留原始的未加水印的图像了。

  我将这个 小工具也集成到了我的SSE做的DEMO里了,有需要的朋友可以试下:SSE_Optimization_Demo.rar

  

 


http://chatgpt.dhexx.cn/article/JIqsyAtF.shtml

相关文章

python脚本实现盲水印提取和添加

python脚本实现盲水印提取和添加 首先github上找个BlindWaterMark-master文件下载一下&#xff0c;打开后是我这个样子 在python里面添加两个库&#xff0c;python.exe目录上方输入cmd pip install opencv-python python.exe -m pip install matplotlib新手这里装库的时候&am…

文字隐写术-盲水印研究笔记

gitee 开源代码(java) shuiyin: blind watermark 盲水印技术实现 java 实现版权保护 - Gitee.comhttps://gitee.com/chejiangyi/shuiyin/tree/master 需求概述 研究盲水印对于版权保护的效果和能力 研究尝试概述 水印实现: 大部分的python或者java算法都基于频域的盲水印效果…

web系统添加盲水印

前言 为增加系统安全性&#xff0c;避免重要敏感信息通过截图方式泄露&#xff0c;对web页面增加盲水印标识&#xff0c;标注系统名称&#xff0c;登陆人&#xff0c;当前时间等信息&#xff0c;这里的盲水印指肉眼不可见的html水印 增加水印 引入watermark.js调用 watermar…

【OpenCV 例程 300篇】219. 添加数字水印(盲水印)

OpenCV 例程200篇 总目录 【youcans 的 OpenCV 例程 300篇】219. 添加数字水印&#xff08;盲水印&#xff09; 8.2 添加数字盲水印 数字水印&#xff0c;是指将特征信息嵌入音频、图像或是视频等数字信号中。 数字水印分为明水印和盲水印&#xff08;blind watermark&#…

照片里的盲水印

这个题是安恒月赛里面的一道misc类型的题&#xff0c;在CTF中misc所谓最考验你的脑洞和检验你的赛事水平&#xff08;赛棍&#xff1f;&#xff1f;&#xff1f;&#xff09; 但是这个方面还是要看一下&#xff0c;就当做开发脑力了。 题目&#xff1a;暴力可解 题目文件 下载…

[python] 基于blind-watermark库添加图片盲水印

blind-watermark是一个能够给图片添加/解析基于频域的数字盲水印的Python库。图像水印image watermark是指在图片里添加文本或图形&#xff0c;以标记图片的来源。但是图像水印会破坏原图。因此另外一种水印形式&#xff0c;即图像盲水印blind image watermark在实践中更多地用…

图片盲水印软件

bulid_watermark_gui Blind&Invisible Watermark &#xff08;图片盲水印&#xff0c;提取水印无须原图&#xff01;) 增加图形界面 项目地址&#xff1a;github开源 软件下载地址&#xff1a;无需环境软件下载 how to use pip install -r requirements.txt 运行main.py…

隐写术(盲水印):从入门到出门

0. 前言 我在做 Blind Watermark 这个库的时候&#xff0c;翻阅了大量材料&#xff0c;学到了关于隐写术、盲水印的很多知识&#xff0c;现在梳理了一遍&#xff0c;发出来。 本文结构&#xff1a; 简介&#xff1a;隐写术的应用场景、分类、特点隐写术&#xff1a;介绍几种…

图片隐写,盲水印,加密logo

1.定义 隐写术算是一种加密技术&#xff0c;权威的 wiki 说法是“ 隐写术是一门关于信息隐藏的技巧与科学&#xff0c;所谓信息隐藏指的是不让除预期的接收者之外的任何人知晓信息的传递事件或者信息的内容。” example&#xff1a; 钱图&#xff08;钱上面的水印&#xff0…

CTF盲水印详解

原创稿件征集 邮箱&#xff1a;eduantvsion.com QQ&#xff1a;3200599554 黑客极客技术、信息安全热点安全研究分析等安全相关的技术文章 稿件通过并发布还能收获 200-800元不等的稿酬 前言 在CTF杂项题型中&#xff0c;盲水印的出现频率是相当高的&#xff0c;但大多数人处于…

盲水印(Blind-WaterMark)

盲水印是一种肉眼不可见的水印方式&#xff0c;对图片资源使用图片盲水印或者文字水印&#xff0c;借此避免数字媒体未经授权的复制和拷贝&#xff0c;可通过对原图进行解码操作&#xff0c;得到水印图来证明版权归属。 这类盲水印是怎样实现的呢&#xff1f;原理并不复杂&…

Windows系统通过CMD连接MySql

1.按压WindowsR快捷键后输入"cmd" 2.点击"确定"按钮后打开命令行 3.通过输入"cd"MySql安装的bin目录&#xff0c;按压"Enter"后进入MySql安装的bin目录 4.输入"mysql -h localhost -u root -p"&#xff0c;此处-h后为对应ho…

cmd中无法连接MySQL

本人电脑是win10系统&#xff0c;安装的是MySQL5.7版本的&#xff0c;最近在登录MySQL的时候老师出现下面的错误&#xff0c;有的时候重启电脑耶也会出现这样的情况 1.先检查你的环境变量是否配置好了&#xff1b; &#xff08;如果没有配置好&#xff0c;请参考一下的步骤&am…

如何使用cmd安装MySQL

步骤一&#xff1a; 在官网MySQL&#xff1a; 点击download显示下载页面&#xff1a; 点击 MySQL community download&#xff08;这个适个人合开发者&#xff0c;免费的&#xff0c;之前的MySQL Enterprise Edition是企业版付费需要 &#xff0c;MySQL cluster CGE 是免费版但…

navicat或者cmd远程连接mysql数据库

问题产生&#xff1a; 一般情况下&#xff0c;MySql数据库是不允许进行远程连接操作的&#xff0c;强行使用Navicat连接会报出下面的错误。 服务器连接错误主机XXX不允许连接到此的MySQL的服务器 方法步骤&#xff1a; 1、远程登录授权 在服务器端进行操作&#xff0c;下…

cmd窗口无法连接MySQL的解决方法

1.将下载Mysql中的bin目录的路径配置到Path环境变量中&#xff1b; 2.winR&#xff0c;输入services.msc&#xff0c;到服务窗口找到mysql服务&#xff0c;其中mysql的服务名可能是mysqlxx而不是mysql&#xff0c;比如我的是mysql80&#xff0c;找到之后点击&#xff0c;然后在…

cmd启动MySQL

命令行启动MySQL step1&#xff1a;打开命令行&#xff1a;win R step2&#xff1a;输入如下命令&#xff0c;root与1234分别为MySQL的用户名与密码 mysql -uroot -p1234出现如下信息即为启动成功 如果连接失败 显示如下错误信息&#xff1a; ERROR 2003 (HY000): Cant …

cmd的mysql命令_MySQL cmd命令

1.MySQL 登录: mysql -h localhost -u root -p test Enter password: ***** -h 指定数据库主机名,默认时登录主机。-u指定用户名(即安装数据库时的设置的用户名)。-p指明登录需要密码,如果没有密码,可省略-p参数。 -p 后面的test并不是密码,而是要登录的数据库名。如果-p后…

cmd命令创建连接MySQL数据库

欢迎大家关注我的公众号【老周聊架构】&#xff0c;Java后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。 一、连接MYSQL 格式&#xff1a; mysql -h 主机地址 -u 用户名 -p 用户密码1、 连接到本机上的MYSQL。 首先打开DOS窗口&#…

解决在cmd情况下无法连接MySQL情况(无脑操作教程)

在输入外部命令&#xff1a;mysql -u root -p的时候&#xff0c;出现下面的情况&#xff1a; &#xff08;网图&#xff0c;侵删&#xff09; 问题分析&#xff1a;可能是MySQL路径问题没有解决&#xff1b; 解决方法&#xff1a;1.打开电脑高级设置&#xff1b; 2.点击环境变…