前段时间我做了一个python脚本来存储图像中的数据,但它有一个小问题,我只是想知道是否有人可能会想到另一种方法。
一个非常基本的想法是它会腌制一些东西,然后在第一个版本中,它直接将ASCII数字写为像素(因为一切都在0到255之间)。这将导致图像看起来有点像电视噪音。
当写入实际图像时,它会检测到需要调整的每个像素的最小位数,因此人眼不会注意到它,它会分割数据并加上或减去几个位每个像素,第一个像素存储它正在使用的方法。然后我将URL作为文件存储在图像中,并通过使用第一个像素中给出的规则将URL中的原始图像与当前图像进行比较来反转它。
一点python伪代码,以防我没解释清楚:
original_image = (200, 200, 200, 100, 210, 255...)
stuff_to_store = "test"
#Convert anything into a list of bytes
data_numbers = [bin(ord(x)) for x in cPickle.dumps(stuff_to_store)]
#This is calculated by the code, but for now it's 2
bytes_per_pixel = 2
store_mode = 'subtract'
#Join the bytes and split them every 2nd character
new_bytes = "".join(data_bytes)
new_bytes_split = [new_bytes[i:i+bytes_per_pixel] for i in range(0, len(new_bytes), bytes_per_pixel)]
#Edit the pixels (by subtraction in this case)
pixel_data = []
for i in range(len(original_image)):
pixel_data = original_image[i] - int(new_bytes_split[i])
但是,由于脚本的整个要点是通过修改像素来存储内容,因此将原始图像URL存储为文件会让人觉得有点不耐烦。我想把URL存储为前几个像素,但每当图像不是灰色时,它最终都会有一条明显的线条。而且,这种方式非常低效,因为它需要两个图像才能正常工作,所以如果有人知道如何避免这样做,那就太好了。
原始代码是here如果有人感兴趣,我在学习编写文档之前就已经做过了,所以有点难以弄明白,现在就问这个,因为我打算重写它并且会喜欢做得更好。
答案 0 :(得分:3)
这是使用PIL进行图像处理,将数据嵌入每通道8位RGB图像文件中像素的每个颜色通道的最低有效位的一种方法。
下面的代码说明了Python中的位流处理。它的效率相当高(只要这样的操作可以在Python中有效),但它牺牲了可读性和效率。必要时使用简单。 :)
#! /usr/bin/env python
''' Steganography with PIL (really Pillow)
Encodes / decodes bits of a binary data file into the LSB of each color
value of each pixel of a non-palette-mapped image.
Written by PM 2Ring 2015.02.03
'''
import sys
import getopt
import struct
from PIL import Image
def readbits(bytes):
''' Generate single bits from bytearray '''
r = range(7, -1, -1)
for n in bytes:
for m in r:
yield (n>>m) & 1
def encode(image_bytes, mode, size, dname, oname):
print 'Encoding...'
with open(dname, 'rb') as dfile:
payload = bytearray(dfile.read())
#Prepend encoded data length to payload
datalen = len(payload)
print 'Data length:', datalen
#datalen = bytearray.fromhex(u'%06x' % datalen)
datalen = bytearray(struct.pack('>L', datalen)[1:])
payload = datalen + payload
databits = readbits(payload)
for i, b in enumerate(databits):
image_bytes[i] = (image_bytes[i] & 0xfe) | b
img = Image.frombytes(mode, size, str(image_bytes))
img.save(oname)
def bin8(i):
return bin(i)[2:].zfill(8)
bit_dict = dict((tuple(int(c) for c in bin8(i)), i) for i in xrange(256))
def decode_bytes(data):
return [bit_dict[t] for t in zip(*[iter(c&1 for c in data)] * 8)]
def decode(image_bytes, dname):
print 'Decoding...'
t = decode_bytes(image_bytes[:24])
datalen = (t[0] << 16) | (t[1] << 8) | t[2]
print 'Data length:', datalen
t = decode_bytes(image_bytes[24:24 + 8*datalen])
with open(dname, 'wb') as dfile:
dfile.write(str(bytearray(t)))
def process(iname, dname, oname):
with Image.open(iname) as img:
mode = img.mode
if mode == 'P':
raise ValueError, '%s is a palette-mapped image' % fname
size = img.size
image_bytes = bytearray(img.tobytes())
#del img
print 'Data capacity:', len(image_bytes) // 8 - 24
if oname:
encode(image_bytes, mode, size, dname, oname)
elif dname:
decode(image_bytes, dname)
def main():
#input image filename
iname = None
#data filename
dname = None
#output image filename
oname = None
def usage(msg=None):
s = msg + '\n\n' if msg else ''
s += '''Embed data into or extract data from the low-order bits of an image file.
Usage:
%s [-h] -i input_image [-d data_file] [-o output_image]
To encode, you must specify all 3 file names.
To decode, just specify the input image and the data file names.
If only the the input image is given, its capacity will be printed,
i.e., the maximum size (in bytes) of data that it can hold.
Uses PIL (Pillow) to read and write the image data.
Do NOT use lossy image formats for output, eg JPEG, or the data WILL get scrambled.
The program will abort if the input image is palette-mapped, as such images
are not suitable.
'''
print >>sys.stderr, s % sys.argv[0]
raise SystemExit, msg!=None
try:
opts, args = getopt.getopt(sys.argv[1:], "hi:d:o:")
except getopt.GetoptError, e:
usage(e.msg)
for o, a in opts:
if o == '-h': usage(None)
elif o == '-i': iname = a
elif o == '-d': dname = a
elif o == '-o': oname = a
if iname:
print 'Input image:', iname
else:
usage('No input image specified!')
if dname:
print 'Data file:', dname
if oname:
print 'Output image:', oname
process(iname, dname, oname)
if __name__ == '__main__':
main()