Is that the fastest way to image grayscale?

Everything else that doesn't fall into one of the other PB categories.
dige
Addict
Addict
Posts: 1247
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Is that the fastest way to image grayscale?

Post 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
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post 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
I may look like a mule, but I'm not a complete ass.
User avatar
tinman
PureBasic Expert
PureBasic Expert
Posts: 1102
Joined: Sat Apr 26, 2003 4:56 pm
Location: Level 5 of Robot Hell
Contact:

Post 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 
If you paint your butt blue and glue the hole shut you just themed your ass but lost the functionality.
(WinXPhSP3 PB5.20b14)
dige
Addict
Addict
Posts: 1247
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Post by dige »

@tinman: yeah! :D we'll come closer: 453ms
thank you!
dige
Addict
Addict
Posts: 1247
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Post 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 
User avatar
tinman
PureBasic Expert
PureBasic Expert
Posts: 1102
Joined: Sat Apr 26, 2003 4:56 pm
Location: Level 5 of Robot Hell
Contact:

Post by tinman »

dige wrote:But why is RGBQUADLong faster?
Because you write all pixels in one operation rather than 3.
If you paint your butt blue and glue the hole shut you just themed your ass but lost the functionality.
(WinXPhSP3 PB5.20b14)
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post 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)
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post 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!
I may look like a mule, but I'm not a complete ass.
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post 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
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post 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
hellhound66
Enthusiast
Enthusiast
Posts: 119
Joined: Tue Feb 21, 2006 12:37 pm

Post by hellhound66 »

Removed.
Last edited by hellhound66 on Wed Mar 19, 2008 11:28 pm, edited 1 time in total.
dige
Addict
Addict
Posts: 1247
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Post 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
hellhound66
Enthusiast
Enthusiast
Posts: 119
Joined: Tue Feb 21, 2006 12:37 pm

Post by hellhound66 »

So there is a difference between a loaded and a created image? WTF!
So ImageID!=ImageID?
otto
New User
New User
Posts: 5
Joined: Mon Sep 26, 2005 11:13 pm

Post 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
User avatar
Michael Vogel
Addict
Addict
Posts: 2666
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Post 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! :wink:

Michael
Post Reply