Page 1 of 2

Fastest way to "learn" an image's pixels?

Posted: Sat Sep 12, 2009 1:32 am
by Seymour Clufley
I'm working on an image distortion routine, which needs every pixel's RGBA value for processing. The way I've got it just now is that the image is simply scanned beforehand, and its pixels' values stored in a 2-dimensional array. This takes time!

Unfortunately, using Point() during the distorting is not appropriate (nor possible), because at that time a completely new image is being drawn, based on the original.

Does anyone know of a "shortcut" to achieve what I want to achieve?

Also, would the prospects of a solution change if I didn't need every pixel's RGBA value? Distortion being as it is, the image is squashed, meaning that some of its original pixels are just irrelevant. Is there a way to handle two different images at once, and use Point() on the original image only when a pixel value is needed for the new image?

Re: Fastest way to "learn" an image's pixels?

Posted: Sat Sep 12, 2009 7:27 am
by idle

Code: Select all

Procedure CopyMemoryToImage(Memory, ImageNumber)
 
  Protected TemporaryDC, TemporaryBitmap.BITMAP, TemporaryBitmapInfo.BITMAPINFO
 
  TemporaryDC = CreateDC_("DISPLAY", #Null, #Null, #Null)
 
  GetObject_(ImageID(ImageNumber), SizeOf(BITMAP), TemporaryBitmap.BITMAP)
 
  TemporaryBitmapInfo\bmiHeader\biSize        = SizeOf(BITMAPINFOHEADER)
  TemporaryBitmapInfo\bmiHeader\biWidth       = TemporaryBitmap\bmWidth
  TemporaryBitmapInfo\bmiHeader\biHeight      = -TemporaryBitmap\bmHeight
  TemporaryBitmapInfo\bmiHeader\biPlanes      = 1
  TemporaryBitmapInfo\bmiHeader\biBitCount    = 32
  TemporaryBitmapInfo\bmiHeader\biCompression = #BI_RGB
 
  SetDIBits_(TemporaryDC, ImageID(ImageNumber), 0, TemporaryBitmap\bmHeight, Memory, TemporaryBitmapInfo, #DIB_RGB_COLORS)
 
  DeleteDC_(TemporaryDC)
 
EndProcedure

Procedure CopyImageToMemory(ImageNumber, Memory)
 
  Protected TemporaryDC, TemporaryBitmap.BITMAP, TemporaryBitmapInfo.BITMAPINFO
 
  TemporaryDC = CreateDC_("DISPLAY", #Null, #Null, #Null)
 
  GetObject_(ImageID(ImageNumber), SizeOf(BITMAP), TemporaryBitmap.BITMAP)
  
   
  TemporaryBitmapInfo\bmiHeader\biSize        = SizeOf(BITMAPINFOHEADER)
  TemporaryBitmapInfo\bmiHeader\biWidth       = TemporaryBitmap\bmWidth
  TemporaryBitmapInfo\bmiHeader\biHeight      = -TemporaryBitmap\bmHeight
  TemporaryBitmapInfo\bmiHeader\biPlanes      = 1
  TemporaryBitmapInfo\bmiHeader\biBitCount    = 32
  TemporaryBitmapInfo\bmiHeader\biCompression = #BI_RGB
 
  GetDIBits_(TemporaryDC, ImageID(ImageNumber), 0, TemporaryBitmap\bmHeight, Memory, TemporaryBitmapInfo, #DIB_RGB_COLORS)
 
  DeleteDC_(TemporaryDC)
 
EndProcedure



then you call it like

Code: Select all

 
global mem = allocatememory(imagewidth(img)*imagheight(img) << 2)

copyImageToMemory(img,mem) 

;create a pointer to the memory *px.long 
define *px.long 
   x=0
    while x<width  
      x=y
       while y<height  
           *px = *mem + ((x + (y * width)) << 2 )
           R=*px\l & $FF
           G=*px\l >> 8 & $FF
           B=*px\l >> 16 & $FF
          y+1
      wend 
    x+1
 wend 

;to set a pixel 

 *px\l = RGB(R,G,B)  



maybe syntax errors in that as I just typed it direct in the box and it looks funny with the new theme :oops:

Re: Fastest way to "learn" an image's pixels?

Posted: Sat Sep 12, 2009 8:08 am
by Kaeru Gaman
some of its original pixels are just irrelevant.
if you do it right, none of the original pixels are ever irrelevant.
you need to calculate a new pixel's color from a weighted average of the surrounding source pixels, quite like antialias or smoothing.

... from what I see at first glance, idle's routines should help you best to directly access the images' mem. that's fast as fast can.

Re: Fastest way to "learn" an image's pixels?

Posted: Sat Sep 12, 2009 12:48 pm
by djes
By pity Idle, don't do that to me. My tears are flowing when I see that:

Code: Select all

global mem = allocatememory(imagewidth(img)*imagheight(img) << 2)

copyImageToMemory(img,mem)

;create a pointer to the memory *px.long
define *px.long
   x=0
    while x<width 
      x=y
       while y<height 
           *px = *mem + ((x + (y * width)) << 2 )
           R=*px\l & $FF
           G=*px\l >> 8 & $FF
           B=*px\l >> 16 & $FF
          y+1
      wend
    x+1
wend

;to set a pixel

*px\l = RGB(R,G,B)  
Get outside the main loop all this unneeded calculations. It doesn't have to be there. Our poor processor is working for nothing. Please. Do it for me!
Edit> I'm sorry, i didn't see that
maybe syntax errors in that as I just typed it direct in the box and it looks funny with the new theme :oops:
Forget what I said ;)

Re: Fastest way to "learn" an image's pixels?

Posted: Sat Sep 12, 2009 9:41 pm
by idle
well I did say might be errors but I couldn't see them, the result of being dyslexic.

Re: Fastest way to "learn" an image's pixels?

Posted: Sat Sep 12, 2009 10:29 pm
by Seymour Clufley
Thanks, Idle. The code works great!
all this unneeded calculations. It doesn't have to be there
What doesn't have to be there? Could the code be simplified?

Re: Fastest way to "learn" an image's pixels?

Posted: Sat Sep 12, 2009 11:17 pm
by Seymour Clufley
Sorry, one more thing...

Code: Select all

           *px = *mem + ((x + (y * width)) << 2 )
           R=*px\l & $FF
           G=*px\l >> 8 & $FF
           B=*px\l >> 16 & $FF
What about the alpha channel?

And would this line

Code: Select all

TemporaryBitmapInfo\bmiHeader\biCompression = #BI_RGB
need to be changed to

Code: Select all

TemporaryBitmapInfo\bmiHeader\biCompression = #RGBA
?

Re: Fastest way to "learn" an image's pixels?

Posted: Sat Sep 12, 2009 11:37 pm
by djes
It's not a premature optimisation to see that this line

Code: Select all

*px = *mem + ((x + (y * width)) << 2 )
in a two levels loop will be damn slow, and is totally useless, as its parts are coming directly from the loops.
So, without fully optimising, we try to have all the multiplications replaced by additions/substractions (as mul are really slow!) and we can get something like that :

Code: Select all

;create a pointer to the memory *px.long
Define *px.long
   x=0
   *px = *mem 
   width2=width+width

    While x<width 
      *opx=*px
       While y<height         
           R=*px\l & $FF
           G=*px\l >> 8 & $FF
           B=*px\l >> 16 & $FF       
          *px+width2
      Wend
      *px = *opx+2
Wend

;to set a pixel

*px\l = RGB(R,G,B)  

Re: Fastest way to "learn" an image's pixels?

Posted: Sun Sep 13, 2009 12:45 am
by idle
yes that much better for 2d access.

As for the alpha channel I don't think it's preserved in the dib, gdi doesn't support alpha channels.
if it did then its would just be a case of
A=*px\l >> 24 & $FF

Think you need another solution!

Re: Fastest way to "learn" an image's pixels?

Posted: Sun Sep 13, 2009 1:17 am
by djes
Why doesn't anybody uses gdi+? Is there some issue?

Re: Fastest way to "learn" an image's pixels?

Posted: Sun Sep 13, 2009 1:50 am
by idle
in my case because I don't know the API, perhaps Netmaestro would be better to ask.

Re: Fastest way to "learn" an image's pixels?

Posted: Sun Sep 13, 2009 2:02 am
by Seymour Clufley
we try to have all the multiplications replaced by additions/substractions (as mul are really slow!)
I've followed this advice, but it doesn't seem to speed up the process at all. 62ms either way!

Re: Fastest way to "learn" an image's pixels?

Posted: Sun Sep 13, 2009 2:16 am
by idle
is that with the debugger off, djes method should be faster.
you could also try to switch the loop order in case it's causing it to page the memory.
it's no good asking a dyslexic for advice given two choices.

why don't you post what your trying to do exactly like are you trying to morph an image or deform an image and do you need it to have transparency

Re: Fastest way to "learn" an image's pixels?

Posted: Thu Sep 17, 2009 10:21 am
by Mistrel
Here is a method which is about 8% faster on my machine, idle. :P

Code: Select all

Procedure CopyImageToMemory(ImageID, Memory)
  Output=ImageOutput(ImageID)
  hDC=StartDrawing(Output)
  TemporaryBitmapInfo.BITMAPINFO
  TemporaryBitmapInfo\bmiHeader\biSize=SizeOf(BITMAPINFOHEADER)
  TemporaryBitmapInfo\bmiHeader\biWidth=ImageWidth(ImageID)
  TemporaryBitmapInfo\bmiHeader\biHeight=ImageHeight(ImageID)
  TemporaryBitmapInfo\bmiHeader\biPlanes=1
  TemporaryBitmapInfo\bmiHeader\biBitCount=Imagedepth(ImageID)
  TemporaryBitmapInfo\bmiHeader\biCompression=#BI_RGB
  GetDIBits_(hDC,ImageID(ImageID),0,ImageHeight(ImageID),Memory,TemporaryBitmapInfo,#DIB_RGB_COLORS)
  StopDrawing()
EndProcedure

Re: Fastest way to "learn" an image's pixels?

Posted: Mon Oct 26, 2009 11:37 pm
by Seymour Clufley
It's a while ago now, but here is the final version I came up with:

Code: Select all

Procedure.b ImageIntoPixelArray(img.i,Array arr.i(2),free.b) ; final version for an independent 2D array
  
  If Not IsImage(img) : ProcedureReturn #False : EndIf
  
  iw = ImageWidth(img)
  ih = ImageHeight(img)
  
  *mem = AllocateMemory(iw*ih << 2)
  CopyImageToMemory(img,*mem)
  If free
      FreeImage(img)
  EndIf
  
  *px.LONG = *mem
  
  For x = 0 To iw-1
      ycounter = 0
      For y = 0 To ih-1
          calc = x+ycounter
          *px = *mem + (calc<<2)
          ;If *px\l
              arr(x,y) = RGBA(*px\l >> 16 & $FF, *px\l >> 8 & $FF, *px\l & $FF, *px\l >> 24 & $FF)
          ;EndIf
          ycounter+iw
      Next y
  Next x
  
  FreeMemory(*mem)
  
  ProcedureReturn #True
  
EndProcedure