【小聪明】图片消失在另一张图片里

概要

 

偶然看到一篇文章伪·黑科技】基于像素微调实现的文字隐写术,受此启发,能把文字转成二进制,那么图片像素转成二进制岂不更容易,于是我就写了一个隐藏图片的代码,也就有了这篇随笔。

但是刚写出来不完美,有几点需要改进的地方,现在还没有想通,做此记录,以后有机会再慢慢改进:

  • python 包 matplotlib 保存数组构成的图片时,会自动加个透明层,即不是我想要的 RGB 格式,而是 RGBA 格式。why,why,不得不转向其它包
  • 包存图片格式时,一般的数组有可能格式不对,如果遇到错误,就保存为 unit8 格式

 


效果图

 

提示:这是我截的图,不是原图,像素可能不满足要求,做实验可用自己的图片

图 1:要被隐藏的图片

图 2:隐藏之后的图片效果图


代码

 

基本的原理就是利用二进制微调宿主像素。代码中的解释已经很清楚了,在此不再多述。

# -*- coding: utf-8 -*-
"""
Created on Thu Mar 29 11:47:43 2018

@author: zhoukui
"""
import numpy as np
from PIL import Image
import matplotlib.image as mpimg

import sys

def image2Bin(imageFile):
    image = mpimg.imread(imageFile)
    imageArr = image.ravel()
    
    binList = list((map(bin,imageArr)))
    
    for i, item in enumerate(binList):  # 把数字全部转换为 8 位的 0 1 数据 
        binList[i] = item[2:]  # 把二进制标志 '0b' 去掉
        binList[i] = '0'*(8 - len(binList[i])) + binList[i]
    
    pixes_x, pixes_y, _ = image.shape # 把原始图片像素保存一下
    
    binX = bin(pixes_x)[2:]
    binY = bin(pixes_y)[2:]
    binList.insert(0,'0'*(16-len(binX)) + binX)
    binList.insert(1,'0'*(16-len(binY)) + binY)
    # 前两个 16 位表示的是像素长宽,像素长宽相乘再乘以 3 得到后面 8 位的数目
    return binList  

def showImage(imageArr, imageTitle):
    
    im = Image.fromarray(imageArr)
    im.save(imageTitle)
    im.show()
    

    
def oneHide(pix, pixValue):
    if pix == '0':  # 使图片像素变为偶数
         if pixValue == 255:
              pixValue = 244
         elif pixValue % 2 == 1:
              pixValue += 1 
    elif pixValue % 2 == 0:
         pixValue += 1
    
    return pixValue  # 返回去改变这像素值     

def hiding(binList, hidingImageFile):
    image = mpimg.imread(hidingImageFile)
    #print(image.shape)
    imageArr = image.ravel()
    
    # 每一个像素隐藏一个 0 或者 1
    # 判断是否藏得下
    if (len(binList)*8+16 > len(imageArr)):
        print("藏不下,换一张宿主更大的图片或把要藏的图片调小")
    else:
        flag = 0  # 标记藏到哪了
        # 先藏前两个 16 位的
        for i in range(2):
            for j in range(16):
                imageArr[flag] = oneHide(binList[i][j], imageArr[flag])
                flag += 1
        
        # 然后藏剩下的
        for _, item in enumerate(binList[2:]): 
            if flag % 8640 == 0:
                sys.stdout.write('\r complete percent ----->:%.0f%%' % (flag/82976.))                
            for j in range(8):
                imageArr[flag] = oneHide(item[j], imageArr[flag])
                flag += 1
        
        sys.stdout.flush()  
    #print(imageArr[:8])
    imageArr = imageArr.reshape(image.shape)  #.astype(np.uint8) 
    
    return imageArr      

def imerging(imageFile):
    image = mpimg.imread(imageFile)  # 这种读取图片直接是 Ndarray
    
    imageArr = image.ravel()
    
    # 先把隐藏图片的大小解析出来
    tempList = [str(i % 2) for i in imageArr[:32]] 
    
    pixesX = int(''.join(tempList[:16]), 2)
    pixesY = int(''.join(tempList[16:]), 2)
    #print(pixesX, pixesY)
    
    # 把隐藏图片的像素解析出来
    temp2List = [str(i % 2) for i in imageArr[32 : 32 + pixesX*pixesY*3*8]]
    
    tempArr = np.zeros(pixesX*pixesY*3,)

    for i in range(pixesX*pixesY*3):
        
        tempArr[i] = int(''.join(temp2List[8*i:8*(i+1)]), 2)
    
    #sys.stdout.flush() 
    
    tempArr = tempArr.reshape(pixesX, pixesY, 3).astype(np.uint8) # 不加会模糊   
    showImage(tempArr, "image_after_decreption.bmp") 

                
def imageEncre(hidedImageFile, hidingImageFile):
    # 可视化隐藏前图片
    #imageArrBefore = mpimg.imread(hidingImageFile)
    #showImage(imageArrBefore, "image before hiding")
    
    # 把要加密的图片转换成 0  1 数字
    print("加密前准备工作...")
    binList = image2Bin(hidedImageFile) 
    
    # 接下来把 0 与 1 藏进另一张图片
    print("开始加密...")
    imageArrAfter = hiding(binList, hidingImageFile)
    #print(imageArrAfter[:8,:,:])
    showImage(imageArrAfter, "image_after_encreption.bmp")

def imageDecre(imageFile):
    print("解码中...")
    imerging(imageFile)
    
    
if __name__ == "__main__":  
    #imageEncre("hided.jpg", "hiding.jpg") 
    imageDecre("image_after_encreption.bmp")
posted @ 2018-04-01 20:04  小鱼吻水  阅读(406)  评论(0编辑  收藏  举报