user3068649
user3068649

Reputation: 421

24 bit bmp to RGB565 file conversion

I want to convert 24 bit bmp files to RGB565 format to write to a serial TFT colour display.

The size of the 24bit bmp will always be 320x240 pixels as my TFT display is 320x240

Has anyone any experience of doing this? It could be C/C++, Shell, Python, Java Script and so on...

Upvotes: 3

Views: 5357

Answers (3)

Mark Setchell
Mark Setchell

Reputation: 208043

I would use either NetPBM (much smaller and lighter-weight) or ImageMagick (much bigger installation) to convert the BMP into a format that is simple to parse and then use Perl to convert that to RGB565 format.

I assume you are planning to write the RGB565 data to a frame buffer, so you would do something like:

./bmp2rgb565 image.bmp > /dev/fb1

So, save the following as bmp2rgb565:

#!/bin/bash
################################################################################
# bmp2rgb565
# Mark Setchell
################################################################################

if [ $# -ne 1 ]; then 
   echo Usage: $0 image.bmp
   exit 1
fi

file=$1

# Use NetPBM's "bmptopnm" to convert BMP to PNM for easy reading
# You could use ImageMagick: convert "$file" PNM: | perl ...
bmptopnm "$file" 2> /dev/null | 
   perl -e '
      my $debug=0;   # Change to 1 for debugging

      # Discard first 3 lines of PNM header:
      #   P3
      #   320 240
      #   255
      my $line=<STDIN>; $line=<STDIN>; $line=<STDIN>;

      # Read file, 3 RGB bytes at a time
      {
         local $/ = \3;
         while(my $pixel=<STDIN>){
            # Extract 8-bit R,G and B from pixel
            my ($r,$g,$b)=unpack("CCC",$pixel);
            printf("R/G/B: %d/%d/%d\n",$r,$g,$b) if $debug;

            # Convert to RGB565
            my $r5=$r>>3;
            my $g6=$g>>2;
            my $b5=$b>>3;
            my $rgb565 = ($r5<<11) | ($g6<<5) | $b5;

            # Convert to little-endian 16-bit (VAX order) and write 2 bytes
            my $v=pack("v",$rgb565);
            syswrite(STDOUT,$v,2);
         }
      }
   '

I don't have a frame buffer handy to test, but it should be pretty close.

Note that you could make the code more robust by starting off with:

convert "$file" -depth 8 -resize 320x240\! PNM: | perl ...

which would make sure the image always matches the framebuffer size, and that it is 8-bit and not 16-bit. You may also want a -flip or -flop in there if the BMP image is upside-down or back-to-front.

Note that if you use ImageMagick convert, the code will work for GIFs, TIFFs, JPEGs, PNGs and around 150 other formats as well as BMP.


Note that if you want to test the code, you can generate a black image with ImageMagick like this:

convert -size 320x240 xc:black BMP3:black.bmp

and then look for a bunch of zeroes in the output if you run:

./bmp2rgb565 black.bmp | xxd -g2

Likewise, you can generate a white image and look for a bunch of ffs:

convert -size 320x240 xc:red BMP3:white.bmp

And so on with red, green and blue:

convert -size 320x240 xc:red  BMP3:red.bmp
convert -size 320x240 xc:lime BMP3:green.bmp
convert -size 320x240 xc:blue BMP3:blue.bmp

# Or make a cyan-magenta gradient image
convert -size 320x240 gradient:cyan-magenta  cyan-magenta-gradient.bmp

Example:

./RGB565 red.bmp | xxd -g2 | more
00000000: 00f8 00f8 00f8 00f8 00f8 00f8 00f8 00f8  ................
00000010: 00f8 00f8 00f8 00f8 00f8 00f8 00f8 00f8  ................

Example:

./RGB565 blue.bmp | xxd -g2 | more
00000000: 1f00 1f00 1f00 1f00 1f00 1f00 1f00 1f00  ................
00000010: 1f00 1f00 1f00 1f00 1f00 1f00 1f00 1f00  ................

Keywords: RGB565, rgb565, framebuffer, frame-buffer, pack, unpack, Perl, BMP, PGM, image, Raspberry Pi, RASPI

Upvotes: 2

Tobias Ribizel
Tobias Ribizel

Reputation: 5421

I'm assuming you simply want to stream out the pixel data without any headers or additional data.

The core of the problem has already been described here.

If you want to implement it for yourself, the following approach will probably help:

After opening the file, yout need to skip the BMP header by reading the bfOffBits value (see this BMP format description), the easiest way would probably be to implement it in C by reading into a struct that matches the BMP header.

(Note: This only works because the dimensions of the image are divisible by 4! Check the format description for more details)

Then seek forward by bfOffBits and while the file has not ended, transform three consecutive bytes (uint8_t) to one 16 bit value (uint16_t) by using the approach from the question I mentioned above. The basic C file operations you would need are fopen, fclose, fread, fwrite, fgetc/fgetwc, fputc/fputwc and fseek

Upvotes: 0

fhossfel
fhossfel

Reputation: 2191

To convert to RGB565 you can use ImageMagick:

convert test.png -resize 320x200 -ordered-dither threshold,32,64,32 test2.png

In what format do you need the output to be?

Upvotes: 0

Related Questions