图片隐写术,即在图片里非显式地嵌入信息,实现图片隐写有很多方法。
第一种方式(来自维基百科):
要从上图中获取下图的信息,只需要将上图色彩空间中的每个二进制数都只保留最后两位(即相当于与3做按位与计算),再将亮度层乘以85即可。用matlab实现这个操作很简单,代码如下所示:
test=imread('D:/tree.png');
for i=1:3
for j=1:200
for k=1:200
test(j,k,i)=bitand(test(j,k,i),3);
end
end
end
test=rgb2hsv(test);
for i=1:200
for j=1:200
test(i,j,3)=test(i,j,3)*85;
end
end
test=hsv2rgb(test);
imshow(test)
要注意的是用imread读入的图片是RGB色彩空间上的,而要改变图片的亮度属性一般要在HSV空间上操作。一开始没有意识到这点的我一直都在更改B层,还好奇为什么总是显示出一只蓝色的猫……
作业要求代码用python实现于是又写了一遍,注意要实现以下代码要先安装pillow和skimage库,而skimage库又需要很多支持库,总之看着装就是了……
from PIL import Image
from skimage import color
import numpy as np
import matplotlib.pyplot as plt
import math
img=np.array(Image.open('D:/tree.png'))#打开图像并转化为数字矩阵
rows,cols,dims=img.shape
for i in range(0,dims):
for j in range(0,rows):
for k in range(0,cols):
img[j,k,i]=img[j,k,i]&3
imghsv=Image.fromarray(img)#矩阵转换为图像
imghsv=color.rgb2hsv(imghsv)
imghsv=np.array(imghsv)
for i in range(0,rows):
for j in range(0,cols):
imghsv[i,j,2]=imghsv[i,j,2]*85
imgrgb=color.hsv2rgb(imghsv)
plt.figure("after")
plt.imshow(imgrgb)
plt.axis('off')
plt.show()
要注意的是用PIL直接读入的图片并不是以矩阵格式保存的,因此要想方便对像素进行操作,可以先把图片转换为矩阵形式。而在矩阵形式下我没有找到便于从RGB颜色空间转换到HSV颜色空间的方法,于是只好又将矩阵转换回PIL的内部形式转换完颜色空间再接着操作……
第二种方式:
要想将一个水印图片加入到另一张图片里去,如果按照上面解码过程的反推则应该要舍弃掉水印图片的大多数信息。如果单纯地将水印图片色彩空间中每一个二进制数的八位的前六位都舍弃掉,那根本就看不出水印里是个啥了(╯’ - ')╯︵ ┻━┻
于是我想到的办法和 @雨诺寒雪 一样,干脆把八位的二进制数分成四块,每块分别加入到载体图片上去,这样能够保证当载体图片的长宽分别为水印图片的长宽的两倍的时候,水印图片的信息不会丢失。不过载体图片还是略有损伤,这点好像是不可避免的……?
上图是载体图片,下图是水印图片,图片来自网络。
依旧是先上matlab代码:
添加水印部分:
mark=imread('D:/mark.png');
image=imread('D:/image.png');
for i=1:3
for j=1:600
for k=1:600
image(j,k,i)=bitand(image(j,k,i),252);
end
end
end
for i=1:3
for j=1:300
for k=1:300
image(2*j-1,2*k-1,i)=image(2*j-1,2*k-1,i)+bitand(mark(j,k,i),192)/64;
image(2*j-1,2*k,i)=image(2*j-1,2*k,i)+bitand(mark(j,k,i),48)/16;
image(2*j,2*k-1,i)=image(2*j,2*k-1,i)+bitand(mark(j,k,i),12)/4;
image(2*j,2*k,i)=image(2*j,2*k,i)+bitand(mark(j,k,i),3);
end
end
end
提取水印部分:
#前部分同上,省略
result=uint8(zeros(300,300,3));
for i=1:3
for j=1:600
for k=1:600
image(j,k,i)=bitand(image(j,k,i),3);
end
end
end
for i=1:3
for j=1:300
for k=1:300
result(j,k,i)=image(2*j-1,2*k-1,i)*64+image(2*j-1,2*k,i)*16+image(2*j,2*k-1,i)*4+image(2*j,2*k,i);
end
end
end
imshow(result)
同样按照作业要求,再用python写一遍:
添加水印部分:
from PIL import Image
from skimage import color
import numpy as np
import matplotlib.pyplot as plt
import math
img=np.array(Image.open('D:/image.png'))
mark=np.array(Image.open('D:/mark.png'))
rows,cols,dims=mark.shape
for i in range(0,dims):
for j in range(0,rows*2):
for k in range(0,cols*2):
img[j,k,i]=img[j,k,i]&252
for i in range(0,dims):
for j in range(0,rows):
for k in range(0,cols):
img[2*j,2*k,i]=img[2*j,2*k,i]+(mark[j,k,i]&192)//64
img[2*j,2*k+1,i]=img[2*j,2*k+1,i]+(mark[j,k,i]&48)//16
img[2*j+1,2*k,i]=img[2*j+1,2*k,i]+(mark[j,k,i]&12)//4
img[2*j+1,2*k+1,i]=img[2*j+1,2*k+1,i]+(mark[j,k,i]&3)
img=Image.fromarray(img)
img.save('D:/image_with_mark.png')
提取水印部分:
from PIL import Image
from skimage import color
import numpy as np
import matplotlib.pyplot as plt
import math
imgwmark=np.array(Image.open('D:/image_with_mark.png'))
result=imgwmark
rows,cols,dims=imgwmark.shape
rows=rows//2
cols=cols//2
for i in range(0,dims):
for j in range(0,rows*2):
for k in range(0,cols*2):
imgwmark[j,k,i]=imgwmark[j,k,i]&3
for i in range(0,dims):
for j in range(0,rows):
for k in range(0,cols):
result[j,k,i]=imgwmark[2*j,2*k,i]*64+imgwmark[2*j,2*k+1,i]*16
+imgwmark[2*j+1,2*k,i]*4+imgwmark[2*j+1,2*k+1,i]
mark_get=Image.fromarray(result)
mark_get.save('D:/mark_get.png')
要注意的是,我使用的是python3,在python3中除法(/)的结果默认是float类型的,因此使用向下取整(//)来强制将结果变回整数。