Log in

View Full Version : Extracting pixel data from bmps with QBASIC


alexh110
23rd February 2013, 19:38
Hey guys.
Have just written a QBASIC program to extract the pixel data from a 24-bit bmp, convert to YUV, then convert back to RGB and write out another bmp.
The idea is to manipulate the YUV data with some code I will insert later on.

However it's not working correctly: my output file has a black bar across the top, though the remainder of the image lower down is OK.

Presumably there's an error in my assumptions about the bmp's header structure, but I'm not sure how to adjust the BMPHeaderType.
Any help would be great!

My code is shown below (NB I'm using the array l(x%, y%) for the Y component values to avoid confusion with the y-coordinate):


TYPE BMPHeaderType
* * id AS STRING * 2*
* * size AS LONG*
* * rr1 AS INTEGER*
* * rr2 AS INTEGER*
* * offset AS LONG*
* * horz AS LONG*
* * wid AS LONG*
* * hei AS LONG*
* * planes AS INTEGER*
* * bpp AS INTEGER*
* * pakbyte AS LONG*
* * imagebytes AS LONG*
* * xres AS LONG*
* * yres AS LONG*
* * colch AS LONG*
* * ic AS LONG*
* * pal AS STRING * 1024*
END TYPE

DIM BmpHeader AS BMPHeaderType

OPEN "4.bmp" FOR BINARY AS #1

GET #1, , BmpHeader


DIM Pixel AS STRING * 1

DIM l(720, 576) AS INTEGER
DIM u(720, 576) AS INTEGER
DIM v(720, 576) AS INTEGER


FOR y% = 1 TO 576
* * FOR x% = 1 TO 720

* * * * GET #1, , Pixel
* * * * B = ASC(Pixel)
* * * * GET #1, , Pixel
* * * * G = ASC(Pixel)
* * * * GET #1, , Pixel
* * * * R = ASC(Pixel)

* * * * l(x%, y%) = 0.299 * R + 0.587 * G + 0.114 * B
* * * * u(x%, y%) = (B - l(x%, y%)) * 0.565
* * * * v(x%, y%) = (R - l(x%, y%)) * 0.713


NEXT x%, y%

CLOSE #1







OPEN "4_mod.bmp" FOR BINARY AS #1

PUT #1, , BmpHeader


FOR y% = 1 TO 576
* * FOR x% = 1 TO 720

* * * * R = l(x%, y%) + 1.403 * v(x%, y%)
* * * * G = l(x%, y%) - 0.344 * u(x%, y%) - 0.714 * v(x%, y%)
* * * * B = l(x%, y%) + 1.770 * u(x%, y%)

* * * * R = INT(R)
* * * * G = INT(G)
* * * * B = INT(B)

* * * * IF R > 255 THEN R = 255
* * * * IF G > 255 THEN G = 255
* * * * IF B > 255 THEN B = 255
* * * * IF R < 0 THEN R = 0
* * * * IF G < 0 THEN G = 0
* * * * IF B < 0 THEN B = 0


* * * * Pixel = CHR$(B)
* * * * PUT #1, , Pixel
* * * * Pixel = CHR$(G)
* * * * PUT #1, , Pixel
* * * * Pixel = CHR$(R)
* * * * PUT #1, , Pixel

NEXT x%, y%

CLOSE #1

IanB
25th February 2013, 23:22
* * pal AS STRING * 1024* is a variable length structure, use * * offset AS LONG* to find and set where the pixel data actually starts.

alexh110
26th February 2013, 13:40
Thanks, but I already tried that.
The value of BmpHeader.offset is 54: so I tried reading the pixel data starting at byte 54; but it didn't seem to make any difference.

Also tried commenting out the palette data from the header, which seemed to reduce the number of missing pixels; but still had a black bar at the top of frame.

I noticed the output file-size is smaller than the input by 405 kbytes. Not sure why.
There shouldn't be any padding bytes on the rows, because the number of bytes per row is a multiple of 4.

This is the new code, using the BmpHeader.offset variable:


TYPE BMPHeaderType
* * id AS STRING * 2*
* * size AS LONG*
* * rr1 AS INTEGER*
* * rr2 AS INTEGER*
* * offset AS LONG*
* * horz AS LONG*
* * wid AS LONG*
* * hei AS LONG*
* * planes AS INTEGER
* * bpp AS INTEGER
* * pakbyte AS LONG*
* * imagebytes AS LONG*
* * xres AS LONG*
* * yres AS LONG*
* * colch AS LONG*
* * ic AS LONG*
* * 'pal AS STRING * 1024*
END TYPE

DIM BmpHeader AS BMPHeaderType

OPEN "4.bmp" FOR BINARY AS #1

GET #1, , BmpHeader


DIM Pixel AS STRING * 1
GET #1, BmpHeader.offset-1, Pixel

DIM R AS INTEGER
DIM G AS INTEGER
DIM B AS INTEGER

DIM l(720, 576) AS INTEGER
DIM u(720, 576) AS INTEGER
DIM v(720, 576) AS INTEGER

FOR y% = 1 TO 576
* * FOR x% = 1 TO 720

* * * * GET #1, , Pixel
* * * * B = ASC(Pixel)
* * * * GET #1, , Pixel
* * * * G = ASC(Pixel)
* * * * GET #1, , Pixel
* * * * R = ASC(Pixel)

* * * * l(x%, y%) = 0.299 * R + 0.587 * G + 0.114 * B
* * * * u(x%, y%) = (B - l(x%, y%)) * 0.565
* * * * v(x%, y%) = (R - l(x%, y%)) * 0.713


NEXT x%, y%

CLOSE #1




OPEN "4_mod.bmp" FOR BINARY AS #1

PUT #1, , BmpHeader
PUT #1, BmpHeader.offset-1, Pixel

FOR y% = 1 TO 576
* * FOR x% = 1 TO 720

* * * * R = l(x%, y%) + 1.403 * v(x%, y%)
* * * * G = l(x%, y%) - 0.344 * u(x%, y%) - 0.714 * v(x%, y%)
* * * * B = l(x%, y%) + 1.770 * u(x%, y%)

* * * * R = INT(R)
* * * * G = INT(G)
* * * * B = INT(B)

* * * * IF R > 255 THEN R = 255
* * * * IF G > 255 THEN G = 255
* * * * IF B > 255 THEN B = 255
* * * * IF R < 0 THEN R = 0
* * * * IF G < 0 THEN G = 0
* * * * IF B < 0 THEN B = 0


* * * * Pixel = CHR$(B)
* * * * PUT #1, , Pixel
* * * * Pixel = CHR$(G)
* * * * PUT #1, , Pixel
* * * * Pixel = CHR$(R)
* * * * PUT #1, , Pixel

NEXT x%, y%

CLOSE #1

LoRd_MuldeR
3rd March 2013, 01:31
Any particular reason you are doing this in BASIC?

Unless your challenge is to get this task done in the most antiquated and obscure programming language (or you want to run this on your old C64), why not use something like C++ or C# or Java or at least VisualBasic?

Furthermore, instead of writing your own Bitmap parser (and thus re-inventing the wheel), why not use one of the various libraries that have existing code for that purpose?


For example, by using Qt, it could be as easy as:
QImage myImage("C:\Some path\input.bmp");

for(int x = 0; x < image.width(); x++)
{
for(int y = 0; y < image.height(); y++)
{
QRgb pixelData = image.pixel(x,y);

/* Do something useful with "pixelData" here, e.g. paste your RGB to YUV code here */

image.setPixel(pixelData);
}
}

myImage.save("C:\Some path\output.bmp");

alexh110
3rd March 2013, 12:59
Have tried to teach myself C++, but found it too much of a challenge, just don't have the time and patience to learn it.
I imagine Visual Basic would present the same problem.
Once I develop the code I can always translate it into C++ using one of the online translation websites.

Not sure what Qt is? Presumably it's the name of a C++ library?

LoRd_MuldeR
3rd March 2013, 14:17
Have tried to teach myself C++, but found it too much of a challenge, just don't have the time and patience to learn it.

Its really not that hard. Surely, a lot of "obscure" stuff can be done in C++ too, but you don't have to start with that :)

Also I think a lot of things will be much easier/cleaner in C++ (or other "modern" languages), compared to BASIC. The object oriented programming paradigm alone is such a big improvement!

Another advantage of C++ is that it's still one of the most versatile and omnipresent programming languages, so you find loads of existing code or libraries for all platforms...

I imagine Visual Basic would present the same problem.

In a way. But it probably would be a more "smooth" transition from QBASIC to VisualBasic than from QBASIC to C++.

And you finally could do BASIC-style programming in a modern IDE, instead of using an IDE that has not been updated for more than 20 years ;)

Once I develop the code I can always translate it into C++ using one of the online translation websites.

I somehow doubt that such automated translation can give you "proper" code...

Not sure what Qt is? Presumably it's the name of a C++ library?

Yup, Qt (https://qt-project.org/resources/getting_started) is a cross-platform "framework" for C++, mainly for GUI development but also has a zillion of other useful stuff. It's what makes C++ worthwhile!

But it also has binding for many other programming languages, e.g. Java or Python.

(BTW: totally forgot to mention Python! If you are just looking for an easy-to-use scripting language, you might want to give Python a try)