Photoshop Levels filter

Share your advanced PureBasic knowledge/code with the community.
Seymour Clufley
Addict
Addict
Posts: 1233
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Photoshop Levels filter

Post by Seymour Clufley »

Photoshop has a filter called "Levels" for adjusting the contrast range in an image. Based on this pseudocode, I have written this emulation of it. The procedure operates on a specified rectangular area of the image.

The input parameters which would result in no change to the image are:
ShadowValue = 0
MidtoneValue = 128
HighlightValue = 255
Increasing the ShadowValue will emphasise the darks in the image. Decreasing the HighlightValue will emphasise the brights. Altering the MidtoneValue will affect the mid tones.

Code: Select all

Macro ContainThis(num,min,max)
  If num<min
      num=min
  EndIf
  If num>max
      num=max
  EndIf
EndMacro

Macro ApplyPhotoshopLevelsToChannel(ChannelValueVar)
  ; apply input levels
  ChannelValueVar = 255 * ( ( ChannelValueVar - ShadowValue ) / ( HighlightValue - ShadowValue ) )
  ; apply midtones
  If MidtoneValue <> 128
    ChannelValueVar = 255 * ( Pow( ( ChannelValueVar / 255 ), GammaCorrection ) )
  EndIf
  ; apply output levels
  ChannelValueVar = ( ChannelValueVar / 255 ) * ( OutHighlightValue - OutShadowValue ) + OutShadowValue
  
  ContainThis(ChannelValueVar,0,255)
EndMacro

Procedure.b PhotoshopLevels(x,y,w,h,ShadowValue.d,MidtoneValue.d,HighlightValue.d,OutShadowValue.d=0,OutHighlightValue.d=255)
  DrawingMode(#PB_2DDrawing_AllChannels)
  GammaCorrection.d = 1
  For dx = x To x+w
    For dy = y To y+h
      clr = Point(dx,dy)
      
      ChannelValueR.d = Red(clr)
      ApplyPhotoshopLevelsToChannel(ChannelValueR)
      ChannelValueG.d = Green(clr)
      ApplyPhotoshopLevelsToChannel(ChannelValueG)
      ChannelValueB.d = Blue(clr)
      ApplyPhotoshopLevelsToChannel(ChannelValueB)
      
      ChannelValueA.i = Alpha(clr)
      clr = RGBA(ChannelValueR,ChannelValueG,ChannelValueB,ChannelValueA)
      Plot(dx,dy,clr)
    Next dy
  Next dx
  ProcedureReturn #True
EndProcedure
PhotoshopLevelsHSL() is an alternative version which uses the HSL colour space instead of RGB:

Code: Select all

Procedure.b PhotoshopLevelsHSL(x,y,w,h,ShadowValue.d=0,MidtoneValue.d=128,HighlightValue.d=255,OutShadowValue.d=0,OutHighlightValue.d=255)
  DrawingMode(#PB_2DDrawing_AllChannels)
  GammaCorrection.d = 1
  minlum.d = 500
  maxlum.d = -500
  NewMap converted.i()
  For dx = x To x+w
    For dy = y To y+h
      clr = Point(dx,dy)
      
      key.s = Str(clr)
      If FindMapElement(converted(),key)
        Plot(dx,dy,converted(key))
        Continue
      EndIf
      
      CheckHSLAMem(key)
      ChannelValueL.d = hsla_rem(key)\lum
      DefeatThis(minlum,ChannelValueL)
      BeatThis(maxlum,ChannelValueL)
      ApplyPhotoshopLevelsToChannel(ChannelValueL)
      ChannelValueA.i = Alpha(clr)
      converted(key) = HSL(hsla_rem(key)\hue,hsla_rem(key)\sat,ChannelValueL,ChannelValueA)
      Plot(dx,dy,converted(key))
    Next dy
  Next dx
  ProcedureReturn #True
EndProcedure
There's also this procedure, for increasing/decreasing alpha gradients:

Code: Select all

Procedure.b PhotoshopLevelsAlpha(x,y,w,h,ShadowValue.d,MidtoneValue.d,HighlightValue.d,OutShadowValue.d=0,OutHighlightValue.d=255)
  DrawingMode(#PB_2DDrawing_AlphaChannel)
  GammaCorrection.d = 1
  For dx = x To x+w
    For dy = y To y+h
      ChannelValueA.d = Alpha(Point(dx,dy))
      ApplyPhotoshopLevelsToChannel(ChannelValueA)
      clr = RGBA(0,0,0,ChannelValueA)
      Plot(dx,dy,clr)
    Next dy
  Next dx
  ProcedureReturn #True
EndProcedure
JACK WEBB: "Coding in C is like sculpting a statue using only sandpaper. You can do it, but the result wouldn't be any better. So why bother? Just use the right tools and get the job done."