2D: Looking for a faster image gamma routine

Just starting out? Need help? Post your questions and find answers here.
dige
Addict
Addict
Posts: 1417
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

2D: Looking for a faster image gamma routine

Post by dige »

Hi guys,

until PB version 6.04 I used the userlib Mtgfx from Neotoma for the image gamma effect.
Unfortunately, this no longer works since version 6.10 x86 and the author is no longer active here.
His lib was incredibly fast. Needs only 20ms for one 12 MP image.

I have created a replacement for the gamma effect and it needs 500ms for the same image.
Can someone do it even faster?

Here you can download the lib and the sources: https://u.pcloud.link/publink/show?code ... LEUujxwxyy

Code: Select all

; Created by ChatGPT 4o
Procedure ApplyGamma(ImageID.i, Gamma.f)
  If IsImage(ImageID)
    ; Get the image dimensions
    Width.i = ImageWidth(ImageID)
    Height.i = ImageHeight(ImageID)
    
    ; Calculate gamma correction factors
    InverseGamma.f = 1.0 / Gamma
    
    ; Start drawing on the image
    StartDrawing(ImageOutput(ImageID))
    For y = 0 To Height - 1
      For x = 0 To Width - 1
        ; Get the color of the current pixel
        Color = Point(x, y)
        
        ; Extract RGB components
        R = Red(Color)
        G = Green(Color)
        B = Blue(Color)
        
        ; Apply gamma correction
        R = Pow(R / 255.0, InverseGamma) * 255
        G = Pow(G / 255.0, InverseGamma) * 255
        B = Pow(B / 255.0, InverseGamma) * 255
        
        ; Combine corrected RGB components back into a single color
        NewColor = RGB(R, G, B)
        
        ; Set the new color to the current pixel
        Plot(x, y, NewColor)
      Next
    Next
    ; Finish drawing on the image
    StopDrawing()
  Else
    MessageRequester("Error", "Invalid Image ID", #PB_MessageRequester_Ok)
  EndIf
EndProcedure

LoadImage(0, "C:\Temp\Gamma.bmp")
time = ElapsedMilliseconds()
ApplyGamma(0, 2.2)
MessageRequester("done", Str(ElapsedMilliseconds() - time) + "ms")
"Daddy, I'll run faster, then it is not so far..."
User avatar
chi
Addict
Addict
Posts: 1087
Joined: Sat May 05, 2007 5:31 pm
Location: Austria

Re: 2D: Looking for a faster image gamma routine

Post by chi »

The lib still works but there are linker errors...

Code: Select all

Mtgfx.lib(matrix.obj) : error LNK2019: unresolved external symbol __ftoul referenced in function _PB_MT_mask2tint2@28
Mtgfx.lib(matrix.obj) : error LNK2019: unresolved external symbol __ftouc referenced in function _PB_MT_Gamma@16
Mtgfx.lib(geometry.obj) : error LNK2001: unresolved external symbol __ftouc
Mtgfx.lib(matrix.obj) : error LNK2019: unresolved external symbol _sqrtd referenced in function _colour_distance
Mtgfx.lib(matrix.obj) : error LNK2001: unresolved external symbol _floorl
Mtgfx.lib(geometry.obj) : error LNK2001: unresolved external symbol _floorl
The quick & dirty solution would be to import and old SystemBase.lib

Code: Select all

Import "D:\Program Files\PureBasic5.73(x86)\Compilers\SystemBase.lib"
EndImport
but there could be problems down the line

Code: Select all

SystemBase.lib(alloca_probe_16_WASM.obj) : warning LNK4006: ___CxxFrameHandler3 already defined in libvcruntime.lib(trnsctrl.obj); second definition ignored
LINK : warning LNK4217: symbol '___CxxFrameHandler' defined in 'libvcruntime.lib(trnsctrl.obj)' is imported by 'SystemBase.lib(alloca_probe_16_WASM.obj)' in function '___CxxFrameHandler3'
PureBasic_Compilation1.exe : warning LNK4088: image being generated due to /FORCE option; image may not run
so the safer way:
Open old SystemBase.lib with 7zip, extract ftouc.obj, ftoul.obj, sqrtd.obj, floorl.obj, isinfl.obj, feraiseexcept.obj, Aliases_WASM.obj and import them

Code: Select all

Import "ftouc.obj" : EndImport
Import "ftoul.obj" : EndImport
Import "sqrtd.obj" : EndImport
Import "floorl.obj" : EndImport
Import "isinfl.obj" : EndImport
Import "feraiseexcept.obj" : EndImport
Import "Aliases_WASM.obj" : EndImport
Et cetera is my worst enemy
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3943
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: 2D: Looking for a faster image gamma routine

Post by wilbert »

dige wrote: Sun Jun 16, 2024 3:38 pm Can someone do it even faster?
You can use a lookup table to make things faster

Code: Select all

Procedure ApplyGamma(ImageID.i, Gamma.f)
  Protected Dim GammaLUT.a(255)
  Protected.f g_inv
  Protected.a r, g, b
  Protected.l i, max_x, max_y, color
  
  If IsImage(ImageID)
    g_inv = 1.0 / Gamma
    For i = 0 To 255
      GammaLUT(i) = Pow(i / 255.0, g_inv) * 255.0
    Next

    max_x = ImageWidth(ImageID) - 1
    max_y = ImageHeight(ImageID) - 1
    
    ; Start drawing on the image
    StartDrawing(ImageOutput(ImageID))
    For y = 0 To max_y
      For x = 0 To max_x
        ; Get the color of the current pixel
        color = Point(x, y)
        
        ; Lookup new RGB components
        r = GammaLUT(color & 255)
        g = GammaLUT((color >> 8) & 255)
        b = GammaLUT((color >> 16) & 255)
        
        ; Set the new color to the current pixel
        Plot(x, y, (b << 16)|(g << 8)|r)
      Next
    Next
    ; Finish drawing on the image
    StopDrawing()
  Else
    MessageRequester("Error", "Invalid Image ID", #PB_MessageRequester_Ok)
  EndIf
EndProcedure
Windows (x64)
Raspberry Pi OS (Arm64)
infratec
Always Here
Always Here
Posts: 7662
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: 2D: Looking for a faster image gamma routine

Post by infratec »

I modified wilberts version to use a CustomFilter.
This was faster (160ms .> 110ms)
Of course without debugger.

Code: Select all

EnableExplicit


Global Dim GammaLUT.a(255)


Procedure FilterCallback(x, y, QuellFarbe.l, ZielFarbe.l)
  ProcedureReturn RGBA(GammaLUT(Red(QuellFarbe)), GammaLUT(Green(QuellFarbe)), GammaLUT(Blue(QuellFarbe)), 255)
EndProcedure



Procedure ApplyGamma(Image.i, Gamma.f)
  
  Protected.f g_inv
  Protected.i i
  
  
  If IsImage(Image)
    g_inv = 1.0 / Gamma
    For i = 0 To 255
      GammaLUT(i) = Pow(i / 255.0, g_inv) * 255.0
    Next
    
    ; Start drawing on the image
    If StartDrawing(ImageOutput(Image))
      
      DrawingMode(#PB_2DDrawing_CustomFilter)
      CustomFilterCallback(@FilterCallback())
      
      DrawImage(ImageID(Image), 0, 0)
      
      StopDrawing()
    EndIf
  Else
    MessageRequester("Error", "Invalid Image ID", #PB_MessageRequester_Ok)
  EndIf
  
EndProcedure



Define.q StartT, EndT
Define Filename$

UseJPEGImageDecoder()

Filename$ = OpenFileRequester("Choose a JPG file", "", "JPG|*.jpg", 0)
If Filename$
  If LoadImage(0, Filename$)
    
    OpenWindow(0, 0, 0, 800, 600, "", #PB_Window_MinimizeGadget|#PB_Window_ScreenCentered)
    CopyImage(0, 1)
    ResizeImage(1, 400, 400)
    ImageGadget(0, 0, 0, 400, 400, ImageID(1))
    ImageGadget(1, 400, 0, 400, 400, 0)
    
    StartT = ElapsedMilliseconds()
    ApplyGamma(0, 0.5)
    EndT = ElapsedMilliseconds()
    
    CopyImage(0, 2)
    ResizeImage(2, 400, 400)
    
    SetGadgetState(1, ImageID(2))
    
    MessageRequester("Time", Str(EndT - StartT))
    
    Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
    
  EndIf
EndIf
Btw. your ChatGPT solution took 1485ms :mrgreen:
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3943
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: 2D: Looking for a faster image gamma routine

Post by wilbert »

This one should be very fast but only works fine if the image has no alpha channel or if all alpha values are either 0 or 255.

Code: Select all

Procedure ApplyGamma(ImageID.i, Gamma.f)
  ; -= Fast gamma correction =-
  ; Image should have no alpha channel
  ; or all alpha values should be either 0 or 255
  Protected Dim GammaLUT.a(255)
  Protected g_inv.f, i.i, *c.Ascii, *e
  
  If IsImage(ImageID)
    ; Create gamma lookup table
    g_inv = 1.0 / Gamma
    For i = 0 To 255
      GammaLUT(i) = Pow(i / 255.0, g_inv) * 255.0
    Next
    ; Update the image pixels
    StartDrawing(ImageOutput(ImageID))
    *c = DrawingBuffer()
    *e = DrawingBufferPitch() * OutputHeight() + *c
    While *c <> *e
      *c\a = GammaLUT(*c\a)
      *c + 1
    Wend
    StopDrawing()
  Else
    MessageRequester("Error", "Invalid Image ID", #PB_MessageRequester_Ok)
  EndIf
EndProcedure

This one is a bit more complicated but also works on transparent images.

Code: Select all

Structure s_cc
  c0.a
  c1.a
  c2.a
EndStructure

Procedure ApplyGamma(ImageID.i, Gamma.f)
  ; -= Fast gamma correction =-
  Protected Dim GammaLUT.a(255)
  Protected g_inv.f, *c.s_cc, *e, *l
  Protected.i i, n, m, w
  
  If IsImage(ImageID)
    ; Create gamma lookup table
    g_inv = 1.0 / Gamma
    For i = 0 To 255
      GammaLUT(i) = Pow(i / 255.0, g_inv) * 255.0
    Next
    ; Update the image pixels
    StartDrawing(ImageOutput(ImageID))
    *c = DrawingBuffer()
    *e = DrawingBufferPitch() * OutputHeight() + *c
    n = ImageDepth(ImageID) >> 3
    w = n * OutputWidth()
    m = DrawingBufferPitch() - w
    While *c <> *e
      *l = *c + w
      While *c <> *l
        *c\c0 = GammaLUT(*c\c0)
        *c\c1 = GammaLUT(*c\c1)
        *c\c2 = GammaLUT(*c\c2)
        *c + n
      Wend
      *c + m
    Wend
    StopDrawing()
  Else
    MessageRequester("Error", "Invalid Image ID", #PB_MessageRequester_Ok)
  EndIf
EndProcedure
Windows (x64)
Raspberry Pi OS (Arm64)
dige
Addict
Addict
Posts: 1417
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: 2D: Looking for a faster image gamma routine

Post by dige »

Thank you guys!

Thanks to @Chi for the "hack" to get an old Lib running again. I also learnt something new about the PB internal things.
@Infratec, thanks for pointing out the possibility of the CustomFilterCallback().
Thanks @Wilbert, your implementation is really incredibly fast! Even faster than the old lib. That's great!

:D
"Daddy, I'll run faster, then it is not so far..."
dige
Addict
Addict
Posts: 1417
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: 2D: Looking for a faster image gamma routine

Post by dige »

Since I also need a new contrast function, I had Wilbert's quick code from ChatGPT rewritten:

Code: Select all

; Based on Wilbert's Gamma Code
; Modified by ChatGPT 4o

UseJPEGImageDecoder()

Structure s_cc
  c0.a
  c1.a
  c2.a
EndStructure

; Clamp function to ensure values are within the 0-255 range
Procedure.f Clamp(value.f, min.f, max.f)
  If value < min
    ProcedureReturn min
  ElseIf value > max
    ProcedureReturn max
  Else
    ProcedureReturn value
  EndIf
EndProcedure

Procedure ApplyContrast(ImageID.i, Contrast.f)
  ; -= Fast contrast correction =-
  Protected Dim ContrastLUT.a(255)
  Protected *c.s_cc, *e, *l
  Protected.i i, n, m, w
  
  If IsImage(ImageID)
    ; Create contrast lookup table
    For i = 0 To 255
      Value.f = i / 255.0
      Value - 0.5
      Value * Contrast
      Value + 0.5
      Value * 255.0
      Value = Clamp(Value, 0, 255)
      ContrastLUT(i) = Value
    Next
    ; Update the image pixels
    StartDrawing(ImageOutput(ImageID))
    *c = DrawingBuffer()
    *e = DrawingBufferPitch() * OutputHeight() + *c
    n = ImageDepth(ImageID) >> 3
    w = n * OutputWidth()
    m = DrawingBufferPitch() - w
    While *c <> *e
      *l = *c + w
      While *c <> *l
        *c\c0 = ContrastLUT(*c\c0)
        *c\c1 = ContrastLUT(*c\c1)
        *c\c2 = ContrastLUT(*c\c2)
        *c + n
      Wend
      *c + m
    Wend
    StopDrawing()
  Else
    MessageRequester("Error", "Invalid Image ID", #PB_MessageRequester_Ok)
  EndIf
EndProcedure



; Example usage
; Load an image
LoadImage(0, "Gamma.jpg")
time = ElapsedMilliseconds()
; Apply contrast correction with a contrast value of 1.5 (greater than 1 increases contrast, between 0 and 1 decreases contrast)
ApplyContrast(0, 1.5)
MessageRequester( "Contrast done", Str(ElapsedMilliseconds() - time))
; Save the corrected image
SaveImage(0, "corrected_example.bmp")
"Daddy, I'll run faster, then it is not so far..."
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3943
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: 2D: Looking for a faster image gamma routine

Post by wilbert »

dige wrote: Mon Jun 17, 2024 8:55 am Since I also need a new contrast function, I had Wilbert's quick code from ChatGPT rewritten:
Nice that ChatGPT can do that for you. :)

If you often need to apply contrast and gamma both on the same image, you can do it at the same time to improve speed.
Brightness correction could also be included to do at the same time.
It is important in this case in what order you use the corrections.
For example brightness -> contrast -> gamma or gamma -> contrast -> brightness.
Windows (x64)
Raspberry Pi OS (Arm64)
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4991
Joined: Sun Apr 12, 2009 6:27 am

Re: 2D: Looking for a faster image gamma routine

Post by RASHAD »

RASHAD GPT 20 ms :D

Code: Select all

DisableDebugger

UseJPEG2000ImageDecoder()
UseJPEG2000ImageEncoder()
UseJPEGImageDecoder()
UseJPEGImageEncoder()
UsePNGImageDecoder()
UsePNGImageEncoder()
UseTGAImageDecoder()
UseTIFFImageDecoder()
UseGIFImageDecoder()

Global Dim bits.a(0), Pitch,Count,Width,Height,Trim

Procedure Effect_ON(img)
  StartDrawing(ImageOutput(img))
  *Buffer     = DrawingBuffer() 
  Pitch       = DrawingBufferPitch()
  count       = Pitch*height
  ReDim bits.a(count)
  CopyMemory(*Buffer,@bits(),count)
  StopDrawing()
EndProcedure

Procedure Effect_OFF(img)
  StartDrawing(ImageOutput(img))
  *Buffer     = DrawingBuffer() 
  CopyMemory(@bits(),*Buffer,count)
  StopDrawing()
EndProcedure

Procedure ColorTables (Array RedTable.a(1), Array GreenTable.a(1), Array  BlueTable.a(1), Array Bits.a(1),  Width, Height)
  For h = 0 To Height-1
    For w = 0 To Width-1
      i = h * Pitch + Trim * w
      Bits(i+2) = RedTable(Bits(i+2))
      Bits(i+1) = GreenTable(Bits(i+1))
      Bits( i ) = BlueTable(Bits( i ))
    Next
  Next	
EndProcedure

Procedure Gamma (img,Gamma.f) 
  width = ImageWidth(img)
  height = ImageHeight(img)
  If ImageDepth(img) = 32
    trim = 4
  Else
    trim = 3
  EndIf  
  
  Effect_ON(img)
  
  Dim gTable.a(256)  
  
  For i = 0 To 255
    gTable(i)= (255 * Pow((i / 255.0), (1.0 / Gamma)))
  Next
  ColorTables (gTable(), gTable(), gTable(), Bits(), Width, Height)
  Effect_OFF(img)
EndProcedure

; Procedure gadtip()
;   SetGadgetText(12,StrF(GetGadgetState(3)/10))
; EndProcedure

Procedure sizeCB()
  ResizeGadget(10,#PB_Ignore,#PB_Ignore,WindowWidth(0)-20,WindowHeight(0)-60)
  ResizeGadget(12,WindowWidth(0)/2-40,WindowHeight(0)-85,80,20)
  ResizeGadget(20,#PB_Ignore,#PB_Ignore,WindowWidth(0)-20,WindowHeight(0)-60)
  ResizeGadget(0,#PB_Ignore,#PB_Ignore,WindowWidth(0)-20,WindowHeight(0)-60)
  ResizeGadget(30,#PB_Ignore,WindowHeight(0)-40,#PB_Ignore,#PB_Ignore)  
  If IsGadget(6)
    ResizeGadget(3,275,WindowHeight(0)-38 ,250,24)
    ResizeGadget(6,535,WindowHeight(0)-38,250,24)
  Else
    ResizeGadget(3,GadgetX(20)+265,WindowHeight(0)-38,WindowWidth(0)-278,24)
  EndIf
EndProcedure

Pattern$ = "All supported formats|*.*;*.bmp; *.gif; *.jpg; *.jpeg; *.png;*.tif;*.tiff;*.tga|TGA image (*.tga)|*.tga|"+
           "TIF image (*.tif)|*.tif|TIFF image (*.tiff)|*.tiff|PNG image (*.png)|*.png|BMP image (*.bmp)|*.bmp|"+
           "JPEG image (*.jpg;*.jpeg)|*.jpg;*.jpeg|GIF image (*.gif)|*.gif|"

LoadFont(0,"tahoma",10)
OpenWindow(0,0,0,800,600,"Contrast 4 Image",#PB_Window_SystemMenu |#PB_Window_ScreenCentered | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget)
WindowBounds(0,600,400,#PB_Default,#PB_Default)
CanvasGadget(10,10,10,780,540,#PB_Canvas_Container)
TextGadget(12,WindowWidth(0)/2-50,WindowHeight(0)-85,100,20 ,"",#PB_Text_Center)
SetGadgetColor(12,#PB_Gadget_BackColor,0)
SetGadgetColor(12,#PB_Gadget_FrontColor,$FFFFFF)
SetGadgetFont(12,FontID(0))
ContainerGadget(20,0,0,780,540,#PB_Container_Flat)
ButtonImageGadget(0,-1,-1,780,540,0)
CloseGadgetList()
DisableGadget(20,1)    
CloseGadgetList()
TrackBarGadget (3,275,562 ,513,24,1,100,$0100)
SetGadgetState(3,5)
ContainerGadget(30,10,560,255,30)
ButtonGadget(1,0,0,60,30,"Open")
ButtonGadget(2,65,0,60,30,"Save")
TextGadget(4,130,6,60,20," W: 0")
SetGadgetFont(4,FontID(0))
TextGadget(5,195,6,60,20," H: 0")
SetGadgetFont(5,FontID(0))
CloseGadgetList()

scale.f = 1
;BindGadgetEvent(3,@gadTIP())
BindEvent(#PB_Event_SizeWindow,@sizeCB())
AddKeyboardShortcut(0,#PB_Shortcut_Up,10)
AddKeyboardShortcut(0,#PB_Shortcut_Down,20)
Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Quit = 1
      
    Case #PB_Event_Menu
      Select EventMenu()        
        Case 10
          If IsImage(0)
            CopyImage(0,1)
            If Run = 0 And scale < 10
              scale.f = scale.f + 0.01
            EndIf
            Run = 1
            SetGadgetAttribute(10, #PB_Canvas_Cursor ,#PB_Cursor_Busy)
            ResizeImage(1,ImageWidth(0)*scale,ImageHeight(0)*scale)
            SetGadgetText(4," W :"+Str(ImageWidth(1)))
            SetGadgetText(5," H :"+Str(ImageHeight(1)))   
            Gamma (1,GetGadgetState(3)/10)
            SetGadgetAttribute(0,#PB_Button_Image,ImageID(1))
            SetGadgetAttribute(10, #PB_Canvas_Cursor ,#PB_Cursor_Default)
            Run = 0
          EndIf
          
        Case 20
          If IsImage(0)
            CopyImage(0,1)
            If Run = 0 And scale > 0.1
              scale.f = scale.f - 0.01
            EndIf
            Run = 1
            SetGadgetAttribute(10, #PB_Canvas_Cursor ,#PB_Cursor_Busy)
            ResizeImage(1,ImageWidth(0)*scale,ImageHeight(0)*scale)
            SetGadgetText(4," W :"+Str(ImageWidth(1)))
            SetGadgetText(5," H :"+Str(ImageHeight(1)))  
            Gamma (1,GetGadgetState(3)/10)
            SetGadgetAttribute(0,#PB_Button_Image,ImageID(1))
            SetGadgetAttribute(10, #PB_Canvas_Cursor ,#PB_Cursor_Default)
            Run = 0
          EndIf
      EndSelect      
      
    Case #PB_Event_Gadget
      Select EventGadget()
        Case 1
          scale.f = 1
          FreeImage(#PB_All)
          SetGadgetAttribute(0,#PB_Button_Image,0)
          File$ = OpenFileRequester("Choose image file to load", "*.*", Pattern$, 0)
          If File$ And FileSize(File$)
            LoadImage(0,File$)
            If IsImage(0)
              time = ElapsedMilliseconds()  
              CopyImage(0,1)              
              Gamma (1,GetGadgetState(3)/10)
              SetGadgetText(4," W :"+Str(ImageWidth(1)))
              SetGadgetText(5," H :"+Str(ImageHeight(1)))
              SetGadgetAttribute(0,#PB_Button_Image,ImageID(1))
              SetActiveGadget(3)
              MessageRequester( "Contrast done", Str(ElapsedMilliseconds() - time))
            Else
              MessageRequester("Error","Format not supported", #PB_MessageRequester_Ok | #PB_MessageRequester_Error)
            EndIf
          EndIf
          
        Case 2
          If IsImage(1)
            sfile.s = SaveFileRequester("Please choose file to save",""," All supported formats|*.bmp; *.jpg; *.png | BMP image (*.bmp)| *.bmp| JPEG image (*.jpg;*.jpeg)|*.jpg| PNG image (*.png)| *.png",0)
            If sfile
              If GetExtensionPart(sfile) = ""
                If SelectedFilePattern() = 1 Or selectpattern = 1
                  sfile + ".bmp"
                ElseIf SelectedFilePattern() = 2 Or selectpattern = 2
                  sfile + ".jpg"
                ElseIf SelectedFilePattern() = 0 Or SelectedFilePattern() = 3 Or selectpattern = 3
                  sfile + ".png"
                EndIf
              EndIf               
              If GetExtensionPart(sfile) = "bmp"
                SaveImage(1, sfile ,#PB_ImagePlugin_BMP)
              ElseIf GetExtensionPart(sfile) = "jpg"
                SaveImage(1, sfile ,#PB_ImagePlugin_JPEG)
              ElseIf GetExtensionPart(sFile) = "png"
                SaveImage(1, sfile ,#PB_ImagePlugin_PNG)
              EndIf
              MessageRequester("Info","File saved successfully", #PB_MessageRequester_Ok | #PB_MessageRequester_Info)
            Else
              MessageRequester("Error","Process failed !", #PB_MessageRequester_Ok | #PB_MessageRequester_Error)
            EndIf
          Else
            MessageRequester("Error","No Image to Save !", #PB_MessageRequester_Ok | #PB_MessageRequester_Error)
          EndIf
          
        Case 3
          If IsImage(0)
            CopyImage(0,1)
            ResizeImage(1,ImageWidth(0)*scale,ImageHeight(0)*scale)
            SetGadgetText(4," W :"+Str(ImageWidth(1)))
            SetGadgetText(5," H :"+Str(ImageHeight(1)))              
            Gamma (1,GetGadgetState(3)/10)
            SetGadgetAttribute(0,#PB_Button_Image,ImageID(1))              
          EndIf
          
        Case 10
          Select EventType()
            Case #PB_EventType_MouseWheel
              If IsImage(0)
                CopyImage(0,1)
                delta = GetGadgetAttribute(10,#PB_Canvas_WheelDelta )
                If delta = 1 And Run = 0
                  If scale < 10          
                    scale.f = scale.f + 0.1        
                  EndIf         
                ElseIf delta = -1 And Run = 0       
                  If scale > 0.1
                    scale.f = scale.f - 0.1
                  EndIf         
                EndIf
                Run = 1
                SetGadgetAttribute(10, #PB_Canvas_Cursor ,#PB_Cursor_Busy)
                ResizeImage(1,ImageWidth(0)*scale,ImageHeight(0)*scale)
                SetGadgetText(4," W :"+Str(ImageWidth(1)))
                SetGadgetText(5," H :"+Str(ImageHeight(1)))
                Gamma (1,GetGadgetState(3)/10)
                SetGadgetAttribute(0,#PB_Button_Image,ImageID(1))
                SetGadgetAttribute(10, #PB_Canvas_Cursor ,#PB_Cursor_Default)
                Run = 0
              EndIf
          EndSelect              
      EndSelect
  EndSelect
Until Quit = 1
Egypt my love
dige
Addict
Addict
Posts: 1417
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: 2D: Looking for a faster image gamma routine

Post by dige »

I now have all 3 effects I need. If all are applied at the same time, I have 56ms here with the Gamma.jpg. That's really great!

Code: Select all

UseJPEGImageDecoder()

Structure s_cc
  c0.a
  c1.a
  c2.a
EndStructure

; Clamp function to ensure values are within the 0-255 range
Procedure.f Clamp(value.f, min.f, max.f)
  If value < min
    ProcedureReturn min
  ElseIf value > max
    ProcedureReturn max
  Else
    ProcedureReturn value
  EndIf
EndProcedure

Procedure.i ApplyContrast(ImageID.i, Contrast.f); Apply contrast correction With a contrast value of 1.5 (greater than 1 increases contrast, between 0 And 1 decreases contrast)
  
  ; -= Fast contrast correction =-
  
;     Die Prozedur ApplyContrast nimmt zwei Parameter entgegen: ImageID (die ID des Bildes) und Contrast (der Kontrastfaktor).
;     Eine Lookup-Tabelle (ContrastLUT) wird erstellt, um die vorab berechneten Kontrastwerte zu speichern.
;     Der Kontrastfaktor wird verwendet, um die Pixelwerte zu skalieren. Die Formel ((value - 0.5) * Contrast + 0.5) * 255 passt die Werte so an, dass sie um den Mittelwert (0.5) skaliert werden.
;     Die Clamp-Funktion stellt sicher, dass die berechneten Werte innerhalb des gültigen Bereichs von 0 bis 255 liegen.
;     StartDrawing initialisiert den Zeichenmodus für das Bild, und DrawingBuffer gibt einen Zeiger auf den Bildspeicher zurück.
;     Die Schleifen durchlaufen die Bilddaten und wenden die Kontrastkorrektur mithilfe der Lookup-Tabelle an.
;     StopDrawing beendet den Zeichenmodus.

  
  Protected Dim ContrastLUT.a(255)
  Protected *c.s_cc, *e, *l
  Protected.i i, n, m, w
  
  If IsImage(ImageID)
    ; Create contrast lookup table
    For i = 0 To 255
      Value.f = i / 255.0
      Value - 0.5
      Value * Contrast
      Value + 0.5
      Value * 255.0
      Value = Clamp(Value, 0, 255)
      ContrastLUT(i) = Value
    Next
    ; Update the image pixels
    StartDrawing(ImageOutput(ImageID))
    *c = DrawingBuffer()
    *e = DrawingBufferPitch() * OutputHeight() + *c
    n = ImageDepth(ImageID) >> 3
    w = n * OutputWidth()
    m = DrawingBufferPitch() - w
    While *c <> *e
      *l = *c + w
      While *c <> *l
        *c\c0 = ContrastLUT(*c\c0)
        *c\c1 = ContrastLUT(*c\c1)
        *c\c2 = ContrastLUT(*c\c2)
        *c + n
      Wend
      *c + m
    Wend
    StopDrawing()
  Else
    ProcedureReturn #False
  EndIf
  
  ProcedureReturn #True
EndProcedure

Procedure ApplyGamma(ImageID.i, Gamma.f)
  ; -= Fast gamma correction =-
  
;     Eine Lookup-Tabelle (GammaLUT) wird erstellt, um die vorab berechneten Gamma-Werte zu speichern.
;     Der Inverse des Gamma-Wertes wird berechnet und verwendet, um die Lookup-Tabelle zu füllen.
;     StartDrawing initialisiert den Zeichenmodus für das Bild und DrawingBuffer gibt einen Zeiger auf den Bildspeicher zurück.
;     Die Schleifen durchlaufen die Bilddaten und wenden die Gamma-Korrektur mithilfe der Lookup-Tabelle an.
;     Der Code berücksichtigt auch die Bildtiefe und die Pufferbreite, um sicherzustellen, dass alle Pixel korrekt verarbeitet werden.
;     StopDrawing beendet den Zeichenmodus.

  
  Protected Dim GammaLUT.a(255)
  Protected g_inv.f, *c.s_cc, *e, *l
  Protected.i i, n, m, w
  
  If IsImage(ImageID)
    ; Create gamma lookup table
    g_inv = 1.0 / Gamma
    For i = 0 To 255
      GammaLUT(i) = Pow(i / 255.0, g_inv) * 255.0
    Next
    ; Update the image pixels
    StartDrawing(ImageOutput(ImageID))
    *c = DrawingBuffer()
    *e = DrawingBufferPitch() * OutputHeight() + *c
    n = ImageDepth(ImageID) >> 3
    w = n * OutputWidth()
    m = DrawingBufferPitch() - w
    While *c <> *e
      *l = *c + w
      While *c <> *l
        *c\c0 = GammaLUT(*c\c0)
        *c\c1 = GammaLUT(*c\c1)
        *c\c2 = GammaLUT(*c\c2)
        *c + n
      Wend
      *c + m
    Wend
    StopDrawing()
  Else
    ProcedureReturn #False
  EndIf
  
  ProcedureReturn #True
EndProcedure


Procedure ApplyBrightness(ImageID.i, Brightness.f)
  ; -= Fast brightness correction =-
  
  ; Die Prozedur ApplyBrightness nimmt zwei Parameter entgegen: ImageID (die ID des Bildes) und Brightness (der Helligkeitsfaktor).
  ; Eine Lookup-Tabelle (BrightnessLUT) wird erstellt, um die vorab berechneten Helligkeitswerte zu speichern.
  ; Der Helligkeitsfaktor wird verwendet, um die Pixelwerte zu verschieben. Die Formel i + (Brightness * 255.0) passt die Werte entsprechend an.
  ; Die Clamp-Funktion stellt sicher, dass die berechneten Werte innerhalb des gültigen Bereichs von 0 bis 255 liegen.
  ; StartDrawing initialisiert den Zeichenmodus für das Bild, und DrawingBuffer gibt einen Zeiger auf den Bildspeicher zurück.
  ; Die Schleifen durchlaufen die Bilddaten und wenden die Helligkeitskorrektur mithilfe der Lookup-Tabelle an.
  ; StopDrawing beendet den Zeichenmodus.
  
  Protected Dim BrightnessLUT.a(255)
  Protected *c.s_cc, *e, *l
  Protected.i i, n, m, w
  
  If IsImage(ImageID)
    ; Create brightness lookup table
    For i = 0 To 255
      Value.f = i + (Brightness * 255.0)
      Value = Clamp(Value, 0, 255)
      BrightnessLUT(i) = Value
    Next
    ; Update the image pixels
    StartDrawing(ImageOutput(ImageID))
    *c = DrawingBuffer()
    *e = DrawingBufferPitch() * OutputHeight() + *c
    n = ImageDepth(ImageID) >> 3
    w = n * OutputWidth()
    m = DrawingBufferPitch() - w
    While *c <> *e
      *l = *c + w
      While *c <> *l
        *c\c0 = BrightnessLUT(*c\c0)
        *c\c1 = BrightnessLUT(*c\c1)
        *c\c2 = BrightnessLUT(*c\c2)
        *c + n
      Wend
      *c + m
    Wend
    StopDrawing()
  Else
    ProcedureReturn #False
  EndIf
  
  ProcedureReturn #True
EndProcedure

Define.q StartT, EndT
Define Filename$

UseJPEGImageDecoder()

Filename$ = OpenFileRequester("Choose a JPG file", "", "JPG|*.jpg", 0)
If Filename$
  If LoadImage(0, Filename$)
    
    OpenWindow(0, 0, 0, 800, 600, "", #PB_Window_MinimizeGadget|#PB_Window_ScreenCentered)
    CopyImage(0, 1)
    CopyImage(0, 3)
    
    ResizeImage(1, 400, 400)
    ImageGadget(0, 0, 0, 400, 400, ImageID(1))
    ImageGadget(1, 400, 0, 400, 400, 0)
    
    TrackBarGadget(2, 0, 440, 400, 20, 0, 255, #PB_TrackBar_Ticks) : TextGadget(5, 400, 440, 400, 20, "Gamma:") : SetGadgetState(2, 100)
    TrackBarGadget(3, 0, 470, 400, 20, 0, 255, #PB_TrackBar_Ticks) : TextGadget(6, 400, 470, 400, 20, "Brightness:") : SetGadgetState(3, 100)
    TrackBarGadget(4, 0, 500, 400, 20, 0, 255, #PB_TrackBar_Ticks) : TextGadget(7, 400, 500, 400, 20, "Contrast:") : SetGadgetState(4, 100)
    
    Repeat 
      Event = WaitWindowEvent()
      
      If Event = #PB_Event_Gadget
        
        OverallTime = ElapsedMilliseconds()
        
        StartT = ElapsedMilliseconds()
        ApplyGamma(0, GetGadgetState(2) / 10)
        EndT = ElapsedMilliseconds()
        SetGadgetText(5, "Gamma: " + StrF(GetGadgetState(2) / 10, 1) + "  |  " + Str(EndT-StartT) + " ms")
        
        StartT = ElapsedMilliseconds()
        ApplyBrightness(0, (GetGadgetState(3)-125) / 100)
        EndT = ElapsedMilliseconds()
        SetGadgetText(6, "Brightness: " + StrF((GetGadgetState(3)-125) / 100, 1) + "  |  " + Str(EndT-StartT) + " ms")
        
        StartT = ElapsedMilliseconds()
        ApplyContrast(0, GetGadgetState(4) / 100)  
        EndT = ElapsedMilliseconds()
        SetGadgetText(7, "Contrast: " + StrF(GetGadgetState(4) / 100, 1) + "  |  " + Str(EndT-StartT) + " ms")        
        
        SetWindowTitle(0, "Processing time: " + Str(ElapsedMilliseconds() - OverallTime) + "ms")
        
        CopyImage(0, 2)
        ResizeImage(2, 400, 400)
        SetGadgetState(1, ImageID(2))
        
        
;         ; Preview
;         CopyImage(1, 0)
        
        ; Performance test with original image size
        CopyImage(3, 0)
      EndIf
      
      
      
    Until Event = #PB_Event_CloseWindow
    
  EndIf
EndIf
"Daddy, I'll run faster, then it is not so far..."
dige
Addict
Addict
Posts: 1417
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: 2D: Looking for a faster image gamma routine

Post by dige »

RASHAD wrote: Mon Jun 17, 2024 11:57 am RASHAD GPT 20 ms :D
:D 👍
"Daddy, I'll run faster, then it is not so far..."
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3943
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: 2D: Looking for a faster image gamma routine

Post by wilbert »

dige wrote: Mon Jun 17, 2024 12:06 pm I now have all 3 effects I need. If all are applied at the same time, I have 56ms here with the Gamma.jpg. That's really great!
What I meant is that you can do something like this

Code: Select all

Structure s_cc
  c0.a
  c1.a
  c2.a
EndStructure

Procedure ApplyBrightnessContrastGamma(ImageID, Brightness.f = 0.0, Contrast.f = 1.0, Gamma.f = 1.0)
  ; -= Fast brightness, contrast and gamma correction =-
  Protected Dim LUT.a(255)
  Protected f.f, g_inv.f, *c.s_cc, *e, *l
  Protected.i n, m, w
  
  If IsImage(ImageID)
    ; Create lookup table
    g_inv = 1.0 / Gamma
    For n = 0 To 255
      f = Pow((((n / 255.0) + Brightness) - 0.5) * Contrast + 0.5, g_inv) * 255.0
      If f < 0.0
        LUT(n) = 0
      ElseIf f > 255.0
        LUT(n) = 255
      Else
        LUT(n) = f
      EndIf      
    Next
    ; Update the image pixels
    StartDrawing(ImageOutput(ImageID))
    *c = DrawingBuffer()
    *e = DrawingBufferPitch() * OutputHeight() + *c
    n = ImageDepth(ImageID) >> 3
    w = n * OutputWidth()
    m = DrawingBufferPitch() - w
    While *c <> *e
      *l = *c + w
      While *c <> *l
        *c\c0 = LUT(*c\c0)
        *c\c1 = LUT(*c\c1)
        *c\c2 = LUT(*c\c2)
        *c + n
      Wend
      *c + m
    Wend
    StopDrawing()
    ProcedureReturn #True
  Else
    ProcedureReturn #False
  EndIf
EndProcedure
So you need to call only one procedure instead of three.
Windows (x64)
Raspberry Pi OS (Arm64)
Fred
Administrator
Administrator
Posts: 18351
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: 2D: Looking for a faster image gamma routine

Post by Fred »

You should also compile with C backend + optimisation, it's 3x faster here (with wilbert routine).
dige
Addict
Addict
Posts: 1417
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: 2D: Looking for a faster image gamma routine

Post by dige »

Fred wrote: Mon Jun 17, 2024 4:02 pm You should also compile with C backend + optimisation, it's 3x faster here (with wilbert routine).
Yes, indeed. That's amazing! :D

Unfortunately, I can't compile my actual project with the C backend: Error: unknown typename 'i_idirect3ddevice9'.
May be I am using DirectX9 as a subsystem.
"Daddy, I'll run faster, then it is not so far..."
Fred
Administrator
Administrator
Posts: 18351
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: 2D: Looking for a faster image gamma routine

Post by Fred »

You should try to narrow your code to isolate the error as it seems to be a bug
Post Reply