Announcement

Collapse
No announcement yet.

Filters 101

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Filters 101

    In other threads, applying filters to images has been part of the discussion. So I'm trying to make sure I understand how the manipulations might be made - how the filter might be applied.

    A filter description might be like this (this one is for a binary color image, as I've discussed in other threads).
    • For each pixel
    • Count the number of pixels with each color in the target pixel's 3x3 neighborhood
    • Make the target pixel whichever color appears most in the neighborhood
    Not having done filters before, I made myself some pictures.

    In this image, I'm describing a 6x5 image. In the image below, I use 0-29 as the array bounds.

    I think of the 3x3 pixels as "neighbors", of which there are 8. In the image below I use 0-7 as the array bounds.

    For processing the neighborhood, I give equations for each neighbor, relative to the target pixel.

    In the bottom right image, the neighbor pixel positions in the array are calculated.

    So for applying a filter to an image, I'd walk through each pixel, get its neighbors, then apply whatever filter logic is involved.

    The new pixel values have to been kept in a new array so as not to mess up the pixel values used in the calculations.


    Click image for larger version  Name:	pb_2143.jpg Views:	0 Size:	28.1 KB ID:	784048

    Is a zero-base array as good as any - for the pixel array and the neighbors array? Are there potential filter calculation where 1-based arrays are easier to work with?

    Is the way I numbered the neighbor pixels as good as any? Or would a different numbering pattern be more useful in filter calculations.

    Is there a standard way to handle border pixels? Do we just attempt to perform the Function and ignore data that calls outside the bounds or the array?

    Other than just creating a list of the neighbor pixels (their position in the array) and then defining a Function that meets the filter requirements, is there a more typical/standard way to do a filter?

    Other than just basic calculations, are there any PowerBASIC statements which can be applied to make or speed up the calculations?

    I realize there are other sized filters, such as 5x5. Are they always square? Are they always odd values?

    It all seems fairly straight-forward but I'm hoping folks more experienced with filters will comment if I've mis-stated any aspect of dealing with filter.

    I'll go nose around the web for similar 101 filter descriptions to see if I find useful info or answers to the questions I've raised.

  • #2
    Well, this is kind of fun.

    I ran across this video, which describes another kind of filtering called kernel convolution. The technique allows generating a variety of filters, such as mean blurs or gaussian filters.

    I thought it was well presented, although I had to stop the video a few times to make sure I understood the math steps.

    Kernel convolution works like this.
    • Start with an array of values (such as pixel info)
    • Create a kernel (3x3, 5x5, etc.), full it with values, such as all 1's
    • Center the kernel over each position in the array of values
    • Multiply the array and kernel values in each corresponding cell
    • Add the results
    • Divide the sum by the kernel size (3x3=9, 5x5=24, etc.) and put that value in the target cell

    Click image for larger version

Name:	pb_2144.jpg
Views:	181
Size:	34.0 KB
ID:	784050
    Please correct me if I've made any mistakes in this ...

    Comment


    • #3
      I've not read of specifics for working with images, but I'd guess one might work on the separate R-G-B values of a pixel and then recreate the color from those modified R-G-B values.

      Time to crash for the night. Hope someone benefits from the summary (and that I wrote wasn't in error!).

      Comment


      • #4
        Originally posted by Gary Beene View Post
        I've not read of specifics for working with images, but I'd guess one might work on the separate R-G-B values of a pixel and then recreate the color from those modified R-G-B values.

        Time to crash for the night. Hope someone benefits from the summary (and that I wrote wasn't in error!).
        This is the stuff I was doing a short time ago.
        Filters can be any size, but increasing the size of the filter increases processing time.
        The values of the filter can change the outcome.
        My work went into a Gaussian distribution, with higher numbers in the center and a five by five filter.
        You need an array for each color, R-G-B, which can be done with an BGRA array AT the pointer of a graphic bits string.
        The world is strange and wonderful.*
        I reserve the right to be horrifically wrong.
        Please maintain a safe following distance.
        *wonderful sold separately.

        Comment


        • #5
          Originally posted by Gary Beene View Post
          In other threads, applying filters to images has been part of the discussion. So I'm trying to make sure I understand how the manipulations might be made - how the filter might be applied.

          A filter description might be like this (this one is for a binary color image, as I've discussed in other threads).
          • For each pixel
          • Count the number of pixels with each color in the target pixel's 3x3 neighborhood
          • Make the target pixel whichever color appears most in the neighborhood
          Using the colour that appears most frequently will give you the "mode." There are two other types of averaging that are more commonly used: mean and median. The mean gives a blurred version of the source image, like a crude form of the Gaussian blur. The median removes single-pixel noise speckles without blurring the image. The mode would both remove noise and sharpen edges, in theory, but it never works properly with only a 3x3 filter.

          I think of the 3x3 pixels as "neighbors", of which there are 8. In the image below I use 0-7 as the array bounds.
          For almost all applications, you should also include the pixel at the centre.

          The new pixel values have to been kept in a new array so as not to mess up the pixel values used in the calculations.
          Yes, this is important for almost all image processing operations.

          Is a zero-base array as good as any - for the pixel array and the neighbors array? Are there potential filter calculation where 1-based arrays are easier to work with?
          For coding elegance, I'd use a 2D array with lower bounds of -1 and upper bounds of +1 (assuming 3x3). But for best performance, zero-based is best.

          Is there a standard way to handle border pixels? Do we just attempt to perform the Function and ignore data that calls outside the bounds or the array?
          I always cropped them off. You could replace with a black frame if you don't want each filter pass to shrink your image.

          I realize there are other sized filters, such as 5x5. Are they always square? Are they always odd values?
          They don't have to be square, but they do need a centre point in order to avoid shifting the image.


          Your understanding of kernel convolution is spot-on. And, as Kurt says, you do indeed work on each RGB colour channel separately.

          Try a kernel like this:
          Code:
          0 1 0
          0 2 0
          0 1 0
          That should blur in the vertical direction, but not horizontally.
          Dan

          Comment


          • #6
            Hey Dan!
            Thanks so much for the answers. All clarifications of the technology are helpful to me at this point.

            Comment


            • #7
              Dan,

              Now that you've exposed experience on the topic, perhaps you can answer another question.

              Is there a somewhat standard, efficient way to determine if a cell in the filter is outside the image? This would be useful in skipping filter cells as appropriate.

              I'm working on that problem but so far it seem like I have to do a bunch of IF tests, which is counter to a fast filter.

              I could crop the image, as you mention, by running the filter only on "internal" pixels, perhaps making edge pixels black. That's easy enough to do but not very satisfying.

              I would think by now the world of filter folks would have created a standard technique, one that all least flags when a filter cell is out of bounds.

              It's similar to the problem of making a move on a board game, where knowing if the move is off the board is important for validating a move.

              In my Google search, I found no mention of such a standard cell validation technique. Perhaps I've just not found the right search terms.

              Using a bunch of IF statements works, but it's hardly the path for a fast out-of-bounds detection.

              One good thing about the cropping approach is that there are NO cell validation steps, so it ought to have a speed advantage. I'll go with that for now but would still be interested in solutions which do not sacrifice the edge pixels.

              Comment


              • #8
                Hi Gary,

                It's a while since I've done this, tbh....

                Originally posted by Gary Beene View Post
                Dan,

                Now that you've exposed experience on the topic, perhaps you can answer another question.

                Is there a somewhat standard, efficient way to determine if a cell in the filter is outside the image? This would be useful in skipping filter cells as appropriate.

                I'm working on that problem but so far it seem like I have to do a bunch of IF tests, which is counter to a fast filter.

                I could crop the image, as you mention, by running the filter only on "internal" pixels, perhaps making edge pixels black. That's easy enough to do but not very satisfying.

                I would think by now the world of filter folks would have created a standard technique, one that all least flags when a filter cell is out of bounds.
                I always set up the loop that passes the kernel over the image to start and end 1 pixel (or 2, for a 5x5 kernel) inside the image edges. No IFs necessary, just modify the loop limits based on the kernel size.

                One other method - which I've never implemented myself - is to do a Fast Fourier Transform of both image and kernel, multiply them together, and take the inverse FT of the result. It's supposed to be faster than a straight convolution algorithm. It would wrap the image at the edges so you wouldn't have the shrinkage/border problem, but you would potentially have some other strangeness going on. I believe this method is the nearest thing to a standard.

                Maybe a simpler way (but also not tried by me) is to pad out the source image before running the kernel over it, by duplicating the top and bottom rows and the left and right columns?
                Dan

                Comment


                • #9
                  Dan,

                  Thanks for the additional comments.

                  >>> modify the loop limits
                  Yep, that's the crop image approach. Certainly the easiest. For my needs, a 1-pixel black border will not be an issue.

                  Although, for speed, I'm not sure that a DIM AT on bmp$ (so that I can use For loops on w and h) will be as fast as code that works with pointers on bmp$. I may have some code that allows "looping" without using DIM AT. I'll go see what I can find.


                  >>>FFT approach...
                  I'll keep it in mind but probably won't look into it until my other options fail.


                  >>>pad out ...
                  I recall doing something like that in a chess app I wrote, but I've been unable to find that code. As I recall, it worked well because there were no separate validation steps. I'll find the code eventually and see if it can apply to convolutions.

                  Comment


                  • #10
                    I'm having trouble getting the convolution to give the expected results (code below). I'm using a 3x3 filter and ignoring the border pixels. The image on the left is modified and displayed on the right. As best I can tell, I am following the algorithm in post #2, but the resulting image definitely doesn't match expectations.

                    Kurt,
                    Thanks for the BGRA array suggestion. It makes accessing the color components very straightforward, particularly when used with an array positioned with DIM AT.


                    In this code, I included a gray scale conversion just for demo purposes ...

                    Code:
                    #Compile Exe  "filter_demo.exe"
                    #Dim All
                    
                    #Debug Error On
                    #Debug Display On
                    
                    %Unicode = 1
                    #Include "Win32API.inc"
                    
                    Enum Equates Singular
                       ID_Timer  = 500
                       IDC_Graphic
                       IDC_Image
                       IDC_Filter
                       IDC_ApplyFilter
                       IDC_ApplyGray
                       IDC_StatusBar
                    End Enum
                    
                    Type BGRA
                       b As Byte
                       g As Byte
                       r As Byte
                       a As Byte
                    End Type
                    
                    Global hDlg, hDCGraphic, hDCImage As Dword, bmpLeft$, bmpRight$
                    Global Filters(), GrayScale As Long
                    Global Filter As WStringZ*%Max_Path
                    Global qFreq, qStart, qStop As Quad
                    Global imgW, imgH, imgWH As Long
                    Global ColorArrayLeft(), ColorArrayRight() As BGRA
                    
                    Function PBMain() As Long
                       Dialog Default Font "Tahoma", 12, 1
                       Dialog New Pixels, 0, "Convolution TestBed" ,,,1340,630, %WS_OverlappedWindow Or %WS_ClipSiblings Or %WS_ClipChildren To hDlg
                       Dialog Set Icon hDlg, "logo"
                       Dialog Show Modal hDlg Call DlgProc
                    End Function
                    
                    Sub AddControls
                       Local w,h As Long, hBMP As Dword
                       Control Add Statusbar, hDlg, %IDC_StatusBar, "", 0,0,0,0
                    
                       Control Add Button, hDlg, %IDC_ApplyFilter, "Convolution", 830,520,100,25
                       Control Add Button, hDlg, %IDC_ApplyGray, "GrayScale", 700,520,100,25
                    
                       Control Add TextBox, hDlg, %IDC_Filter, "1:2:1" + $CrLf + "2:4:2" + $CrLf + "1:2:1", 950,520,100,65, %ES_MultiLine Or %ES_WantReturn Or %WS_TabStop Or %WS_Border
                    
                       Control Add Graphic, hDlg, %IDC_Image, "", 10,10,640,480, %WS_Border Or %SS_Notify
                       Graphic Attach hDlg, %IDC_Image
                       Graphic Get DC To hDCImage
                    
                       Graphic Bitmap Load "tree.bmp", w,h To hBMP
                       Graphic Copy hBMP, 0
                    
                       Control Add Graphic, hDlg, %IDC_Graphic, "", 680,10,640,480, %WS_Border Or %SS_Notify
                       Graphic Attach hDlg, %IDC_Graphic
                       Graphic Get DC To hDCGraphic
                    End Sub
                    
                    CallBack Function DlgProc() As Long
                       Select Case Cb.Msg
                          Case %WM_InitDialog
                             QueryPerformanceFrequency qFreq
                             AddControls
                             ReDim Filters(1 To 9)
                          Case %WM_Command
                             Select Case Cb.Ctl
                                Case %IDC_ApplyFilter : GrayScale = 0 : ApplyFilter
                                Case %IDC_ApplyGray   : GrayScale = 1 : ApplyFilter
                             End Select
                       End Select
                    End Function
                    
                    Sub ApplyFilter
                       QueryPerformanceCounter   qStart
                    
                       Graphic Attach hDlg, %IDC_Image, ReDraw    'graphic control on the left
                       Graphic Get Bits To bmpLeft$
                       imgW  = Cvl(bmpLeft$,1)
                       imgH  = Cvl(bmpLeft$,5)
                       imgWH = imgW * imgH
                    
                       'all algorithms have bmp$ available
                       If GrayScale = 1 Then ApplyGrayScale
                       If GrayScale = 0 Then GetFilterAValues : Convolution  'acts on temp bmp$
                    
                       Graphic Attach hDlg, %IDC_Graphic          'graphc control on the right
                       Graphic Set Bits bmpRight$
                       Graphic ReDraw
                    
                       QueryPerformanceCounter   qStop
                       Statusbar Set Text hDlg, %IDC_StatusBar, 1,0, IIf$(GrayScale,"  GrayScale:  ","   Convolution:  ") + Format$((qStop-qStart)/qFreq,"###.000") & " seconds    " + IIf$(GrayScale,"",FilterValues)
                    End Sub
                    
                    Sub ApplyGrayScale
                       Local i, iColor, B,G,R As Long, p As Long Ptr
                       p = StrPtr(bmpLeft$) + 8
                       For i = 1 To imgWH
                          iColor = @p
                          B = iColor Mod 256
                          G = (iColor\256) Mod 256
                          R = (iColor\256\256) Mod 256
                          iColor = 0.299*R + 0.587*G + 0.114*B
                          @p = Bgr(iColor, iColor, iColor)
                          Incr p
                       Next i
                       bmpRight$ = bmpLeft$
                    End Sub
                    
                    Sub GetFilterAValues
                       Control Get Text hDlg, %IDC_Filter To Filter
                       Replace $CrLf With ":" In Filter
                       Filters(1) = Val(Parse$(Filter,":",1))
                       Filters(2) = Val(Parse$(Filter,":",2))
                       Filters(3) = Val(Parse$(Filter,":",3))
                       Filters(4) = Val(Parse$(Filter,":",4))
                       Filters(5) = Val(Parse$(Filter,":",5))
                       Filters(6) = Val(Parse$(Filter,":",6))
                       Filters(7) = Val(Parse$(Filter,":",7))
                       Filters(8) = Val(Parse$(Filter,":",8))
                       Filters(9) = Val(Parse$(Filter,":",9))
                    
                    End Sub
                    
                    Function FilterValues() As String
                       Function = Str$(Filters(1)) + Str$(Filters(2)) + Str$(Filters(3)) + _
                         Str$(Filters(4)) + Str$(Filters(5)) + Str$(Filters(6)) + _
                         Str$(Filters(7)) + Str$(Filters(8)) + Str$(Filters(9))
                    End Function
                    
                    Sub Convolution  'modifies bmpTemp$
                    
                       Local i,j,iCount As Long, leftP, rightP As Long Ptr
                       bmpRight$ = bmpLeft$               'original image on Left. modified image on Right$
                       leftP  = StrPtr(bmpLeft$)+8         'pointer to start of original image pixel information
                       ReDim ColorArrayLeft(1 To imgW, 1 To imgH) As BGRA At leftP  'bmp$
                       rightP  = StrPtr(bmpRight$) + 8   'pointer to start of modified image pixel information
                       ReDim ColorArrayRight(1 To imgW, 1 To imgH) As BGRA At rightP  'bmpTemp$
                       'get string position of coordinates and modify the string at that position
                       For i = 2 To imgW-1     'col
                          For j = 2 To imgH-1  'row
                             'blue
                             iCount = ColorArrayLeft(i-1,j-1).b * Filters(1)
                             iCount+= ColorArrayLeft(i  ,j-1).b * Filters(2)
                             iCount+= ColorArrayLeft(i+1,j-1).b * Filters(3)
                    
                             iCount+= ColorArrayLeft(i-1,j  ).b * Filters(4)
                             iCount+= ColorArrayLeft(i,  j  ).b * Filters(5)
                             iCount+= ColorArrayLeft(i+1,j  ).b * Filters(6)
                    
                             iCount+= ColorArrayLeft(i-1,j+1).b * Filters(7)
                             iCount+= ColorArrayLeft(i  ,j+1).b * Filters(8)
                             iCount+= ColorArrayLeft(i+1,j+1).b * Filters(9)
                             ColorArrayRight(i,j).b = iCount/9
                    
                             'green
                             iCount = ColorArrayLeft(i-1,j-1).g * Filters(1)
                             iCount+= ColorArrayLeft(i  ,j-1).g * Filters(2)
                             iCount+= ColorArrayLeft(i+1,j-1).g * Filters(3)
                    
                             iCount+= ColorArrayLeft(i-1,j  ).g * Filters(4)
                             iCount+= ColorArrayLeft(i,  j  ).g * Filters(5)
                             iCount+= ColorArrayLeft(i+1,j  ).g * Filters(6)
                    
                             iCount+= ColorArrayLeft(i-1,j+1).g * Filters(7)
                             iCount+= ColorArrayLeft(i  ,j+1).g * Filters(8)
                             iCount+= ColorArrayLeft(i+1,j+1).g * Filters(9)
                             ColorArrayRight(i,j).g = iCount/9
                    
                             'red
                             iCount = ColorArrayLeft(i-1,j-1).r * Filters(1)
                             iCount+= ColorArrayLeft(i  ,j-1).r * Filters(2)
                             iCount+= ColorArrayLeft(i+1,j-1).r * Filters(3)
                    
                             iCount+= ColorArrayLeft(i-1,j  ).r * Filters(4)
                             iCount+= ColorArrayLeft(i,  j  ).r * Filters(5)
                             iCount+= ColorArrayLeft(i+1,j  ).r * Filters(6)
                    
                             iCount+= ColorArrayLeft(i-1,j+1).r * Filters(7)
                             iCount+= ColorArrayLeft(i  ,j+1).r * Filters(8)
                             iCount+= ColorArrayLeft(i+1,j+1).r * Filters(9)
                             ColorArrayRight(i,j).r = iCount/9
                          Next j
                       Next i
                    End Sub
                    Last edited by Gary Beene; 29 Aug 2019, 12:28 AM. Reason: Code error ...

                    Comment


                    • #11
                      Here an image (640x480) for use in the above code ...

                      Click image for larger version  Name:	tree.bmp Views:	1 Size:	900.1 KB ID:	784405

                      Comment


                      • #12
                        One thing I noticed is that I divided by 9 ... which I don't think is correct. I think the divisor should be the sum of all filter values. In the video, where I got the 9, the author was using a mean blur, so 9 is both the number of filter cells and the sum of all filter values. So my description of the algorithm in post #2 needs to clarified on how to calculate the divisor.

                        But even when I replace the 9 with 1+2+1+2+4+2+1+2+1 = 16, I still don't think the resulting blur is what I expect to see.

                        Comment


                        • #13
                          Hmm... just a data point ... if I use an identify kernel ...

                          0:0:0
                          0:1:0
                          0:0:0

                          ... the modified image seems correct.

                          Comment


                          • #14
                            Ok, I think I found one error - miscalculation of the sum of the products. I fixed that in the code of #10.

                            But I still was expecting "more blur" in the output. So if anyone tries it out, please let me know if the blur looks appropriate.

                            Comment


                            • #15
                              Gary,

                              Where have I seen that MovAvi image before ?
                              hutch at movsd dot com
                              The MASM Forum

                              www.masm32.com

                              Comment


                              • #16
                                Originally posted by Gary Beene View Post
                                One thing I noticed is that I divided by 9 ... which I don't think is correct. I think the divisor should be the sum of all filter values. In the video, where I got the 9, the author was using a mean blur, so 9 is both the number of filter cells and the sum of all filter values. So my description of the algorithm in post #2 needs to clarified on how to calculate the divisor.

                                But even when I replace the 9 with 1+2+1+2+4+2+1+2+1 = 16, I still don't think the resulting blur is what I expect to see.
                                The divisor is the total of all the data weights.
                                Your filter provides a weight to each pixel, which determines how much it contributes.
                                Code:
                                _____________
                                |_1_|_1_|_1_|
                                |_1_|_3_|_1_|
                                |_1_|_1_|_1_|
                                This gives your center pixel, the one being altered, a 3/11 weight.
                                You would multiply the surrounding pixels by 1, and the center pixel by 3, and divide by 11.
                                You can also process twice, by row and by column, to make it faster.
                                This function is a simple variable blur. You pass it the bitstring of a graphic, the strength of the blur, and the number of iterations.
                                It returns a bitstring containing the modified graphic.
                                There is accompanying code posted somewhere here on the web site.
                                The actual process is one of taking the data for each pixels R, G and B values and averaging it with neighboring pixels.
                                Code:
                                FUNCTION PB_AVG_blur(StrBMP AS STRING, strength AS LONG, iterations AS LONG) AS STRING
                                    ' adjusting number of iterations extends the blur by bleeding into an extended area.
                                    ' adjusting strength of blur reduces bleedover.
                                    LOCAL STR_work, STR_ref AS STRING
                                    LOCAL x, y, xl, yl, wgt, dvzr, avg AS LONG
                                    LOCAL LPTR_ref AS LONG POINTER
                                    LOCAL bg0(), bg1() AS bgra
                                    STR_ref = Strbmp
                                    LPTR_ref = STRPTR(StrBMP) + 8
                                    xl = @LPTR_ref[-2] - 1
                                    yl = @LPTR_ref[-1] - 1
                                    wgt = 32 - (strength AND 31)
                                    REDIM bg0(xl, yl) AT STRPTR(STR_ref) + 8
                                    FOR avg = 0 TO iterations
                                        ' Using a separate copy of the image, average from the original and store in the edited version.
                                        ' By updating the reference copy for each iteration, the reiteratioin bleed-thru is made possible.
                                        STR_work = STR_ref
                                        REDIM bg1(xl, yl) AT STRPTR(STR_work) + 8
                                        'corners
                                        dvzr = wgt + 3
                                        'upper left corner
                                        bg1(0,0).b = ((wgt*bg0(0,0).b) +bg0(0,1).b+bg0(1,0).b+bg0(1,1).b) \dvzr
                                        bg1(0,0).g = ((wgt*bg0(0,0).g) +bg0(0,1).g+bg0(1,0).g+bg0(1,1).g) \dvzr
                                        bg1(0,0).r = ((wgt*bg0(0,0).r) +bg0(0,1).r+bg0(1,0).r+bg0(1,1).r) \dvzr
                                        'upper right corner
                                        bg1(xl,0).b = ((wgt*bg0(xl,0).b) +bg0(xl,1).b+bg0(xl-1,0).b+bg0(xl-1,1).b) \dvzr
                                        bg1(xl,0).g = ((wgt*bg0(xl,0).g) +bg0(xl,1).g+bg0(xl-1,0).g+bg0(xl-1,1).g) \dvzr
                                        bg1(xl,0).r = ((wgt*bg0(xl,0).r) +bg0(xl,1).r+bg0(xl-1,0).r+bg0(xl-1,1).r) \dvzr
                                        'lower left corner
                                        bg1(0,yl).b = ((wgt*bg0(0,yl).b) +bg0(0,yl-1).b+bg0(1,yl).b+bg0(1,yl-1).b) \dvzr
                                        bg1(0,yl).g = ((wgt*bg0(0,yl).g) +bg0(0,yl-1).g+bg0(1,yl).g+bg0(1,yl-1).g) \dvzr
                                        bg1(0,yl).r = ((wgt*bg0(0,yl).r) +bg0(0,yl-1).r+bg0(1,yl).r+bg0(1,yl-1).r) \dvzr
                                        'lower right corner
                                        bg1(xl,yl).b = ((wgt*bg0(xl,yl).b) +bg0(xl,yl-1).b+bg0(xl-1,yl).b+bg0(xl-1,yl-1).b) \dvzr
                                        bg1(xl,yl).g = ((wgt*bg0(xl,yl).g) +bg0(xl,yl-1).g+bg0(xl-1,yl).g+bg0(xl-1,yl-1).g) \dvzr
                                        bg1(xl,yl).r = ((wgt*bg0(xl,yl).r) +bg0(xl,yl-1).r+bg0(xl-1,yl).r+bg0(xl-1,yl-1).r) \dvzr
                                        'left edge
                                        dvzr = wgt + 5
                                        FOR y = 1 TO yl - 1
                                            bg1(0,y).b = ((wgt*bg0(0,y).b) +bg0(0,y-1).b+bg0(0,y+1).b+bg0(1,y-1).b+bg0(1,y).b+bg0(1,y+1).b) \dvzr
                                            bg1(0,y).g = ((wgt*bg0(0,y).g) +bg0(0,y-1).g+bg0(0,y+1).g+bg0(1,y-1).g+bg0(1,y).g+bg0(1,y+1).g) \dvzr
                                            bg1(0,y).r = ((wgt*bg0(0,y).r) +bg0(0,y-1).r+bg0(0,y+1).r+bg0(1,y-1).r+bg0(1,y).r+bg0(1,y+1).r) \dvzr
                                        NEXT
                                        'right edge
                                        FOR y = 1 TO yl - 1
                                            bg1(xl,y).b = ((wgt*bg0(xl,y).b) +bg0(xl,y-1).b+bg0(xl,y+1).b+bg0(xl-1,y-1).b+bg0(xl-1,y).b+bg0(xl-1,y+1).b) \dvzr
                                            bg1(xl,y).g = ((wgt*bg0(xl,y).g) +bg0(xl,y-1).g+bg0(xl,y+1).g+bg0(xl-1,y-1).g+bg0(xl-1,y).g+bg0(xl-1,y+1).g) \dvzr
                                            bg1(xl,y).r = ((wgt*bg0(xl,y).r) +bg0(xl,y-1).r+bg0(xl,y+1).r+bg0(xl-1,y-1).r+bg0(xl-1,y).r+bg0(xl-1,y+1).r) \dvzr
                                        NEXT
                                        'top edge
                                        dvzr = wgt + 5
                                        FOR x = 1 TO xl - 1
                                            bg1(x,0).b = ((wgt*bg0(x,0).b) +bg0(x-1,0).b+bg0(x+1,0).b+bg0(x,1).b+bg0(x-1,1).b+bg0(x+1,1).b) \dvzr
                                            bg1(x,0).g = ((wgt*bg0(x,0).g) +bg0(x-1,0).g+bg0(x+1,0).g+bg0(x,1).g+bg0(x-1,1).g+bg0(x+1,1).g) \dvzr
                                            bg1(x,0).r = ((wgt*bg0(x,0).r) +bg0(x-1,0).r+bg0(x+1,0).r+bg0(x,1).r+bg0(x-1,1).r+bg0(x+1,1).r) \dvzr
                                        NEXT
                                        'bottom edge
                                        FOR x = 1 TO xl - 1
                                            bg1(x,yl).b = ((wgt*bg0(x,yl).b) +bg0(x-1,yl).b+bg0(x+1,yl).b+bg0(x,yl-1).b+bg0(x-1,yl-1).b+bg0(x+1,yl-1).b) \dvzr
                                            bg1(x,yl).g = ((wgt*bg0(x,yl).g) +bg0(x-1,yl).g+bg0(x+1,yl).g+bg0(x,yl-1).g+bg0(x-1,yl-1).g+bg0(x+1,yl-1).g) \dvzr
                                            bg1(x,yl).r = ((wgt*bg0(x,yl).r) +bg0(x-1,yl).r+bg0(x+1,yl).r+bg0(x,yl-1).r+bg0(x-1,yl-1).r+bg0(x+1,yl-1).r) \dvzr
                                        NEXT
                                        'center image
                                        dvzr = wgt + 8
                                        FOR x = 1 TO xl - 1
                                            FOR y = 1 TO yl - 1
                                                bg1(x,y).b = ((wgt*bg0(x,y).b) +bg0(x-1,y).b+bg0(x+1,y).b +bg0(x-1,y-1).b+bg0(x,y-1).b+bg0(x+1,y-1).b +bg0(x-1,y+1).b+bg0(x,y+1).b+bg0(x+1,y+1).b) \ dvzr
                                                bg1(x,y).g = ((wgt*bg0(x,y).g) +bg0(x-1,y).g+bg0(x+1,y).g +bg0(x-1,y-1).g+bg0(x,y-1).g+bg0(x+1,y-1).g +bg0(x-1,y+1).g+bg0(x,y+1).g+bg0(x+1,y+1).g) \ dvzr
                                                bg1(x,y).r = ((wgt*bg0(x,y).r) +bg0(x-1,y).r+bg0(x+1,y).r +bg0(x-1,y-1).r+bg0(x,y-1).r+bg0(x+1,y-1).r +bg0(x-1,y+1).r+bg0(x,y+1).r+bg0(x+1,y+1).r) \ dvzr
                                            NEXT
                                        NEXT
                                        STR_ref = STR_work
                                    NEXT
                                    FUNCTION = STR_work
                                END FUNCTION
                                The world is strange and wonderful.*
                                I reserve the right to be horrifically wrong.
                                Please maintain a safe following distance.
                                *wonderful sold separately.

                                Comment


                                • #17
                                  Originally posted by Gary Beene View Post
                                  One thing I noticed is that I divided by 9 ... which I don't think is correct. I think the divisor should be the sum of all filter values. In the video, where I got the 9, the author was using a mean blur, so 9 is both the number of filter cells and the sum of all filter values. So my description of the algorithm in post #2 needs to clarified on how to calculate the divisor.

                                  But even when I replace the 9 with 1+2+1+2+4+2+1+2+1 = 16, I still don't think the resulting blur is what I expect to see.
                                  The divisor should indeed be equal to the sum of the kernel values - unless the sum is zero (as it would be for edge-detection).

                                  A 3x3 kernel is small, so you wouldn't expect a huge amount of blurring.

                                  Try this kernel:
                                  Code:
                                  -1 -1 -1
                                  -1  8 -1
                                  -1 -1 -1
                                  (and a divisor of 1)
                                  Dan

                                  Comment


                                  • #18
                                    Hey Steve!

                                    Do you mean the image of the tree? I had it floating around on my PC - not sure where it came from.

                                    Comment


                                    • #19
                                      Kurt!
                                      Thanks for your comments/example. As best I can tell, my example in #10 matches the description you gave. It would seem that as Dan says, the blur for a single pass 3x3 filter is quite minimal.

                                      I tried out your code with strength=64 and iterations=20 and the blurring was minimal. Does that sound right? I'll go find the forum link for your code and see what values are appropriate to get a more pronounced blur.

                                      I'm not sure what you mean by this:

                                      You can also process twice, by row and by column, to make it faster.
                                      Would you give some more details on what you're describing?

                                      I had not seen the w/h of the image calculated as you did, with this ...

                                      Code:
                                          xl = @LPTR_ref[-2] - 1
                                          yl = @LPTR_ref[-1] - 1
                                      I've just followed Bob's example of using CVL.

                                      Comment


                                      • #20
                                        Dan,

                                        I tested your filter and got this. Does this look correct?

                                        Click image for larger version

Name:	pb_2146.jpg
Views:	78
Size:	15.6 KB
ID:	784418

                                        I'm still not sure that my filter code is working correctly.

                                        Comment

                                        Working...
                                        X