Convolution filters/matrix/kernels if you're unaware are one of the common ways to apply a variety of effects such as blur, sharpen, emboss, edge detect, etc, with the one algorithm, just different values in an array (3x3 in this case).
See https://en.wikipedia.org/wiki/Kernel_(image_processing) for more info.
Anyway here is my version, along with 60 different effects. Windows/Linux/Mac, 24/32-bit images, x86/x64, Ascii/Unicode, weekdays/weekends.
The results should match that of Adobe Photoshop, which you can verify in Photoshop by going to "Filter menu -> Other -> Custom", and you'll see a 5x5 convolution kernel. Leave the outer (5x5) values blank and just set the values within the 3x3 area.
Here's an example using one of the emboss kernels. To properly see the effect of all the filters it's best to try them on photos as opposed to 2d 'flat-color' art (works ok for emboss though), but its just easiest to link to the purebasiclogo.bmp that comes with PB:
Example.pb
Code: Select all
XIncludeFile("ConvolutionFilter.pbi")
;1. Create or load an image to modify
Imgfile$ = #PB_Compiler_Home + "examples/sources/Data/PureBasicLogo.bmp" ;not very good for testing filters, use a photo!
hImg1 = LoadImage(#PB_Any, Imgfile$)
If Not hImg1
MessageRequester("Error","Couldnt load " + Imgfile$): End
EndIf
;2 - Initialise the 3x3 kernel
kernelid = #K3x3_EMBOSS_v4
kernelflip = #KERNEL_FLIPH ;usually #KERNEL_NOFLIP (0)
If Not LoadKernel3x3(kernelid, kernelflip): MessageRequester("Error","Invalid kernel id"): End: EndIf
;3. Create convolution filtered image. Convolution filters like these cannot modify the
; original without adversely distorting the result, so they must output to a 2nd image.
hImg2 = ConvolutionFilterImage(hImg1)
;Display
width=ImageWidth(hImg1): height=ImageHeight(hImg1)
If OpenWindow(0, 0, 0, (width)+20, (height*2)+30, "3x3 Convolution Filter", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ImageGadget(#PB_Any, 10, 10, width, height, ImageID(hImg1))
ImageGadget(#PB_Any, 10, 20+height, width, height, ImageID(hImg2))
Repeat
Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow
EndIf
ConvolutionFilter.pbi
Code: Select all
XIncludeFile("Kernels3x3.pbi")
Global Dim ConvMatrix.f(2,2)
Global ConvScale.f, ConvOffset.a
#KERNELDATASIZE = 41 ;bytes. No real gains in this case from aligning to 44.
#KERNEL_NOFLIP = 0
#KERNEL_FLIPV = 1
#KERNEL_FLIPH = 2
Procedure LoadKernel3x3(kernelid, flip=0)
*krn = ?KERNELS_3x3_BEGIN + (kernelid * #KERNELDATASIZE)
If *krn <= (?KERNELS_3x3_END - #KERNELDATASIZE)
ConvMatrix(0,0) = PeekF(*krn): ConvMatrix(0,1) = PeekF(*krn+4): ConvMatrix(0,2) = PeekF(*krn+8)
ConvMatrix(1,0) = PeekF(*krn+12): ConvMatrix(1,1) = PeekF(*krn+16): ConvMatrix(1,2) = PeekF(*krn+20)
ConvMatrix(2,0) = PeekF(*krn+24): ConvMatrix(2,1) = PeekF(*krn+28): ConvMatrix(2,2) = PeekF(*krn+32)
ConvScale = PeekF(*krn+36): ConvOffset= PeekA(*krn+40)
If flip=1 ;Vertical
For i = 0 To 2: Swap ConvMatrix(0,i), ConvMatrix(2,i): Next i
ElseIf flip=2 ;Horizontal
For i = 0 To 2: Swap ConvMatrix(i,0), ConvMatrix(i,2): Next i
EndIf
ProcedureReturn #True
EndIf
EndProcedure
Procedure.l ConvolvePixel(*imgbuf, X,Y, width,height, pitch, pixbytes)
Protected r.f, g.f, b.f, color.l, ar.a, ab.a, ag.a
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 => width: curx = width-1: EndIf
If cury < 0: cury = 0: EndIf
If cury => height: cury = height-1: EndIf
color.l = PeekL(*imgbuf + (cury * pitch) + (curx * pixbytes))
r + (Red(color) * filter)
g + (Green(color) * filter)
b + (Blue(color) * filter)
Next xx
Next yy
If ConvScale
r / ConvScale: g / ConvScale: b / ConvScale ;Scale
EndIf
If ConvOffset
r + ConvOffset: g + ConvOffset: b + ConvOffset ;Offset
EndIf
If r < 0: r = 0: EndIf: If r > 255: r = 255: EndIf ;Clamp
If g < 0: g = 0: EndIf: If g > 255: g = 255: EndIf
If b < 0: b = 0: EndIf: If b > 255: b = 255: EndIf
ar = r: ab = b: ag = g
color = (ab << 16 | ag << 8 | ar) | $FF000000
ProcedureReturn color
EndProcedure
Procedure ConvolutionFilterImage(hOrigImg)
Protected *buf, hImg, width, height
If hOrigImg And IsImage(hOrigImg)
If StartDrawing(ImageOutput(hOrigImg))
width=ImageWidth(hOrigImg): height=ImageHeight(hOrigImg)
*buf = DrawingBuffer()
depth = ImageDepth(hOrigImg)
StopDrawing()
EndIf
EndIf
If *buf
hImg = CreateImage(#PB_Any, width, height, depth)
If hImg
If StartDrawing(ImageOutput(hImg))
pitch = DrawingBufferPitch()
*out = DrawingBuffer()
Pixbytes = depth/8
w=width-1
h=height-1
For y = 0 To h
*pixelin = *buf + (y * pitch)
*pixelout = *out + (y * pitch)
For x = 0 To w
color.l = ConvolvePixel(*buf, X, Y, width,height, pitch, pixbytes)
PokeL(*pixelout, color)
*pixelin + Pixbytes
*pixelout + Pixbytes
Next x
Next y
StopDrawing()
EndIf
EndIf
EndIf
ProcedureReturn hImg
EndProcedure