Thursday, January 21, 2021

Capturing a YUV4:2:2 pixel format image from the USB port, and displaying it as RGB.

 I'm troubleshooting why I can't receive the RGB data stream from the Framos Realsense D435e.  I decided to take a step back to the Intel Realsense D435 USB, and verify the pixel format of the data stream.  



The Realsense software indicates there are multiple RGB camera stream formats:  YUYV, RGB8, RGBA8, etc.


Each of these streams should use a pixel format that is different from the others.  The data in the communication packet should be arranged based on the pixel format.  YUYV is a 16 bit per pixel format that contains the Luminance (brightness) of every pixel, and only half of the Chroma (color) data for each pixel.  The rest of the Chroma data is supplied in the next pixel. So when using YUYV you need to get pixel pairs.  And to make things a little more complicated, you need to convert the YUYV data to RGB data before you get the full color image.  

But the goal of the test was to see what pixel formats can come from the camera.  Turn out, even though there are multiple formats listed, the data in the packet is still only YUYV.   Selecting RGBA8 should require more bytes per pixel, and they should be in a different pixel format, but the data packets all look the same as YUYV. 

I discovered this using Wireshark's USBPcap feature.  The length of the image frame packets did not change when a different stream was selected.


WireShark reports that the image data packet is 115475 bytes long.  I exported the data by right clicking the hex and selecting "Copy as Escaped string", then pasting into notepad.  


The actual image data starts at 0x0113 or 275 bytes into the data packet.

There are 27 bytes of USB packet data, followed by 248 bytes of image header data.  I did not find a way of deciphering this image data header.  I'm sure it's in a standard out there somewhere, but ain't nobody got time for that.  I just calculated the number of bytes that I should get based on 320 x 180 pixels, and 2 bytes per pixel (115200 bytes)  So 115475 - 115200 = 275 should be the start.  And it is!

The YUV format is YUYV 4:2:2  (also this might be called YUY2).

A Microsoft developer doc describes the byte array as:  

Y0U0 Y1V0  Y2U1 Y3V1   ... where there is a Y byte for every pixel, and a 'U' (Chroma blue or Cb) byte for the first (odd) pixels, followed by a 'V' (Chroma red or Cr) byte for the second (even) pixels.

To convert YUV to RGB the YUV4:2:2 is converted to YUV4:4:4 first.  That just means you need Y U and V bytes for every pixel.  If a pixel is missing V, just copy the V from the next even number pixel. If it is missing a U, just copy the U value from the previous pixel. 

Then do some math, maybe with floating point numbers like this:

Make new variables C D & E

C = Y - 16

D = U - 128

E = V - 128

the formulas to convert YUV to RGB is:

R = clip( round( 1.164383 * C                   + 1.596027 * E  ) )

G = clip( round( 1.164383 * C - (0.391762 * D) - (0.812968 * E) ) )

B = clip( round( 1.164383 * C +  2.017232 * D                   ) )

*Clip just means limit the result back to a value from 0 to 255.  You can cast to an unsigned 8bit number to do this.

And here is the result in all it's amazing techno color:


What's up with the green box?  Well just ignore that.  Wire shark only captures 65535 bytes per packet.


Here are the intermediate steps:

Get the Luma Y and Chroma Cb Cr channels for all pixels:

Luma

Chroma Cb



Chroma Cr


Using the algorithm above, convert the pixels to R G B channels.
R



G


B


Wednesday, January 6, 2021

Balluff BVS Serial Command format

 

Balluff BVS used as a bar code reader

The Balluff BVS-ID-3-005-E is a vision sensor with barcode reader tools.  The sensor can be triggered using an RS-232 serial command.


Balluff BVS001R  BVS-ID-3-005-E  Vision sensor and barcode reader.

I had some trouble figuring out that the serial command and the ethernet command have slightly different syntax.

The RS-232 serial command to trigger the camera is:

TRIGGER<0x00>

  where <0x00> is a byte with the value of zero.

The response is:

<0x02>OK&ACK<0x0D><0x0A>        

  byte value 02 followed by ASCII ending with Carriage Return and Line Feed bytes 13 (0D) and 10 (0A)



When using ethernet the trigger command is:

TRIGGER

No Null byte is needed.

The response is:

OK&ACK<0x00>

The null byte is part of the response.


Why are these different?  I spent about 5 hours troubleshooting this.


Edit 20200121:

And get this, the PLC that needs to send the trigger command can't automatically send the <0x00> null byte at the end of the TRIGGER string.  The PLC's string and serial com library treat the <0x00> as the null termination character for the string.  So it doesn't send the the <0x00> byte.  We tried to concatenate a '$00' in structured text (which is the null byte <0x00>) and it just ignores it.  Ultimately Roger, the programmer of the PLC, had to overwrite the serial communication "Data to Send" register, from 7 to 8 bytes.   Sysmax studio may require the overwrite.