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

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

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.
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