gnuastro-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnuastro-commits] master aea662d: Mean and Median filtering added to Ar


From: Mohammad Akhlaghi
Subject: [gnuastro-commits] master aea662d: Mean and Median filtering added to Arithmetic program
Date: Sun, 8 Oct 2017 22:11:25 -0400 (EDT)

branch: master
commit aea662d139d17ee0e1b8c1a6869611970e4e0fb6
Author: Mohammad Akhlaghi <address@hidden>
Commit: Mohammad Akhlaghi <address@hidden>

    Mean and Median filtering added to Arithmetic program
    
    The Arithmetic program (not library currently) now has two new operators:
    `filter-median' and `filter-mean'. Currently they are implemented as a
    function directly in `bin/arithmetic/arithmetic.c', but we will later move
    it to the libraries (either as a separate library in `gnuastro/filter.h' or
    as part of an existing header.
---
 NEWS                        |   4 +
 bin/arithmetic/arithmetic.c | 428 +++++++++++++++++++++++++++++++++++++++-----
 bin/arithmetic/arithmetic.h |  15 ++
 doc/gnuastro.texi           |  45 ++++-
 lib/arithmetic.c            |   2 +
 lib/gnuastro/arithmetic.h   |   2 +
 lib/statistics.c            |   2 +-
 7 files changed, 453 insertions(+), 45 deletions(-)

diff --git a/NEWS b/NEWS
index 6ea4787..0fa2662 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,10 @@ GNU Astronomy Utilities NEWS                          -*- 
outline -*-
   All programs: a value of `0' to the `--numthreads' option will use the
   number of threads available to the system at run time.
 
+  Arithmetic: The new operators `filter-median' and `filter-mean' can be
+  used to filter (smooth) the input. The size of the filter can be set as
+  the other operands to these operators.
+
   BuildProgram: The new `--la' option allows the identification of a
   different Libtool `.la' file for Libtool linking information.
 
diff --git a/bin/arithmetic/arithmetic.c b/bin/arithmetic/arithmetic.c
index b213e7d..36ed627 100644
--- a/bin/arithmetic/arithmetic.c
+++ b/bin/arithmetic/arithmetic.c
@@ -31,6 +31,9 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 #include <gnuastro/wcs.h>
 #include <gnuastro/fits.h>
+#include <gnuastro/threads.h>
+#include <gnuastro/dimension.h>
+#include <gnuastro/statistics.h>
 #include <gnuastro/arithmetic.h>
 
 #include <gnuastro-internal/checkset.h>
@@ -60,7 +63,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
     CTYPE a=*(CTYPE *)(data->array); if(a>0) return a;    }
 
 static size_t
-set_number_of_operands(struct arithmeticparams *p, gal_data_t *data,
+pop_number_of_operands(struct arithmeticparams *p, gal_data_t *data,
                        char *token_string)
 {
   /* Check if its a number. */
@@ -121,6 +124,313 @@ set_number_of_operands(struct arithmeticparams *p, 
gal_data_t *data,
 
 
 
+/**********************************************************************/
+/****************         Filtering operators         *****************/
+/**********************************************************************/
+#define ARITHMETIC_FILTER_DIM 10
+
+struct arithmetic_filter_p
+{
+  int      operator;            /* The type of filtering.          */
+  size_t     *fsize;            /* Filter size.                    */
+  size_t   *hpfsize;            /* Positive Half-filter size.      */
+  size_t   *hnfsize;            /* Negative Half-filter size.      */
+  gal_data_t *input;            /* Input dataset.                  */
+  gal_data_t   *out;            /* Output dataset.                 */
+
+  int      hasblank;            /* If the dataset has blank values.*/
+};
+
+
+
+
+
+/* Main filtering work function. */
+static void *
+arithmetic_filter(void *in_prm)
+{
+  struct gal_threads_params *tprm=(struct gal_threads_params *)in_prm;
+  struct arithmetic_filter_p *afp=(struct arithmetic_filter_p *)tprm->params;
+  gal_data_t *input=afp->input;
+
+  size_t ind,index;
+  int out_type_checked=0;
+  gal_data_t *result=NULL;
+  size_t *hpfsize=afp->hpfsize, *hnfsize=afp->hnfsize;
+  size_t *tsize, *dsize=input->dsize, *fsize=afp->fsize;
+  size_t i, j, coord[ARITHMETIC_FILTER_DIM], ndim=input->ndim;
+  size_t start[ARITHMETIC_FILTER_DIM], end[ARITHMETIC_FILTER_DIM];
+  gal_data_t *tile=gal_data_alloc(NULL, input->type, ndim, afp->fsize, NULL,
+                                  0, -1, NULL, NULL, NULL);
+
+  /* Prepare the tile. */
+  free(tile->array);
+  tsize=tile->dsize;
+  tile->block=input;
+
+
+  /* Go over all the pixels that were assigned to this thread. */
+  for(i=0; tprm->indexs[i] != GAL_BLANK_SIZE_T; ++i)
+    {
+      /* Get the coordinate of the pixel. */
+      ind=tprm->indexs[i];
+      gal_dimension_index_to_coord(ind, ndim, dsize, coord);
+
+      /* See which dimensions need trimming. */
+      tile->size=1;
+      for(j=0;j<ndim;++j)
+        {
+          /* Estimate the coordinate of the filter's starting point. Note
+             that we are dealing with size_t (unsigned int) type here, so
+             there are no negatives. A negative result will produce an
+             extremely large number, so instead of checking for negative,
+             we can just see if the result of a subtraction is less than
+             the width of the input. */
+          if( (coord[j] - hnfsize[j] > dsize[j])
+              || (coord[j] + hpfsize[j] >= dsize[j]) )
+            {
+              start[j] = ( (coord[j] - hnfsize[j] > dsize[j])
+                           ? 0 : coord[j] - hnfsize[j] );
+              end[j]   = ( (coord[j] + hpfsize[j] >= dsize[j])
+                           ? dsize[j]
+                           : coord[j] + hpfsize[j] + 1);
+              tsize[j] = end[j] - start[j];
+            }
+          else  /* We are NOT on the edge (given requested filter width). */
+            {
+              tsize[j] = fsize[j];
+              start[j] = coord[j] - hnfsize[j];
+            }
+          tile->size *= tsize[j];
+        }
+
+      /* Set the tile's starting pointer. */
+      index=gal_dimension_coord_to_index(ndim, dsize, start);
+      tile->array=gal_data_ptr_increment(input->array, index, input->type);
+
+      /* Do the necessary calculation. */
+      switch(afp->operator)
+        {
+        case ARITHMETIC_OP_FILTER_MEDIAN:
+          result=gal_statistics_median(tile, 0);
+          break;
+
+        case ARITHMETIC_OP_FILTER_MEAN:
+          result=gal_statistics_mean(tile);
+          break;
+
+        default:
+          error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
+                "fix the problem. `afp->operator' code %d is not "
+                "recognized", PACKAGE_BUGREPORT, __func__, afp->operator);
+        }
+
+      /* Make sure the output array type and result's type are the same. We
+         only need to do this once, but we'll suffice to once for each
+         thread for simplicify of the code, it is too negligible to have
+         any real affect. */
+      if( out_type_checked==0)
+        {
+          if(result->type!=afp->out->type )
+            error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s so "
+                  "we can address the problem. The tyes of `result' and "
+                  "`out' aren't the same, they are respectively: `%s' and "
+                  "`%s'", __func__, PACKAGE_BUGREPORT,
+                  gal_type_name(result->type, 1),
+                  gal_type_name(afp->out->type, 1));
+          out_type_checked=1;
+        }
+
+      /* Copy the result into the output array. */
+      memcpy(gal_data_ptr_increment(afp->out->array, ind, afp->out->type),
+             result->array, gal_type_sizeof(afp->out->type));
+
+      /* Clean up. */
+      gal_data_free(result);
+    }
+
+
+  /* Clean up. */
+  tile->array=NULL;
+  tile->block=NULL;
+  gal_data_free(tile);
+
+
+  /* Wait for all the other threads to finish, then return. */
+  if(tprm->b) pthread_barrier_wait(tprm->b);
+  return NULL;
+}
+
+
+
+
+
+static void
+wrapper_for_filter(struct arithmeticparams *p, char *token, int operator)
+{
+  size_t i=0, ndim, one=1;
+  int type=GAL_TYPE_INVALID;
+  struct arithmetic_filter_p afp={0};
+  size_t fsize[ARITHMETIC_FILTER_DIM];
+  gal_data_t *tmp, *tmp2, *zero, *comp, *fsize_list=NULL;
+  size_t hnfsize[ARITHMETIC_FILTER_DIM], hpfsize[ARITHMETIC_FILTER_DIM];
+
+
+  /* Get the input's number of dimensions. */
+  afp.input=operands_pop(p, token);
+  afp.operator=operator;
+  ndim=afp.input->ndim;
+  afp.hnfsize=hnfsize;
+  afp.hpfsize=hpfsize;
+  afp.fsize=fsize;
+
+
+  /* A small sanity check. */
+  if(ndim>ARITHMETIC_FILTER_DIM)
+    error(EXIT_FAILURE, 0, "%s: currently only datasets with less than "
+          "%d dimensions are acceptable. The input has %zu dimensions",
+          __func__, ARITHMETIC_FILTER_DIM, ndim);
+
+
+  /* A zero value for checking the value of input widths. */
+  zero=gal_data_alloc(NULL, GAL_TYPE_INT32, 1, &one, NULL, 1, -1, NULL,
+                      NULL, NULL);
+
+
+  /* Based on the dimensions of the popped operand, pop the necessary
+     number of operands. */
+  for(i=0;i<ndim;++i)
+    gal_list_data_add(&fsize_list, operands_pop(p, token));
+
+
+  /* Make sure the filter size only has single values. */
+  i=0;
+  for(tmp=fsize_list; tmp!=NULL; tmp=tmp->next)
+    {
+      ++i;
+      if(tmp->size!=1)
+        error(EXIT_FAILURE, 0, "the filter length values given to the "
+              "filter operators can only be numbers. Value number %zu has "
+              "%zu elements, so its an array", ndim-i-1, tmp->size);
+    }
+
+
+  /* If the input only has one element, filtering makes no sense, so don't
+     waste time, just add the input onto the stack. */
+  if(afp.input->size==1) afp.out=afp.input;
+  else
+    {
+      /* Allocate an array for the size of the filter and fill it in. The
+         values must be written in the inverse order since the user gives
+         dimensions with the FITS standard. */
+      i=ndim-1;
+      for(tmp=fsize_list; tmp!=NULL; tmp=tmp->next)
+        {
+          /* Make sure the user has given an integer type. */
+          if(tmp->type==GAL_TYPE_FLOAT32 || tmp->type==GAL_TYPE_FLOAT64)
+            error(EXIT_FAILURE, 0, "lengths of filter along dimensions "
+                  "must be integer values, not floats. The given length "
+                  "along dimension %zu is a float", ndim-i);
+
+          /* Make sure it isn't negative. */
+          comp=gal_arithmetic(GAL_ARITHMETIC_OP_GT, 0, tmp, zero);
+          if( *(uint8_t *)(comp->array) == 0 )
+            error(EXIT_FAILURE, 0, "lengths of filter along dimensions "
+                  "must be positive. The given length in dimension %zu"
+                  "is either zero or negative", ndim-i);
+
+          /* Convert the input into size_t and put it into the array that
+             keeps the filter size. */
+          tmp2=gal_data_copy_to_new_type(tmp, GAL_TYPE_SIZE_T);
+          fsize[ i ] = *(size_t *)(tmp2->array);
+          gal_data_free(tmp2);
+
+          /* If the width is larger than the input's size, change the width
+             to the input's size. */
+          if( fsize[i] > afp.input->dsize[i] )
+            error(EXIT_FAILURE, 0, "%s: the filter size along dimension %zu "
+                  "(%zu) is greater than the input's length in that "
+                  "dimension (%zu)", __func__, i, fsize[i],
+                  afp.input->dsize[i]);
+
+          /* Go onto the previous dimension. */
+          --i;
+        }
+
+
+      /* Set the half filter sizes. Note that when the size is an odd
+         number, the number of pixels before and after the actual pixel are
+         equal, but for an even number, we will look into one element more
+         when looking before than the ones after. */
+      for(i=0;i<ndim;++i)
+        {
+          if( fsize[i]%2 )
+            hnfsize[i]=hpfsize[i]=fsize[i]/2;
+          else
+            { hnfsize[i]=fsize[i]/2; hpfsize[i]=fsize[i]/2-1; }
+        }
+
+
+      /* See if the input has blank pixels. */
+      afp.hasblank=gal_blank_present(afp.input, 1);
+
+      /* Set the type of the output dataset. */
+      switch(operator)
+        {
+        case ARITHMETIC_OP_FILTER_MEDIAN:
+          type=afp.input->type;
+          break;
+
+        case ARITHMETIC_OP_FILTER_MEAN:
+          type=GAL_TYPE_FLOAT64;
+          break;
+
+        default:
+          error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s to fix "
+                "the problem. The `operator' code %d is not recognized",
+                PACKAGE_BUGREPORT, __func__, operator);
+        }
+
+      /* Allocate the output dataset. Note that filtering doesn't change
+         the units of the dataset. */
+      afp.out=gal_data_alloc(NULL, type, ndim, afp.input->dsize,
+                             afp.input->wcs, 0, afp.input->minmapsize,
+                             NULL, afp.input->unit, NULL);
+
+      /* Spin off threads for each pixel. */
+      gal_threads_spin_off(arithmetic_filter, &afp, afp.input->size,
+                           p->cp.numthreads);
+    }
+
+
+  /* Add the output to the top of the stack. */
+  operands_add(p, NULL, afp.out);
+
+
+  /* Clean up and add the output on top of the stack */
+  gal_data_free(afp.input);
+  gal_list_data_free(fsize_list);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 /***************************************************************/
 /*************      Reverse Polish algorithm       *************/
 /***************************************************************/
@@ -274,6 +584,13 @@ reversepolish(struct arithmeticparams *p)
           else if (!strcmp(token->v, "float64"))
             { op=GAL_ARITHMETIC_OP_TO_FLOAT64;    nop=1;  }
 
+          /* Filters. */
+          else if (!strcmp(token->v, "filter-median"))
+            { op=ARITHMETIC_OP_FILTER_MEDIAN;     nop=0;  }
+          else if (!strcmp(token->v, "filter-mean"))
+            { op=ARITHMETIC_OP_FILTER_MEAN;       nop=0;  }
+
+
           /* Finished checks with known operators */
           else
             error(EXIT_FAILURE, 0, "the argument \"%s\" could not be "
@@ -281,51 +598,76 @@ reversepolish(struct arithmeticparams *p)
                   token->v);
 
 
-          /* Pop the necessary number of operators. Note that the operators
-             are poped from a linked list (which is last-in-first-out). So
-             for the operators which need a specific order, the first poped
-             operand is actally the last (right most, in in-fix notation)
-             input operand.*/
-          switch(nop)
+          /* See if the arithmetic library must be called or not. */
+          if(nop)
             {
-            case 1:
-              d1=operands_pop(p, token->v);
-              break;
-
-            case 2:
-              d2=operands_pop(p, token->v);
-              d1=operands_pop(p, token->v);
-              break;
-
-            case 3:
-              d3=operands_pop(p, token->v);
-              d2=operands_pop(p, token->v);
-              d1=operands_pop(p, token->v);
-              break;
-
-            case -1:
-              /* This case is when the number of operands is itself an
-                 operand. So the first popped operand must be an integer
-                 number, we will use that to construct a linked list of any
-                 number of operands within the single `d1' pointer. */
-              d1=NULL;
-              numop=set_number_of_operands(p, operands_pop(p, token->v),
-                                           token->v);
-              for(i=0;i<numop;++i)
-                gal_list_data_add(&d1, operands_pop(p, token->v));
-              break;
-
-            default:
-              error(EXIT_FAILURE, 0, "No operators need %d operands", nop);
+              /* Pop the necessary number of operators. Note that the
+                 operators are poped from a linked list (which is
+                 last-in-first-out). So for the operators which need a
+                 specific order, the first poped operand is actally the
+                 last (right most, in in-fix notation) input operand.*/
+              switch(nop)
+                {
+                case 1:
+                  d1=operands_pop(p, token->v);
+                  break;
+
+                case 2:
+                  d2=operands_pop(p, token->v);
+                  d1=operands_pop(p, token->v);
+                  break;
+
+                case 3:
+                  d3=operands_pop(p, token->v);
+                  d2=operands_pop(p, token->v);
+                  d1=operands_pop(p, token->v);
+                  break;
+
+                case -1:
+                  /* This case is when the number of operands is itself an
+                     operand. So the first popped operand must be an
+                     integer number, we will use that to construct a linked
+                     list of any number of operands within the single `d1'
+                     pointer. */
+                  d1=NULL;
+                  numop=pop_number_of_operands(p, operands_pop(p, token->v),
+                                               token->v);
+                  for(i=0;i<numop;++i)
+                    gal_list_data_add(&d1, operands_pop(p, token->v));
+                  break;
+
+                default:
+                  error(EXIT_FAILURE, 0, "no operators, `%s' needs %d "
+                        "operand(s)", token->v, nop);
+                }
+
+
+              /* Run the arithmetic operation. Note that `gal_arithmetic'
+                 is a variable argument function (like printf). So the
+                 number of arguments it uses depend on the operator. So
+                 when the operator doesn't need three operands, the extra
+                 arguments will be ignored. */
+              operands_add(p, NULL, gal_arithmetic(op, flags, d1, d2, d3));
             }
 
-
-          /* Run the arithmetic operation. Note that `gal_arithmetic' is a
-             variable argument function (like printf). So the number of
-             arguments it uses depend on the operator. So when the operator
-             doesn't need three operands, the extra arguments will be
-             ignored. */
-          operands_add(p, NULL, gal_arithmetic(op, flags, d1, d2, d3));
+          /* No need to call the arithmetic library, call the proper
+             wrappers directly. */
+          else
+            {
+              switch(op)
+                {
+                case ARITHMETIC_OP_FILTER_MEAN:
+                case ARITHMETIC_OP_FILTER_MEDIAN:
+                  wrapper_for_filter(p, token->v, op);
+                  break;
+
+                default:
+                  error(EXIT_FAILURE, 0, "%s: a bug! please contact us at "
+                        "%s to fix the problem. The code %d is not "
+                        "recognized for `op'", __func__, PACKAGE_BUGREPORT,
+                        op);
+                }
+            }
         }
     }
 
diff --git a/bin/arithmetic/arithmetic.h b/bin/arithmetic/arithmetic.h
index e7f2ea0..833c5bf 100644
--- a/bin/arithmetic/arithmetic.h
+++ b/bin/arithmetic/arithmetic.h
@@ -23,6 +23,21 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #ifndef IMGARITH_H
 #define IMGARITH_H
 
+
+#include <gnuastro/arithmetic.h>
+
+
+/* These are operator codes for functions that aren't in the arithmetic
+   library. */
+enum arithmetic_prog_operators
+{
+  ARITHMETIC_OP_FILTER_MEDIAN=GAL_ARITHMETIC_OP_LAST_CODE,
+  ARITHMETIC_OP_FILTER_MEAN,
+};
+
+
+
+
 void
 imgarith(struct arithmeticparams *p);
 
diff --git a/doc/gnuastro.texi b/doc/gnuastro.texi
index 305dcfc..daeb86e 100644
--- a/doc/gnuastro.texi
+++ b/doc/gnuastro.texi
@@ -8694,6 +8694,49 @@ standard deviation of the respective pixels in all 
operands in the stack.
 Similar to @command{min}, but the pixels of the output will contain
 the median of the respective pixels in all operands in the stack.
 
address@hidden filter-mean
+Apply mean filtering (or @url{https://en.wikipedia.org/wiki/Moving_average,
+moving average}) on the input dataset. During mean filtering, each pixel
+(data element) is replaced by the mean value of all its surrounding pixels
+(excluding blank values). The number of surrounding pixels in each
+dimension (to calculate the mean) is determined through the earlier
+operands that have been pushed onto the stack prior to the input
+dataset. The number of necessary operands is determined by the
+dimensionality of the input dataset (first popped operand). The order of
+the dimensions on the command-line is the order in FITS format. Here is one
+example:
+
address@hidden
+$ astarithmetic 5 4 image.fits filter-mean
address@hidden example
+
address@hidden
+In this example, each pixel is replaced by the mean of a 5 by 4 box around
+it. The box is 5 pixels along the first FITS dimension (horizontal when
+viewed in ds9) and 4 pixels along the second FITS dimension (vertical).
+
+Each pixel will be placed in the center of the box that the mean is
+calculated on. If the given width along a dimension is even, then the
+center is assumed to be between the pixels (not in the center of a
+pixel). When the pixel is close to the center, the pixels of the box that
+fall outside the image are ignored. Therefore, on the edge, less points
+will be used in calculating the mean.
+
+The final effect of mean filtering is to smooth the input image, it is
+essentially a convolution with a kernel that has identical values for all
+its pixels (is flat), see @ref{Convolution process}.
+
address@hidden filter-median
+Apply @url{https://en.wikipedia.org/wiki/Median_filter, median filtering}
+on the input dataset. This is very similar to @command{filter-mean}, except
+that instead of the mean value of the box pixels, the median value is used
+to replace a pixel value. For more on how to use this operator, please see
address@hidden
+
+The median is less susceptible to outliers compared to the mean. As a
+result, after median filtering, the pixel values will be more discontinuous
+than mean filtering.
+
 @item lt
 Less than: If the second popped (or left operand in infix notation, see
 @ref{Reverse polish notation}) value is smaller than the first popped
@@ -17481,7 +17524,7 @@ number of threads on the system and spin-off threads. 
You can see a
 demonstration of using these functions in @ref{Library demo -
 multi-threaded operation}.
 
address@hidden {C @code{struct})} gal_threads_params
address@hidden {C @code{struct}} gal_threads_params
 Structure keeping the parameters of each thread. When each thread is
 created, a pointer to this structure is passed to it. The @code{params}
 element can be the pointer to a structure defined by the user which
diff --git a/lib/arithmetic.c b/lib/arithmetic.c
index 6e20539..db61e9c 100644
--- a/lib/arithmetic.c
+++ b/lib/arithmetic.c
@@ -1484,6 +1484,8 @@ gal_arithmetic(int operator, unsigned char flags, ...)
       out=arithmetic_abs(flags, d1);
       break;
 
+
+    /* Multi-operand operators */
     case GAL_ARITHMETIC_OP_MIN:
     case GAL_ARITHMETIC_OP_MAX:
     case GAL_ARITHMETIC_OP_NUM:
diff --git a/lib/gnuastro/arithmetic.h b/lib/gnuastro/arithmetic.h
index 5d50518..ba20c6a 100644
--- a/lib/gnuastro/arithmetic.h
+++ b/lib/gnuastro/arithmetic.h
@@ -132,6 +132,8 @@ enum gal_arithmetic_operators
   GAL_ARITHMETIC_OP_TO_INT64,     /* Convert to int64_t.                   */
   GAL_ARITHMETIC_OP_TO_FLOAT32,   /* Convert to float32.                   */
   GAL_ARITHMETIC_OP_TO_FLOAT64,   /* Convert to float64.                   */
+
+  GAL_ARITHMETIC_OP_LAST_CODE,    /* Last code of the library operands.    */
 };
 
 
diff --git a/lib/statistics.c b/lib/statistics.c
index 6d5e013..a85ce88 100644
--- a/lib/statistics.c
+++ b/lib/statistics.c
@@ -1805,7 +1805,7 @@ gal_statistics_sigma_clip(gal_data_t *input, float 
multip, float param,
       meanstd=gal_statistics_mean_std(nbs);
 
       /* Put the three final values in usable (with a type) pointers. */
-      med = median_d->array;
+      med  = median_d->array;
       mean = meanstd->array;
       std  = &((double *)(meanstd->array))[1];
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]