Page 1 of 1

Bitmaps under Linux

Posted: Tue Mar 21, 2017 1:03 pm
by ray
I wish to examine, in APL, the contents of a bitmap on a Linux (Raspberry Pi) machine.

Under Windows, with the bitmap held on file, I would use:

Code: Select all

 'item'⎕WC'Bitmap'('File'image)
 bits←item.CBits

where image is the file name of a ".bmp", ".jpg" or other image file.

But under Linux I get an error (as expected)
Process '/home/pi/image2.bmp'
DOMAIN ERROR: There was an error processing the property at position 0 of the ri
ght argument
Process[7] 'item'⎕WC'Bitmap'('File'image)



Other than the code in my 1993 Vector artical "Windows.BMP files and APL" (Vol.10 No.1) which does it via ⎕NREAD under STSC's Pocket APL, is there any code around for reading in bitmaps that I can use?

Re: Bitmaps under Linux

Posted: Wed Mar 22, 2017 4:16 pm
by ray
Here is my solution to reading a ".BMP" file created by a Raspberry Pi camera.

Code: Select all

 (rc err rgb)←Read_BI_RGB fname;tie;bits;len;type;size;unused;start;headersize;width;height;planes;bpp;comp;isize;hres;vres;ncols;ignore;bytes;padded_len;unpadded_len;padding;vec;item;rgb;shape;mat
⍝ Reads pixel data from a Device-independent bitmap file in BI_RGB form.
⍝ rc  ←→ Return code 0=OK 1=Error
⍝ err ←→'OK' or error text
⍝ rgb ←→ 3D array of pixel colours or ⍬ if error

⍝ Return pixel data from a Device-independent bitmaps(DIB) with ".BMP" bitmap file (of Windows "BITMAPINFOHEADER" type BI_RGB)
⍝ BI_RGB: The bitmap is in uncompressed red green blue (RGB) format that is not compressed and does not use color masks.
⍝ See https://en.wikipedia.org/wiki/BMP_file_format

⍝ For compatibility reasons, most applications use the older DIB headers for saving files.
⍝ With OS/2 no longer supported after Windows 2000, the most common format i use now has the BITMAPINFOHEADER header.
⍝ NOTE This is also the format used in Raspberry Pi Camera images when ".BMP" file is requested.

⍝ NOTE All values are to be stored as unsigned integers except where noted (such as width and height)

⍝ The pixels are returned as a 3 dimensional matrix
⍝ with the 3 colour planes (Red Green Blue) in the 1st dimension
⍝ and the other two dimensions being the rows and columns of the origional image
⍝ This allows easy processing by colour  EG rgb[1;;] ⍝ Just the RED image

 rc←1                                 ⍝ default for error
 rgb←⍬                                ⍝ default for error

⍝ Get the bits from the file as a boolean vector
 tie←fname ⎕NTIE 0
 bits←⎕NREAD tie 11 ¯1 0              ⍝ Read the whole file  as a bit string
 ⎕NUNTIE tie

⍝ Process the bitmap header to check it is in the expected format
⍝ Bitmap type as 2 character: BM. BA, CI, CP, IC, or PT
 len←16                               ⍝ 2 bytes/characters
 type←80 ⎕DR len↑bits                 ⍝ Convert first 16 bits into 2 characters
 bits←len↓bits                        ⍝ drop the data already used
 :If 'BM'≢type                        ⍝ Check if the expected type
     err←'Not a Windows ".BMP" type bitmap'
     :Return
 :End
⍝ Size of the BMP file including header, pallet, bitmap data etc
 len←32                               ⍝ 4 bytes
 size←323 ⎕DR len↑bits                ⍝ size data NOT USED HERE
 bits←len↓bits
⍝ 4 (Application specific) unused bytes
 len←32                               ⍝ 4 bytes
 unused←323 ⎕DR len↑bits              ⍝ this data NOT USED HERE
 bits←len↓bits
⍝ Staring point of bitmap pixels in the data
 len←32                               ⍝ 4 bytes
 start←323 ⎕DR len↑bits               ⍝ 54 expected (14 for bitmap header, 40 for DID header)
 bits←len↓bits
⍝ DIB header (bitmap information header)
 len←32                               ⍝ 4 bytes
 headersize←323 ⎕DR len↑bits          ⍝ Size of the DIB header
 bits←len↓bits
 :If headersize≠40                    ⍝ DIB header is expected to take up 40 bytes
     err←'Not a "BITMAPINFOHEADER"'
     :Return
 :End
⍝ We appear to have read a windows "BITMAPINFOHEADER" BI_RGB file
⍝ Next item in the header is the bitmap width (columns) in pixels (signed integer)
 len←32                               ⍝ 4 bytes
 width←323 ⎕DR len↑bits
 bits←len↓bits
⍝ the bitmap height (rows) in pixels (signed integer)
 len←32                               ⍝ 4 bytes
 height←323 ⎕DR len↑bits              ⍝ NOTE a negative value implies an inverted image (top to bottom)
 bits←len↓bits
⍝ the number of color planes (must be 1)
 len←16                               ⍝ 2 bytes
 planes←163 ⎕DR len↑bits              ⍝ NOT USED HERE
 bits←len↓bits
⍝ the number of bits per pixel, (24) which is the color depth of the image. Typical values are 1, 4, 8, 16, 24 and 32.
 len←16                               ⍝ 2 bytes
 bpp←163 ⎕DR len↑bits
 bits←len↓bits
 :If bpp≠24
     err←'Unexpected colour depth'
     :Return
 :End
⍝ the compression method being used. 0=BI_RGB (Most Common) means NO COMPRESSION
 len←32                               ⍝ 4 bytes
 comp←323 ⎕DR len↑bits
 bits←len↓bits
 :If comp≠0                           ⍝ Expected to be 0
     err←'Compressed bitmat'
     :Return
 :End

⍝ The next block of the header can be ignored.
⍝ the image size. This is the size of the raw bitmap data; a dummy 0 can be given for BI_RGB bitmaps.
 len←32                               ⍝ 4 bytes
 isize←323 ⎕DR len↑bits               ⍝ NOT USED HERE
 bits←len↓bits
⍝ the horizontal resolution of the image. (pixel per meter, signed integer)
 len←32                               ⍝ 4 bytes
 hres←323 ⎕DR len↑bits                ⍝ NOT USED HERE
 bits←len↓bits
⍝ the vertical resolution of the image. (pixel per meter, signed integer)
 len←32                               ⍝ 4 bytes
 vres←323 ⎕DR len↑bits                ⍝ NOT USED HERE
 bits←len↓bits
⍝ the number of colors in the color palette, or 0 to default to 2n
 len←32                               ⍝ 4 bytes
 ncols←323 ⎕DR len↑bits               ⍝ NOT USED HERE
 bits←len↓bits
⍝ the number of important colors used, or 0 when every color is important; generally ignored
 len←32                               ⍝ 4 bytes
 ignore←323 ⎕DR len↑bits              ⍝ NOT USED HERE
 bits←len↓bits

⍝ Pixels
⍝ The 24-bit pixel (24 bpp) format supports 16,777,216 distinct colors
⍝ and stores 1 pixel value per 3 bytes.
⍝ Each pixel value defines the red, green and blue samples of the pixel.
⍝ Specifically, in the order: Blue, Green and Red (8 bits per each sample).

⍝ Convert bit vector into 8 bit unsigned integer vector. 83 ⎕DR would produced 8 bit signed values.
 bytes←⎕UCS 80 ⎕DR bits               ⍝ blue green red unsigned 8 bit values 0-255

⍝ Check for padded rows (All rows must be a multiple of 4 bytes)
 padded_len←(⍴bytes)÷height           ⍝ bytes in each row as supplied with any padding
 unpadded_len←width×3                 ⍝ bytes in each row without trailing padding
 padding←padded_len-unpadded_len      ⍝ number of padding bytes at end of each row
 :If padding≠0                        ⍝ Need to remove padding bytes
     bytes←,(height,unpadded_len)↑(height,padded_len)⍴bytes
 :End

⍝ convert byte vector into a 2D array with 3 columns
 mat←⌽(((⍴bytes)÷3),3)⍴bytes          ⍝ one row per pixel each with a columns for each colour in RGB order

⍝ convert into a 3D array with the most useful orientation for colour manipulation
 rgb←3 2 1⍉(height,width,3)⍴mat       ⍝ colours by rows by columns
 rc←0
 err←'OK'

⍝⍝⍝ commented out code to cross-check against CBits property read via ⎕WC
⍝⍝ vec←256⊥⍉mat                       ⍝ vector of RGB 24bit pixels
⍝⍝ CBits←⊖(height,width)⍴vec          ⍝ matches dyalog CBits property of a Bitmap
⍝⍝ 'item'⎕WC'Bitmap'('File'fname)
⍝⍝ :If CBits≢item.CBits
⍝⍝     rc←1
⍝⍝     err←'Problem CBits cross-check failed'
⍝⍝     rgb←⍬
⍝⍝ :End


I used the commented out code at the end to confirm that the function could return the same data as available in the CBits property when tested under Windows.

However, I have returned the pixel array as a "number of colours" by "number of rows", by "number of columns" byte array, which (I expect) will make further processing easier