LSB图像数字水印嵌入算法(含python代码)

参考:

数字水印技术:LSB加密详解(附python代码)

理论知识

在学习这篇博客的内容之前,你需要了解如下内容
1)python图像处理 PIL的使用

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

watermark=watermark.convert("RGB")
print(watermark)

在这里插入图片描述
看到水印图片已经转换为RGB格式了

获取水印图片的每个像素的RGB值
代码

#获取水印图片的每一个像素值,i:指定要检查的像素点的逻辑X轴坐标。j:指定要检查的像素点的逻辑Y轴坐标。
for i in range(watermark.size[0]):
    for j in range(watermark.size[1]):
        #获取每个像素的RGB值
        rgb=watermark.getpixel((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值
        rgb=watermark.getpixel((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 = img.getpixel((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 = Image.new("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代码运行得到的,为了便于观看,Image.new()的时候宽和高增加了两个像素

代码

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值
            rgb = watermark.getpixel((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):
    # 计数器
    count = 0
    codelen = len(code)
    print (codelen)
    for i in range(img.size[0]):
        for j in range(img.size[1]):
            # 获取每个像素的RGB值
            data = img.getpixel((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 = watermark.convert("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 = img.getpixel((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


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 = Image.new("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()

img1 = Image.open("F:/watermark_im.png")
rgb_img1 = img1.convert('RGB')
length = 90576
wt = deEncry(rgb_img1, length)
showImage(wt)
  • 33
    点赞
  • 184
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Nefelibat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值