GaussianBlur filter

Share your advanced PureBasic knowledge/code with the community.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

GaussianBlur filter

Post by wilbert »

It's pretty fast but not perfect (especially on images with transparency) :wink:

Gaussian.pbi

Code: Select all

; Gaussian.pbi (requires MMX)

Procedure Gaussian1D(*PixelBuf32, NumPixels, NextPixelOffset = 4)
  
  ; Matrix : 1 - 4 - 6 - 4 - 1 (/16)
  
  ; exit if NumPixels < 3
  !mov ecx, [p.v_NumPixels]
  !sub ecx, 3
  !js gaussian1d_exit
  !mov eax, [p.v_NextPixelOffset]  
  
  ; initial setup
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    !mov rdx, [p.p_PixelBuf32]
    !movd mm1, [rdx]
    !movd mm4, [rdx + rax]
  CompilerElse
    !mov edx, [p.p_PixelBuf32]
    !movd mm1, [edx]
    !movd mm4, [edx + eax]
  CompilerEndIf
  !pxor mm7, mm7
  !pxor mm6, mm6
  !punpcklbw mm1, mm6
  !punpcklbw mm4, mm6
  !movq mm2, mm1
  !movq mm3, mm1
  
  ; loop
  !gaussian1d_loop0:
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    !movd mm5, [rdx + rax * 2]
  CompilerElse
    !movd mm5, [edx + eax * 2]
  CompilerEndIf
  !punpcklbw mm5, mm6
  !gaussian1d_loop1:
  !movq mm0, mm2
  !paddw mm0, mm3
  !paddw mm0, mm4
  !psllw mm0, 2
  !paddw mm0, mm1
  !paddw mm0, mm3
  !paddw mm0, mm3
  !paddw mm0, mm5
  !paddw mm0, mm7
  !movq mm7, mm0
  !psraw mm0, 4
  !packuswb mm0, mm0
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    !movd [rdx], mm0
    !add rdx, rax
  CompilerElse
    !movd [edx], mm0
    !add edx, eax
  CompilerEndIf
  !movq mm1, mm2
  !movq mm2, mm3
  !movq mm3, mm4
  !movq mm4, mm5
  !punpcklbw mm0, mm6
  !psllw mm0, 4
  !psubw mm7, mm0
  !dec ecx
  !jns gaussian1d_loop0
  !cmp ecx, -3
  !jne gaussian1d_loop1
  !gaussian1d_exit:
  !emms
  
EndProcedure

Procedure Gaussian2D(*PixelBuf32, Width, Height, BufferPitch = 0)
  
  ; 1 -  4 -  6 -  4 - 1
  ; 4 - 16 - 24 - 16 - 4
  ; 6 - 24 - 36 - 24 - 4
  ; 4 - 16 - 24 - 16 - 4
  ; 1 -  4 -  6 -  4 - 1
  
  If BufferPitch = 0
    BufferPitch = Width << 2
  EndIf
  
  Protected i.i = 0
  While i < Height
    Gaussian1D(*PixelBuf32 + BufferPitch * i, Width)
    i + 1  
  Wend
  
  i = 0
  While i < Width
    Gaussian1D(*PixelBuf32 + i << 2, Height, BufferPitch)
    i + 1  
  Wend
  
EndProcedure

Procedure GaussianBlur(Image, Strength = 1)
  
  Protected.i i, w, h, x, y, max_x, max_y
  
  If StartDrawing(ImageOutput(Image))
    w = OutputWidth()
    h = OutputHeight()
    
    If DrawingBufferPixelFormat() & $60; 32 bit buffer ?
      For i = 0 To Strength
        Gaussian2D(DrawingBuffer(), w, h, DrawingBufferPitch())
      Next
    Else
      max_x = w - 1
      max_y = h - 1
      Dim Buffer.l(max_y, max_x)
      DrawingMode(#PB_2DDrawing_AllChannels)
      For y = 0 To max_y
        For x = 0 To max_x
          Buffer(y, x) = Point(x, y)
        Next
      Next
      For i = 0 To Strength
        Gaussian2D(@Buffer(), w, h)
      Next
      For y = 0 To max_y
        For x = 0 To max_x
          Plot(x, y, Buffer(y, x))
        Next
      Next
    EndIf
    StopDrawing()
    
  EndIf
  
EndProcedure
Example

Code: Select all

EnableExplicit

XIncludeFile "Gaussian.pbi"

UseJPEGImageDecoder()
UsePNGImageDecoder()

Define.i w, h

LoadImage(0, "MyImage.jpg")

w = ImageWidth(0)
h = ImageHeight(0)

GaussianBlur(0)

If OpenWindow(0, 0, 0, w, h, "GaussianBlur Filter", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  ImageGadget(0, 0, 0, w, h, ImageID(0))
  Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
Michael Vogel
Addict
Addict
Posts: 2806
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: GaussianBlur filter

Post by Michael Vogel »

Wilbert,

did you ever tuned your blur filter?

I try to speed up a shadow function for text by using your blur function instead of multiple DrawText calls and it works fine for dark shadows but not for light ones.

The following example shows a simplified version for fast text shadows, press '1' to '5' to see different black shadow examples and '6' to '9' for a texts with a white shadow. The white shadow gets the edges darkend...

Code: Select all

#Intensity=$A0

Procedure.i iMin(ia.i,ib.i)

	EnableASM
	CompilerIf #PB_Compiler_Processor=#PB_Processor_x86
		MOV eax,ia
		CMP eax,ib
		CMOVG eax,ib
	CompilerElse
		MOV rax,ia
		CMP rax,ib
		CMOVG rax,ib
	CompilerEndIf
	DisableASM
	ProcedureReturn

EndProcedure
Procedure.i iMax(ia.i,ib.i)

	EnableASM
	CompilerIf #PB_Compiler_Processor=#PB_Processor_x86
		MOV eax,ia
		CMP eax,ib
		CMOVNG eax,ib
	CompilerElse
		MOV rax,ia
		CMP rax,ib
		CMOVNG rax,ib
	CompilerEndIf
	DisableASM
	ProcedureReturn

EndProcedure
Procedure Gaussian1D(*PixelBuf32,NumPixels,NextPixelOffset=4)

	!mov ecx, [p.v_NumPixels]
	!sub ecx, 3
	!js gaussian1d_exit
	!mov eax, [p.v_NextPixelOffset]

	CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
		!mov rdx, [p.p_PixelBuf32]
		!movd mm1, [rdx]
		!movd mm4, [rdx + rax]
	CompilerElse
		!mov edx, [p.p_PixelBuf32]
		!movd mm1, [edx]
		!movd mm4, [edx + eax]
	CompilerEndIf
	!punpcklbw mm1, mm1
	!punpcklbw mm4, mm4
	!psrlw mm1, 4
	!psrlw mm4, 4
	!movq mm2, mm1
	!movq mm3, mm1

	; loop
	!gaussian1d_loop0:
	CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
		!movd mm5, [rdx + rax * 2]
	CompilerElse
		!movd mm5, [edx + eax * 2]
	CompilerEndIf
	!punpcklbw mm5, mm5
	!psrlw mm5, 4
	!gaussian1d_loop1:
	!movq mm0, mm2
	!paddw mm0, mm3
	!paddw mm0, mm4
	!psllw mm0, 2
	!paddw mm0, mm1
	!paddw mm0, mm3
	!paddw mm0, mm3
	!paddw mm0, mm5
	!psrlw mm0, 8
	!packuswb mm0, mm0
	CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
		!movd [rdx], mm0
		!add rdx, rax
	CompilerElse
		!movd [edx], mm0
		!add edx, eax
	CompilerEndIf
	!movq mm1, mm2
	!movq mm2, mm3
	!movq mm3, mm4
	!movq mm4, mm5
	!dec ecx
	!jns gaussian1d_loop0
	!cmp ecx, -3
	!jne gaussian1d_loop1
	!gaussian1d_exit:
	!emms

EndProcedure
Procedure Gaussian2D(*PixelBuf32,Width,Height,BufferPitch=0)

	If BufferPitch=0
		BufferPitch=Width<<2
	EndIf

	Protected i.i=0
	While i<Height
		Gaussian1D(*PixelBuf32 + BufferPitch * i, Width)
		i+1
	Wend

	i=0
	While i<Width
		Gaussian1D(*PixelBuf32+i<<2,Height,BufferPitch)
		i+1
	Wend

EndProcedure
Procedure GaussianBlur(Image,Strength=2)

	Protected.i i, w, h, x, y, max_x, max_y

	If StartDrawing(ImageOutput(image))
		w = OutputWidth()
		h = OutputHeight()

		If DrawingBufferPixelFormat() & $60; 32 bit buffer ?
			For i=0 To Strength
				Gaussian2D(DrawingBuffer(), w, h,  DrawingBufferPitch())
			Next

		Else
			max_x=w-1
			max_y=h-1

			Dim Buffer.l(max_y, max_x)
			DrawingMode(#PB_2DDrawing_AllChannels)

			For y=0 To max_y
				For x = 0 To max_x
					Buffer(y,x)=Point(x,y)
				Next
			Next

			For i=0 To Strength
				Gaussian2D(@Buffer(),w,h)
			Next

			For y=0 To max_y
				For x=0 To max_x
					Plot(x,y,Buffer(y,x))
				Next
			Next
		EndIf
		StopDrawing()

	EndIf

EndProcedure

Procedure CreateText(x,y,text.s,color,shadow,depth,offset)

	Protected weight

	color|$FF000000

	tw=TextWidth(text)
	th=TextHeight(text)
	StopDrawing()

	weight=iMax(0,(shadow>>24)&$FF)
	shadow=shadow&$FFFFFF

	offset*th/50

	If depth
		depth*iMax(1,th/50)
		gauss=iMin(1+depth+(weight>>3),100)
	Else
		depth=1
		gauss=0
	EndIf

	CreateImage(1,tw+gauss<<1,th+gauss<<1,32,#PB_Image_Transparent)
	StartDrawing(ImageOutput(1))
	DrawingFont(FontID(0))
	DrawingMode(#PB_2DDrawing_AlphaBlend)
	DrawText(gauss,gauss,text,shadow|(weight<<24),#Null)
	StopDrawing()

	GaussianBlur(1,gauss)

	CreateImage(2,tw,th,32,#PB_Image_Transparent)
	StartDrawing(ImageOutput(2))
	DrawingFont(FontID(0))
	DrawingMode(#PB_2DDrawing_AlphaBlend)
	DrawText(0,0,text,color|$FF000000,#Null)
	StopDrawing()

	StartDrawing(ImageOutput(0))
	DrawingFont(FontID(0))
	DrawingMode(#PB_2DDrawing_AlphaBlend)

	If gauss

		x0=x-gauss
		y0=y-gauss
		DrawImage(ImageID(1),x0+offset+depth,y0+offset)
		DrawImage(ImageID(1),x0+offset-depth,y0+offset)
		DrawImage(ImageID(1),x0+offset,y0+offset+depth)
		DrawImage(ImageID(1),x0+offset,y0+offset-depth)
		If 0
			dep30=0.8660*depth
			depth=depth>>1
			DrawImage(ImageID(1),x0+offset+dep30,y0+offset+depth)
			DrawImage(ImageID(1),x0+offset+depth,y0+offset+dep30)
			DrawImage(ImageID(1),x0+offset+dep30,y0+offset-depth)
			DrawImage(ImageID(1),x0+offset+depth,y0+offset-dep30)
			DrawImage(ImageID(1),x0+offset-dep30,y0+offset+depth)
			DrawImage(ImageID(1),x0+offset-depth,y0+offset+dep30)
			DrawImage(ImageID(1),x0+offset-dep30,y0+offset-depth)
			DrawImage(ImageID(1),x0+offset-depth,y0+offset-dep30)
		Else
			depth=0.7071*depth
			DrawImage(ImageID(1),x0+offset+depth,y0+offset+depth)
			DrawImage(ImageID(1),x0+offset+depth,y0+offset-depth)
			DrawImage(ImageID(1),x0+offset-depth,y0+offset+depth)
			DrawImage(ImageID(1),x0+offset-depth,y0+offset-depth)
		EndIf

	Else
		DrawImage(ImageID(1),x+offset,y+offset)

	EndIf

	DrawImage(ImageID(2),x,y)

EndProcedure
Procedure DoText(test)
	
	LoadFont(0,"Segoe UI",20*8,#PB_Font_HighQuality)
	CreateImage(0,1400,500,32,#Blue)

	StartDrawing(ImageOutput(0))
	For i=0 To 1399
		LineXY(i,0,i,499,Random(#White)|$8080c0)
	Next i
	DrawingFont(FontID(0))
	DrawingMode(#PB_2DDrawing_AlphaBlend)
	
	width=(test-'1')%5
	If width>2
		offset=width-1
		width=2
	Else
		offset=0
	EndIf
	width+1
	
	Select test
	Case '1' To '5'
		CreateText(20,0,"shadow : )",#White,#Intensity<<24+#Black,width,offset)
	Case '6' To '9'
		CreateText(20,0,"shadow : (",#Black,#Intensity<<24+#White,width,offset)
	Default
		CreateText(20,0,"press '1' to '9' !",#White,$1f<<24+#Blue,1,3)
	EndSelect

	StopDrawing()
	
	SetGadgetState(0,ImageID(0))
	
EndProcedure

OpenWindow(0,0,0,1400,500,"")
ImageGadget(0,0,0,1400,500,0)
DoText(0)

Repeat
	Select WaitWindowEvent()
	Case #WM_CHAR
		Select EventwParam()
		Case ' ',#ESC
			End
		Case '0' To '9'
			DoText(EventwParam())
		EndSelect

	EndSelect

ForEver
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: GaussianBlur filter

Post by wilbert »

Michael,
I'm not sure what you mean by tuning the filter.

As for your code, the problem is that the surrounding pixels of the text are transparent black instead of transparent white for the white shadow.
Try adding these two lines

Code: Select all

   DrawingMode(#PB_2DDrawing_AllChannels)
   Box(0,0,OutputWidth(),OutputHeight(), shadow & $ffffff)  
like this ...

Code: Select all

   ...

   CreateImage(1,tw+gauss<<1,th+gauss<<1,32,#PB_Image_Transparent)
   StartDrawing(ImageOutput(1))
   
   DrawingMode(#PB_2DDrawing_AllChannels)
   Box(0,0,OutputWidth(),OutputHeight(), shadow & $ffffff)  
   
   DrawingFont(FontID(0))
   DrawingMode(#PB_2DDrawing_AlphaBlend)
   DrawText(gauss,gauss,text,shadow|(weight<<24),#Null)
   StopDrawing()

   GaussianBlur(1,gauss)

   ...
Nice effect :D
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
Michael Vogel
Addict
Addict
Posts: 2806
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: GaussianBlur filter

Post by Michael Vogel »

T h a n k _ Y o u !
Post Reply