[GRASS5] Gaussian filter for GRASS

Hallo developers,
I have tryed to program the Gaussian filter for raster maps. It works only with
CELL maps for now, but I hope to extend it for DCELL and FCELL maps too.

Because this is my first raster module for GRASS and also my first bigger
C-program, I would be very thankfull, if someone could have a look at the
code at to tell me, what I could do better/safer.

I would like to write some other filters for raster maps too, the next stage
should be Mean of least variance filter. But before I start, I would like to be
shure, that I don't do something really stupid in the program.

I could not to solve, how to work on the boundaries of the raster. I had to
skip these cells, like e.g. r.mapcalc does -- so NULL values appear there.
How to do it some better way?

I'm also not shure, if I do the allocation of neigborhood rows the right way.

I hope, I did not forget to free all the buffers at the end.

I'm looking forward to your hints

Jachym
--
Jachym Cepicky
e-mail: jachym.cepicky@centrum.cz
URL: http://les-ejk.cz
GPG: http://www.fle.czu.cz/~jachym/gnupg_public_key/

(attachments)

r.gauss.tgz (17.6 KB)

Jachym Cepicky wrote:

I have tryed to program the Gaussian filter for raster maps. It
works only with CELL maps for now, but I hope to extend it for DCELL
and FCELL maps too.

The simplest solution is to use DCELL throughout. All CELL and FCELL
values can be represented as DCELL without loss of range or precision.

Because this is my first raster module for GRASS and also my first bigger
C-program, I would be very thankfull, if someone could have a look at the
code at to tell me, what I could do better/safer.

Use a scrolling window; don't read each row three times.

At the moment, you're reading rows 0/1/2, then 1/2/3, then 2/3/4 and
so on. Note that you're reading each row (except the first and last
two) three times. Also note that you already have two out of the three
rows in memory from the previous pass (again, except for the first and
last two passes).

See r.neighbors or r.grow2.

Actually, is there any reason why this filter can't be added to
r.neighbors as another method?

I would like to write some other filters for raster maps too, the next stage
should be Mean of least variance filter. But before I start, I would like to be
shure, that I don't do something really stupid in the program.

I could not to solve, how to work on the boundaries of the raster. I had to
skip these cells, like e.g. r.mapcalc does -- so NULL values appear there.
How to do it some better way?

In general, you can't. Convolution-style filters always result in the
map being "shrunk".

I'm also not shure, if I do the allocation of neigborhood rows the right way.

I hope, I did not forget to free all the buffers at the end.

It doesn't matter about freeing buffers; all memory will be reclaimed
by the OS when the program terminates.

--
Glynn Clements <glynn@gclements.plus.com>

hallo,
thank you very much for your hints! No I try to rewrite it some better way.

On Thu, Apr 21, 2005 at 07:06:39PM +0100, Glynn Clements wrote:

Jachym Cepicky wrote:

> I have tryed to program the Gaussian filter for raster maps. It
> works only with CELL maps for now, but I hope to extend it for DCELL
> and FCELL maps too.

The simplest solution is to use DCELL throughout. All CELL and FCELL
values can be represented as DCELL without loss of range or precision.

OH! I didn't know this! I have to read it in the Programers manual - I
thought, DCELL is just 'some' another type of map :frowning:

> Because this is my first raster module for GRASS and also my first bigger
> C-program, I would be very thankfull, if someone could have a look at the
> code at to tell me, what I could do better/safer.

Use a scrolling window; don't read each row three times.

At the moment, you're reading rows 0/1/2, then 1/2/3, then 2/3/4 and
so on. Note that you're reading each row (except the first and last
two) three times. Also note that you already have two out of the three
rows in memory from the previous pass (again, except for the first and
last two passes).

See r.neighbors or r.grow2.

I looked there, but I did not see it. Thanks again...

Actually, is there any reason why this filter can't be added to
r.neighbors as another method?

Well, I wanted somehow to start to write my own modules. Gaussian is not much complicated
filter, but mean of least variance, bilateral filter and others will be a bit
complicated (well - I thing so). So I wanted to start somehow, so I know, how to
write a module from the beginning. I learned very much on this.

> I would like to write some other filters for raster maps too, the next stage
> should be Mean of least variance filter. But before I start, I would like to be
> shure, that I don't do something really stupid in the program.
>
> I could not to solve, how to work on the boundaries of the raster. I had to
> skip these cells, like e.g. r.mapcalc does -- so NULL values appear there.
> How to do it some better way?

In general, you can't. Convolution-style filters always result in the
map being "shrunk".

But one could count the value of the central cell only from the values, which
are laying in the raster

+-----------+
| 1| 2| 3| 4| So for cell number 1, one would take the cells 1,2,5 and 6
+-----------+ in the count. For number 2, One would take 1,2,3,5,6,7
| 5| 6| 7| 8| first for cell number 6, one would take all 9 values around.
+-----------+
| 9|10|11|12| But it would be too complicated tough :-/
+-----------+

> I'm also not shure, if I do the allocation of neigborhood rows the right way.
>
> I hope, I did not forget to free all the buffers at the end.

It doesn't matter about freeing buffers; all memory will be reclaimed
by the OS when the program terminates.

Oh, I did! I did it by realy BAD way: For each CELL I allocated the 3 rows
again!! And than I forget to free it! But it is all right now.

--
Glynn Clements <glynn@gclements.plus.com>

OK, I will work with DCELL type, than I implement the row swiching from r.grow2

    tmp = in_rows[0];
    for (i = 0; i < size * 2; i++)
      in_rows[i] = in_rows[i + 1];
    in_rows[size * 2] = tmp;

This is a good trick and I post the result again, so you and others could have
a look at it.

Now it works nearly 2x faster, than r.mapcalc filter (from GRASS add-ons). With
these modifications, it will be even better!

Thank you again

Jachym
--
Jachym Cepicky
e-mail: jachym.cepicky@centrum.cz
URL: http://les-ejk.cz
GPG: http://www.fle.czu.cz/~jachym/gnupg_public_key/

Hallo developers,
so I gave it some more time and tryed to work in all the comments from Glynn
and I hope, I can say, GRASS has its own Gaussian filter.

The module handels all the maps as DCELL map, it uses scrolling window
(scrolling rows), like r.neighbors or r.grow2 and it hopefully frees all the
buffers.

The first version, I posted into this list, is really bad and I would suggest
all of you, not to look at the code, it could hurt to your electronic cardiac
pacemaker.

I would like, if some one could have a look at the code, like Glynn did, and to
tell me, what one (me) could do better or if there is no bigger bug. It would
be very important for me, as this is the first modul, I wrote, and even first
C-program.

Comparison with r.mapcalc implementation:
-----------------------------------------
nsres: 0.5
ewres: 0.5
rows: 3332
cols: 3413

r.mapcalc |
(gaussian.pl at GRASS add-ons site):expressionless: r.gauss:
------------------------------------+-------------------
real 0m26.534s | real 0m8.482s
user 0m24.281s | user 0m7.787s
sys 0m0.898s | sys 0m0.437s

Which means, it is really faster :slight_smile:

Why did I write standalone module and why didn't I merge it with e.g.
r.neighbors:

1) I wanted to learn, how to write own modules in GRASS (this was the most
   importand for me)

2) Gaussian filter needs parametr sigma on it's input - none of methods from
   r.neighbors needs any other parametr - it would be unconsistent.
   
3) I hope to write some other tools for remote sensing. All the filters need
   special parametres, so I personaly don't see the way, how to merge them into
   one (two) big module(s) :-/
   Gaussian filter is the simplest filter. I wrote it more from study reasons,
   as that I would really need it for my work. But it is mentioned, as the
   basic low pass filter.

I don't stand on, put my r.gaussian into CVS-tree. It can live on the GRASS
add-ons site (if not somewhere else). I would just please someone to check
my work, if there is someone who could give it some time ... sometimes :slight_smile:

Thank you

Jachym
--
Jachym Cepicky
e-mail: jachym.cepicky@centrum.cz
URL: http://les-ejk.cz
GPG: http://www.fle.czu.cz/~jachym/gnupg_public_key/

(attachments)

r.gauss.tgz (11.3 KB)

Jachym Cepicky wrote:

> > I have tryed to program the Gaussian filter for raster maps. It
> > works only with CELL maps for now, but I hope to extend it for DCELL
> > and FCELL maps too.
>
> The simplest solution is to use DCELL throughout. All CELL and FCELL
> values can be represented as DCELL without loss of range or precision.

OH! I didn't know this! I have to read it in the Programers manual - I
thought, DCELL is just 'some' another type of map :frowning:

No, they're just aliases for the standard C types:

  CELL int
  FCELL float
  DCELL double

> > Because this is my first raster module for GRASS and also my first bigger
> > C-program, I would be very thankfull, if someone could have a look at the
> > code at to tell me, what I could do better/safer.
>
> Use a scrolling window; don't read each row three times.
>
> At the moment, you're reading rows 0/1/2, then 1/2/3, then 2/3/4 and
> so on. Note that you're reading each row (except the first and last
> two) three times. Also note that you already have two out of the three
> rows in memory from the previous pass (again, except for the first and
> last two passes).
>
> See r.neighbors or r.grow2.

I looked there, but I did not see it. Thanks again...

Look at how in_rows is used in r.grow2. [OK, reading further, you
already have.]

For r.neighbors, the equivalent is ncb.buf; however, r.neighbors is
split across several files, so the logic is a bit harder to follow.

> > I could not to solve, how to work on the boundaries of the raster. I had to
> > skip these cells, like e.g. r.mapcalc does -- so NULL values appear there.
> > How to do it some better way?
>
> In general, you can't. Convolution-style filters always result in the
> map being "shrunk".

But one could count the value of the central cell only from the values, which
are laying in the raster

+-----------+
| 1| 2| 3| 4| So for cell number 1, one would take the cells 1,2,5 and 6
+-----------+ in the count. For number 2, One would take 1,2,3,5,6,7
| 5| 6| 7| 8| first for cell number 6, one would take all 9 values around.
+-----------+
| 9|10|11|12| But it would be too complicated tough :-/
+-----------+

For simple aggregates which don't care about the relative positions of
the cells (sum, mean etc), you can get an answer, but it isn't
necessarily meaningful (e.g. for a sum, the values of the edge cells
will tend to be lower because you're summing fewer values).

For convolution filters, silently "ignoring" missing cells tends to
produce the same result as if the cell's value was zero. Usually this
doesn't make sense.

More realistic would be to define separate weight matrices for each
case, but this is usually too involved. Or you could scale the weights
by the total number of cells, then divide by the actual number of
non-NULL cells; that will tend to give reasonable results for typical
smoothing filters.

Whatever approach you use should treat "stored" NULLs the same as
cells which are NULL by virtue of being outside of the current region.
I.e. if you were to enlarge the region, filling the boundary cells
with NULLs, you should get the same result.

r.grow2 just fills input cells with NULL if they are outside of the
region, so there is guaranteed to be no difference with stored NULLs.

OK, I will work with DCELL type, than I implement the row swiching from r.grow2

    tmp = in_rows[0];
    for (i = 0; i < size * 2; i++)
      in_rows[i] = in_rows[i + 1];
    in_rows[size * 2] = tmp;

Yeah, that's the essence of a scrolling window.

--
Glynn Clements <glynn@gclements.plus.com>