小编典典

比较原始图像和可能不需要原始图像的已编辑图像的另一种方法

python

前一段时间,我做了一个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存储为前几个像素,但是只要图像不是灰色,它就会以明显的一行结尾。同样,这种方式效率极低,因为它需要两个图像才能正常工作,因此如果有人对如何避免这种想法有所了解,那就太好了。

如果有人感兴趣的话,这里是原始代码,我在学习编写文档之前就做了,所以很难弄清楚,现在只是问这个问题,因为我打算重写它,并且希望做得更好。


阅读 342

收藏
2021-01-20

共1个答案

小编典典

这是一种使用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()
2021-01-20