BBC BASIC for Windows
Programming >> Graphics and Games >> Reading and Writing Screen Pixels
http://bb4w.conforums.com/index.cgi?board=graphics&action=display&num=1423296297

Reading and Writing Screen Pixels
Post by Kipper on Feb 7th, 2015, 07:04am


Hi

I would like to be able to read the colour of a pixel anywhere on the screen (i.e. not just in the input focus of BBW window) to help in a project analysing video. The background is code to analyse the size of the pupil (in the eye) for a medical project. We seek the black pixels of the pupil and then compute the area. It would also be helpful to be able to write back (i.e. change the colour) of the selected pixel so that we can show the area selected as "pupil." If it were possible to specify the pixel X and Y coordinates then grab its colour that would be great.

Thanks in advance for any help.

Kipeer
Re: Reading and Writing Screen Pixels
Post by rtr2 on Feb 7th, 2015, 10:51am

on Feb 7th, 2015, 07:04am, Kipper wrote:
I would like to be able to read the colour of a pixel anywhere on the screen (i.e. not just in the input focus of BBW window)

I think this is effectively a duplicate of the question asked in this thread:

http://bb4w.conforums.com/index.cgi?board=os&action=display&num=1394457926

Richard.
Re: Reading and Writing Screen Pixels
Post by Kipper on Feb 10th, 2015, 5:50pm


Hi Richard & thanks for the suggestion.

I followed the thread and found the code but it is very slow to operate. For example to complete this line:

SYS "GetPixel", hdc%, pt.x%, pt.y% TO rgb%

Takes between 20 and 30ms whereas I am trying to locate black pixels in order to calculate pupil (of eye) area when illuminated with infra red using IR camera. With correct IR illumination only the pupil is black so calculating pupil size (area) is merely about counting black pixels across rows/columns of an image. I was hoping to process an image of at least 300 x 300 pixels at a rate of at least 10Hz (something I once did on a RISC PC using an Eagle Video card). I do not have direct access to the USB video converter so was (lazily) trying to get the information from its screen image. I guess I could save the relevant segment of screen as a BMP then process that (not sure how quick this would be) but maybe you have a better idea? Perhaps the get pixel command would be faster if it were possible to define a smaller screen area (??) but I tried the same code using a much lower screen resolution and that did not seem to speed it up much....

Any suggestions welcome!

Kipper


Re: Reading and Writing Screen Pixels
Post by rtr2 on Feb 10th, 2015, 6:01pm

on Feb 10th, 2015, 5:50pm, Kipper wrote:
I do not have direct access to the USB video converter

Are you sure? I would have expected any video capture device to support the recognised interfaces, like the old AVICAP32 API or more modern DirectShow.

The project I am currently working on involves (amongst other things) reading in and processing 800x600 video at 50 frames per second. That all works in real-time, using a hybrid BBC BASIC and assembler technique.

So your task sounds straightforward by comparison, but definitely you should not be trying to sample the video by reading the screen! If your USB video source doesn't support a capture API - which as I said I would find surprising - can't you switch to one which does?

Richard.

Re: Reading and Writing Screen Pixels
Post by Kipper on Feb 11th, 2015, 12:16am


All points taken Richard! The existing code from the Web mentioned in video section of this site crashes *(blue screens) my PC when trying to access my KWorld USB video capture device but does access the PC inbuilt web cam. So would be of interest to know what video digitiser works with your code, especially as Kworld devices have often been flakey on my machines. While hoping to hear back I am using a version of code below to capture screen (till faster solution obtains). On this point, after a segment of the window is selected as BMP (see below) how can you plot it from memory? All the code I can find seems to plot from the file disc address but I assume there would be some lines of code that could be inserted prior to PROCsavebpm below to plot direct to screen. The answer is probably really obvious but after quite a bit of searching no success! In case relevant code below is the original but I adjusted to save from screen coordinates rather than from an actual window.


DEF PROCcapturewindow2(hwnd%, file$)
LOCAL rc{}, hdc%, hbm%, ddc%, oldbm%
DIM rc{l%,t%,r%,b%}
SYS "GetWindowRect", hwnd%, rc{}
SYS "CreateDC", "DISPLAY", 0, 0, 0 TO ddc%
SYS "CreateCompatibleDC", @memhdc% TO hdc%
SYS "CreateCompatibleBitmap", @memhdc%, rc.r%-rc.l%, rc.b%-rc.t% TO hbm%
SYS "SelectObject", hdc%, hbm% TO oldbm%
SYS "BitBlt", hdc%, 0, 0, rc.r%-rc.l%, rc.b%-rc.t%, ddc%, rc.l%, rc.t%, &CC0020
SYS "SelectObject", hdc%, oldbm%
PROCsaveasbmp(hbm%, file$)
SYS "DeleteObject", hbm%
SYS "DeleteDC", hdc%
SYS "DeleteDC", ddc%
ENDPROC
Re: Reading and Writing Screen Pixels
Post by rtr2 on Feb 11th, 2015, 09:06am

on Feb 11th, 2015, 12:16am, Kipper wrote:
how can you plot it from memory? All the code I can find seems to plot from the file disc address

Don't rule out the 'intermediate file' approach if it's simpler. Files are cached in memory by Windows so can be a quick and convenient method of moving data from A to B, even if you don't actually need the file. You can specify a temporary file by using the @tmp$ variable.

However if you want to transfer the bitmap more directly you can adapt the code you listed so that it blits to your program's output DC @memhdc% instead of the temporary DC hdc%. Having done that you'll need to force a refresh using InvalidateRect:

Code:
      DEF PROCcapturewindow2(hwnd%, x%, y%)
      LOCAL rc{}, ddc%
      DIM rc{l%,t%,r%,b%}
      SYS "GetWindowRect", hwnd%, rc{}
      SYS "CreateDC", "DISPLAY", 0, 0, 0 TO ddc%
      SYS "BitBlt", @memhdc%, x%, y%, rc.r%-rc.l%, rc.b%-rc.t%, ddc%, rc.l%, rc.t%, &CC0020
      SYS "DeleteDC", ddc%
      SYS "InvalidateRect", @hwnd%, 0, 0
      ENDPROC 

Richard.

Re: Reading and Writing Screen Pixels
Post by Kipper on Feb 11th, 2015, 9:32pm


Hi Richard

Your code worked straight away as shown below (and was really fast plotting!) but I have (hopefully) one last question posed at end of the PROC shown below.

90 DEF PROCcapturewindow2(file$)
100 LOCAL rc{}, hdc%, hbm%, ddc%, oldbm%
LOCAL bmp%, width%, height%, size%, data%, res%
110 DIM rc{l%,t%,r%,b%}
120 rc.l%=60
130 rc.t%=20
140 rc.b%=470
150 rc.r%=600
160 SYS "CreateDC", "DISPLAY", 0, 0, 0 TO ddc%
170 SYS "CreateCompatibleDC", @memhdc% TO hdc%
180 SYS "CreateCompatibleBitmap", @memhdc%, rc.r%-rc.l%, rc.b%-rc.t% TO hbm%
190
200 SYS "SelectObject", hdc%, hbm% TO oldbm%
310 SYS "BitBlt", @memhdc%, 0, 0, rc.r%-rc.l%, rc.b%-rc.t%, ddc%, rc.l%, rc.t%, &CC0020
320 SYS "SelectObject", hdc%, oldbm%
350 SYS "InvalidateRect", @hwnd%, 0, 0
360 SYS "DeleteDC", ddc%
380 ENDPROC

I would like to read the pixel values from the image held in memory. I know I can get some data image size data using lines below but how can I index the values for (I assume) RGB and perhaps Alpha? Could you provide a line of code or a short loop that points me to the right place or a link? Many thanks in advance.


430 REM. Find the bitmap dimensions and file size:
440 DIM bmp% LOCAL 26
450 bmp% = (bmp% + 3) AND -4
460 SYS "GetObject", hbm%, 24, bmp% TO res%
470 IF res%=0 ERROR 100, "GetObject failed"
480
490 width% = bmp%!4
500 height% = bmp%!8
510 size% = 54 + height%*((width%*3 + 3) AND -4)



Re: Reading and Writing Screen Pixels
Post by rtr2 on Feb 11th, 2015, 10:13pm

on Feb 11th, 2015, 9:32pm, Kipper wrote:
I would like to read the pixel values from the image held in memory... how can I index the values for (I assume) RGB and perhaps Alpha?

It's not as straightforward as you hope it will be!

Firstly, to maximise performance, the output bitmap used by BB4W always has the same format as the screen. So it might be 24-bits RGB or 32-bits RGBA or (more likely on older machines) 16-bits 5-6-5 RGB etc.

Secondly, you cannot guarantee that BB4W's output bitmap is actually mapped into your process's address space; it could be resident in kernel memory or graphics memory and therefore inaccessible from BASIC code.

So in general it's either difficult or impossible to read the RGB data directly from memory. If you want to do that, the first thing you must do is to replace BB4W's output bitmap with one which is guaranteed to be memory-mapped and which has a guaranteed format.

The way to do that is to adapt the code you can find in the Help manual under 'Hints and Tips... Using windows larger than 1920 x 1440 pixels'. You don't need to change the window size, but the bitmap created by CreateDIBSection has a format that you specify, and it is guaranteed to be mapped into user memory (it even gives you a handy pointer to the data):

http://www.bbcbasic.co.uk/bbcwin/manual/bbcwini.html#hint2

Richard.
Re: Reading and Writing Screen Pixels
Post by Kipper on Feb 11th, 2015, 11:11pm



Hi

Trying to make it work but what is the "handy pointer"? Could you give me one line of code showing addressing of data indexed by pointer?

420 SYS "CreateDIBSection", @memhdc%, bmih{}, 0, ^bits%, 0, 0 TO hbm%

Assuming it is in the above line?

Any help appreciated !

Kipper
Re: Reading and Writing Screen Pixels
Post by rtr2 on Feb 12th, 2015, 08:29am

on Feb 11th, 2015, 11:11pm, Kipper wrote:
Trying to make it work but what is the "handy pointer"?

MSDN has all the details:

https://msdn.microsoft.com/en-us/library/windows/desktop/dd183494.aspx

Quote:
Could you give me one line of code showing addressing of data indexed by pointer?

Sorry, I can't, because I don't know the format of your bitmap.

In general terms you will need to calculate the offset into the bitmap from your x and y coordinates, add that offset to the base address (bits% in the case of the code you listed), then read the pixel data from memory and separate it into its RGB components (assuming you have selected a colour format rather than monochrome).

But the detailed code will depend on your bitmap's format and dimensions (for example that will determine whether you need to pad each line to an exact multiple of DWORDs).

Richard.