Search code examples
python-3.ximagematrixpngdata-conversion

Conversion 2-dimensional rgb matrix to PNG Image with Python 3.6


I need to convert a 2-dimensional 24bit rgb- matrix with Python 3.6

[[rr11gg11bb11, rr12gg12bb12,..  ..,rr1mgg1mbb1m],
.
.
 [rri1gg11bb11, rr12ggi2bb12,..  ..,rr1mgg1mbb1m]
.
.
 [rrn1ggn1bbn1, rrn2ggn2bbn2,.. ..,rrmnggmnbbmn]]

into an image of the format tif, png and jpg.

I've also got the matrix in form of a file

image.bin.

From this binary file I can whatch the image with the help of a RAW Pixel Viewer:

http://rawpixels.net/ 

I have found the PIL library, but I didn't find any method to build an image object out of these data. How can I do that? I use Python 3.6.

Now I've tried the following:

im = Image.frombuffer('RGB', (self.width, self.height), self.matrix, 'raw', 
'RGB', 0, 1)              
logger.info("image object created.")
im.save('result.png')
logger.info("image saved as result.png")

But I get the message:

TypeError: a bytes-like object is required, not 'list'

Now I've found out that I must convert the object into a bytearray:

 npm = np.array(self.matrix)
 arr = bytearray(npm)
 print(arr)
 im = Image.frombuffer('RGB', (self.width, self.height), arr, 'raw', 
'RGB', 0, 1)        

The conversion just works, when I use 'L', or 'RGBA', but not with 'RGB'. Strangely in this case it refuses a bytearray object.


Solution

  • Updated Answer

    So, essentially, you don't have a Numpy array, but you have a list of lists wherein each element is a single 24-bit number that represents an RGB triplet RGB888.

    So, I can make a representation of your list like this:

    f=[ 
    [0xFFFFFF,0xFFFFFF,0xFFFFFF,0xFFFFFF,0x000000,0xFFFFFF,0xFFFFFF,0xFFFFFF,0xFFFFFF,0xFFFFFF],
    [0xFFFFFF,0xFFFFFF,0xFFFFFF,0x000000,0x000000,0xFFFFFF,0xFFFFFF,0xFFFFFF,0xFFFFFF,0xFFFFFF],
    [0xFFFFFF,0xFFFFFF,0x000000,0xFFFFFF,0x000000,0xFFFFFF,0xFFFFFF,0xFFFFFF,0xFFFFFF,0xFFFFFF],
    [0xFFFFFF,0xFFFFFF,0xFFFFFF,0xFFFFFF,0x000000,0xFFFFFF,0xFFFFFF,0xFFFFFF,0xFFFFFF,0xFFFFFF],
    [0xFFFFFF,0xFFFFFF,0xFFFFFF,0xFFFFFF,0x000000,0xFFFFFF,0xFFFFFF,0xFFFFFF,0xFFFFFF,0xFFFFFF]
    ] 
    

    So, I can make that into a Numpy array of uint32 elements like this:

    na = np.array(f).astype(np.uint32) 
    

    And then make an RGB array that PIL will like using:

    h,w = 5,10
    RGB=np.zeros((h,w,3),dtype=np.uint8)
    

    Then I just have to shift and copy the triplets into the right places:

    RGB[:,:,0] = na>>16            # Take red from top
    RGB[:,:,1] = (na>>8) & 0xff    # Take green from middle
    RGB[:,:,2] = na & 0xff         # Take blue from bottom
    

    Now I can make a PIL image from that RGB array and save it to disk:

    pIm = Image.fromarray(RGB).save('result.png')
    

    Original Answer

    Let's create a file of test data with ImageMagick. First as a PNG, just so you can see it:

    magick -size 640x480 gradient:magenta-yellow image.png
    

    enter image description here

    And now the same thing again, but this time as RGB888 to match your file:

    magick -size 640x480 gradient:magenta-yellow -depth 8 rgb:image.bin
    

    Now:

    from PIL import Image
    
    # Open the file and read contents into "data"
    with open('image.bin','rb') as f: 
        data = f.read() 
    
    # Convert that into PIL Image
    im = Image.frombuffer('RGB', (640, 480), data, 'raw', 'RGB', 0, 1)
    
    # Save
    im.save('result.png')
    

    You could equally let Numpy read the file and then convert the result to a PIL Image:

    import numpy as np
    
    im = Image.fromarray(np.fromfile('image.bin',dtype=np.uint8).reshape(480,640,3))
    

    If you want to check your binary file actually contains what you think it contains, you can also convert it to a PNG with ImageMagick to test it. Say you think the file is 640 pixels wide by 480 pixels tall and RGB888, you can ask ImageMagick to make it into a PNG with:

    magick -depth 8 -size 640x480 RGB:<YOURFILENAME> image.png
    

    Or, if you think it is a single 16-bit, single channel greyscale image with a 128 byte header that you want to ignore:

    magick -depth 16 -size 640x480+128 GRAY:<YOURFILENAME> image.png
    

    Keywords: Raw, raw image, binary file, image, image processing, Python, ImageMagick, convert.