• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 350
  • Last Modified:

3x3 filters/smoothing but on array *not* bitmap

Hi All,

Tough one and I'm on the road for the next few days so won't have access to my compliers. Will have access to my email though so keep the solutions coming. I need the following to be very fast and very stable. All points go to whoever gives me a working routine. Because I need this quickly and of a high quality I am offering 750 points. OK, here we go:

I have an acoustic model from a 3rd Party that produces as output a file containing values. The values are then sent to a contouring routine to plot them on screen. I have the model working and the contouring working. However, the output from the model is a bit 'peeky'.

What I want is a routine that can 'smooth' or 'blur' a 2D array of values using a 3x3 matrix. This is very similar to blur/smooth 3x3 filters such as used in Paintshop Pro but to be used on a set of values rather than a bitmap. Still, very similar. (Image|User Defined Filters in Painthsop Pro).

OK, I want a routine that I can pass two arrays and two integers to:

The first array containing the values. This array will contain 601 x 601 values, but may be variable. I.e. the routine has to accept an array that has been sized dynamically in the main program. However, it will always be square i.e. 100x100 or 900x900. We will call this array ArrayPoints, and will be of type double

The second array will contain the matrix values and will always be 3x3. We will call this array ArrayMatrix and will be of type integer.

The first integer is the Division factor. We will call this MatrixDivision.

The second integer is the Bias factor. We will call this MatrixBias.

The routine will use the 3x3 ArrayMatrix and the two integer values passed in to 'scan' over the values in ArrayPoints, applying the filter in the same way as Paintshop Pro. The values in the 3x3 array will of course be set before calling the routine.

Online at http://www.netcomuk.co.uk/~iwatkins/test.zip contains three files:

1. BEFORE.TXT   -    A file containing 601 x 601 values
2. BEFORE.JPG   -    A bitmap representation of the values in the file above before adjustment
3. AFTER.JPG    -    A bitmap representation of the values in the file above after filtering

Obviously, file 3 is just made up to give you some idea what I am after.

Good luck.

Cheers

Ian
0
iwatkins
Asked:
iwatkins
  • 7
  • 7
  • 3
  • +1
1 Solution
 
viktornetCommented:
0
 
viktornetCommented:
Also, I think you'd find LOTS of useful stuff on this website..

http://www.efg2.com/lab/library/

Good Luck!!

..-=ViKtOr=-..
0
 
rwilson032697Commented:
:o)
0
Take Control of Web Hosting For Your Clients

As a web developer or IT admin, successfully managing multiple client accounts can be challenging. In this webinar we will look at the tools provided by Media Temple and Plesk to make managing your clients’ hosting easier.

 
EpsylonCommented:
Victor, what kind of answer is that?    :o(
0
 
EpsylonCommented:
I mean ViKtor, what kind of answer is that?    :o(

0
 
viktornetCommented:
I dunno Epsylon, you tell me.... ---->>> http://www.efg2.com/lab/library/Delphi/MathFunctions/Parsers.htm
0
 
viktornetCommented:
0
 
EpsylonCommented:
Viktor, I think you should read the question again!!!!

AND STOP ACTING LIKE A FOOL....
0
 
viktornetCommented:
Me? Fool? Since when? I'm not the one who started this whole shitty thing on the Delphi area...

and my answer is absolutely correct... on the UDDF site, there is an example how to a specified area and smooth it out... on the other hand, the other webiste has got some great image effects, which would be helpful in the development of such software, so please back off...

Epsylon, I was just about to post an answer to that question which did some parsing and other stuff, but since you posted the link, i decided not to do it.. what's the point anyway...

..-=fOoL=-..
0
 
iwatkinsAuthor Commented:
Not good enough guys.

Yes, I have had a good dig through all the stuff at efg (where does he get all the time to do this stuff?). But it all seems to be suited to images but I am *not* dealing with images.

Keep them coming.

Cheers

Ian
0
 
viktornetCommented:
well if you're not dealing with images, then I think this would be pretty fast :)

okay... I have a website, but don't remember the URL... it will teach you exactly what you want... but it actually is a C/C++ website, so if you don't understand somethin you just ask and someone including myself will tell you what exactly that is, etc... the website mainly deals with algorithms, so I think this should be the right one for you,.... anyway... please let us know what you think... and give some more details...

btw, you can extract the algorithm from the UDDF code, so you can use it on your array of bytes or whatever it is..

..-=fOoL=-..
0
 
viktornetCommented:
you can do something like this...

in order to make a SINGLE pixel (or a value at a certail col and row in that array), you need to take the average of the values surrounding that pixel you're currently on (you're looping through then remember? :))... then write that average value in the pixel you're currently on...

that is just an algorithm...

the difference with your situation is that you need to take the factor of that pixel... well you need to multiply by the fact and write the pixel in there... i think that's about it...

..-=ViKtOr=-..
0
 
EpsylonCommented:
iwatkins, what would you like to do with the borders? Assume zero's there?
0
 
EpsylonCommented:
Here is a demo with the function you need. It's not small but it's FAST!!! I can easily make it smaller but that will cost ya some speed.

Just put a button and 2 labels on a form. Make sure the labels have enough space to draw a 10x10 matrix.



unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  PDoubleArray = ^TDoubleArray;
  TDoubleArray = array[0..32767] of Double;

  TMatrix = array[0..2, 0..2] of Byte;

  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    Label2: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  m: TMatrix = ((0,1,0),
                (1,2,1),
                (0,1,0));
  a: array[0..9, 0..9] of Double = ((0,0,0,0,0,0,0,0,0,0),
                                    (0,8,0,0,0,0,0,0,8,0),
                                    (0,0,0,0,0,0,0,0,0,0),
                                    (0,0,0,0,0,0,0,0,0,0),
                                    (0,0,0,0,0,0,0,0,0,0),
                                    (0,0,0,0,0,0,0,0,0,0),
                                    (0,0,0,0,0,0,0,0,0,0),
                                    (8,0,0,0,0,0,8,8,0,0),
                                    (0,0,0,0,0,0,0,0,0,0),
                                    (0,0,0,0,0,0,0,0,0,0));

implementation

{$R *.DFM}

procedure MyFilter(src: PDoubleArray; xres, yres: Integer;
                   matrix: TMatrix; bias, mplr: Integer);
var x, y, i, l: Integer;
    t: PDoubleArray;
    value: Double;
begin
  l := xres * yres;
  t := AllocMem(l * sizeof(Double));
// the central part
  for y := 1 to yres - 2 do
    for x := 1 to xres - 2 do
    begin
      i := y * xres + x;
      value := src[i] * matrix[1,1] +
               src[i - xres - 1] * matrix[0,0] +
               src[i - xres]     * matrix[1,0] +
               src[i - xres + 1] * matrix[2,0] +
               src[i - 1]        * matrix[0,1] +
               src[i + 1]        * matrix[2,1] +
               src[i + xres - 1] * matrix[0,2] +
               src[i + xres]     * matrix[1,2] +
               src[i + xres + 1] * matrix[2,2];
      t[i] := (value / mplr) + bias;
    end;

// the edges
  for x := 1 to xres - 2 do
  begin
    i := x;
    value := src[i] * matrix[1,1] +
             src[i - 1]        * matrix[0,1] +
             src[i + 1]        * matrix[2,1] +
             src[i + xres - 1] * matrix[0,2] +
             src[i + xres]     * matrix[1,2] +
             src[i + xres + 1] * matrix[2,2];
    t[i] := (value / mplr) + bias;
  end;

  for x := 1 to xres - 2 do
  begin
    i := (yres - 1) * xres + x;
    value := src[i] * matrix[1,1] +
             src[i - xres - 1] * matrix[0,0] +
             src[i - xres]     * matrix[1,0] +
             src[i - xres + 1] * matrix[2,0] +
             src[i - 1]        * matrix[0,1] +
             src[i + 1]        * matrix[2,1];
    t[i] := (value / mplr) + bias;
  end;

  for y := 1 to yres - 2 do
  begin
    i := y * xres;
    value := src[i] * matrix[1,1] +
             src[i - xres]     * matrix[1,0] +
             src[i - xres + 1] * matrix[2,0] +
             src[i + 1]        * matrix[2,1] +
             src[i + xres]     * matrix[1,2] +
             src[i + xres + 1] * matrix[2,2];
    t[i] := (value / mplr) + bias;
  end;

  for y := 1 to yres - 2 do
  begin
    i := y * xres + (xres - 1);
    value := src[i] * matrix[1,1] +
             src[i - xres - 1] * matrix[0,0] +
             src[i - xres]     * matrix[1,0] +
             src[i - 1]        * matrix[0,1] +
             src[i + xres - 1] * matrix[0,2] +
             src[i + xres]     * matrix[1,2];
    t[i] := (value / mplr) + bias;
  end;

// the corners
  i := 0;
  value := src[i] * matrix[1,1] +
           src[i + 1]        * matrix[2,1] +
           src[i + xres]     * matrix[1,2] +
           src[i + xres + 1] * matrix[2,2];
  t[i] := (value / mplr) + bias;

  i := xres - 1;
  value := src[i] * matrix[1,1] +
           src[i - 1]        * matrix[0,1] +
           src[i + xres - 1] * matrix[0,2] +
           src[i + xres]     * matrix[1,2];
  t[i] := (value / mplr) + bias;

  i := (yres - 1) * xres;
  value := src[i] * matrix[1,1] +
           src[i - xres]     * matrix[1,0] +
           src[i - xres + 1] * matrix[2,0] +
           src[i + 1]        * matrix[2,1];
  t[i] := (value / mplr) + bias;

  i := (yres - 1) * xres + (xres - 1);
  value := src[i] * matrix[1,1] +
           src[i - xres - 1] * matrix[0,0] +
           src[i - xres]     * matrix[1,0] +
           src[i - 1]        * matrix[0,1];
  t[i] := (value / mplr) + bias;  

// copy back to original buffer
  for i := 0 to l - 1 do
    src[i] := t[i];
  FreeMem(t);
end;

procedure TForm1.Button1Click(Sender: TObject);
var x, y: Integer;
    s: String;
begin
  s := '';
  for x := 0 to 9 do
  begin
    for y := 0 to 9 do
      s := s + Format('%6.2f  ', [a[x,y]]);
    s := s + #10#13;
  end;
  Label1.Caption := s;

  MyFilter(@a, 10, 10, m, 0, 8);

  s := '';
  for x := 0 to 9 do
  begin
    for y := 0 to 9 do
      s := s + Format('%6.2f  ', [a[x,y]]);
    s := s + #10#13;
  end;
  Label2.Caption := s;
end;

end.



Regards,

Epsylon.
0
 
EpsylonCommented:
Oops, I swapped the x and y of the 2 dimensional arrays by accident. Here is a correction:



unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  PDoubleArray = ^TDoubleArray;
  TDoubleArray = array[0..32767] of Double;

  TMatrix = array[0..2, 0..2] of Byte;

  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    Label2: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  m: TMatrix = ((0,1,0),
                (1,2,1),
                (0,1,0));
  a: array[0..9, 0..9] of Double = ((0,0,0,0,5,0,0,0,0,0),
                                    (0,4,0,0,0,0,0,0,8,0),
                                    (0,0,0,0,0,0,0,0,0,0),
                                    (0,0,0,0,0,0,0,0,0,0),
                                    (0,0,0,0,0,0,0,0,0,0),
                                    (0,0,0,0,0,0,0,0,0,0),
                                    (8,0,0,0,0,0,0,8,0,0),
                                    (0,0,0,0,0,0,8,8,0,0),
                                    (0,0,0,0,0,0,0,0,0,0),
                                    (8,0,0,0,0,0,0,0,0,0));

implementation

{$R *.DFM}

procedure MyFilter(src: PDoubleArray; xres, yres: Integer;
                   matrix: TMatrix; bias, mplr: Integer);
var x, y, i, l: Integer;
    t: PDoubleArray;
    value: Double;
begin
  l := xres * yres;
  t := AllocMem(l * sizeof(Double));
// the central part
  for y := 1 to yres - 2 do
    for x := 1 to xres - 2 do
    begin
      i := y * xres + x;
      value := src[i] * matrix[1,1] +
               src[i - xres - 1] * matrix[0,0] +
               src[i - xres]     * matrix[0,1] +
               src[i - xres + 1] * matrix[0,2] +
               src[i - 1]        * matrix[1,0] +
               src[i + 1]        * matrix[1,2] +
               src[i + xres - 1] * matrix[2,0] +
               src[i + xres]     * matrix[2,1] +
               src[i + xres + 1] * matrix[2,2];
      t[i] := (value / mplr) + bias;
    end;

// the edges
  for x := 1 to xres - 2 do
  begin
    i := x;
    value := src[i] * matrix[1,1] +
             src[i - 1]        * matrix[1,0] +
             src[i + 1]        * matrix[1,2] +
             src[i + xres - 1] * matrix[2,0] +
             src[i + xres]     * matrix[2,1] +
             src[i + xres + 1] * matrix[2,2];
    t[i] := (value / mplr) + bias;
  end;

  for x := 1 to xres - 2 do
  begin
    i := (yres - 1) * xres + x;
    value := src[i] * matrix[1,1] +
             src[i - xres - 1] * matrix[0,0] +
             src[i - xres]     * matrix[0,1] +
             src[i - xres + 1] * matrix[0,2] +
             src[i - 1]        * matrix[1,0] +
             src[i + 1]        * matrix[1,2];
    t[i] := (value / mplr) + bias;
  end;

  for y := 1 to yres - 2 do
  begin
    i := y * xres;
    value := src[i] * matrix[1,1] +
             src[i - xres]     * matrix[0,1] +
             src[i - xres + 1] * matrix[0,2] +
             src[i + 1]        * matrix[1,2] +
             src[i + xres]     * matrix[2,1] +
             src[i + xres + 1] * matrix[2,2];
    t[i] := (value / mplr) + bias;
  end;

  for y := 1 to yres - 2 do
  begin
    i := y * xres + (xres - 1);
    value := src[i] * matrix[1,1] +
             src[i - xres - 1] * matrix[0,0] +
             src[i - xres]     * matrix[0,1] +
             src[i - 1]        * matrix[1,0] +
             src[i + xres - 1] * matrix[2,0] +
             src[i + xres]     * matrix[2,1];
    t[i] := (value / mplr) + bias;
  end;

// the corners
  i := 0;
  value := src[i] * matrix[1,1] +
           src[i + 1]        * matrix[1,2] +
           src[i + xres]     * matrix[2,1] +
           src[i + xres + 1] * matrix[2,2];
  t[i] := (value / mplr) + bias;

  i := xres - 1;
  value := src[i] * matrix[1,1] +
           src[i - 1]        * matrix[1,0] +
           src[i + xres - 1] * matrix[2,0] +
           src[i + xres]     * matrix[2,1];
  t[i] := (value / mplr) + bias;

  i := (yres - 1) * xres;
  value := src[i] * matrix[1,1] +
           src[i - xres]     * matrix[0,1] +
           src[i - xres + 1] * matrix[0,2] +
           src[i + 1]        * matrix[1,2];
  t[i] := (value / mplr) + bias;

  i := (yres - 1) * xres + (xres - 1);
  value := src[i] * matrix[1,1] +
           src[i - xres - 1] * matrix[0,0] +
           src[i - xres]     * matrix[0,1] +
           src[i - 1]        * matrix[1,0];
  t[i] := (value / mplr) + bias;  

// copy back to original buffer
  for i := 0 to l - 1 do
    src[i] := t[i];
  FreeMem(t);
end;

procedure TForm1.Button1Click(Sender: TObject);
var x, y: Integer;
    s: String;
begin
  s := '';
  for y := 0 to 9 do
  begin
    for x := 0 to 9 do
      s := s + Format('%6.2f  ', [a[y,x]]);
    s := s + #10#13;
  end;
  Label1.Caption := s;

  MyFilter(@a, 10, 10, m, 0, 8);

  s := '';
  for y := 0 to 9 do
  begin
    for x := 0 to 9 do
      s := s + Format('%6.2f  ', [a[y,x]]);
    s := s + #10#13;
  end;
  Label2.Caption := s;
end;

end.
0
 
iwatkinsAuthor Commented:
Epsylon,

Nice one, works perfectly on the 10x10 array supplied. I will award you the points now as it works, but I have yet to test it on my 601x601 array. Even so, it appears to be very, very fast.

Anyway, thanks for your effort on this one.

I will let you know how I get on with the rest of the project.

Cheers

Ian
0
 
iwatkinsAuthor Commented:
Hi All and especially Epsylon,

Working well with a 601x601 array (361201 float values). It is actually taking longer to read in the values from file and back to file than it is to run the 'blur'.

In fact, I have just finished testing and the best 'blur' seems to be by making running the routine 5 times, i.e. 5 passes. This executes in under two seconds on my 450 Mhz.

For anybody else wanting to run this routine, best results are with:

Bias = 0
Divisor = 9

Matrix =
1,1,1
1,1,1
1,1,1
       
Very happy with it.

Cheers
0
 
EpsylonCommented:
Hi, I tested it with a 1000x1000 array and it was ready within a second (450MHz too)     :o)

CPU's are fast these days!!!

....but never fast enough   :o)

Cheers,

Eps.
0

Featured Post

The 14th Annual Expert Award Winners

The results are in! Meet the top members of our 2017 Expert Awards. Congratulations to all who qualified!

  • 7
  • 7
  • 3
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now