Android Question How to create a BMP from sensor data ?

JTmartins

Active Member
Licensed User
Longtime User
Hi,

I have created a class for a bluetooth Fingerprint scanner and I managed to get working most of the function without many problems.

However, there is a function which allows me to retrieve a image from the sensor, which I would like to try.

From the reader I get an array of bytes, which are the image data (no headers or any other information). From documentation I understand that this is suposed to be translated to a black & white BMP.

So I have the array of bytes. Does any one knows how to transform and save them as BMP ?

I have tryed to convert this Java code to B4A, either by creating B4A code or using inline Java without success...Allways get a bad image file.

I think I'm close..but I'm not getting there.

Some clue will be very helpfull.

Many thanks

B4X:
 private byte[] changeByte(int data) {
        byte b4 = (byte) ((data) >> 24);
        byte b3 = (byte) (((data) << 8) >> 24);
        byte b2 = (byte) (((data) << 16) >> 24);
        byte b1 = (byte) (((data) << 24) >> 24);
        byte[] bytes = { b1, b2, b3, b4 };
        return bytes;
    }
  
    private byte[] toBmpByte(int width, int height, byte[] data) {
        byte[] buffer = null;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            DataOutputStream dos = new DataOutputStream(baos);

            int bfType = 0x424d;
            int bfSize = 54 + 1024 + width * height;
            int bfReserved1 = 0;
            int bfReserved2 = 0;
            int bfOffBits = 54 + 1024;

            dos.writeShort(bfType);
            dos.write(changeByte(bfSize), 0, 4);
            dos.write(changeByte(bfReserved1), 0, 2);
            dos.write(changeByte(bfReserved2), 0, 2);
            dos.write(changeByte(bfOffBits), 0, 4);

            int biSize = 40;
            int biWidth = width;
            int biHeight = height;
            int biPlanes = 1;
            int biBitcount = 8;
            int biCompression = 0;
            int biSizeImage = width * height;
            int biXPelsPerMeter = 0;
            int biYPelsPerMeter = 0;
            int biClrUsed = 256;
            int biClrImportant = 0;

            dos.write(changeByte(biSize), 0, 4);
            dos.write(changeByte(biWidth), 0, 4);
            dos.write(changeByte(biHeight), 0, 4);
            dos.write(changeByte(biPlanes), 0, 2);
            dos.write(changeByte(biBitcount), 0, 2);
            dos.write(changeByte(biCompression), 0, 4);
            dos.write(changeByte(biSizeImage), 0, 4);
            dos.write(changeByte(biXPelsPerMeter), 0, 4);
            dos.write(changeByte(biYPelsPerMeter), 0, 4);
            dos.write(changeByte(biClrUsed), 0, 4);
            dos.write(changeByte(biClrImportant), 0, 4);

            byte[] palatte = new byte[1024];
            for (int i = 0; i < 256; i++) {
                palatte[i * 4] = (byte) i;
                palatte[i * 4 + 1] = (byte) i;
                palatte[i * 4 + 2] = (byte) i;
                palatte[i * 4 + 3] = 0;
            }
            dos.write(palatte);

            dos.write(data);
            dos.flush();
            buffer = baos.toByteArray();
            dos.close();
            baos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return buffer;
    }
 

JTmartins

Active Member
Licensed User
Longtime User
Many thanks..I just tested it, but I get an error loading bitmap.

I guess that is because I only have the actual pixel information. No BMP header with all the information related to the image.

I think I have to build the header from scratch, which so far I havent been able to do properly.

All help is welcome.
 
Last edited:
Upvote 0

JTmartins

Active Member
Licensed User
Longtime User
Ok...I managed to get there...Instead of trying to make elegant code, I'used the ugly approach...Step by Step Line by line and testing. Not very pretty, but it works. I throw in the data from the sensor with the Width and Height and it generates the BMP.

I leave my Sub to Generate the BMP, in case it helps someone, Its not pretty, but works

Note : The pallete is for the purpose I need. It will have to be adapted for other cases.

No I just need some extra help. If you look at the image, it is not correct, it looks like everything needs to be shifted to the right. Is there some BMP guru out there that can help me in finding the solution for this ?

Many thanks

B4X:
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.       
    Type BMPFileHeader (strFileType() As Byte, FileSize() As Byte, bReserved1() As Byte,bReserved2() As Byte,BitmapOffset() As Byte)
    Type BMPInfo (biSize() As Byte, biWidth() As Byte, biHeight() As Byte, biPlanes() As Byte, biBitCount() As Byte, Bicompression() As Byte, biSizeImage() As Byte, biXpelsPerMeter() As Byte, biYpelsPerMeter() As Byte, BiClrUsed() As Byte, biClrImportant() As Byte)
    Type BMPPalette (lngBlue As Byte,lngGreen As Byte,lngRed As Byte,lngReserved As Byte)
End Sub

Sub bmpGenerate (imagedata() As Byte,width As Int,Height As Int)

   
    Private bc As ByteConverter
    bc.LittleEndian=True
    Private header As BMPFileHeader
    Private Info As BMPInfo
   
    '** HEADER **
    header.strFileType=bc.HexToBytes("424D")
    header.FileSize=bc.IntsToBytes(Array As Int (54 + 1024 + width * Height))
    header.bReserved1=bc.ShortsToBytes(Array As Short (0))
    header.bReserved2=bc.ShortsToBytes(Array As Short(0))
    header.BitmapOffset=bc.intsToBytes(Array As Int (54 + 1024))
    '** HEADER iNFO ***
    Info.biSize=bc.IntsToBytes(Array As Int (40))
    Info.biWidth=bc.intsToBytes(Array As Int(width))
    Info.biHeight=bc.intsToBytes(Array As Int(Height))
    Info.biPlanes=bc.ShortsToBytes(Array As Short (1))
    Info.biBitCount=bc.ShortsToBytes(Array As Short (8))
    Info.Bicompression=bc.intsToBytes(Array As Int (0))
    Info.biSizeImage=bc.intsToBytes(Array As Int(width*Height))
    Info.biXpelsPerMeter=bc.intsToBytes(Array As Int(0))
    Info.biYpelsPerMeter=bc.intsToBytes(Array As Int(0))
    Info.BiClrUsed=bc.intsToBytes(Array As Int(256))
    Info.biClrImportant=bc.intsToBytes(Array As Int(0))
   
    Private palette(1024) As Byte
    For i=0 To 255
        palette(i*4)=i
        palette(i*4+1)=i
        palette(i*4+2)=i
        palette(i*4+3)=0
    Next
    '** Write header
    Dim bmpfile As RandomAccessFile
    bmpfile.Initialize(File.DirRootExternal,"testebmp.bmp",False)
    bmpfile.WriteBytes(header.strFileType,0,header.strFileType.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(header.filesize,0,header.FileSize.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(header.bReserved1,0,header.bReserved1.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(header.bReserved2,0,header.bReserved2.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(header.BitmapOffset,0,header.BitmapOffset.Length,bmpfile.CurrentPosition)
    '** Write info
    bmpfile.WriteBytes(Info.biSize,0,Info.biSize.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.biWidth,0,Info.biWidth.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.biHeight,0,Info.biHeight.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.biPlanes,0,Info.biPlanes.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.biBitCount,0,Info.biBitCount.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.Bicompression,0,Info.Bicompression.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.biSizeImage,0,Info.biSizeImage.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.biXpelsPerMeter,0,Info.biXpelsPerMeter.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.biYpelsPerMeter,0,Info.biYpelsPerMeter.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.BiClrUsed,0,Info.BiClrUsed.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.biClrImportant,0,Info.biClrImportant.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(palette,0,palette.Length,bmpfile.CurrentPosition)
    '** Write palete
    bmpfile.WriteBytes(palette,0,palette.Length,bmpfile.CurrentPosition)
    '** Write imagedata
    bmpfile.WriteBytes(imagedata,0,imagedata.Length,bmpfile.CurrentPosition)
    bmpfile.Close
End Sub
 

Attachments

  • testebmp.jpg
    testebmp.jpg
    26.2 KB · Views: 139
Last edited:
Upvote 0

JordiCP

Expert
Licensed User
Longtime User
There are some tricks concerning space reserved for each line in BMP files when there are 8bpp or less. According to what I have found, space reserved for lines seems to be multiple of 16bits (at least in some implementations). If width parameter is even, then that is already ok.

But I guess the real problem is that you are writing the palette twice. As you have specified BitmapOffset=54+1024, but in fact now it is beginning at 54+2048. So, the first lines may be the "second" palette and the bitmap data appears, but displaced because of this added offset
 
Upvote 0

JTmartins

Active Member
Licensed User
Longtime User
You are very correct JordiCP.
Many thanks I actually didn't noticed my bug.

I was trying to be so carefull writing line by line to ensure I was writting the bytes correctly...and endup writing the same line twice. I deserve a brick thrown to my head.

Many thanks once again.

I leave here the corrected version as an example, in case some one needs to create a BMP file from scratch.

B4X:
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.     
    Type BMPFileHeader (strFileType() As Byte, FileSize() As Byte, bReserved1() As Byte,bReserved2() As Byte,BitmapOffset() As Byte)
    Type BMPInfo (biSize() As Byte, biWidth() As Byte, biHeight() As Byte, biPlanes() As Byte, biBitCount() As Byte, Bicompression() As Byte, biSizeImage() As Byte, biXpelsPerMeter() As Byte, biYpelsPerMeter() As Byte, BiClrUsed() As Byte, biClrImportant() As Byte)
    Type BMPPalette (lngBlue As Byte,lngGreen As Byte,lngRed As Byte,lngReserved As Byte)
End Sub

Sub bmpGenerate (imagedata() As Byte,width As Int,Height As Int)

 
    Private bc As ByteConverter
    bc.LittleEndian=True
    Private header As BMPFileHeader
    Private Info As BMPInfo
 
    '** HEADER **
    header.strFileType=bc.HexToBytes("424D")
    header.FileSize=bc.IntsToBytes(Array As Int (54 + 1024 + width * Height))
    header.bReserved1=bc.ShortsToBytes(Array As Short (0))
    header.bReserved2=bc.ShortsToBytes(Array As Short(0))
    header.BitmapOffset=bc.intsToBytes(Array As Int (54 + 1024))
    '** HEADER iNFO ***
    Info.biSize=bc.IntsToBytes(Array As Int (40))
    Info.biWidth=bc.intsToBytes(Array As Int(width))
    Info.biHeight=bc.intsToBytes(Array As Int(Height))
    Info.biPlanes=bc.ShortsToBytes(Array As Short (1))
    Info.biBitCount=bc.ShortsToBytes(Array As Short (8))
    Info.Bicompression=bc.intsToBytes(Array As Int (0))
    Info.biSizeImage=bc.intsToBytes(Array As Int(width*Height))
    Info.biXpelsPerMeter=bc.intsToBytes(Array As Int(0))
    Info.biYpelsPerMeter=bc.intsToBytes(Array As Int(0))
    Info.BiClrUsed=bc.intsToBytes(Array As Int(256))
    Info.biClrImportant=bc.intsToBytes(Array As Int(0))
 
    Private palette(1024) As Byte
    For i=0 To 255
        palette(i*4)=i
        palette(i*4+1)=i
        palette(i*4+2)=i
        palette(i*4+3)=0
    Next
    '** Write header
    Dim bmpfile As RandomAccessFile
    bmpfile.Initialize(File.DirRootExternal,"testebmp.bmp",False)
    bmpfile.WriteBytes(header.strFileType,0,header.strFileType.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(header.filesize,0,header.FileSize.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(header.bReserved1,0,header.bReserved1.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(header.bReserved2,0,header.bReserved2.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(header.BitmapOffset,0,header.BitmapOffset.Length,bmpfile.CurrentPosition)
    '** Write info
    bmpfile.WriteBytes(Info.biSize,0,Info.biSize.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.biWidth,0,Info.biWidth.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.biHeight,0,Info.biHeight.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.biPlanes,0,Info.biPlanes.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.biBitCount,0,Info.biBitCount.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.Bicompression,0,Info.Bicompression.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.biSizeImage,0,Info.biSizeImage.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.biXpelsPerMeter,0,Info.biXpelsPerMeter.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.biYpelsPerMeter,0,Info.biYpelsPerMeter.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.BiClrUsed,0,Info.BiClrUsed.Length,bmpfile.CurrentPosition)
    bmpfile.WriteBytes(Info.biClrImportant,0,Info.biClrImportant.Length,bmpfile.CurrentPosition)
    '** Write palete
    bmpfile.WriteBytes(palette,0,palette.Length,bmpfile.CurrentPosition)
    '** Write pixeldata
    bmpfile.WriteBytes(imagedata,0,imagedata.Length,bmpfile.CurrentPosition)
    bmpfile.Close
End Sub
 
Last edited:
Upvote 0
Top