pythonarrayslistimagepython-imaging-library

image from uint16 array from serial port


I'm getting values uint16 (RGB565) from serial port. The array contains 76800 uint16 values from an image 320x240. Right now I'm just sending the test bar image and I'm getting this image:

enter image description here

which it seems to be generated multiple times by the python script I used, which is this:

def TakePicture():
    xdim = 320
    ydim = 240
    i=0
    RGB_list.pop(0)
    im = Image.new("RGB",(xdim,ydim))
    for k in range(76800):
        value1=int(RGB_list[k])
        RGB_values.append(value1)
    for y in range(ydim):
        for x in range(xdim):
            px = RGB_values[i]
            i = i+1
            im.putpixel((x,y),((px&0xF800) >> 8, (px&0x07E0) >> 3, (px&0x001F) << 3))  
    im.save('image.png')  
    im.show()    

def main():
    try:
        serialPort=serial.Serial(port='/dev/ttyUSB1', baudrate=115200,bytesize=8,parity=serial.PARITY_NONE,stopbits=1,timeout=0)
    except:
        print("unable to open port")
    while(1):
        if(serialPort.in_waiting>0):
            incoming=serialPort.readline().decode('ascii','ignore')
            incoming=incoming.rstrip()
            if('ET' not in incoming):
                if(not incoming):
                    incoming='0'
                print(incoming)
                RGB_list.append(incoming)
            else:
                with open('serial_python.txt','w',newline='',encoding='utf-8') as txtfile:
                    writer=csv.writer(txtfile)
                    for i in RGB_list:
                        writer.writerow([i])
                TakePicture()

I'm using PIL to take every value from the array and generate a pixel, but why is not wroking? I take this values through serial port and convert those values to integer and fill another array to later generated every pixel.

At first I though it was the encoding, but then I realize that it seems something related with the way the image is generated. I found something related to this in the forum and many used np arrays.


Solution

  • Following on from my comments - you shouldn't treat a binary image as lines of text delimited by linefeeds, because the image is binary and any pixel could appear exactly the same and indistinguishable from a linefeed. Remember readline() reads bytes till it finds linefeeds, so you need to use read() and specify the number of bytes in a frame and use write() to send the data from the other end.

    Let's make an RGB565 file on disk with ImageMagick with your dimensions:

    magick logo: -resize 320x240\! -flip -define bmp:subtype=RGB565 bmp2:- | tail -c $((320*240*2)) > rgb565.bin
    

    That image looks like this - although StackOverflow cannot display RGB565 so this is actually a PNG:

    enter image description here

    Now your code would look like this:

    #!/usr/bin/env python3
    
    import numpy as np
    from PIL import Image
    from pathlib import Path
    
    # Define parameters
    w, h = 320, 240
    
    # Load image data from file - you would read this many bytes from Serial instead
    buffer = Path('rgb565.bin').read_bytes()
    
    # Make buffer into h x w Numpy array of uint16 and split out channels by shifting
    rgb565 = np.frombuffer(buffer, dtype=np.uint16).reshape((h,w))
    R = (rgb565 >> 8) & 0xf8
    G = (rgb565 >> 3) & 0xfc
    B = (rgb565 << 3)
    
    # Make 3 arrays of h x w into one RGB888 array of h x w x 3
    RGB888 = np.dstack((R, G, B)).astype(np.uint8)
    Image.fromarray(RGB888).show()