Convolution filter for image processing special effects

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

Convolution filter for image processing special effects

Post by Keya »

It seems code for convolution filters is virtually nonexistant, although from the two links in that google search there is a nice inline asm one by wilbert and another one by Crusiatus Black but thats about it it seems, no plug-and-play examples.

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


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
Last edited by Keya on Wed Mar 29, 2017 2:08 pm, edited 10 times in total.
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Re: Convolution filter for image processing special effects

Post by Keya »

Kernels, each one is a different special effect - there are currently 60.
If you have any more please consider sharing.

Kernels3x3.pbi

Code: Select all

Enumeration KERNELS3x3  ;IMPORTANT: Must be in the same order as they appear in the DataSection
  #K3x3_IDENTITY ;No change
  
  ;### Blur/smooth
  #K3x3_GAUSSIANBLUR_2
  #K3x3_GAUSSIANBLUR_3
  #K3x3_GAUSSIANBLUR_4
  #K3x3_GAUSSIANBLUR_6
  #K3x3_GAUSSIANBLUR_8
  #K3x3_GAUSSIANBLUR_10
  #K3x3_MOTIONBLUR
  #K3x3_MOTIONBLUR_RIGHT  
  #K3x3_MOTIONBLUR_LEFT
  #K3x3_SMOOTH_1
  #K3x3_SMOOTH_2
  #K3x3_SMOOTH_3
  #K3x3_SMOOTH_4
  #K3x3_MEANSMOOTH
  
  ;### Sharpen
  #K3x3_SHARPEN_15
  #K3x3_SHARPEN_20
  #K3x3_SHARPEN_30
  #K3x3_SHARPEN_50
  #K3x3_SHARPEN_MEANREMOVAL
  
  ;### Emboss/raise/extrude
  #K3x3_OUTLINE
  #K3x3_EMBOSS_v1
  #K3x3_EMBOSS_v2
  #K3x3_EMBOSS_v3
  #K3x3_EMBOSS_v4
  #K3x3_RAISED
  
  ;### Edge detect/enhance
  #K3x3_EDGEDETECT_HV
  #K3x3_EDGEDETECT_H
  #K3x3_EDGEDETECT_V
  #K3x3_EDGEDETECT_DIFFERENTIAL
  #K3x3_EDGEENHANCE_H
  #K3x3_EDGEENHANCE_V
  #K3x3_PREWITT_H
  #K3x3_PREWITT_V
  #K3x3_SOBEL_H
  #K3x3_SOBEL_V
  #K3x3_SOBELFELDMAN_H
  #K3x3_SOBELFELDMAN_V
  #K3x3_LAPLACE
  #K3x3_LAPLACE_INV
  #K3x3_LAPLACE_DIAGONAL
  #K3x3_SCHARR_H
  #K3x3_SCHARR_V
  #K3x3_EDGE360_KEYA
  #K3x3_GRADIENTDETECT_V
  #K3x3_GRADIENTDETECT_H
  
  ;### Brighten, darken
  #K3x3_BRIGHTEN_1
  #K3x3_BRIGHTEN_2
  #K3x3_BRIGHTEN_3
  #K3x3_BRIGHTEN_4
  #K3x3_BRIGHTEN_5
  #K3x3_DARKEN_1
  #K3x3_DARKEN_2
  #K3x3_DARKEN_3
  #K3x3_DARKEN_4
  #K3x3_DARKEN_5  
  #K3x3_DARKEN_6  
  #K3x3_DARKEN_7  
  #K3x3_DARKEN_8  
  #K3x3_DARKEN_9  
EndEnumeration




DataSection
  KERNELS_3x3_BEGIN:
  
  ;IMPORTANT: Must be in the same order as they appear in Enumeration KERNELS3x3
  ;---  
  
  K3x3_IDENTITY: ;aka Original (this filter does not result in any changes, it's like a NOP)
  ;
  Data.f 0, 0, 0
  Data.f 0, 1, 0
  Data.f 0, 0, 0
  Data.f 1
  Data.a 0  
  
  ;### Blur/smooth
  
  K3x3_GAUSSIANBLUR_2:
   Data.f 1, 2, 1
   Data.f 2, 2, 2
   Data.f 1, 2, 1
   Data.f 14.0
   Data.a 0
  
  K3x3_GAUSSIANBLUR_3:
   Data.f 1, 2, 1
   Data.f 2, 3, 2
   Data.f 1, 2, 1
   Data.f 15.0
   Data.a 0
   
  K3x3_GAUSSIANBLUR_4: ;Standard 3x3 gaussian model
   Data.f 1, 2, 1
   Data.f 2, 4, 2
   Data.f 1, 2, 1
   Data.f 16.0
   Data.a 0
   
  K3x3_GAUSSIANBLUR_6:
   Data.f 1, 2, 1
   Data.f 2, 6, 2
   Data.f 1, 2, 1
   Data.f 18.0
   Data.a 0
   
  K3x3_GAUSSIANBLUR_8:
   Data.f 1, 2, 1
   Data.f 2, 8, 2
   Data.f 1, 2, 1
   Data.f 20.0
   Data.a 0
   
  K3x3_GAUSSIANBLUR_10:
   Data.f 1, 2,  1
   Data.f 2, 10, 2
   Data.f 1, 2,  1
   Data.f 22.0
   Data.a 0   
  
  K3x3_MOTIONBLUR_RIGHT:
  Data.f 0, 0, 1
  Data.f 0, 0, 0
  Data.f 1, 0, 0
  Data.f 2.0
  Data.a 0
  
  K3x3_MOTIONBLUR_LEFT:
  Data.f 1, 0, 0
  Data.f 0, 0, 0
  Data.f 0, 0, 1
  Data.f 2.0
  Data.a 0
  
  K3x3_MOTIONBLUR:
  Data.f 0.3333, 0, 0
  Data.f 0, 0.3333, 0
  Data.f 0, 0, 0.3333
  Data.f 1.0
  Data.a 0
  
  K3x3_SMOOTH_1:
  Data.f 1.0,   1.0,    1.0
  Data.f 1.0,   5.0,    1.0
  Data.f 1.0,   1.0,    1.0
  Data.f 13.0
  Data.a 0
  
  K3x3_SMOOTH_2:
  Data.f 1.0,   1.0,    1.0
  Data.f 1.0,   4.0,    1.0
  Data.f 1.0,   1.0,    1.0
  Data.f 12.0
  Data.a 0  
    
  K3x3_SMOOTH_3:
  Data.f 1.0,   1.0,    1.0
  Data.f 1.0,   3.0,    1.0
  Data.f 1.0,   1.0,    1.0
  Data.f 11.0
  Data.a 0  
  
  K3x3_SMOOTH_4:
  Data.f 1.0,   1.0,    1.0
  Data.f 1.0,   2.0,    1.0
  Data.f 1.0,   1.0,    1.0
  Data.f 10.0
  Data.a 0
  
  K3x3_MEANSMOOTH: ;aka Average/Mean/Box Blur
  Data.f 1.0,   1.0,    1.0
  Data.f 1.0,   1.0,    1.0
  Data.f 1.0,   1.0,    1.0
  Data.f 9.0
  Data.a 0
  
  ;### Sharpen
  
  K3x3_SHARPEN_15:
  Data.f  0.0,   -0.125,   0.0
  Data.f -0.125,  1.5,    -0.125
  Data.f  0.0,   -0.125,   0.0
  Data.f 1.0
  Data.a 0
  
  K3x3_SHARPEN_20:
  Data.f  0.0,   -0.25,   0.0
  Data.f -0.25,   2.0,   -0.25
  Data.f  0.0,   -0.25,   0.0
  Data.f 1.0
  Data.a 0
  
  K3x3_SHARPEN_30:
  Data.f  0.0,  -0.5,   0.0
  Data.f -0.5,   3.0,  -0.5
  Data.f  0.0,  -0.5,   0.0
  Data.f 1.0
  Data.a 0  
  
  K3x3_SHARPEN_50:
  Data.f  0.0,  -1.0,   0.0
  Data.f -1.0,   5.0,  -1.0
  Data.f  0.0,  -1.0,   0.0
  Data.f 1.0
  Data.a 0
  
  K3x3_SHARPEN_MEANREMOVAL:      ;aka Mean Removal
  Data.f -1.0,  -1.0, -1.0
  Data.f -1.0,   9.0, -1.0
  Data.f -1.0,  -1.0, -1.0
  Data.f 1.0
  Data.a 0  
    
  ;### Emboss/raise/extrude
  
  K3x3_EXTRUDE:
  Data.f 1.0,   1.0,    1.0
  Data.f 1.0,   -7.0,    1.0
  Data.f 1.0,   1.0,    1.0
  Data.f 1.0
  Data.a 0    
  
  K3x3_EMBOSS_v1:
  Data.f -1,  -1,  0
  Data.f -1,   0,  1
  Data.f  0,   1,  1
  Data.f 9
  Data.a 128
  
  K3x3_EMBOSS_v2:
  Data.f  0, 0, 0
  Data.f -1, 0, 1
  Data.f  0, 0, 0
  Data.f 9
  Data.a 128
  
  K3x3_EMBOSS_v3:
  Data.f -1, -1,  0
  Data.f -1,  0,  1
  Data.f  0,  1,  1
  Data.f 9
  Data.a 128
  
  K3x3_EMBOSS_v4:
  Data.f -2, -1,  0
  Data.f -1,  1,  1
  Data.f  0,  1,  2
  Data.f 9
  Data.a 128    
  
  K3x3_RAISED:
  Data.f 0, 0, -2
  Data.f 0, 2, 0
  Data.f 1, 0, 0
  Data.f 1.0
  Data.a 0
    
  ;### Edge detect/enhance
  
  K3x3_EDGEDETECT_HV:      
  Data.f 0.0,  1.0,  0.0
  Data.f 1.0, -4.0,  1.0
  Data.f 0.0,  1.0,  0.0
  Data.f 1.0
  Data.a 0
  
  K3x3_EDGEDETECT_H:
  Data.f  0.0,  0.0,  0.0
  Data.f -1.0,  2.0, -1.0
  Data.f  0.0,  0.0,  0.0
  Data.f 1.0
  Data.a 0
  
  K3x3_EDGEDETECT_V:
  Data.f 0.0, -1.0, 0.0
  Data.f 0.0,  2.0, 0.0
  Data.f 0.0, -1.0, 0.0
  Data.f 1.0
  Data.a 0
  
  K3x3_EDGEDETECT_DIFFERENTIAL:
  Data.f -0.25, 0.0,  0.25
  Data.f  0.0,  0.0,  0.0
  Data.f  0.25, 0.0, -0.25
  Data.f 1.0
  Data.a 0
  
  K3x3_EDGEENHANCE_H:
  Data.f  0, 0, 0
  Data.f -1, 1, 0
  Data.f  0, 0, 0
  Data.f 1.0
  Data.a 0
  
  K3x3_EDGEENHANCE_V:
  Data.f  0,-1, 0
  Data.f  0, 1, 0
  Data.f  0, 0, 0
  Data.f 1.0
  Data.a 0
  
  K3x3_PREWITT_H:
  Data.f 1, 0, -1
  Data.f 1, 0, -1
  Data.f 1, 0, -1
  Data.f 9.0
  Data.a 0
  
  K3x3_PREWITT_V:
  Data.f  1,  1,  1
  Data.f  0,  0,  0
  Data.f -1, -1, -1
  Data.f 1.0
  Data.a 0
  
  K3x3_SOBEL_H:
  Data.f -1, 0, 1
  Data.f -2, 0, 2
  Data.f -1, 0, 1
  Data.f 1.0
  Data.a 0
  
  K3x3_SOBEL_V:
  Data.f  1,  2,  1
  Data.f  0,  0,  0
  Data.f -1, -2, -1
  Data.f 1.0
  Data.a 0
  
  K3x3_SOBELFELDMAN_H:
  Data.f 3,  0,  -3
  Data.f 10, 0, -10
  Data.f  3, 0,  -3
  Data.f 1.0
  Data.a 0
  
  K3x3_SOBELFELDMAN_V:
  Data.f  3,  10,  3
  Data.f  0,  0,   0
  Data.f -3, -10, -3
  Data.f 1.0
  Data.a 0
  
  K3x3_LAPLACE:
  Data.f 0,  1, 0
  Data.f 1, -4, 1
  Data.f 0,  1, 0
  Data.f 1.0
  Data.a 0  
  
  K3x3_LAPLACE_INV:
  Data.f  0, -1,  0
  Data.f -1,  4, -1
  Data.f  0, -1,  0
  Data.f 1.0
  Data.a 0
  
  K3x3_LAPLACE_DIAGONAL:
  Data.f 0.25, 0.5, 0.25
  Data.f 0.5, -3.0, 0.5
  Data.f 0.25, 0.5, 0.25
  Data.f 1.0
  Data.a 0
  
  K3x3_SCHARR_H:
  Data.f  3.0,  10.0,  3.0
  Data.f  0.0,   0.0,  0.0
  Data.f -3.0, -10.0, -3.0
  Data.f 1.0
  Data.a 0
  
  K3x3_SCHARR_V:
  Data.f -3.0, -10.0, -3.0
  Data.f  0.0,   0.0,  0.0
  Data.f  3.0,  10.0,  3.0
  Data.f 1.0
  Data.a 0
  
  K3x3_EDGE360_KEYA:
  Data.f -1, -1, -1
  Data.f -1,  8, -1
  Data.f -1, -1, -1
  Data.f 1.0
  Data.a 0  
  
  K3x3_GRADIENTDETECT_V:
  Data.f -1, -1, -1
  Data.f  0,  0,  0
  Data.f  1,  1,  1
  Data.f 1.0
  Data.a 0
  
  K3x3_GRADIENTDETECT_H:
  Data.f -1, 0, 1
  Data.f -1, 0, 1
  Data.f -1, 0, 1
  Data.f 1.0
  Data.a 0
  
  ;### Brighten, darken
  
  K3x3_BRIGHTEN_1:
  Data.f 0, 0,  0
  Data.f 0, 1.1,0
  Data.f 0, 0,  0
  Data.f 1
  Data.a 0  
  
  K3x3_BRIGHTEN_2:
  Data.f 0, 0,  0
  Data.f 0, 1.2,0
  Data.f 0, 0,  0
  Data.f 1
  Data.a 0  
  
  K3x3_BRIGHTEN_3:
  Data.f 0, 0,  0
  Data.f 0, 1.3,0
  Data.f 0, 0,  0
  Data.f 1
  Data.a 0  
    
  K3x3_BRIGHTEN_4:
  Data.f 0, 0,  0
  Data.f 0, 1.4,0
  Data.f 0, 0,  0
  Data.f 1
  Data.a 0  
    
  K3x3_BRIGHTEN_5:
  Data.f 0, 0,  0
  Data.f 0, 1.5,0
  Data.f 0, 0,  0
  Data.f 1
  Data.a 0  
      
  K3x3_DARKEN_1:
  Data.f 0, 0,  0
  Data.f 0, 0.9,0
  Data.f 0, 0,  0
  Data.f 1
  Data.a 0  
  
  K3x3_DARKEN_2:
  Data.f 0, 0,  0
  Data.f 0, 0.8,0
  Data.f 0, 0,  0
  Data.f 1
  Data.a 0  
  
  K3x3_DARKEN_3:
  Data.f 0, 0,  0
  Data.f 0, 0.7,0
  Data.f 0, 0,  0
  Data.f 1
  Data.a 0  
    
  K3x3_DARKEN_4:
  Data.f 0, 0,  0
  Data.f 0, 0.6,0
  Data.f 0, 0,  0
  Data.f 1
  Data.a 0  
    
  K3x3_DARKEN_5:
  Data.f 0, 0,  0
  Data.f 0, 0.5,0
  Data.f 0, 0,  0
  Data.f 1
  Data.a 0  
    
  K3x3_DARKEN_6:
  Data.f 0, 0,  0
  Data.f 0, 0.4,0
  Data.f 0, 0,  0
  Data.f 1
  Data.a 0  
  
  K3x3_DARKEN_7:
  Data.f 0, 0,  0
  Data.f 0, 0.3,0
  Data.f 0, 0,  0
  Data.f 1
  Data.a 0  
    
  K3x3_DARKEN_8:
  Data.f 0, 0,  0
  Data.f 0, 0.2,0
  Data.f 0, 0,  0
  Data.f 1
  Data.a 0  
    
  K3x3_DARKEN_9:
  Data.f 0, 0,  0
  Data.f 0, 0.1,0
  Data.f 0, 0,  0
  Data.f 1
  Data.a 0  
    
  ;---  
  KERNELS_3x3_END:  
EndDataSection
Last edited by Keya on Tue Mar 28, 2017 5:56 pm, edited 3 times in total.
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5353
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Convolution filter for image processing special effects

Post by Kwai chang caine »

Works very well on W7 X86 :D
Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
said
Enthusiast
Enthusiast
Posts: 342
Joined: Thu Apr 14, 2011 6:07 pm

Re: Convolution filter for image processing special effects

Post by said »

I mostly need this on weekends :D :lol:
One more nice contribution :D thanks for sharing
User avatar
StarBootics
Addict
Addict
Posts: 984
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: Convolution filter for image processing special effects

Post by StarBootics »

Hello everyone,

Sorry to re-open an 2 years old topic but I have some questions.

#1 Question :

Code: Select all

  K3x3_EMBOSS_v1:
  Data.f -1,  -1,  0
  Data.f -1,   0,  1
  Data.f  0,   1,  1
  Data.f 9
  Data.a 128
 
  K3x3_EMBOSS_v3:
  Data.f -1, -1,  0
  Data.f -1,  0,  1
  Data.f  0,  1,  1
  Data.f 9
  Data.a 128
These two kernels are the same, why ?
#2 Question

Code: Select all

  K3x3_PREWITT_H:
  Data.f 1, 0, -1
  Data.f 1, 0, -1
  Data.f 1, 0, -1
  Data.f 9.0
  Data.a 0
 
  K3x3_PREWITT_V:
  Data.f  1,  1,  1
  Data.f  0,  0,  0
  Data.f -1, -1, -1
  Data.f 1.0
  Data.a 0
The Prewitt_H as the scale set to 9.0 while the Prewitt_V as the scale set to 1.0. Why they don't have the same scale and which scale is correct.

Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
User avatar
idle
Always Here
Always Here
Posts: 5093
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Convolution filter for image processing special effects

Post by idle »

weight for the prewitt should be 1

and a guess the emboss v3

Code: Select all

 K3x3_EMBOSS_v3:
  Data.f 1, 1,  0
  Data.f 1, 0, -1
  Data.f 0,-1, -1
  Data.f 9
  Data.a 128
Windows 11, Manjaro, Raspberry Pi OS
Image
User avatar
StarBootics
Addict
Addict
Posts: 984
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: Convolution filter for image processing special effects

Post by StarBootics »

Thanks you Idle

Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
User avatar
StarBootics
Addict
Addict
Posts: 984
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: Convolution filter for image processing special effects

Post by StarBootics »

Hello everyone,

There is a modified version of the Keya original code, it can be downloaded here : https://www.dropbox.com/s/ia8y5iuv7t8ga ... r.zip?dl=0
I have used an OOP approach so the code might not be suitable for everyone so be careful. Also I have added some filters to the available ones, there is a complete list (73 filters in total)
  • IDENTITY
    GAUSSIAN_BLUR_2
    GAUSSIAN_BLUR_3
    GAUSSIAN_BLUR_4
    GAUSSIAN_BLUR_5
    GAUSSIAN_BLUR_6
    GAUSSIAN_BLUR_7
    GAUSSIAN_BLUR_8
    GAUSSIAN_BLUR_9
    GAUSSIAN_BLUR_10
    MOTION_BLUR_RIGHT
    MOTION_BLUR_LEFT
    MOTION_BLUR
    SMOOTH_1
    SMOOTH_2
    SMOOTH_3
    SMOOTH_4
    MEAN_SMOOTH
    SHARPEN_15
    SHARPEN_20
    SHARPEN_25
    SHARPEN_30
    SHARPEN_35
    SHARPEN_40
    SHARPEN_45
    SHARPEN_50
    SHARPEN_MEAN_REMOVAL
    EXTRUDE
    EMBOSS_V1
    EMBOSS_V2
    EMBOSS_V3
    EMBOSS_V4
    EMBOSS_V5
    RAISED
    EDGE_DETECT_HV
    EDGE_DETECT_H
    EDGE_DETECT_V
    EDGE_DETECT_DIFFERENTIAL
    EDGE_ENHANCE_H
    EDGE_ENHANCE_V
    PREWITT_H
    PREWITT_V
    SOBEL_H
    SOBEL_V
    SOBEL_FELDMAN_H
    SOBEL_FELDMAN_V
    LAPLACE
    LAPLACE_INV
    LAPLACE_DIAGONAL
    SCHARR_H
    SCHARR_V
    EDGE_360_KEYA
    GRADIENT_DETECT_H
    GRADIENT_DETECT_V
    BRIGHTEN_1
    BRIGHTEN_2
    BRIGHTEN_3
    BRIGHTEN_4
    BRIGHTEN_5
    BRIGHTEN_6
    BRIGHTEN_7
    BRIGHTEN_8
    BRIGHTEN_9
    DARKEN_1
    DARKEN_2
    DARKEN_3
    DARKEN_4
    DARKEN_5
    DARKEN_6
    DARKEN_7
    DARKEN_8
    DARKEN_9
    SPREAD_PIXEL
Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Convolution filter for image processing special effects

Post by IdeasVacuum »

Hi Keya

Just tried this with an MS windows sample image, Penguins.jpg, which is 1024 x 768 24bit, ram size 2.25MB

ConvolutionFilter.pbi, line 84: PokeL(*pixelout, color)

Errors out:

[00:01:23] [ERROR] ConvolutionFilter.pbi (Line: 84)
[00:01:23] [ERROR] Invalid memory access. (write error at address 85655552)

Penguins.jpg

Windows7 64bit PB 5.73 LTS (x64)
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
infratec
Always Here
Always Here
Posts: 6869
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Convolution filter for image processing special effects

Post by infratec »

Maybe this works:

ConvolutionFilter.pbi

Code: Select all

CompilerIf #PB_Compiler_IsMainFile
  EnableExplicit
CompilerEndIf

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


Structure RGBAStructure
  r.a
  g.a
  b.a
  a.a
EndStructure


Procedure.i LoadKernel3x3(kernelid.i, flip.i=#KERNEL_NOFLIP)
  
  Protected Result.i, *krn.float, i.i
  
  
  *krn = ?KERNELS_3x3_BEGIN + (kernelid * #KERNELDATASIZE)
  If *krn <= (?KERNELS_3x3_END - #KERNELDATASIZE)
    
    ConvMatrix(0,0) = *krn\f
    *krn + SizeOf(Float)
    ConvMatrix(0,1) = *krn\f
    *krn + SizeOf(Float)
    ConvMatrix(0,2) = *krn\f
    *krn + SizeOf(Float)
    
    ConvMatrix(1,0) = *krn\f
    *krn + SizeOf(Float)
    ConvMatrix(1,1) = *krn\f
    *krn + SizeOf(Float)
    ConvMatrix(1,2) = *krn\f
    *krn + SizeOf(Float)
    
    ConvMatrix(2,0) = *krn\f
    *krn + SizeOf(Float)
    ConvMatrix(2,1) = *krn\f
    *krn + SizeOf(Float)
    ConvMatrix(2,2) = *krn\f
    *krn + SizeOf(Float)
    
    ConvScale = *krn\f
    *krn + SizeOf(Float)
    
    ConvOffset= PeekA(*krn)
    
    If flip = #KERNEL_FLIPV
      For i = 0 To 2: Swap ConvMatrix(0,i), ConvMatrix(2,i): Next i
    ElseIf flip = #KERNEL_FLIPH
      For i = 0 To 2: Swap ConvMatrix(i,0), ConvMatrix(i,2): Next i
    EndIf
    
    Result = #True
    
  EndIf
  
  ProcedureReturn Result
  
EndProcedure


Procedure ConvolvePixel(*imgbuf, X,Y, width,height, pitch, pixbytes, *Color.RGBAStructure)
  
  Protected r.f, g.f, b.f, color.l, yy.i, xx.i, filter.f, curx.i, cury.i, *pixel.RGBAStructure;, ar.a, ab.a, ag.a
  
  
  For yy = -1 To 1    ;\ Good candidates for unrolling
    For xx = -1 To 1  ;/
      filter = 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
      *pixel = *imgbuf + (cury * pitch) + (curx * pixbytes)
      r + (*pixel\r * filter)
      g + (*pixel\g * filter)
      b + (*pixel\b * 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
  *Color\r = r
  *Color\g = g
  *Color\b = b
;  *Color\a = $ff
  
EndProcedure


Procedure.i ConvolutionFilterImage(hOrigImg.i)
  
  Protected *buf, hImg.i, width.i, height.i, depth.i, pitch.i, *out, Pixbytes.i, w.i, h.i, y.i, x.i, *pixelout.RGBAStructure, color.l, PadBytes.i, *color.RGBAStructure
  
  
  If IsImage(hOrigImg)
    width = ImageWidth(hOrigImg)
    height = ImageHeight(hOrigImg)
    depth = ImageDepth(hOrigImg)
    If StartDrawing(ImageOutput(hOrigImg))
      *buf = DrawingBuffer()
      StopDrawing()
    EndIf
    
    If *buf
      hImg = CreateImage(#PB_Any, width, height, depth)
      If hImg
        If StartDrawing(ImageOutput(hImg))
          pitch = DrawingBufferPitch()
          *out = DrawingBuffer()
          Select DrawingBufferPixelFormat()
            Case #PB_PixelFormat_32Bits_RGB : Pixbytes = 4
            Case #PB_PixelFormat_32Bits_BGR : Pixbytes = 4
            Default : Pixbytes = 3
          EndSelect
          
          w = width - 1
          h = height - 1
          *pixelout = *out
          PadBytes = Pitch - (width * Pixbytes)
          *color = @color
          For y = 0 To h
            For x = 0 To w       
              ConvolvePixel(*buf, x, y, width, height, pitch, pixbytes, @color)
              *pixelout\r = *color\r
              *pixelout\g = *color\g
              *pixelout\b = *color\b
              *pixelout + Pixbytes
            Next x
            *pixelout + PadBytes
          Next y 
          StopDrawing()
        EndIf   
      EndIf
    EndIf
    
  EndIf
  
  ProcedureReturn hImg
  
EndProcedure
It should be speed optimized and writes only 3 bytes instead of 4 with PokeL()
If pitch is equal to width * pixelbytes, PokeL() results in an bufferoverflow at the last pixel if it is a 24bit image.
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Convolution filter for image processing special effects

Post by IdeasVacuum »

Hi infratec

Yes you are right, a pitch of 3 pixels for 24bit and 4 for 32bit - I have seen it elsewhere as "stride".

Thanks for sharing your fix.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Re: Convolution filter for image processing special effects

Post by Keya »

infratec wrote: Thu Jan 21, 2021 9:05 am Maybe this works:

...

It should be speed optimized and writes only 3 bytes instead of 4 with PokeL()
If pitch is equal to width * pixelbytes, PokeL() results in an bufferoverflow at the last pixel if it is a 24bit image.
Very good catch + solution! thankyou :)
Post Reply