Alternative method to compare original image with edited one that may not need the original

后端 未结 1 699
情深已故
情深已故 2020-12-22 04:27

A while back I did a python script to store data in images, however it has a small problem and I\'m just wondering if anyone might be able to think of an alternative method.

相关标签:
1条回答
  • 2020-12-22 04:43

    Here's one way to embed data into the least significant bit of each colour channel of the pixels in a 8 bit per channel RGB image file, using PIL to do the image handling.

    The code below illustrates bit stream handling in Python. It's reasonably efficient (as far as such operations can be efficient in Python), but it sacrifices efficiency for readability & simplicity of use when necessary. :)

    #! /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()
    
    0 讨论(0)
提交回复
热议问题