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):
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
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
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