Reversible Data Hiding可逆数据隐藏的python实现
这是一篇针对可逆数据隐藏的经典研究论文(IEEE TRANSACTIONS ON CIRCUITS AND SYSTEMS FOR VIDEO TECHNOLOGY , VOL. 16, NO. 3, MARCH 2006),笔者使用python将原论文中的算法进行实现。
实现代码已上传至百度云:
链接:https://pan.baidu.com/s/13FbDkLrRrVlZLYyLbzUJ8w
提取码:wlsc
代码详细解释见第一部分算法介绍之后的内容,如果本文对你有所帮助,请多多点赞收藏。
一,算法介绍
1.算法目的:
该算法分为信息隐藏和信息提取两个部分。前者是将一组二进制数据嵌入到原图像中,且不影响原图像的显示内容。后者是将已嵌入信息的处理图像进行信息提取并还原图像信息。
2.信息隐藏:
1)生成原图像的灰度直方图H(x),寻找H(x)中的最大值H(a)和最小值H(b),其中的最大值点a和最小值点b对应原图像中数量最多和数量最少的灰度值。
2)如果最小值H(b)是非零的,需要将原图像中该最小灰度值的像素信息进行另存(即开销信息O),然后将这些像素的灰度值重新编码(具体操作在算法实现中我会讲解),使最小灰度值的像素数量为0,即H(b) = 0,得到的开销信息后面和隐藏信息一起嵌入到图像中。
3)为了保持一般性,假设最小值点b > 最大值点a,将原图像中灰度值在(b,a)之间的像素灰度值加1(如果b
4)开始嵌入操作,扫描整幅图像,当遇到灰度值为a的像素,检查嵌入数据,如果嵌入数据为1,则将该像素灰度值加1;如果嵌入数据为0,则保持不变。
3.信息提取:
3.1提取隐藏数据:
扫描整幅图像,当遇到像素灰度值为a+1时,提取”1”,当遇到像素灰度值为a时,提取”0”。
3.2恢复图像数据:
1)复原开销信息:获取开销信息长度,用以提取后面的开销信息。根据开销信息中保存的原像素位置,将对应像素的灰度值置为b。
2)将直方图中灰度值在[b,a)间的像素整体减1(b>a则加1),即左移一个单位。经过以上两步操作,图像的原始信息和隐藏信息得到分离,图像没有任何损失。
二,嵌入操作(算法实现)
1.所使用的库函数
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
2.读取数据
为了满足读取图像以及保存图像的无损过程,本文采用格式为PNG的图像。(JPEG图像使用imwrite函数无法无损保存)
为便于操作,本文使用“Lena”的灰度图像,获取图像各像素灰度值后将其转化为一维数组,便于后续操作。
img = cv.imread("lena.png",0) # Use the classical Image 'Lena'
h, w = img.shape[:2] # 512 * 512
pixelSequence = img.reshape([h * w, ]) # [262144,]
3.寻找直方图最值点
通过直方图,判断最大值点a和最小值点b。如果最小值非零,即将原图像中灰度值为该最小值的像素位置打包为开销信息(另外保存),然后将像素值置为(b,a)之外的值,因为最小值的数量少,不会影响图像整体显示。(本文将最小值左移一位,即b-1)
numberBins = [i + 0.5 for i in list(range(0, 256))] # set the range of bins
histogram, bins, patch = plt.hist(pixelSequence, numberBins,
facecolor='blue', histtype='stepfilled') # histograming
max_a = max(histogram) # max pixel number
min_b = min(histogram) # min pixel number
histogram_list = histogram.tolist()
MaxPoint = histogram_list.index(max(histogram_list))+1 # max gray value
MinPoint = histogram_list.index(min(histogram_list))+1 # min gray value
# judge the min pixel number is 0 or not
BookKeeping = []
if min_b != 0:
for i in range(len(pixelSequence)):
if pixelSequence[i] == MinPoint:
BookKeeping.append(i)
pixelSequence[i] = MinPoint - 1
4.移动直方图
在不丧失一般性的情况下,且根据max/min函数的特性,本文的最小值点b < 最大值点a。因此,只需将灰度值在(b,a)以内(不包括a,b)的像素灰度值减1即可。
for i in range(len(pixelSequence)):
if MinPoint < pixelSequence[i] < MaxPoint:
pixelSequence[i] -= 1
5.嵌入数据
根据算法原理,嵌入数据为二进制数组。扫描整幅图像,遇到灰度值为最大值点a时进行判断(假设此时扫描到像素n):
1)如果嵌入值为1时,像素n的灰度值-1;
2)如果嵌入值为0时,像素n的灰度值保存不变。
Hidden_Data = [1,1,0,0,1,0,1,0,0,1,1] # hidden data
n = 0
for i in range(len(pixelSequence)):
if pixelSequence[i] == MaxPoint:
if Hidden_Data[n] == 1:
pixelSequence[i] -= 1
else:
pass
n += 1
if n == len(Hidden_Data):
break
6.生成处理图像
使用cv.imwrite函数,其中[cv.IMWRITE_PNG_COMPRESSION, Compression Ratio]的第二个参数表示压缩比例,从0~9,数值越高压缩比例越高。(笔者测试:无论设置压缩比例为多少,图像的像素序列以及灰度值都不会变,但是图像的内存会减小)
Marked_Image = pixelSequence.reshape(h, w)
cv.imwrite('Marked_Image.png', Marked_Image, [cv.IMWRITE_PNG_COMPRESSION, 0])
三.提取还原操作(算法实现)
上一部分已经将数据嵌入到图像中,提取还原只需要对之前的步骤进行逆向操作即可。
1.读取图像
img = cv.imread("Marked_Image.png",0) # Use the classical Image 'Lena'
h, w = img.shape[:2] # get the pixel's high->512 and wide->512
pixelSequence = img.reshape([h * w, ]) # [262144,]
2.提取隐藏数据
扫描整幅图像:
1)当遇到像素灰度值为a-1,提取1;
2)当遇到像素灰度值为a,提取0。
Recover_Data = []
count = 0
for i in range(len(pixelSequence)):
if pixelSequence[i] == MaxPoint-1:
Recover_Data.append(1)
count += 1
if pixelSequence[i] == MaxPoint:
Recover_Data.append(0)
count += 1
if count == len(Hidden_Data):
break
现在,我们打印隐藏数据和恢复数据:
print('Hidden_Data:',Hidden_Data)
print('Recover_Data:',Recover_Data)
显然,二者应该相等。
Hidden_Data: [1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1]
Recover_Data: [1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1]
3.恢复原图像数据
该恢复过程分为三步:
1)复原开销信息:根据开销信息中保存的原像素位置,将对应位置像素灰度值置为b。
for i in range(len(BookKeeping)):
pixelSequence[BookKeeping[i]] = MinPoint
2)直方图[b,a)整体右移一个单位。
for i in range(len(pixelSequence)):
if MinPoint <= pixelSequence[i] <= MaxPoint-1:
pixelSequence[i] += 1
3)恢复原图像。
Recover_Image = pixelSequence.reshape(h,w)
cv.imwrite('Recover_Image.png', Recover_Image, [cv.IMWRITE_PNG_COMPRESSION, 0])
四.验证算法
为了验证算法的正确性,随机生成不同长度的二进制数组,先进行嵌入操作,然后保存嵌入数据后的图像。重新加载处理后的图像,从图像中进行数据提取和图像复原,分别查看提取后的数据和原数据是否一致,再将复原后的图像像素值与原图像像素值进行比较。
1.验证隐藏数据与提取数据:
设置隐藏数据长度为最大数据其嵌入量,并随机排序0和1,将隐藏数据与复原数一一比较。
随机二进制数组生成如下:
Hidden_Data = np.random.randint(0,2,int(max_a)-len(BookKeeping))
# 验证隐藏数据与提取数据一致性
Flag = 0
for i in range(len(Hidden_Data)):
if Hidden_Data[i] != Recover_Data[i]:
Flag = 1
break
if Flag == 0 :
print('数据提取成功')
else:
print('数据提取失败')
2.验证原图像与复原图像:
分别取原图像和复原图像的像素灰度值进行一一比较。
# 验证原图像与复原图像像素一致性
Flag = 0
for i in range(len(originpixel)):
if originpixel[i] != pixelSequence[i]:
Flag = 1
break
if Flag == 0 :
print('图像复原成功')
else:
print('图像复原失败')
最后我们执行程序,执行结果如下所示:
the max number: 3041.0
the min number: 0.0
the max gray value: 37
the min gray value: 1
数据提取成功
图像复原成功
[Finished in 3.7s]
可以在文件夹中查看生成的处理图像和最后的复原图像,二者通过人眼看不出任何区别。
转载:感谢您对网站平台的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人站长或者朋友圈,但转载请说明文章出处“来源搜码吧-程序员大本营,技术文章内容聚合第一站”。Reversible Data Hiding可逆数据隐藏的python实现--搜码吧-程序员大本营,技术文章内容聚合第一站