OpenCV轻松入门:面向Python
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.8 数字水印

最低有效位(Least Significant Bit, LSB)指的是一个二进制数中的第0位(即最低位)。最低有效位信息隐藏指的是,将一个需要隐藏的二值图像信息嵌入载体图像的最低有效位,即将载体图像的最低有效位层替换为当前需要隐藏的二值图像,从而实现将二值图像隐藏的目的。由于二值图像处于载体图像的最低有效位上,所以对于载体图像的影响非常不明显,其具有较高的隐蔽性。

在必要时直接将载体图像的最低有效位层提取出来,即可得到嵌入在该位上的二值图像,达到提取秘密信息的目的。

这种信息隐藏也被称为数字水印,通过该方式可以实现信息隐藏、版权认证、身份认证等功能。例如,如果嵌入载体图像内的信息是秘密信息,就实现了信息隐藏;如果嵌入载体图像内的信息是版权信息,就能够实现版权认证;如果嵌入载体图像内的信息是身份信息,就可以实现数字签名,等等。所以,被嵌入载体图像内的信息也被称为数字水印信息。

数字水印信息可以是文本、视频、音频等多种形式,这里我们仅讨论数字水印信息是二值图像的情况。

3.8.1 原理

从位平面的角度考虑,数字水印的处理过程分为下面两步。

● 嵌入过程:将载体图像的第0个位平面替换为数字水印信息(一幅二值图像)。

● 提取过程:将载体图像的最低有效位所构成的第0个位平面提取出来,得到数字水印信息。

1.嵌入过程

嵌入过程是将数字水印嵌入载体图像的过程。该过程实现的是,将载体图像的最低有效位用数字水印信息替换,得到包含水印信息的载体图像。

为了处理和说明起来方便,这里以原始图像为灰度图像、水印图像为二值图像为例。在实际处理中,原始图像、水印图像均可以为彩色图像,这时需要先对它们进行通道分解、图层分解,后续的处理方法与在灰度图像内嵌入二值水印图像的处理方法相同。

(1)原始载体图像预处理

为了便于理解,我们将载体图像处理为二进制形式,并标记出最低有效位。

例如,有灰度图像O作为载体图像,图像O中各个像素点的像素值为:

其对应的二进制表示OB为:

为了更直观,我们用下画线将图像的二进制表示OB的最低有效位标记出来了。被标记的值构成了载体图像O的第0个位平面,即“最低有效位”位平面OBLSB,其具体值为:

(2)水印图像处理

为了方便处理,在嵌入水印前,需要将水印信息处理为二值图像。比较典型的情况是将灰度二值水印信息进行阈值处理,将其处理为二进制二值水印信息。

在灰度二值图像中,像素值只有0和255两种类型值,分别用来表示黑色和白色。可以将其中的255转换为1,这样就得到了一幅二进制二值图像。在二进制二值图像中,仅仅用一个比特位表示一个像素值,像素值只有0和1两种可能值。经过上述处理,能够更方便地实现水印嵌入。

例如,有灰度二值水印图像W,其像素值为:

不能直接将上述水印图像嵌入载体图像内,需要将其转换为二进制二值水印图像,以方便嵌入。通过阈值处理,得到二进制二值水印图像WB,其具体值为:

经过处理后,二进制二值水印信息正好可以嵌入载体图像的最低有效位上。

(3)嵌入水印

将载体图像的最低有效位替换为二进制水印图像,完成水印的嵌入。

例如,将载体图像OB的最低有效位用水印信息WB替换,得到含水印载体图像的二进制形式WOB,其具体值为:

将WOB转为十进制形式,即得到含水印载体图像的十进制值形式WO,其值为:

由于信息的最低有效位对值的大小影响有限,因此,将载体图像最低有效位的值用水印信息替换后,载体图像像素的值并没有发生太大变化,人眼不足以看出区别,水印具有较高的隐蔽性。

2.提取过程

提取过程是指将水印信息从包含水印信息的载体图像内提取出来的过程。提取水印时,先将含水印载体图像的像素值转换为二进制形式,然后从其最低有效位提取出水印信息即可。因此,可以通过提取含水印载体图像的“最低有效位”位平面的方式来得到水印信息。

例如,有包含水印信息的载体图像WOE,其具体值为:

将含水印信息的载体图像WOE转换为二进制形式WOEB,得到:

提取WOEB的最低有效位信息(“最低有效位”位平面,即第0个位平面),即可得到水印信息WE,其值为:

根据需要,决定是否进行阈值处理。如有必要,则通过阈值处理将其中值为1的像素点转换为255,得到含有0和255两个值的二值水印图像WET,具体值为:

通过上述例题可以发现,经过上述处理后,得到的水印图像WET与嵌入的水印图像W是一致的。

为了便于理解,这里仅介绍了原始载体图像为灰度图像的情况,在实际中可以根据需要在多个通道内嵌入相同的水印(提高鲁棒性,即使部分水印丢失,也能提取出完整水印信息),或在各个不同的通道内嵌入不同的水印(提高嵌入容量)。在彩色图像的多个通道内嵌入水印的方法,与在灰度图像内嵌入水印的方法相同。

3.8.2 实现方法

最低有效位水印的实现包含嵌入过程和提取过程,下面对具体的实现方法进行简单介绍。

1.嵌入过程

嵌入过程完成的操作是,将数字水印信息嵌入载体图像内,其主要步骤如下。

(1)载体图像预处理

读取原始载体图像,并获取载体图像的行数M和列数N

例如,有原始载体图像O,其像素值为:

将其对应的二进制形式记为OB,其具体值为:

(2)建立提取矩阵

建立一个M×N大小、元素值均为254的提取矩阵(数组),用来提取载体图像的高七位。

例如,按照原始图像O的大小建立一个4×4大小、元素值均为254的数组T,其具体为:

T所对应的二进制形式记为TB,其具体值为:

(3)保留载体图像的高七位,将最低位置零

为了实现该操作,需要将载体图像与元素值均为254的提取矩阵进行按位与运算。

将一个值在[0,255]之间的像素值P与数值254进行按位与运算,则会将像素值P的最低有效位置零,只保留其高七位。例如:

● 某个像素Pa的像素值为217,将像素Pa与254进行按位与运算,则像素Pa的二进制像素值高七位保持不变,最低有效位被清零。像素Pa的最低有效位上原来的值是1,因此,经过运算后像素Pa的像素值217减少1,变为216。

● 某个像素Pb的像素值为216,将像素Pb与254进行按位与运算,则像素Pb的二进制像素值高七位保持不变,最低有效位被清零。像素Pb的最低有效位上原来的值就是0,因此在运算后像素Pb的像素值仍然是216。

该运算示例具体如表3-17所示。

表3-17 按位与运算示例

表中的LSB表示Least Significant Bit,即最低有效位。

根据以上分析,将载体图像与元素值均为254的提取矩阵进行按位与运算,相当于将载体图像内的每个像素值均与值254进行按位与运算。这样就实现了将整个图像内所有像素二进制值的高七位保留、最低位置零。

例如,将原始载体图像OB与元素值均为254的提取矩阵TB进行按位与运算,则OB的高七位保持不变,而最低有效位被置零。即实现了只保留OB的高七位,得到OBH,其具体值为:

提示】要保留图像的高七位,还有一种实现方法,即先对像素进行右移一位的位操作,再进行左移一位的位操作。

(4)水印图像处理

有些情况下需要对水印进行简单处理。例如,当水印图像为8位灰度图的二值图像时,就需要将其转换为二进制二值图像,以方便将其嵌入载体图像的最低位。

例如,有一幅灰度二值水印图像W,具体值为:

我们将其中的像素值255转换为像素值1,以方便嵌入载体图像。该灰度二值图像对应的二进制图像为WT,其值具体为:

其对应的8位二进制形式WTB为:

(5)嵌入水印

将原始载体图像进行“保留高七位、最低位置零”的操作后,我们得到一幅新的图像,将新图像与水印图像进行按位或运算,就能实现将水印信息嵌入原始载体图像内的效果。

将一个最低有效位(LSB)为0的数值A与一个只有一位的二进制值B(单位二进制值)进行按位或运算时:

● 当该二进制值B为0时,按位或运算的结果是0,数值A的值保持不变。由于B的值为0,因此,如果从最低有效位的角度理解,可以理解为数值A的最低有效位被替换为单位二进制值B的值,也可以理解为将单位二进制值B嵌入数值A内(的最低有效位上)。

● 当该二进制值B为1时,按位或运算的结果是1,数值A的高七位保持变,而最低有效位变为1。因此,如果从最低有效位的角度理解,可以理解为数值A的最低有效位被替换为单位二进制值B的值,也可以理解为将单位二进制值B嵌入数值A内(的最低有效位上)。

总结来看,将一个最低有效位(LSB)为0的数值A与一个单位二进制值B进行按位或运算,相当于用该单位二进制值B替换原始数值A的最低有效位,即可以实现将单位二进制值B嵌入数值A的最低有效位上。

例如,将最低有效位是0的数字216分别与单位二进制值0和单位二进制值1进行按位或运算,如表3-18所示。

表3-18 按位或运算示例

可以推断,如果将二进制二值水印图像(单位二进制值的水印图像)与最低有效位被置零后得到的原始载体图像进行按位或运算,就可以实现将水印信息嵌入原始载体图像内。

因此,将水印信息WB与原始载体图像O的高七位图像OBH进行按位或运算,即完成将水印信息嵌入原始载体图像O的OBH内,就可以得到含水印载体图像WO。WO的具体值为:

(6)显示图像

完成上述处理后,分别显示原始载体图像、水印图像、含水印图像。

水印嵌入过程的流程图如图3-12所示。

图3-12 水印嵌入过程流程图

2.提取过程

提取过程将完成数字水印的提取,具体步骤如下。

(1)含水印载体图像处理

读取包含水印的载体图像,获取含水印载体图像的大小M×N

例如,读取含水印载体图像WO,获取其大小为4×4。WO的具体值为:

(2)建立提取矩阵

定义一个与含水印载体图像等大小的值为1的矩阵(数组)作为提取矩阵。

例如,定义一个大小为4×4的矩阵作为提取矩阵,使其中的值均为1,将该矩阵标记为T1:

其对应的8位二进制形式Te为:

(3)提取水印信息

将含水印载体图像与提取矩阵进行按位与运算,提取水印信息。

将一个值在[0, 255]之间的像素P与数值1进行按位与运算,则会将像素P的像素值的高七位置零,只保留像素P的最低有效位(LSB)。

下面分别以像素P的最低有效位为0和1为例进行说明。

● 如果像素P的最低有效位为1,则会得到值1。

例如,某像素Pa的值为217,将其与数值1进行按位与运算,则Pa的高七位被置零,只有其最低有效位被保留,得到值1。

● 如果像素P的最低有效位为0,则会得到值0。

例如,某像素Pb的值为216,将其与数值1进行按位与运算,则Pb的高七位被置零,只有其最低有效位被保留,得到值0。

该实例的具体计算如表3-19所示。

表3-19 与数值1进行按位与运算示例

基于上述规则,针对图像内的每个像素,将其与数值1进行按位与操作,即可将图像的最低有效位提取出来。

因此,可以将含水印载体图像与元素值均为1的提取矩阵进行按位与运算,以实现提取水印信息。

例如,含水印载体图像WO为:

将含水印图像WO与提取矩阵Te进行按位与运算,即可得到二进制值水印信息We,其值为:

将提取出来的二进制水印信息We进行阈值处理,将其中值为1的像素值调整为255,以便显示。阈值处理后,得到二值水印图像WG,具体值为:

(4)计算去除水印后的载体图像

有时需要删除包含在水印载体图像内的水印信息。通过将含水印载体图像的最低有效位置零,即可实现删除水印信息。

建立一个大小为4×4、元素值均为254的矩阵,将该矩阵标记为T2,其具体为:

将上述T2所对应的二进制形式记为TB,其具体值为:

通过将含水印载体图像WO与TB进行按位与运算,即可将载体图像WO的最低有效位置零,得到删除水印信息的载体图像ODW。该操作的具体实现原理及过程,与水印嵌入时对原始图像的最低有效位置零操作是类似的。

(5)显示图像

根据需要,分别显示提取出来的水印图像WG、删除水印信息的载体图像ODW。

水印提取过程的流程图如图3-13所示。

图3-13 水印提取过程流程图

提示】将像素值对2取模,可以获取像素值的最低有效位。因此,可以通过让含水印载体图像对2取模的方式,获取图像的“最低有效位”位平面,提取到的位平面即为水印信息。

就是说,也可以通过让含水印载体图像像素值对2取模的方式,来获取最低有效位水印。

3.8.3 例题

例3.15】编写程序,模拟数字水印的嵌入和提取过程。

根据题目要求,编写代码如下:

        import cv2
        import numpy as np
        #读取原始载体图像
        lena=cv2.imread("lena.bmp",0)
        #读取水印图像
        watermark=cv2.imread("watermark.bmp",0)
        #将水印图像内的值255处理为1,以方便嵌入
        #后续章节会介绍使用threshold处理
        w=watermark[:, :]>0
        watermark[w]=1
        #读取原始载体图像的shape值
        r, c=lena.shape
        #============嵌入过程============
        #生成元素值都是254的数组
        t254=np.ones((r, c), dtype=np.uint8)*254
        #获取lena图像的高七位
        lenaH7=cv2.bitwise_and(lena, t254)
        #将watermark嵌入lenaH7内
        e=cv2.bitwise_or(lenaH7, watermark)
        #============提取过程============
        #生成元素值都是1的数组
        t1=np.ones((r, c), dtype=np.uint8)
        #从载体图像内提取水印图像
        wm=cv2.bitwise_and(e, t1)
        print(wm)
        #将水印图像内的值1处理为255,以方便显示
        #后续章节会介绍使用threshold实现
        w=wm[:, :]>0
        wm[w]=255
        #============显示============
        cv2.imshow("lena", lena)
        cv2.imshow("watermark", watermark*255)   #当前watermark内最大值为1
        cv2.imshow("e", e)
        cv2.imshow("wm", wm)
        cv2.waitKey()
        cv2.destroyAllWindows()

运行上述程序,结果如图3-15所示,其中:

● 图(a)是原始图像lena。

● 图(b)是水印图像watermark。在程序中,该图像首先会被处理为二值图像,在显示时将其元素值乘以255,以方便显示。

● 图(c)是在图像lena内嵌入水印图像watermark后得到的含水印载体图像e。

● 图(d)是从含水印载体图像e内提取到的水印图像wm。

从图3-14可以发现,通过肉眼无法观察出含水印载体图像和原始图像的不同,水印的隐蔽性较高。但是,由于该方法过于简单,其安全性并不高,在实际处理中会通过更复杂的方式实现水印的嵌入。

图3-14 【例3.15】程序的运行结果