Search code examples
pythonwandraw-datamagickwand

How to create an image with magick wand from raw data string in Python


I have a raw data string, for example like this:

raw_data = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3300005CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3300002BFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9500B158DFFFF3300002BFFFFFFFFFFFFFFFFFFFFFFFFFFFFB200000003FFFF3300002BFFFFFFFFFFFFFFFFFFFFFFFFFFFFB100000002FFFF3300002BFFFFFFFFFFFFFFFFFFFFFFFFFFFFF643040B80FFFF3300002BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF3300002BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3300002BFFED843913051B59D1FFFFFFFFFFFF03000184FFFF3300002BAB0F00000000000007BBFFFFFFFFFF03000057FFFF330000080100000000000000001DF8FFFFFFFF03000057FFFF330000000042A9D4D08D0D000000ADFFFFFFFF03000057FFFF330000059DFFFFFFFFFFAA00000070FFFFFFFF03000057FFFF3300002BFFFFFFFFFFFFFA05000051FFFFFFFF03000057FFFF3300002BFFFFFFFFFFFFFF18000045FFFFFFFF03000057FFFF3300002BFFFFFFFFFFFFFF1B000043FFFFFFFF03000057FFFF3300002BFFFFFFFFFFFFFF1B000043FFFFFFFF03000057FFFF3300002BFFFFFFFFFFFFFF1B000043FFFFFFFF03000057"

I was wondering how it's possible to create a viewable .png out of it.

I tried using the Image class

with wand.image.Image (???) as img:

but I'm not sure what to put in the brackets. I tried file=raw_data, width=25,height=25,depth=8

Whatever I try it gives me an error.

Any ideas?


Solution

  • You will need to convert the string to bytes, and implement ImageMagick's MagickImportImagePixels C-API method.

    import binascii
    import ctypes
    
    from wand.api import library
    from wand.image import Image
    from wand.color import Color
    
    # Map C-API to Python
    # -------------------
    # magick-image.h
    # WandExport MagickBooleanType MagickImportImagePixels(MagickWand *wand,
    #   const ssize_t x,const ssize_t y,const size_t columns,const size_t rows,
    #   const char *map,const StorageType storage,const void *pixels)
    library.MagickImportImagePixels.argtypes = (ctypes.c_void_p,
                                                ctypes.c_ssize_t,
                                                ctypes.c_ssize_t,
                                                ctypes.c_size_t,
                                                ctypes.c_size_t,
                                                ctypes.c_char_p,
                                                ctypes.c_int,
                                                ctypes.c_void_p)
    
    
    # Map enum StorageType
    StorageType = ('undefined', 'char', 'double', 'float',
                   'integer', 'long', 'quantum', 'short')
    
    # Extend wand.image.Image to implement import_pixels method
    class MyImage(Image):
        def import_pixels(self, blob, pixel_format='RGBA', pixel_size='char'):
            storage_type = StorageType.index(pixel_size)
            library.MagickImportImagePixels(self.wand,
                                            0,
                                            0,
                                            self.width,
                                            self.height,
                                            pixel_format.encode(),
                                            storage_type,
                                            blob)
    # Usage
    raw_data_string = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3300005CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3300002BFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9500B158DFFFF3300002BFFFFFFFFFFFFFFFFFFFFFFFFFFFFB200000003FFFF3300002BFFFFFFFFFFFFFFFFFFFFFFFFFFFFB100000002FFFF3300002BFFFFFFFFFFFFFFFFFFFFFFFFFFFFF643040B80FFFF3300002BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF3300002BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3300002BFFED843913051B59D1FFFFFFFFFFFF03000184FFFF3300002BAB0F00000000000007BBFFFFFFFFFF03000057FFFF330000080100000000000000001DF8FFFFFFFF03000057FFFF330000000042A9D4D08D0D000000ADFFFFFFFF03000057FFFF330000059DFFFFFFFFFFAA00000070FFFFFFFF03000057FFFF3300002BFFFFFFFFFFFFFA05000051FFFFFFFF03000057FFFF3300002BFFFFFFFFFFFFFF18000045FFFFFFFF03000057FFFF3300002BFFFFFFFFFFFFFF1B000043FFFFFFFF03000057FFFF3300002BFFFFFFFFFFFFFF1B000043FFFFFFFF03000057FFFF3300002BFFFFFFFFFFFFFF1B000043FFFFFFFF03000057'
    raw_data_bytes = binascii.unhexlify(raw_data_string)
    
    with MyImage(width=25, height=25, background=Color('WHITE')) as img:
        img.import_pixels(raw_data_bytes, pixel_format='R', pixel_size='char')
        img.save(filename='output.png')
    

    output.png

    As pointed out in the comments, you will be responsible for knowing / defining the channel layout (i.e. RGBA, BGR, ARBG &tc), the data-type size (i.e. char/short/int/long, or float/double), data-stream layout (like interlacing), and of course the actual width/height of the expected image.