Page 1 of 1

Edge blur

Posted: Sun Mar 26, 2017 2:21 pm
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

Re: Edge blur

Posted: Fri Mar 31, 2017 12:15 pm
by Mijikai
Thanks for sharing.

Re: Edge blur

Posted: Fri Mar 31, 2017 2:15 pm
by Thunder93
... like Mijikai, once more I'm wanting to show my appreciations to Keya for the superb shared code. Thank you.

Re: Edge blur

Posted: Fri Mar 31, 2017 2:25 pm
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.....