RGB565 to RGB888 Color Conversion

While working with Charlotte Latin FabLab students this year, we came across an issue. Alex was using a camera module for her Arduino project (ArduCam) that gave RGB565 formatted pixel data but she needed to convert it to RGB888. Though there are tons of posts on stackExchange about how to do it, I couldn’t find a simplified broken-down explanation of how it works to point her to, so I decided to write one.

RGB565 means there are 5-bits of data for the Red component of the pixel, 6-bits for green and 5-bits for the blue.  It looks like this:
pixel value =
RED= R4, R3, R2, R1, R0,
GREEN= G5, G4, G3, G2, G1, G0,  
BLUE= B4, B3, B2, B1, B0

But Remember RGB565 is stored as two separate values (VL and VH from the example code for the camera we used), with the low value on the left and high value on the right.
VL = G2, G1, G0, B4, B3, B2, B1, B0     VH = R4, R3, R2, R1, R0, G5, G4, G3

Some clever bitmasking can strip out each component color and place them into individual variables. We’ll AND the bits of the values we want from VL and VH with ‘1’ or ‘0’ depending on which we want at a given time.

Of course this has to be done for each and every pixel, and she had 320 x 240 in her original image.

This is a simple nested for loop that already used in the example code to read the data from the camera

for (int i=0; i<240; i++){

  
   for (int j=0; j<320; j++){
  
    //Convert the RGB 565 to RGB 888 of the pixel in the bitmap and store it as PIXEL data type in the new larger array.

  
    //Step A convert to the new PIXELS format

  
    ///Step B scale from 565 to 888

  
    }//end for j

  
 }//end for i

STEP A: Convert the color to a new pixel format.

Based on some example code, it’s best to create a structure to contain the R, G and B values:

typedef struct { 
unsigned char blue; 
unsigned char green; 
unsigned char red; 
} PIXELS; 

Then we need a large array to store the pixels of the new image:

PIXELS bigPIXELS [12] [12]; //the resulting RGB888 picture pixels are stored here

Then you can convert each pixel by using logic operations and bit shifting.

/*Blue is the easiest to see how this works so we'll do it first.
  
 The values for Blue is stored in VL So for example if VL = 1011  0101( blue = 21 in decimal) 
 to get the blue values all alone, we can AND them with 1s and AND the green bits with 0s 
 leaving you with an 8-bit number with only the blue data in the right spot. The result 
 is: 0001  0101  or 21 in decimal. 
*/
  bigPIXELS [i][j].blue =    ( VL & 0b00011111 ) ; //0x1F
 
 

/*Red is only slightly more complicated. We first get bits for red alone, then shift them 
 to the decimal point The 0b11111000 gives you only red data as before, but the digits 
 aren't in the right place. 
 
 There are place-holding 0s that make the value of red too big: 
 For example is VH = 1101  0110  The value of red is 26 in decimal here. ANDing as before
 would give you only 1101  0000, but if we convert this number to decimal, it =208, which 
 isn't right at all. Shifting to the right by 3 (>>3) moves the red value down to the 
 decimal point and gives you 0001  1010 = 26 in decimal in 8-bits
*/
  
bigPIXELS [i][j].red =   ((VH & 0b11111000) >> 3); //0xF8
 

/*Green is the most complicated as it has components in each VL and VH so we'll AND the 
 bits with 1s and then shift them to the right places, then we need to OR them to the correct spot
  
 VL = G2, G1, G0, B4, B3, B2, B1, B0  so to get only green values
 */ 

// since we don't use this variable for anything else, we can just dump the result of this operation back into VL
 VL&= 0b11100000 ;  // 0xE0  

 // VH = R4, R3, R2, R1, R0, G5, G4, G3  so to get only green values  
 VH &= 0b00000111; //0x07
  
 //Now shift the bits to the right places and OR them together to glue them into one number
bigPIXELS [i][j].green =  (uint8_t(VL) >> 5) | (VH << 3); //Shift lower 3 bits in VL to the decimal point, and shift the upper 3-bits in VH to their correct positions.

Great! Now we have our RGB values by themselves, now let’s let them stretch their arms to take up the full 8-bits that’s now available to them.

Step B: Converting from one colorspace to another is simply a function of scaling. You’ve already done this a lot actually. Any time you’ve read an analog input, then used that to control an analog output on arduino you’ve done it. Imagine a knob on the Arduino’s analog input. Analog inputs can read values from 0-1023 (10-bits) where 100% is = 1023. Imagine a single red pixel as an LED on an analog output. It can only take values between 0 and 255 (8-bits) where 255 is 100% brightness of the LED.

You can do the same thing here. In a 5-bit number system, 100% is when all bits are 1’s so it is 11111. This needs to be converted to an 8-bit system where 100% is = 1111  1111.

You use Arduino’s map() function to map the values from one scale(0-1023) to the other (the LED’s 0-255), however this is very inefficient and slow.

Since you know what the numbers are going to be for the scaling calculation, and they won’t ever change you can pre-calculate them for your code. You’re scaling from 5-bits to 8-bits for red and blue which is 0xFF / 0x1F = 255 / 31 = 8. For green you start with 6-bits and convert to 8 which is 0xFF / 0x03 = 255 / 63 = 4.  You could multiply these hardcoded numbers to scale red *= 8; green *=4; blue*=8; however since these are powers of 2, there’s a trick to make this code faster. Simply bit shift left. For each bit you shift, it is equal to a power of two so the code would be red and blue << 3 and green << by 2.

//If you want to see the RGB 565 values print them here, or comment out if you don't need them
Serial.print(" RGB565 =");
Serial.write(bigPIXELS [i][j].red);
Serial.write(bigPIXELS [i][j].green);
Serial.write(bigPIXELS [i][j].blue);

bigPIXELS[i][j].blue = bigPIXELS[i][j].blue << 3;//blue and red were 5-bits

bigPIXELS[i][j].red=  bigPIXELS[i][j].red<<3;

bigPIXELS[i][j].green =  bigPIXELS[i][j].green <<2;//Remember that green has 6 pixels to begin with so it only shifts 2 places

Now you can do with these as you please. Add a BMP header and print them to the serial port to be able to view this BMP image on your computer.

Serial.print(" RGB888 R= ");
Serial.write(bigPIXELS [i][j].red);
Serial.write(bigPIXELS [i][j].green);
Serial.write(bigPIXELS [i][j].blue);

 

Cheapest and Fastest COVID-19 Face Shield

I’m working with some folks on a project to 3D print a ton of face shields for Charlotte hospitals during the COVID-19 crisis, but I figured there has to be a faster and cheaper way. I worked something out with parts I had in the garage and it costs pennies to make. it even adjusts so you can lift it up or lower it down over your face.

What you’ll need:

Drill a hole with a drill bit or a utility knife (drill with the knife by spinning it, don’t cu ta hole or it will tear and be weak)

It’s adjustable.

 

Make 3D Pictures with Our Free Web App

If you didn’t already know, Jess recently wrote a great book full of science experiments for parents and kids to do at home. If you haven’t heard about it, check it out here. All bias aside, it really is the best science experiments book I’ve ever read. The projects are really cool and engaging. This book is written for ages 8 to 12 but there are projects that you would enjoy at any age. Everything is explained in very easy to understand terms from double-pendulums and chaotic motion to making holograms by hand.

One of the really cool projects in the book is making your own 3D glasses with stuff laying around your house. I wrote a little app to help you play around with depth and perspective when drawing your own 3D images. Check out the examples below.  You can adjust the depth and lightness of each line you draw and you can actually edit lines you’ve previously drawn. Click here to play with the 3D anaglyph maker.

Here’s an example of the type of results you can get with the app. You can tell I am no artist… but you can draw with the mouse and adjust the depth and darkness of the lines, then export the image to your computer. Feel free to upload your pic to imgur and paste a link in the comments to show us what you create!

 

 

 

 

Digital Caliper Power Cable

The worse thing about digital calipers are those stupid-small batteries that don’t last 5 minutes and don’t stay in too well.  Sometimes you lose the battery cover then you’re really screwed.  To find a workable solution to this issue, I googles and saw a bunch of 3D printed solutions for data cables on these calipers.  I decided that I didn’t care much for the data part, but to 3D print a connector just for this seemed a waste of time. Sure, you can find some Dupont connectors to 3D print, but is it worth it? I set about a different way.

I happen to have a large number of servo connector cables from my quadcopters days. It just so happens that one of these female 3-pin dupont connectors fit perfectly in the caliper’s data slot. And if you used male wires, bent them backwards and shoved them in the data connector slot, it holds tight and provides a great connection for the power rails.

First, get a stabby thing and take the dupont connectors off some single-wire connectors (red and black are a good choice for colors).

tools

Capture

Take out all 3 female connectors form a servo cable and shove the male wires on the two ends of the servo connector as shown. Next you want to be careful and bend the male wires around the edge. You have to make sure your orientation is correct for the pins as they need to line up red with the (+) terminal and black with the Gnd otherwise you’ll be making a new cable—ask me how I know…   I did this in 2 stages since the pins are fragile. I slowly bent them against a tabletop to 90 degrees. Assessed their quality and angles and bent the remaining excess to 180 degrees

bend90degrees 

 

When done you should have something that looks like this:

180degrees

 

Plug it into the data port and you should have good connectivity with the power rails. It is a tight interference fit.  final Product

 

I’ve considered adding this to a AAA or AAAA battery pack I could mount on top or the back of the calipers. I’m pretty busy so that might not happen. If it does, I’ll post my janky design here though.

DIY Card Scraper From Old Credit Cards

image Credit card companies such as Chase and  American Express have been adding metal to their cards as a gimmick. I had a few old credit cards that have metal piece inside them. Sometimes when you get a new one, the card company sends you an envelope to return the old ones in for recycling, or they suggest you use tin snips to destroy them.  I found a better use.

In woodworking, many times in order to get a nice smooth surface, you might use a card scraper, which is just a thin steel card with sharp edges. You hold it roughly perpendicular to the surface of the wood and draw along the surface perpendicular to the flat side of the card.  This shaves a very thin layer off the top of the wood.

You could buy some card scrapers, but since I had these extra credit cards, I tried to make some myself. I attempted a few different methods to remove the plastic form both sides of the cards. Firstly, went in brute force and pulled the plastic off both sides. As the edges of these cards are sharp without the plastic, I gave myself a pretty good gash in my thumb. This is not a great method. 

For the second card, I attempted to melt the plastic with acetone…. which didn’t fully work and made a nasty, stinky mess.

The third attempt, I went in the garage and used a little torch to heat up the plastic on the card.  Once I saw that it could work, I stopped.  I recommend doing this completely outside with the garage door shut.  Of course use some pliers to hold the card.  This method could work well, but just be sure not to heat the metal until it changes color. You can then clean off the melted plastic blobs with a flame retardant sacrificial rag.

The end results work great!  I also punched out the tiny SIM card in mine, but you can leave it in. It is potted with clear epoxy so you can see the chip and gold connection wires on the back side if you leave it in which is pretty cool.

Protected by CleanTalk Anti-Spam