Edge blur

Share your advanced PureBasic knowledge/code with the community.
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Edge blur

Post by Keya »

just a simple idea i had today and wanted to see how it'd turn out. The goal is to blur only the edges of any object, by detecting transition to/from the background color which is known a priori. For example, when a white background changes to black at the start or end of a part of a character, or the start and end of a circle. By design it ONLY blurs the edge areas, not the full image. I didn't design this with textual anti-aliasing in mind, just any-object edge-blurring, but i guess it can also be used for that to some extent.

It uses a standard 3x3 Gaussian filter for the blurring, but rather than use a 3x3 edge detection filter such as Sobel im just using a very simple transition detection, which can be seen in the EdgeDetect() procedure, but i find this approach more reliable than Sobel etc filters for this particular task because i don't want to detect all edges - only the ones relating to the background.

It's 3-pass, linear time... i'm not sure if that means it's "n(3)" time complexity? however, each pixel modified by a 3x3 convolution filter requires reading 9 other pixels, so maybe not quite! Passes 1 & 2 perform the horizontal and vertical edge detection, and pass 3 performs the gaussian blur, but only on pixels marked by pass 1 or 2.

It supports two modes: 1 pixel and 2 pixel (when it detects an edge in 2-pixel mode it also marks the appropriate neighboring pixel). There's plenty of room for improvement, I wasn't writing it with speed in mind but it's not bad.

Here is a screenshot mashup of 1) Original, 2) 1-pixel mode, 3) 2-pixel mode (and partial ZOOMED-IN of each also):
Image

Here's a better (?) example showing how the main content is preserved - only the edges are blurred, as opposed to the entire image like a typical blur:

EdgeBlur.pbi

Code: Select all

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  Macro BlueMacro(color):  ((color & $00FF0000) >> 16): EndMacro
  Macro GreenMacro(color): ((color & $0000FF00) >> 8): EndMacro
  Macro RedMacro(color):   (color &  $000000FF): EndMacro
CompilerElse
  Macro RedMacro(color):   ((color & $00FF0000) >> 16): EndMacro
  Macro GreenMacro(color): ((color & $0000FF00) >> 8): EndMacro
  Macro BlueMacro(color):  (color &  $000000FF): EndMacro
CompilerEndIf

#DOUBLEPIXELBLUR=1

Global Dim EdgeMatrix.f(0,0)
Global Dim ConvMatrix.f(2,2)

Procedure.l GaussianBlur(*buf, x,y, w,h, pitch, pixsize=4)
  Protected fpxl.f, numpixels
  Protected r.f, g.f, b.f, rnext, gnext, bnext
  For yy = -1 To 1    ;\ Good candidates for unrolling
    For xx = -1 To 1  ;/
      filter.f = ConvMatrix(xx+1,yy+1)
      curx = x + xx: cury = y + yy
      If curx < 0: curx = 0: EndIf
      If curx => w: curx = w: EndIf
      If cury < 0: cury = 0: EndIf
      If cury => h: cury = h: EndIf        
      CompilerIf #PB_Compiler_OS = #PB_OS_Windows
        color.l = PeekL(*buf + (h * pitch) - (cury * pitch) + (curx * pixsize)) ;Point(curx,cury)
      CompilerElse
        color.l = PeekL(*buf + (cury * pitch) + (curx * pixsize)) ;Point(curx,cury)
      CompilerEndIf      
      rnext = RedMacro(color)
      gnext = GreenMacro(color)
      bnext = BlueMacro(color)
      CompilerIf #PB_Compiler_OS = #PB_OS_Windows
        Swap rnext,bnext
      CompilerEndIf          
      r + (rnext * filter)
      g + (gnext * filter)
      b + (bnext * filter)              
    Next xx
  Next yy
  color.l = RGB(r,g,b)
  ProcedureReturn color
EndProcedure
;Gaussian Blur
ConvMatrix(0,0) = 0.0625:  ConvMatrix(0,1) = 0.125:  ConvMatrix(0,2) = 0.0625
ConvMatrix(1,0) = 0.125:   ConvMatrix(1,1) = 0.25:  ConvMatrix(1,2)  = 0.125
ConvMatrix(2,0) = 0.0625:  ConvMatrix(2,1) = 0.125:  ConvMatrix(2,2) = 0.0625



Procedure SetEdgeMatrix(x,y,w,h, direction, endedge)
  If x => 0 And y => 0
    EdgeMatrix(x,y) = 1   ;the main (centroid) pixel
    
    CompilerIf #DOUBLEPIXELBLUR  ;also blur the appropriate neighbor
      If direction = 0           ;horizontal pass
        If endedge
          If x < w: EdgeMatrix(x+1,y) = 1: EndIf
        Else
          If x > 0: EdgeMatrix(x-1,y) = 1: EndIf
        EndIf
      Else             ;Vertical pass
        If endedge
          If y < h: EdgeMatrix(x,y+1) = 1: EndIf
        Else
          If y > 0: EdgeMatrix(x,y-1) = 1: EndIf
        EndIf    
      EndIf
    CompilerEndIf
  EndIf
EndProcedure


Procedure EdgeDetect(bgColor, x,y, w,h, state, direction)
  color = Point(x,y)
  If color <> bgColor
    If state = 0 ;new edge
      state = 1
      SetEdgeMatrix(x,y,w,h, direction, 0)
    EndIf
  Else ;color = bgColor
    If state = 1 ;end of edge
      state = 0
      If direction
        SetEdgeMatrix(x,y-1,w,h, direction, 1)
      Else
        SetEdgeMatrix(x-1,y,w,h, direction, 1)
      EndIf
    EndIf
  EndIf
  ProcedureReturn state
EndProcedure


Procedure BlurBackgroundEdgeMatrix(hImg, bgColor, width, height, pitch, pixsize=4)  
  If hImg And StartDrawing(ImageOutput(hImg))    
    If width>0 And height>0      
      Protected w,h, state
      w=width-1
      h=height-1
      Global Dim EdgeMatrix(w,h)   
      For y = 0 To h        ;### PASS 1 - Horizontal Edge Detect
        For x = 0 To w
          state = EdgeDetect(bgColor, x,y, width,height, state, 0)
        Next x
      Next y    
      state=0                ;### PASS 2 - Vertical Edge Detect
      For x = 0 To w
        For y = 0 To h
          state = EdgeDetect(bgColor, x,y, width,height, state, 1)
        Next y    
      Next x          
      *buf = DrawingBuffer()
      StopDrawing()      
      hImg2 = CreateImage(#PB_Any, width, height, pixsize*8)
      If hImg2
        If StartDrawing(ImageOutput(hImg2))      
          For y = 0 To h
            For x = 0 To w        ;### PASS 3 - Gaussian Blur (guided)
              If EdgeMatrix(x,y) > 0.0
                color.l = GaussianBlur(*buf, x,y, w,h, pitch, pixsize) ;EdgeMatrix(x,y)
                Plot(x,y, color)
              Else                
                CompilerIf #PB_Compiler_OS = #PB_OS_Windows
                  color.l = PeekL(*buf + (h * pitch) - (y * pitch) + (x * pixsize)) ;Point(curx,cury)                  
                CompilerElse
                  color.l = PeekL(*buf + (y * pitch) + (x * pixsize))
                CompilerEndIf                
                r = RedMacro(color)
                g = GreenMacro(color)
                b = BlueMacro(color)
                CompilerIf #PB_Compiler_OS = #PB_OS_Windows
                  Swap r,b
                CompilerEndIf               
                Plot(x,y, RGB(r,g,b)) ;Point(curx,cury)                
              EndIf
            Next x    
          Next y 
        EndIf
        StopDrawing()
        ProcedureReturn hImg2
      EndIf      
    EndIf    
  EndIf  
EndProcedure
EdgeBlurDemo.pb

Code: Select all

XIncludeFile("EdgeBlur.pbi")

bgColor = RGB(255,255,255)

If OpenWindow(0, 0, 0, 500, 300, "Edge Blur", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  hImg = CreateImage(#PB_Any, 50, 60, 32, bgColor) 
  If hImg And StartDrawing(ImageOutput(hImg))
    Circle(25,25, 11, RGB(50,50,50))
    pitch = DrawingBufferPitch()
    StopDrawing() 
    ImageGadget(0, 30, 30, 200, 200, ImageID(hImg))
    
    hImg2 = BlurBackgroundEdgeMatrix(hImg, bgColor, ImageWidth(hImg), ImageHeight(hImg), pitch, ImageDepth(hImg)/8)
    ImageGadget(1, 30, 100, 200, 200, ImageID(hImg2))
  EndIf  
  
  hFont = LoadFont(#PB_Any, "Arial", 18)
  hImg3 = CreateImage(#PB_Any, 100, 40, 32, bgColor) 
  If hImg3 And StartDrawing(ImageOutput(hImg3)) 
    pitch = DrawingBufferPitch()
    DrawingMode(#PB_2DDrawing_Transparent)
    If hFont: DrawingFont(FontID(hFont)): EndIf
    DrawText(0,0,"Text 123",RGB(0,0,255))
    StopDrawing()
    ImageGadget(2, 100, 10, 200, 200, ImageID(hImg3))
    
    hImg4 = BlurBackgroundEdgeMatrix(hImg3, bgColor, ImageWidth(hImg3), ImageHeight(hImg3), pitch, ImageDepth(hImg)/8)
    ImageGadget(3, 100, 90, 200, 200, ImageID(hImg4))
  EndIf
  
EndIf
Repeat
  Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow
You can also use other 3x3 blur filters as opposed to the standard gaussian. For example, a general smoothing one ready for copy-and-paste over the original:

Code: Select all

ConvMatrix(0,0) = 0.11111:  ConvMatrix(0,1) = 0.11111:  ConvMatrix(0,2) = 0.11111
ConvMatrix(1,0) = 0.11111:  ConvMatrix(1,1) = 0.11111:  ConvMatrix(1,2) = 0.11111
ConvMatrix(2,0) = 0.11111:  ConvMatrix(2,1) = 0.11111:  ConvMatrix(2,2) = 0.11111
See also: you might also be interested in this Median blur of sorts i also came up with
Last edited by Keya on Tue Mar 28, 2017 6:56 am, edited 7 times in total.
User avatar
Mijikai
Addict
Addict
Posts: 1360
Joined: Sun Sep 11, 2016 2:17 pm

Re: Edge blur

Post by Mijikai »

Thanks for sharing.
User avatar
Thunder93
Addict
Addict
Posts: 1788
Joined: Tue Mar 21, 2006 12:31 am
Location: Canada

Re: Edge blur

Post by Thunder93 »

... like Mijikai, once more I'm wanting to show my appreciations to Keya for the superb shared code. Thank you.
ʽʽSuccess is almost totally dependent upon drive and persistence. The extra energy required to make another effort or try another approach is the secret of winning.ʾʾ --Dennis Waitley
User avatar
Rings
Moderator
Moderator
Posts: 1427
Joined: Sat Apr 26, 2003 1:11 am

Re: Edge blur

Post by Rings »

Rings wrote:
Fred wrote:Removed the picture, it has no place on this forum.
Fred wrote:Please focus on the original topic, any other comments will be deleted.
i delete all un'topic' stuff now, to keep the forum ....
and locked.....
SPAMINATOR NR.1
Locked