我发现的代码旨在将秘密消息(文本文件)隐藏在一个从固定文件路径中检索到的密写图像中。
我想优化代码,以便可以将秘密消息(文本文件)隐藏在多个密写图像中,这些图像再次将从固定文件路径中检索。
我认为必须创建一个数组来存储图像(像素)和通道(RGB)的宽度和高度。
我希望当最低有效位隐写术使用了一定数量的像素(例如,使用了1000个像素)时,程序将跳至另一幅图像并执行相同的操作,直到所有秘密消息(存储在文本文件中)可以隐藏在这些图像中。
我还认为,为了执行此操作,必须创建另一个数组才能存储秘密消息(文本文件)的位。
此代码已经配置为可以处理PNG图像文件格式,以避免在压缩时出错。
预期结果是该代码将能够在多个图像中编码消息并从这些图像中解码消息,以便再次显示秘密消息。你能帮我吗?
请澄清一下,我已经4年没有接触python了,所以基本上我主要缺少语法
谢谢
import cv2
import docopt
import numpy as np
class SteganographyException(Exception):
pass
class LSBSteg():
def __init__(self, im):
self.image = im
self.height, self.width, self.nbchannels = im.shape
self.size = self.width * self.height
self.maskONEValues = [1,2,4,8,16,32,64,128]
#Mask used to put one ex:1->00000001, 2->00000010 .. associated with OR bitwise
self.maskONE = self.maskONEValues.pop(0) #Will be used to do bitwise operations
self.maskZEROValues = [254,253,251,247,239,223,191,127]
#Mak used to put zero ex:254->11111110, 253->11111101 .. associated with AND bitwise
self.maskZERO = self.maskZEROValues.pop(0)
self.curwidth = 0 # Current width position
self.curheight = 0 # Current height position
self.curchan = 0 # Current channel position
def put_binary_value(self, bits): #Put the bits in the image
for c in bits:
val = list(self.image[self.curheight,self.curwidth]) #Get the pixel value as a list
if int(c) == 1:
val[self.curchan] = int(val[self.curchan]) | self.maskONE #OR with maskONE
else:
val[self.curchan] = int(val[self.curchan]) & self.maskZERO #AND with maskZERO
self.image[self.curheight,self.curwidth] = tuple(val)
self.next_slot() #Move "cursor" to the next space
def next_slot(self):#Move to the next slot were information can be taken or put
if self.curchan == self.nbchannels-1: #Next Space is the following channel
self.curchan = 0
if self.curwidth == self.width-1: #Or the first channel of the next pixel of the same line
self.curwidth = 0
if self.curheight == self.height-1:#Or the first channel of the first pixel of the next line
self.curheight = 0
if self.maskONE == 128: #Mask 1000000, so the last mask
raise SteganographyException("No available slot remaining (image filled)")
else: #Or instead of using the first bit start using the second and so on..
self.maskONE = self.maskONEValues.pop(0)
self.maskZERO = self.maskZEROValues.pop(0)
else:
self.curheight +=1
else:
self.curwidth +=1
else:
self.curchan +=1
def read_bit(self): #Read a single bit int the image
val = self.image[self.curheight,self.curwidth][self.curchan]
val = int(val) & self.maskONE
self.next_slot()
if val > 0:
return "1"
else:
return "0"
def read_byte(self):
return self.read_bits(8)
def read_bits(self, nb): #Read the given number of bits
bits = ""
for i in range(nb):
bits += self.read_bit()
return bits
def byteValue(self, val):
return self.binary_value(val, 8)
def binary_value(self, val, bitsize): #Return the binary value of an int as a byte
binval = bin(val)[2:]
if len(binval) > bitsize:
raise SteganographyException("binary value larger than the expected size")
while len(binval) < bitsize:
binval = "0"+binval
return binval
def encode_text(self, txt):
l = len(txt)
binl = self.binary_value(l, 16) #Length coded on 2 bytes so the text size can be up to 65536 bytes long
self.put_binary_value(binl) #Put text length coded on 4 bytes
for char in txt: #And put all the chars
c = ord(char)
self.put_binary_value(self.byteValue(c))
return self.image
def decode_text(self):
ls = self.read_bits(16) #Read the text size in bytes
l = int(ls,2)
i = 0
unhideTxt = ""
while i < l: #Read all bytes of the text
tmp = self.read_byte() #So one byte
i += 1
unhideTxt += chr(int(tmp,2)) #Every chars concatenated to str
return unhideTxt
def encode_image(self, imtohide):
w = imtohide.width
h = imtohide.height
if self.width*self.height*self.nbchannels < w*h*imtohide.channels:
raise SteganographyException("Carrier image not big enough to hold all the datas to steganography")
binw = self.binary_value(w, 16) #Width coded on to byte so width up to 65536
binh = self.binary_value(h, 16)
self.put_binary_value(binw) #Put width
self.put_binary_value(binh) #Put height
for h in range(imtohide.height): #Iterate the hole image to put every pixel values
for w in range(imtohide.width):
for chan in range(imtohide.channels):
val = imtohide[h,w][chan]
self.put_binary_value(self.byteValue(int(val)))
return self.image
def decode_image(self):
width = int(self.read_bits(16),2) #Read 16bits and convert it in int
height = int(self.read_bits(16),2)
unhideimg = np.zeros((width,height, 3), np.uint8) #Create an image in which we will put all the pixels read
for h in range(height):
for w in range(width):
for chan in range(unhideimg.channels):
val = list(unhideimg[h,w])
val[chan] = int(self.read_byte(),2) #Read the value
unhideimg[h,w] = tuple(val)
return unhideimg
def encode_binary(self, data):
l = len(data)
if self.width*self.height*self.nbchannels < l+64:
raise SteganographyException("Carrier image not big enough to hold all the datas to steganography")
self.put_binary_value(self.binary_value(l, 64))
for byte in data:
byte = byte if isinstance(byte, int) else ord(byte) # Compat py2/py3
self.put_binary_value(self.byteValue(byte))
return self.image
def decode_binary(self):
l = int(self.read_bits(64), 2)
output = b""
for i in range(l):
output += chr(int(self.read_byte(),2)).encode("utf-8")
return output
def main():
args = docopt.docopt(__doc__, version="0.2")
in_f = args["--in"]
out_f = args["--out"]
in_img = cv2.imread(in_f)
steg = LSBSteg(in_img)
if args['encode']:
data = open(args["--file"], "rb").read()
res = steg.encode_binary(data)
cv2.imwrite(out_f, res)
elif args["decode"]:
raw = steg.decode_binary()
with open(out_f, "wb") as f:
f.write(raw)
if __name__=="__main__":
main()