Page 1 of 2
Is that the fastest way to image grayscale?
Posted: Fri Mar 07, 2008 10:54 am
by dige
hi folks,
I need a hint, how to improve this code:
Code: Select all
Procedure.b GrayScaleImg ( ImgID.l ); Works only with 32 Bit Images!!
Protected Grey.l
#lumared = 3
#lumagreen = 6
#lumablue = 1
If Not IsImage( ImgID ) Or Not GetObject_(ImageID(ImgID), SizeOf(BITMAP), bmp.BITMAP) : ProcedureReturn #False : EndIf
*ptr.RGBQUAD = bmp\bmBits
Size = *ptr + ImageWidth(ImgID) * ImageHeight(ImgID) * 4 - 4
Repeat
Grey = (#lumared * *ptr\rgbRed&$FF + #lumagreen * *ptr\rgbGreen&$FF + #lumablue * *ptr\rgbBlue&$FF) / 10
*ptr\rgbRed = Grey
*ptr\rgbGreen = *ptr\rgbRed
*ptr\rgbBlue = *ptr\rgbRed
*ptr + 4
Until *ptr > Size
ProcedureReturn #True
EndProcedure
If CreateImage( 1, 4000, 4000, 32)
time = ElapsedMilliseconds()
GrayScaleImg( 1 )
MessageRequester("", Str(ElapsedMilliseconds() - time), 0)
EndIf
Results:
---------
PB 4.10: 594ms
PB 4.20: 594ms
C-Lib: 188ms
Posted: Fri Mar 07, 2008 11:15 am
by srod
Here's another way. It makes a copy of the original image, for which I have opted for a 4-bit 16 color image etc.
It would be quicker to use more API, but I hadn't time. Whether this is quicker than your method I do not know? One thing is though that this should work with images of any depth.
Code: Select all
If OpenWindow(0, 100, 100, 500, 300, "PureBasic - Image")
;Set up a 'grey' colortable.
Dim colors(15) ;Used as RGBQUAD's.
For i = 0 To 15
color = i*16
If color = 256
color = 255
EndIf
colors(i) = color + color<<8 + color<<16
Next
LoadImage(1, "bm2.bmp") ;Change to your own image.
;Create a new image which will hold the 'grey scale' equivalent of the above image.
;We use a 4 bit image. Probably better with 8 bits.
CreateImage(2, ImageWidth(1), ImageHeight(1), 4)
;Convert image 1 to grey scale.
hdc = StartDrawing(ImageOutput(2))
If hdc
SetDIBColorTable_(hdc, 0, 16, @colors())
DrawImage(ImageID(1),0,0)
StopDrawing()
EndIf
Repeat
EventID = WaitWindowEvent()
If EventID = #PB_Event_Repaint
;Display both images.
StartDrawing(WindowOutput(0))
DrawImage(ImageID(1), 0, 0)
DrawImage(ImageID(2), ImageWidth(1)+10, 0)
StopDrawing()
EndIf
Until EventID = #PB_Event_CloseWindow
EndIf
End
Posted: Fri Mar 07, 2008 11:19 am
by tinman
This works faster for me but you won't get anywhere near the timing for the C code if that is your idea.
Code: Select all
Structure RGBQUADLong
StructureUnion
rgbq.RGBQUAD
rgbl.l
EndStructureUnion
EndStructure
Procedure.b GrayScaleImg ( ImgID.l ); Works only with 32 Bit Images!!
Protected Grey.l
#lumared = 3
#lumagreen = 6
#lumablue = 1
If Not IsImage( ImgID ) Or Not GetObject_(ImageID(ImgID), SizeOf(BITMAP), bmp.BITMAP) : ProcedureReturn #False : EndIf
;*ptr.RGBQUAD = bmp\bmBits
*ptr.RGBQUADLong = bmp\bmBits
Size = *ptr + ImageWidth(ImgID) * ImageHeight(ImgID) * 4 - 4
Repeat
Grey = (#lumared * *ptr\rgbq\rgbRed&$FF + #lumagreen * *ptr\rgbq\rgbGreen&$FF + #lumablue * *ptr\rgbq\rgbBlue&$FF) / 10
Grey = Grey * $00010101
*ptr\rgbl = Grey
;*ptr\rgbRed = Grey
;*ptr\rgbGreen = *ptr\rgbRed
;*ptr\rgbBlue = *ptr\rgbRed
*ptr + 4
Until *ptr > Size
ProcedureReturn #True
EndProcedure
If CreateImage( 1, 4000, 4000, 32)
time = ElapsedMilliseconds()
GrayScaleImg( 1 )
MessageRequester("", Str(ElapsedMilliseconds() - time), 0)
EndIf
Posted: Fri Mar 07, 2008 1:27 pm
by dige
@tinman: yeah!

we'll come closer: 453ms
thank you!
Posted: Fri Mar 07, 2008 1:36 pm
by dige
359ms!!
But why is RGBQUADLong faster?
Code: Select all
Structure RGBQUADLong
StructureUnion
rgbq.RGBQUAD
rgbl.l
EndStructureUnion
EndStructure
Procedure.b GrayScaleImg ( ImgID.l ); Works only with 32 Bit Images!!
Protected Grey.l
#lumared = 3
#lumagreen = 6
#lumablue = 1
If Not IsImage( ImgID ) Or Not GetObject_(ImageID(ImgID), SizeOf(BITMAP), bmp.BITMAP) : ProcedureReturn #False : EndIf
*ptr.RGBQUADLong = bmp\bmBits
Size = *ptr + ImageWidth(ImgID) * ImageHeight(ImgID) * 4 - 4
Repeat
*ptr\rgbl = $00010101 * ((#lumared * *ptr\rgbq\rgbRed&$FF + #lumagreen * *ptr\rgbq\rgbGreen&$FF + #lumablue * *ptr\rgbq\rgbBlue&$FF) / 10)
*ptr + 4
Until *ptr > Size
ProcedureReturn #True
EndProcedure
If CreateImage( 1, 4000, 4000, 32)
time = ElapsedMilliseconds()
GrayScaleImg( 1 )
MessageRequester("", Str(ElapsedMilliseconds() - time), 0)
EndIf
Posted: Fri Mar 07, 2008 1:53 pm
by tinman
dige wrote:But why is RGBQUADLong faster?
Because you write all pixels in one operation rather than 3.
Posted: Fri Mar 07, 2008 3:44 pm
by Trond
Hmm. I send in my 32-bit image and it fails.
Code: Select all
Procedure.b GrayScaleImg ( ImgID.l ); Works only with 32 Bit Images!!
Protected Grey.l
#lumared = 3
#lumagreen = 6
#lumablue = 1
If Not IsImage( ImgID ) Or Not GetObject_(ImageID(ImgID), SizeOf(BITMAP), bmp.BITMAP) : ProcedureReturn #False : EndIf
Debug bmp\bmBitsPixel ; Shows 32
Debug bmp\bmBits ; Shows 0
*ptr.RGBQUADLong = bmp\bmBits
Size = *ptr + ImageWidth(ImgID) * ImageHeight(ImgID) * 4 - 4
Repeat
*ptr\rgbl = $00010101 * ((#lumared * *ptr\rgbq\rgbRed&$FF + #lumagreen * *ptr\rgbq\rgbGreen&$FF + #lumablue * *ptr\rgbq\rgbBlue&$FF) / 10)
*ptr + 4
Until *ptr > Size
ProcedureReturn #True
EndProcedure
LoadImage(1, "c:\map.bmp", #PB_Image_DisplayFormat)
Posted: Fri Mar 07, 2008 3:54 pm
by srod
That could be because LoadImage() may well not be creating a DIBSection etc. In such cases, GetObject_() will not return the pixel bits - you need GetDIBits_() for that.
Try drawing it to a 32-bit image created with CreateImage() first etc. Worth a shot!
Posted: Fri Mar 07, 2008 4:23 pm
by Trond
Slightly faster (compile in ascii mode):
Code: Select all
Structure CRGBQUAD
r.c
g.c
b.c
EndStructure
Structure RGBQUADLong
StructureUnion
rgbq.CRGBQUAD
rgbl.l
EndStructureUnion
EndStructure
Procedure.l GrayScaleImgX ( ImgID.l ); Works only with 32 Bit Images!!
Protected Grey.l
#lumared = 3
#lumagreen = 6
#lumablue = 1
If Not IsImage( ImgID ) Or Not GetObject_(ImageID(ImgID), SizeOf(BITMAP), bmp.BITMAP) : ProcedureReturn #False : EndIf
;*ptr.RGBQUAD = bmp\bmBits
*ptr.RGBQUADLong = bmp\bmBits
Size = *ptr + ImageWidth(ImgID) * ImageHeight(ImgID) * 4 - 4
Repeat
Grey = (#lumared * *ptr\rgbq\r + #lumagreen * *ptr\rgbq\g + #lumablue * *ptr\rgbq\b) / 10
Grey = Grey * $00010101
*ptr\rgbl = Grey
;*ptr\rgbRed = Grey
;*ptr\rgbGreen = *ptr\rgbRed
;*ptr\rgbBlue = *ptr\rgbRed
*ptr + 4
Until *ptr > Size
ProcedureReturn #True
EndProcedure
Posted: Fri Mar 07, 2008 5:18 pm
by Trond
It doesn't seem to get any much faster than this:
Code: Select all
Procedure.l GrayScaleImg ( ImgID.l ); Works only with 32 Bit Images!!
Protected Grey.l
#maskb = $000000FF
#maskg = $0000FF00 ; >> 8
#maskr = $00FF0000 ; >> 16
If Not IsImage( ImgID ) Or Not GetObject_(ImageID(ImgID), SizeOf(BITMAP), bmp.BITMAP) : ProcedureReturn #False : EndIf
*ptr.RGBQUADLong = bmp\bmBits
Size = *ptr + ImageWidth(ImgID) * ImageHeight(ImgID) * 4 - 4
Repeat
d = *ptr\rgbl
r = ((d & #maskr) >> 16) * 0.2989
g = ((d & #maskg) >> 8) * 0.5870
d = ((d & #maskb)) * 0.1140
r + g + d
*Ptr\rgbl = r + (r<<8) + (r<<16)
*ptr + 4
Until *ptr > Size
ProcedureReturn #True
EndProcedure
Posted: Fri Mar 07, 2008 6:40 pm
by hellhound66
Removed.
Posted: Fri Mar 07, 2008 7:38 pm
by dige
Take this, if you wanna use your own images..
Code: Select all
file.s = ""; Edit Filename
LoadImage( 0, file )
If CreateImage( 1, ImageWidth(0), ImageHeight(0), 32)
If StartDrawing( ImageOutput(1))
DrawImage(ImageID(0), 0, 0)
StopDrawing()
GrayScaleImg ( 1 )
SaveImage( file + ".bmp" ); Save Result
EndIf
EndIf
Posted: Fri Mar 07, 2008 7:55 pm
by hellhound66
So there is a difference between a loaded and a created image? WTF!
So ImageID!=ImageID?
Posted: Sun Mar 09, 2008 12:52 am
by otto
Maybe this can help you to improve the speed a little bit:
First, Iam working with PB 4.0
use it only on CPUs with SSE2 and higher
Code: Select all
Procedure bmpToGray(*bmp.BITMAP)
; *bmp\bmBits must be set and aligned to a mem64 / 8 bytes boundary
; inlineASM must be activated
; speed depends also on memory throughput
pixels=*bmp\bmWidth**bmp\bmHeight
addr=*bmp\bmBits
If addr=0 Or addr&%111 Or *bmp\bmBitsPixel<>32
ProcedureReturn 0
EndIf
; multipliers from 0.0 to 0.99999
lumR=65536*0.3
lumG=65536*0.6
lumB=65536*0.1
!pxor xmm6,xmm6
!pinsrw xmm6,[p.v_lumR],2
!pinsrw xmm6,[p.v_lumG],1
!pinsrw xmm6,[p.v_lumB],0
!punpcklqdq xmm6,xmm6
!pcmpeqw xmm5,xmm5
!psrlq xmm5,16
!pxor xmm7,xmm7
MOV Esi,addr
MOV Eax,pixels
SHR Eax,1
!.greyLoop:
!movq xmm0,[Esi]
!punpcklbw xmm0,xmm7
!pmulhuw xmm0,xmm6
!movdqa xmm1,xmm0
!movdqa xmm2,xmm0
!psllq xmm1,16
!paddw xmm0,xmm1 ; add green
!psllq xmm2,32
!paddw xmm0,xmm2 ; add blue
!pshuflw xmm0,xmm0,10101010b
!pshufhw xmm0,xmm0,10101010b
!pand xmm0,xmm5
!packuswb xmm0,xmm7
!movq [Esi],xmm0
ADD Esi,8
SUB Eax,1
!JNZ .greyLoop
ProcedureReturn 1
EndProcedure
hBmp2=CreateImage(1, 4000, 4000, 32)
bmp2.BITMAP
n=GetObject_(hBmp2, SizeOf(BITMAP), bmp2)
perfFrequency.q
perfCount.q
lastPerfCount.q
queryPerformanceFrequency_(@perfFrequency)
queryPerformanceCounter_(@lastPerfCount.q)
perfFrequency/1000
rs=bmpToGray(bmp2)
; GrayScaleImg(1)
queryPerformanceCounter_(@perfCount.q)
time.q=(perfCount-lastPerfCount)/perfFrequency
If rs
MessageRequester("Zeit", Str(time)+" milliseconds")
Else
MessageRequester("Error", "Error")
EndIf
@ Dige
What Time do you get now?
I have 83ms on P4-925D
Posted: Sun Mar 09, 2008 1:21 am
by Michael Vogel
Hi otto,
good start (if your message count is not a fake;) for the show!
My notebook does obviously not have a SEwhateverCPU, I'll get an error. But thats not the point - more important is, that there is another "maniac" here in our forum, you're welcome on board!
Michael