• 图片隐写之LSB(Least Significant Bit)原理及其代码实现


    1. 什么是隐写?

    隐写术是一门关于信息隐藏的技巧与科学,所谓信息隐藏指的是不让除预期的接收者之外的任何人知晓信息的传递事件或者信息的内容。隐写术的英文叫做Steganography,来源于特里特米乌斯的一本讲述密码学与隐写术的著作Steganographia,该书书名源于希腊语,意为“隐秘书写”。

    2. 什么是图片隐写?

    图片隐写术这项技术可以将秘密信息嵌入到图片媒介中而不损坏它的载体的质量。第三方既觉察不到秘密信息的存在,也不知道存在秘密信息。因此密钥、数字签名和私密信息都可以在开放的环境(如Internet或者内联网)中安全的传送。简单概括就是信息明明就在眼前,但是你却视而不见

    3. LSB隐写的原理

    在png图片的存储中,每个颜色表示需要有8bit,即有256种颜色,一共包含256的三次方个颜色,即16777216中颜色,人类的眼睛可以区分约1,000万种不同的颜色,剩下无法区分的颜色就有6777216。
    在这里插入图片描述
    那么什么是LSB呢,每个像素点的颜色可以用十进制0~255的数值表示,那么上图中的RGB(218,150,149)表示成二进制为:
    在这里插入图片描述
    LSB隐写就是修改RGB颜色分量的最低二进制位也就是最低有效位(LSB),而人类的眼睛不会注意到这前后的变化,我们仅对RGB中的最低位进行修改,如当前像素位点最后一位修改成1,1,0.则上述RGB变成218->219:
    在这里插入图片描述
    上述变化很难用肉眼察觉到,而且每一个像素位点携带了一位信息,那么我们可以利用八个字节的最低位存储一个比特信息,而该比特信息则可以转化为ASCII字符,从而达到隐写信息的目的。

    4. 如何实现LSB隐写?

    import numpy as np
    import PIL.Image as Image
    
    # 读取图片的像素信息
    picture = Image.open('./pic/pic.jpg')
    pic_data = np.array(picture)
    
    # 读取要隐写的文件,长度为4893
    with open('./pic/secret.py', encoding="utf-8") as file:
        secrets = file.read()
    
    # 将图片拷贝一份,作为最终的图片数据
    im_data = np.array(picture.copy()).ravel().tolist()
    
    def cover_lsb(bin_index, data):
        '''
        :param bin_index:  当前字符的ascii的二进制
        :param data: 取出数组像素的八个数值
        :return: LSB隐写后的字符
        '''
        res = []
        for i in range(8):
            data_i_bin = bin(data[i])[2:].zfill(8)
            if bin_index[i] == '0':
                data_i_bin = data_i_bin[0:7] + '0'
            elif bin_index[i] == '1':
                data_i_bin = data_i_bin[0:7] + '1'
            res.append(int(data_i_bin, 2))
        return res
    
    
    pic_idx = 0
    # 采用LSB隐写技术,横向取数据,每次取9个数据,改变8个像素最低位
    res_data = []
    for i in range(len(secrets)):
        # 拿到隐写文件的字符ascii数值, 并转换为二进制,填充成八位
        index = ord(secrets[i])
        bin_index = bin(index)[2:].zfill(8)
        # 对数据进行LSB隐写,替换操作
        res = cover_lsb(bin_index, im_data[pic_idx * 8: (pic_idx + 1) * 8])
        pic_idx += 1
        res_data += res
    # 对剩余未填充的数据进行补充填充,防止图像无法恢复
    res_data += im_data[pic_idx * 8:]
    
    # 将新生成的文件进行格式转换并保存,此处一定保存为压缩的png文件
    new_im_data = np.array(res_data).astype(np.uint8).reshape((pic_data.shape))
    res_im = Image.fromarray(new_im_data)
    res_im.save('./pic/res_encode.png')
    

    5. 如何实现隐写数据还原?

    import numpy as np
    import PIL.Image as Image
    
    # 打开隐写文件
    picture = Image.open('./pic/res_encode.png')
    pic_datas = np.array(picture).ravel().tolist()
    
    # 字符的长度为4893
    with open('./pic/secret.py', encoding="utf-8") as file:
        secrets = file.read()
    
    str_len = len(secrets)
    print('字符的长度为:', str_len)
    
    # 将图片拷贝一份,作为最终的图片数据
    im_data = np.array(picture.copy()).ravel().tolist()
    
    
    def lsb_decode(data):
        '''
        :param bin_index:  当前字符的ascii的二进制
        :param data: 取出数组像素的八个数值
        :return: LSB隐写后的字符
        '''
        str = ''
        for i in range(len(data)):
            print(bin(data[i])[2:])
            data_i_bin = bin(data[i])[2:][-1]
            str += data_i_bin
        return str
    
    
    pic_idx = 0
    # 采用LSB隐写技术,横向取数据,每次取9个数据,改变8个像素最低位
    res_data = []
    
    for i in range(len(secrets)):
        # 拿到第i个数据,转换成二进制
        data = im_data[i * 8: (i + 1) * 8]
        data_int = lsb_decode(data)
        # 找到最低位
        res_data.append(int(data_int, 2))
    
    # 将二进制数据转换成ASCII
    str_data = ''
    for i in res_data:
        temp = chr(i)
        str_data += temp
    print(str_data)
    

    6. 总结

    基本的LSB原理很简单,最容易实现,但是鲁棒性不佳,后续更好的隐藏信息,则会通过数字水印技术实现,后面文章再进行更新。

  • 相关阅读:
    基础架构之持续集成
    win10 配置VSCode 的C++环境
    阿里druid-spring-boot-starter 配置,个人整理以及遇到的问题(防止之后找不到)
    (科学:计算能量)编写程序,计算将水从初始温度加热到最终温度所需的能量。程序应该提示用户输入水的重量(以千克为单位),以及水的初始温度和最终温度。
    Node.js | 搭建后端服务器(含内置模块 http | url | querystring 的使用)
    优雅处理Golang中的异常
    汽车辅助系统
    大一新生HTML期末作业,网页制作作业——明星介绍易烊千玺网站HTML+CSS
    Linux 特权 SUID/SGID 的详解
    对话MySQL之父:一个优秀程序员可抵5个普通程序员
  • 原文地址:https://blog.csdn.net/l8947943/article/details/127088875