LSB图像数字⽔印嵌⼊算法(含python代码)
参考:
⽬录
理论知识
在学习这篇博客的内容之前,你需要了解如下内容
1)
2)图⽚的属性
在灰度图像中,每个像素通常为8位,在RGB图像中,每个通道占8位,共有24位。每位的取值为0或者1,这就是为什么每个通道的最⼤值为255,每个像素位对图像的贡献值是不同的,这样,把整个图像分为8个位平⾯,从LSB(最低有效位0)到MSB(最⾼有效位1)。因为低位(根据⼀个公式)表达的信息少,所以将⽔印嵌⼊在LSB上,这样对不会对图像的质量产⽣较⼤的影响。
3)LSB隐写(最低有效位的隐写),是指通过改变图⽚中像素的最低位来实现信息的隐藏的。这种隐写⽅式需要图⽚是⽆压缩的位图,因此⼀般⽤于bmp和png图⽚。
LSB的优缺点
缺点: 改变最低有效位数据,数字图像进⾏数学变换等攻击⽅式使得嵌有数字⽔印的图⽚很容易受到攻击,缺乏鲁棒性
优点: 实现简单,隐藏量⼤。
LSB算法操作步骤
LSB对数字⽔印的操作主要包括⽔印嵌⼊过程和⽔印提取过程
1)⽔印嵌⼊过程
2)⽔印提取过程
⽔印嵌⼊
⾸先取两张png图⽚
主图(要嵌⼊⽔印的图⽚)如下
⽔印图⽚
然后将⽔印图⽚嵌⼊到主图中去
我们先来看⼀下每张图⽚的mode
from PIL import Image
im=Image.open("F:/girl.png")
watermark=Image.open("F:/watermark.png")
print (im)
print (watermark)
我们从打印结果可以看出⽔印图⽚的mode是RGBA,将⽔印图⽚的mode转换为RGB
vert("RGB")
print(watermark)
看到⽔印图⽚已经转换为RGB格式了
获取⽔印图⽚的每个像素的RGB值
代码
#获取⽔印图⽚的每⼀个像素值,i:指定要检查的像素点的逻辑X轴坐标。j:指定要检查的像素点的逻辑Y轴坐标。
for i in range(watermark.size[0]):
for j in range(watermark.size[1]):
#获取每个像素的RGB值
pixel((i,j))
print(rgb)
因为获取的结果太长就不输出了,截图输出数据的⼀部分如下
可以获取的RGB值是⽤三元组来进⾏保存的
将RGB形式转换为⼆进制的形式,这样通道的值就转换为8位2进制,RGB有三个通道就转换为⼆进制就是24位
def plus(str):
return str.zfill(8)
#获取⽔印图⽚的每⼀个像素值,i:指定要检查的像素点的逻辑X轴坐标。j:指定要检查的像素点的逻辑Y轴坐标。
for i in range(watermark.size[0]):
for j in range(watermark.size[1]):
#获取每个像素的RGB值
pixel((i,j))
str=plus(bin(rgb[0]).replace('0b',''))
str=str + plus(bin(rgb[1]).replace('0b',''))
str=str + plus(bin(rgb[2]).replace('0b',''))
print (str)
打印出每⼀个像素点的RGB值的⼆进制
其中bin是python的进制转换函数,能将⼗进制转换为⼆进制,唯⼀不⾜的就是会⾃动在转换的数字前⾯加0b,因此以空字符串替换掉
0b。此外,⼗进制转换⼆进制的时候,如果转换的⼆进制是以0开头的,那么会⾃动省略前⽅的0,这不符合我们的要求。因此以255为标准,⼆进制为1111 1111,其余的不⾜8位的在最前⽅补0,如1就是0000 0001。plus函数就是补⾜8位。
⽤⽔印图⽚的每⼀位替换主图中的每个像素点的R、G、B的⼆进制形式的末尾数字,但是主图的变化值最多为1,主观上是察觉不到的
⽔印提取
解密过程
1)将⽔印图⽚的像素点RGB值还原
2)⽤⽔印的宽和⾼来进⾏还原。计算公式是宽*⾼ 38
这⾥我⽔印图⽚的宽为111,⾼为34,根据公式计算相乘得到length=90576
然后从主图像中提取⽔印的值
def deEncry(img,length):
width=im.size[0]
height=im.size[1]
#计数器
count= 0
wt=""
for i in range(width):
for j in range(height):
# 获取像素点的值
rgb = pixel((i, j))
# 提取R通道的附加值
if count % 3 == 0:
count += 1
wt = wt + str(rgb[0] % 2)
if count == length:
break
# 提取G通道的附加值
if count % 3 == 1:
count += 1
wt = wt + str(rgb[1] % 2)
if count == length:
break
# 提取B通道的附加值
if count % 3 == 2:
count += 1
wt = wt + str(rgb[2] % 2)
if count == length:
break
if count == length:
break
return wt
然后将wt的值还原为RGB的值,并显⽰图⽚
def showImage(wt):
# str2初始化为list
str2 = []
#将wt的值转变为RGB值
for i in range(0, len(wt), 8):
# 以每8位为⼀组⼆进制,转换为⼗进制
str2.append(int(wt[i:i + 8], 2))
#绘制和显⽰⽔印图⽚
img_out = w("RGB", (113, 36))
flag = 0
for m in range(0, 111):
for n in range(0, 34):
img_out.putpixel((m, n), (str2[flag], str2[flag + 1], str2[flag + 2]))
flag += 3
img_out.show()
还原到的⽔印图⽚是
上图是python代码运⾏得到的,为了便于观看,w()的时候宽和⾼增加了两个像素
代码
from PIL import Image
# print(watermark)
def plus(str):
return str.zfill(8)
# 获取⽔印图⽚的每⼀个像素值,i:指定要检查的像素点的逻辑X轴坐标。j:指定要检查的像素点的逻辑Y轴坐标。def getcode(watermark):
str1 = ""
for i in range(watermark.size[0]):
for j in range(watermark.size[1]):
# 获取每个像素的RGB值
python代码转换rgb = pixel((i, j))
str1 = str1 + plus(bin(rgb[0]).replace('0b', ''))
str1 = str1 + plus(bin(rgb[1]).replace('0b', ''))
str1 = str1 + plus(bin(rgb[2]).replace('0b', ''))
# print (str)
return str1
# 加密
def encry(img, code):
def encry(img, code):
# 计数器
count = 0
codelen = len(code)
print (codelen)
for i in range(img.size[0]):
for j in range(img.size[1]):
# 获取每个像素的RGB值
data = pixel((i, j))
if count == codelen:
break
r = data[0]
g = data[1]
b = data[2]
# print (r)
# print(codelen)#24
r = (r - r % 2) + int(code[count])
count += 1
if count == codelen:
img.putpixel((i, j), (r, g, b))
break
g = (g - g % 2) + int(code[count])
count += 1
if count == codelen:
img.putpixel((i, j), (r, g, b))
break
b = (b - b % 2) + int(code[count])
count += 1
if count == codelen:
img.putpixel((i, j), (r, g, b))
break
# 每3次循环表⽰⼀组RGB值被替换完毕,可以进⾏写⼊ if count % 3 == 0:
img.putpixel((i, j), (r, g, b))
img.save('F:/watermark_im.png')
im = Image.open("F:/girl.png")
watermark = Image.open("F:/watermark.png")
watermark = vert("RGB")
code = getcode(watermark)
encry(im, code)
def deEncry(img, length):
width = im.size[0]
height = im.size[1]
# 计数器
count = 0
wt = ""
for i in range(width):
for j in range(height):
# 获取像素点的值
rgb = pixel((i, j))
# 提取R通道的附加值
if count % 3 == 0:
count += 1
wt = wt + str(rgb[0] % 2)
if count == length:
break
# 提取G通道的附加值
if count % 3 == 1:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论