Ticket 782: Handle 16bit data in bropo
authorAnders Henja <anders@baltrad.eu>
Thu, 4 Jul 2019 11:56:50 +0000 (13:56 +0200)
committerAnders Henja <anders@baltrad.eu>
Thu, 4 Jul 2019 11:56:50 +0000 (13:56 +0200)
13 files changed:
pyropo/pyfmiimage.c
ropo/fmi_image.c
ropo/fmi_image.h
ropo/fmi_image_histogram.c
ropo/fmi_image_restore.c
ropo/rave_fmi_image.c
ropo/rave_fmi_image.h
ropo/rave_fmi_volume.c
ropo/rave_ropo_generator.c
test/pytest/FmiImageTest.py
test/pytest/PyRopoGeneratorTest.py
test/pytest/fixtures/pvol_rix.h5
test/pytest/fixtures/sebaa-scan_0_5_20190613.h5 [new file with mode: 0644]

index cb551a8..b065bf8 100644 (file)
@@ -401,13 +401,14 @@ fail:
 static PyObject* _pyfmiimage_toPolarScan(PyFmiImage* self, PyObject* args)
 {
   char* quantity = NULL;
+  int datatype=0;
   PolarScan_t* scan = NULL;
   PyObject* result = NULL;
 
-  if (!PyArg_ParseTuple(args, "|s", &quantity)) {
+  if (!PyArg_ParseTuple(args, "|si", &quantity, &datatype)) {
     return NULL;
   }
-  scan = RaveFmiImage_toPolarScan(self->image, quantity);
+  scan = RaveFmiImage_toPolarScan(self->image, quantity, datatype);
   if (scan != NULL) {
     result = (PyObject*)PyPolarScan_New(scan);
   }
@@ -425,11 +426,12 @@ static PyObject* _pyfmiimage_toRaveField(PyFmiImage* self, PyObject* args)
 {
   RaveField_t* field = NULL;
   PyObject* result = NULL;
+  int datatype=0;
 
-  if (!PyArg_ParseTuple(args, "")) {
+  if (!PyArg_ParseTuple(args, "|i", &datatype)) {
     return NULL;
   }
-  field = RaveFmiImage_toRaveField(self->image);
+  field = RaveFmiImage_toRaveField(self->image, datatype);
   if (field != NULL) {
     result = (PyObject*)PyRaveField_New(field);
   }
@@ -437,6 +439,83 @@ static PyObject* _pyfmiimage_toRaveField(PyFmiImage* self, PyObject* args)
   return result;
 }
 
+/**
+ * Sets the value in the processing array
+ * @param[in] self - self
+ * @param[in] args - (x,y),value
+ * @return None
+ */
+static PyObject* _pyfmiimage_setValue(PyFmiImage* self, PyObject* args)
+{
+  long x,y,v;
+
+  if (!PyArg_ParseTuple(args, "lll", &x, &y, &v)) {
+    return NULL;
+  }
+
+  put_pixel(RaveFmiImage_getImage(self->image), x, y, 0, (Byte)v);
+
+  Py_RETURN_NONE;
+}
+
+/**
+ * Returns the value in the processing array
+ * @param[in] self - self
+ * @param[in] args - (x,y)
+ * @return the value
+ */
+static PyObject* _pyfmiimage_getValue(PyFmiImage* self, PyObject* args)
+{
+  long x,y,v;
+
+  if (!PyArg_ParseTuple(args, "ll", &x, &y)) {
+    return NULL;
+  }
+
+  v = get_pixel(RaveFmiImage_getImage(self->image), x, y, 0);
+
+  return PyLong_FromLong(v);
+}
+
+/**
+ * Sets the value in the original array
+ * @param[in] self - self
+ * @param[in] args - (x,y),value
+ * @return None
+ */
+static PyObject* _pyfmiimage_setOriginalValue(PyFmiImage* self, PyObject* args)
+{
+  long x,y;
+  double v;
+
+  if (!PyArg_ParseTuple(args, "lld", &x, &y, &v)) {
+    return NULL;
+  }
+
+  put_pixel_orig(RaveFmiImage_getImage(self->image), x, y, 0, v);
+
+  Py_RETURN_NONE;
+}
+
+/**
+ * Returns the value in the original array
+ * @param[in] self - self
+ * @param[in] args - (x,y)
+ * @return the value
+ */
+static PyObject* _pyfmiimage_getOriginalValue(PyFmiImage* self, PyObject* args)
+{
+  long x,y;
+  double v;
+  if (!PyArg_ParseTuple(args, "ll", &x, &y)) {
+    return NULL;
+  }
+
+  v = get_pixel_orig(RaveFmiImage_getImage(self->image), x, y, 0);
+
+  return PyFloat_FromDouble(v);
+}
+
 /*@} End Of FmiImage */
 
 /**
@@ -446,11 +525,17 @@ static struct PyMethodDef _pyfmiimage_methods[] =
 {
   {"offset", NULL},
   {"gain", NULL},
+  {"undetect", NULL},
+  {"nodata", NULL},
   {"addAttribute", (PyCFunction)_pyfmiimage_addAttribute, 1},
   {"getAttribute", (PyCFunction)_pyfmiimage_getAttribute, 1},
   {"getAttributeNames", (PyCFunction)_pyfmiimage_getAttributeNames, 1},
   {"toPolarScan", (PyCFunction)_pyfmiimage_toPolarScan, 1},
   {"toRaveField", (PyCFunction)_pyfmiimage_toRaveField, 1},
+  {"setValue", (PyCFunction) _pyfmiimage_setValue, 1},
+  {"getValue", (PyCFunction) _pyfmiimage_getValue, 1},
+  {"setOriginalValue", (PyCFunction) _pyfmiimage_setOriginalValue, 1},
+  {"getOriginalValue", (PyCFunction) _pyfmiimage_getOriginalValue, 1},
   {NULL, NULL} /* sentinel */
 };
 
@@ -463,6 +548,10 @@ static PyObject* _pyfmiimage_getattro(PyFmiImage* self, PyObject* name)
     return PyFloat_FromDouble(RaveFmiImage_getOffset(self->image));
   } else if (PY_COMPARE_STRING_WITH_ATTRO_NAME("gain", name) == 0) {
     return PyFloat_FromDouble(RaveFmiImage_getGain(self->image));
+  } else if (PY_COMPARE_STRING_WITH_ATTRO_NAME("nodata", name) == 0) {
+    return PyFloat_FromDouble(RaveFmiImage_getNodata(self->image));
+  } else if (PY_COMPARE_STRING_WITH_ATTRO_NAME("undetect", name) == 0) {
+    return PyFloat_FromDouble(RaveFmiImage_getUndetect(self->image));
   }
   return PyObject_GenericGetAttr((PyObject*)self, name);
 }
@@ -497,6 +586,26 @@ static int _pyfmiimage_setattro(PyFmiImage* self, PyObject* name, PyObject* val)
       raiseException_gotoTag(done, PyExc_TypeError, "offset is a number");
     }
     RaveFmiImage_setGain(self->image, v);
+  } else if (PY_COMPARE_STRING_WITH_ATTRO_NAME("nodata", name) == 0) {
+    double v = 0.0;
+    if (PyFloat_Check(val)) {
+      v = PyFloat_AsDouble(val);
+    } else if (PyLong_Check(val)) {
+      v = PyLong_AsDouble(val);
+    } else {
+      raiseException_gotoTag(done, PyExc_TypeError, "nodata is a number");
+    }
+    RaveFmiImage_setNodata(self->image, v);
+  } else if (PY_COMPARE_STRING_WITH_ATTRO_NAME("undetect", name) == 0) {
+    double v = 0.0;
+    if (PyFloat_Check(val)) {
+      v = PyFloat_AsDouble(val);
+    } else if (PyLong_Check(val)) {
+      v = PyLong_AsDouble(val);
+    } else {
+      raiseException_gotoTag(done, PyExc_TypeError, "undetect is a number");
+    }
+    RaveFmiImage_setUndetect(self->image, v);
   } else {
     raiseException_gotoTag(done, PyExc_AttributeError, PY_RAVE_ATTRO_NAME_TO_STRING(name));
   }
index 4c61094..f1bafe9 100644 (file)
@@ -62,6 +62,10 @@ new_image(int sweep_count)
   for(i=0; i<sweep_count; i++) {
     result[i].heights=NULL;
     result[i].array=NULL;
+    result[i].original=NULL;
+    result[i].original_nodata=255.0;
+    result[i].original_undetect=0.0;
+    result[i].original_type = RaveDataType_UNDEFINED;
     result[i].elevation_angle=0.0;
     result[i].channels=0;
     result[i].bin_depth=0.0;
@@ -82,6 +86,10 @@ void init_new_image(FmiImage* img) {
   img->sweep_count = 0;
   img->heights = NULL;
   img->array = NULL;
+  img->original_type = RaveDataType_UNDEFINED;
+  img->original_nodata=255.0;
+  img->original_undetect=0.0;
+  img->original = NULL;
   img->elevation_angle = 0.0;
   img->channels = 0;
   img->bin_depth = 0;
@@ -100,6 +108,7 @@ int initialize_image(FmiImage *img){
   img->area=img->width*img->height;
   img->volume=img->area*img->channels;
   img->array=(Byte *) RAVE_MALLOC(img->volume);
+  img->original=(double*) RAVE_MALLOC(img->volume*sizeof(double));
   img->coord_overflow_handler_x=BORDER;
   img->coord_overflow_handler_y=BORDER;
   img->max_value=255;
@@ -238,6 +247,9 @@ int copy_image_properties(FmiImage *sample,FmiImage *target){
   target->sweep_count     = sample->sweep_count;
   target->bin_depth       = sample->bin_depth;
   target->elevation_angle = sample->elevation_angle;
+  target->original_nodata = sample->original_nodata;
+  target->original_undetect = sample->original_undetect;
+  target->original_type   = sample->original_type;
 
   if(sample->heights == NULL)
     target->heights   = NULL;
@@ -254,7 +266,6 @@ int copy_image_properties(FmiImage *sample,FmiImage *target){
   target->area=target->width*target->height;
   target->volume=target->channels*target->area;
 
-
   return 1;
 }
 
@@ -281,6 +292,9 @@ int check_image_properties(FmiImage *sample,FmiImage *target){
   target->max_value=sample->max_value;
   target->area=target->width*target->height;
   target->volume=target->channels*target->area;
+  target->original_nodata = sample->original_nodata;
+  target->original_undetect = sample->original_undetect;
+  target->original_type = sample->original_type;
   return 1;
 }
 
@@ -289,7 +303,7 @@ int canonize_image(FmiImage *sample,FmiImage *target){
        fmi_debug(2,"canonize_image?");
        if (!check_image_properties(sample,target)){
                fmi_debug(2,"canonize_image: YES");
-               copy_image_properties(sample,target);
+               copy_image_properties(sample, target);
                initialize_image(target);
        }
        fmi_debug(2,"canonize_image END");
@@ -299,7 +313,8 @@ int canonize_image(FmiImage *sample,FmiImage *target){
 void reset_image(FmiImage *image){
   switch (image->type){
   case TRUE_IMAGE:
-    RAVE_FREE (image->array);
+    RAVE_FREE(image->array);
+    RAVE_FREE(image->original);
   case NULL_IMAGE:
   case LINK_IMAGE:
     image->width=0;
@@ -309,7 +324,11 @@ void reset_image(FmiImage *image){
     image->sweep_count=0;
     image->bin_depth=0.0;
     image->elevation_angle=0.0;
-    RAVE_FREE (image->array);
+    image->original_type = RaveDataType_UNDEFINED;
+    image->original_nodata = 255.0;
+    image->original_undetect = 0.0;
+    RAVE_FREE(image->array);
+    RAVE_FREE(image->original);
     RAVE_FREE(image->heights);
     image->type=NULL_IMAGE;
     return;
@@ -369,6 +388,12 @@ void put_pixel(FmiImage *img,int x,int y,int channel,Byte c){
   /*  return 1; */
 }
 
+void put_pixel_orig(FmiImage *img,int x,int y,int channel,double c){
+  handle_coord_overflow(img,&x,&y);
+  img->original[channel*img->area + y*img->width + x]=c;
+  /*  return 1; */
+}
+
 void put_pixel_direct(FmiImage *img,int address,Byte c){
   img->array[address]=c;
 }
@@ -412,6 +437,12 @@ Byte get_pixel(FmiImage *img,int x,int y,int channel){
   return (img->array[channel*img->area + y*img->width + x]); 
 }
 
+double get_pixel_orig(FmiImage *img,int x,int y,int channel){
+  /*  return (img->array[y*img->width+x][channel]); */
+  handle_coord_overflow(img,&x,&y);
+  return (img->original[channel*img->area + y*img->width + x]);
+}
+
 void fill_image(FmiImage *img,Byte c){
   register int i;
   img->area=img->width*img->height;
@@ -420,6 +451,15 @@ void fill_image(FmiImage *img,Byte c){
     img->array[i]=c;
 }
 
+void fill_image_orig(FmiImage *img,double c){
+  register int i;
+  img->area=img->width*img->height;
+  img->volume=img->area*img->channels;
+  for (i=0;i<img->volume;i++)
+    img->original[i]=c;
+}
+
+
 void image_fill_random(FmiImage *img,Byte mean,Byte amplitude){
   register int i;
   img->area=img->width*img->height;
index 85566ed..6d080f5 100644 (file)
@@ -26,7 +26,7 @@
 #define FMI_IMAGE_VER "fmi_image \t v2.2\t Jul 2002 (c) Markus.Peura@fmi.fi"
 
 #include <stdio.h>
-
+#include "rave_types.h"
 /*
   typedef Coord short int;
   typedef Byte unsigned char;
@@ -125,7 +125,6 @@ typedef unsigned char ColorMap256[256][3];
 
 extern int FMI_IMAGE_COMMENT;
 
-
 struct fmi_image {
 
   /*
@@ -146,6 +145,12 @@ struct fmi_image {
   /* depth ? */
   /* unsigned char **array;*/
   Byte *array;
+
+  RaveDataType original_type;
+  double       original_nodata;
+  double       original_undetect;
+  double       *original; /**< the original data if it was greater than byte */
+
   CoordOverflowHandler coord_overflow_handler_x, coord_overflow_handler_y;
   /*  unsigned char *stream;*/
   char comment_string[MAX_COMMENT_LENGTH];
@@ -192,11 +197,14 @@ int canonize_image(FmiImage *sample,FmiImage *target);
 
 
 Byte get_pixel(FmiImage *img,int x,int y,int channel);
+double get_pixel_orig(FmiImage *img,int x,int y,int channel);
+
 /*#define get_pixel(img,x,y,channel) get(img,x,y,channel)  */
 
 Byte get_pixel_direct(FmiImage *img,int i);
 
 void put_pixel(FmiImage *img,int x,int y,int channel,Byte c);
+void put_pixel_orig(FmiImage *img,int x,int y,int channel, double c);
 /*#define put_pixel(img,x,y,channel,c) put(img,x,y,channel,c)  */
 void put_pixel_direct(FmiImage *img,int address,Byte c);
 void put_pixel_direct_inc(FmiImage *img,int address);
@@ -207,6 +215,7 @@ void put_pixel_or(FmiImage *img,int x,int y,int channel,Byte c);
 void put_pixel_and(FmiImage *img,int x,int y,int channel,Byte c);
 
 void fill_image(FmiImage *img,Byte c);
+void fill_image_orig(FmiImage *img,double c);
 void image_fill_random(FmiImage *img,Byte mean,Byte amplitude);
 void invert_image(FmiImage *img);
 void translate_intensity(FmiImage *img,Byte from,Byte to);
index 681178f..f59561b 100644 (file)
@@ -222,9 +222,10 @@ int histogram_mean_nonzero(Histogram h){
   sum=0;
   s=0;
   /* i=1,2,... */
-  for (i=1;i<256;i++){
+  for (i=1;i<256;i++) {
     s+=h[i];
-    sum+=h[i]*i;}
+    sum+=h[i]*i;
+  }
   if (s>0)
     return (sum/s);
   else
@@ -450,26 +451,31 @@ void initialize_histogram(FmiImage *source,Histogram histogram,int hrad,int vrad
   clear_histogram(histogram);   /*full? */
 
   /* EXCLUSIVE INITS */
-  if (hist_func==histogram_mean_weighted){
+  if (hist_func==histogram_mean_weighted) {
     fmi_debug(2,"initialize_histogram: histogram_mean_weighted, source:");
     image_info(source);
     fmi_debug(2,"initialize_histogram: histogram_mean_weighted, weight:");
     /* canonize_images(source,histogram_weight_image); */
     image_info(histogram_weight_image);
     histogram[HIST_SIZE]=0;
-    for (k=0;k<source->channels;k++)
-      for (m=-hrad;m<=hrad;m++) 
-       for (n=-vrad;n<=vrad;n++){
-         w=get_pixel(histogram_weight_image,i+m,j+n,k);
-         histogram[get_pixel(source,i+m,j+n,k)]+=w;
-         histogram[HIST_SIZE]+=w;
-       }
+    for (k=0;k<source->channels;k++) {
+      for (m=-hrad;m<=hrad;m++) {
+        for (n=-vrad;n<=vrad;n++) {
+          w=get_pixel(histogram_weight_image,i+m,j+n,k);
+          histogram[get_pixel(source,i+m,j+n,k)]+=w;
+          histogram[HIST_SIZE]+=w;
+        }
+      }
+    }
   }
   else {
-    for (k=0;k<source->channels;k++)
-      for (m=-hrad;m<=hrad;m++) 
-       for (n=-vrad;n<=vrad;n++) 
-         ++histogram[get_pixel(source,i+m,j+n,k)];
+    for (k=0; k < source->channels; k++) {
+      for (m=-hrad; m <= hrad; m++) {
+        for (n=-vrad; n <= vrad; n++) {
+          ++histogram[get_pixel(source,i+m,j+n,k)];
+        }
+      }
+    }
   } 
 
   /* quick add - check if ok? */
@@ -478,7 +484,7 @@ void initialize_histogram(FmiImage *source,Histogram histogram,int hrad,int vrad
 
   /* ADDED INITS */
   if (hist_func==histogram_variance_rot)
-    for (i=0;i<256;i++){
+    for (i=0;i<256;i++) {
       alpha=((float)i)/255*2.0*PI;
       histogram_cosine[i]=128+127*cos(alpha);
       histogram_sine[i]  =128+127*sin(alpha);
@@ -740,9 +746,9 @@ void pipeline_process(FmiImage *source,FmiImage *target,int hrad,int vrad,int (*
     fmi_debug(2,"pipeline_process: histogram_mean_weighted");
   }
 
-  if (width>height)
-    pipeline_process_row_major(source,target,hrad,vrad,histogram_function,histogram);
-  else
-  /*  printf(" width=%d\t height=%d\n",width,height); */
-    pipeline_process_col_major(source,target,hrad,vrad,histogram_function,histogram);
+  if (width > height) {
+    pipeline_process_row_major(source, target, hrad, vrad, histogram_function, histogram);
+  } else {
+    pipeline_process_col_major(source, target, hrad, vrad, histogram_function, histogram);
+  }
 }
index 54fec53..9224d72 100644 (file)
@@ -35,16 +35,20 @@ void mark_image(FmiImage *target,FmiImage *prob,Byte threshold,Byte marker){
 
 
 /* simple */
-void restore_image(FmiImage *source,FmiImage *target,FmiImage *prob,Byte threshold){ 
+void restore_image(FmiImage *source, FmiImage *target, FmiImage *prob, Byte threshold){
   register int i;
   canonize_image(source,prob);
   canonize_image(source,target);
 
-  for (i=0;i<prob->volume;i++)
-    if (prob->array[i]>=threshold)
-      target->array[i]=0;
-    else
+  for (i=0;i<prob->volume;i++) {
+    if (prob->array[i ]>= threshold) {
+      target->array[i] = 0;
+      target->original[i] = target->original_undetect;
+    } else {
       target->array[i]=source->array[i];
+      target->original[i] = source->original[i];
+    }
+  }
 }
 
 void restore_image_neg(FmiImage *source,FmiImage *target,FmiImage *prob,Byte threshold){ 
@@ -59,30 +63,76 @@ void restore_image_neg(FmiImage *source,FmiImage *target,FmiImage *prob,Byte thr
       target->array[i]=source->array[i];
 }
 
+static double calculate_original_mean(FmiImage* source, int x, int y, int hrad, int vrad)
+{
+  int h,v;
+  double sum = 0.0;
+  int nhits = 0;
+  for (h = x - hrad; h <= x + hrad; h++) {
+    for (v = y - vrad; v <= y + vrad; v++) {
+      if (h >= 0 && h < source->width && v >= 0 && v < source->height) {
+        double val = get_pixel_orig(source, h, v, 0);
+        if (val != source->original_undetect) {
+          sum += val;
+          nhits++;
+        }
+      }
+    }
+  }
+  if (nhits > 0) {
+    return (double)(sum / (double)nhits);
+  }
+  return source->original_undetect;
+}
+
+static void process_original_mean(FmiImage* source, FmiImage* target, int hrad, int vrad)
+{
+  int x, y;
+  for (x = 0; x < source->width; x++) {
+    for (y = 0; y < source->height; y++) {
+      put_pixel_orig(target, x, y, 0, calculate_original_mean(source, x, y, hrad, vrad));
+    }
+  }
+}
+
 /* other */
 void restore_image2(FmiImage *source,FmiImage *target,FmiImage *prob,Byte threshold){ 
   register int i;
   FmiImage median;
-
+  FmiImage original_mean;
   init_new_image(&median);
+  init_new_image(&original_mean);
 
   canonize_image(source,prob);
   canonize_image(source,target);
   canonize_image(source,&median);
+  canonize_image(source,&original_mean);
 
   /* ERASE ANOMALIES (to black) */
-  for (i=0;i<prob->volume;i++)
-    if (prob->array[i]>=threshold)
-      target->array[i]=0;
-    else
-      target->array[i]=source->array[i];
+  for (i=0; i < prob->volume; i++) {
+    if (prob->array[i] >= threshold) {
+      target->array[i] = 0;
+      target->original[i] = target->original_undetect;
+    } else {
+      target->array[i] = source->array[i];
+      target->original[i] = source->original[i];
+    }
+  }
 
   /* CALCULATE ME(DI)AN OF NONZERO PIXELS */
-  pipeline_process(target,&median,2,2,histogram_mean_nonzero);
+  pipeline_process(target, &median, 2, 2, histogram_mean_nonzero); /* Affects the 8-bit version */
+
+  process_original_mean(target, &original_mean, 2, 2); /* And the original data */
 
   /* REPLACE ANOMALOUS PIXELS WITH THAT NEIGHBORHOOD ME(DI)AN */
-  for (i=0;i<prob->volume;i++)
-    if (prob->array[i]>=threshold)
-      target->array[i]=median.array[i];
+  for (i = 0; i < prob->volume; i++){
+    if (prob->array[i] >= threshold) {
+      target->array[i] = median.array[i];
+      target->original[i] = original_mean.original[i];
+    }
+  }
+
   reset_image(&median);
+  reset_image(&original_mean);
 }
+
index 32e93fe..2acfb00 100644 (file)
@@ -56,6 +56,9 @@ static void RaveFmiImageInternal_resetImage(RaveFmiImage_t* img)
     if (img->image->array != NULL) {
       RAVE_FREE(img->image->array);
     }
+    if (img->image->original != NULL) {
+      RAVE_FREE(img->image->original);
+    }
     RAVE_FREE(img->image);
   }
   img->image = NULL;
@@ -128,6 +131,7 @@ static int RaveFmiImageInternal_scanToFmiImage(PolarScan_t* scan, const char* qu
 {
   int i = 0, j = 0;
   int result = 0;
+  double gain, offset;
   PolarScanParam_t* param = NULL;
   FmiImage* image = NULL;
 
@@ -155,20 +159,42 @@ static int RaveFmiImageInternal_scanToFmiImage(PolarScan_t* scan, const char* qu
     goto done;
   }
   if (PolarScanParam_getDataType(param) != RaveDataType_CHAR &&
-      PolarScanParam_getDataType(param) != RaveDataType_UCHAR) {
-    RAVE_WARNING0("FmiImages can only support 8-bit data");
+      PolarScanParam_getDataType(param) != RaveDataType_UCHAR &&
+      PolarScanParam_getDataType(param) != RaveDataType_SHORT &&
+      PolarScanParam_getDataType(param) != RaveDataType_USHORT) {
+    RAVE_WARNING0("FmiImages can only support 8 and 16-bit data");
     goto done;
   }
 
+  gain = PolarScanParam_getGain(param);
+  offset = PolarScanParam_getOffset(param);
+
   RaveFmiImage_setGain(raveimg, PolarScanParam_getGain(param));
   RaveFmiImage_setOffset(raveimg, PolarScanParam_getOffset(param));
+  RaveFmiImage_setNodata(raveimg, PolarScanParam_getNodata(param));
+  RaveFmiImage_setUndetect(raveimg, PolarScanParam_getUndetect(param));
 
+  image->original_type = PolarScanParam_getDataType(param);
 
   for (j = 0; j < image->height; j++) {
     for (i = 0; i < image->width; i++) {
-      double value = 0.0;
-      PolarScanParam_getValue(param, i, j, &value);
-      put_pixel(image, i, j, 0, (Byte)(value)); // why + 0.5 ?
+      double value = 0.0, bvalue = 0.0;;
+      if (image->original_type == RaveDataType_CHAR || image->original_type == RaveDataType_UCHAR) {
+        PolarScanParam_getValue(param, i, j, &value);
+        bvalue = value;
+      } else {
+        /* This is slightly dangerous since there might actually be data that is 0 but hopefully it's not going to be an issue since we create a new range that is used in the array used for the bropo calculations */
+        RaveValueType t = PolarScanParam_getValue(param, i, j, &value);
+        if (t == RaveValueType_UNDETECT) {
+          bvalue = 0;
+        } else if (t == RaveValueType_NODATA) {
+          bvalue = 255;
+        } else {
+          bvalue = ((value*gain + offset) - (-32.0))/0.5;
+        }
+      }
+      put_pixel(image, i, j, 0, (Byte)(bvalue)); // why + 0.5 ?
+      put_pixel_orig(image, i, j, 0, value);
     }
   }
 
@@ -188,6 +214,8 @@ static int RaveFmiImageInternal_fieldToFmiImage(RaveField_t* field, FmiImage* im
 {
   int i = 0, j = 0;
   int result = 0;
+  double nodata=255.0, undetect=0.0, gain=1.0, offset=0.0;
+  RaveAttribute_t* attr = NULL;
 
   RAVE_ASSERT((field != NULL), "field == NULL");
   RAVE_ASSERT((image != NULL), "image == NULL");
@@ -199,16 +227,56 @@ static int RaveFmiImageInternal_fieldToFmiImage(RaveField_t* field, FmiImage* im
   initialize_image(image);
 
   if (RaveField_getDataType(field) != RaveDataType_CHAR &&
-      RaveField_getDataType(field) != RaveDataType_UCHAR) {
-    RAVE_WARNING0("FmiImages can only support 8-bit data");
+      RaveField_getDataType(field) != RaveDataType_UCHAR &&
+      RaveField_getDataType(field) != RaveDataType_SHORT &&
+      RaveField_getDataType(field) != RaveDataType_USHORT) {
+    RAVE_WARNING0("FmiImages can only support 8 and 16-bit data");
     goto done;
   }
+  attr = RaveField_getAttribute(field, "what/gain");
+  if (attr != NULL) {
+    RaveAttribute_getDouble(attr, &gain);
+  }
+  RAVE_OBJECT_RELEASE(attr);
+  attr = RaveField_getAttribute(field, "what/offset");
+  if (attr != NULL) {
+    RaveAttribute_getDouble(attr, &offset);
+  }
+  RAVE_OBJECT_RELEASE(attr);
+  attr = RaveField_getAttribute(field, "what/nodata");
+  if (attr != NULL) {
+    RaveAttribute_getDouble(attr, &nodata);
+  }
+  RAVE_OBJECT_RELEASE(attr);
+  attr = RaveField_getAttribute(field, "what/undetect");
+  if (attr != NULL) {
+    RaveAttribute_getDouble(attr, &undetect);
+  }
+  RAVE_OBJECT_RELEASE(attr);
+
+  image->original_type = RaveField_getDataType(field);
 
   for (j = 0; j < image->height; j++) {
     for (i = 0; i < image->width; i++) {
-      double value = 0.0;
-      RaveField_getValue(field, i, j, &value);
-      put_pixel(image, i, j, 0, (Byte)(value)); // why + 0.5 ?
+      double value = 0.0, bvalue = 0.0;;
+
+      if (image->original_type == RaveDataType_CHAR || image->original_type == RaveDataType_UCHAR) {
+        RaveField_getValue(field, i, j, &value);
+        bvalue = value;
+      } else {
+        /* This is slightly dangerous since there might actually be data that is 0 but hopefully it's not going to be an issue since we create a new range that is used in the array used for the bropo calculations */
+        RaveField_getValue(field, i, j, &value);
+        if (value == undetect) {
+          bvalue = 0;
+        } else if (value == nodata) {
+          bvalue = 255;
+        } else {
+          bvalue = ((value*gain + offset) - (-32.0))/0.5;
+        }
+      }
+
+      put_pixel(image, i, j, 0, (Byte)(bvalue)); // why + 0.5 ?
+      put_pixel_orig(image, i, j, 0, value);
     }
   }
 
@@ -223,7 +291,7 @@ done:
  * @param[in] quantity - the quantity of the parameter. If null, default is DBZH.
  * @return the polar scan on success otherwise NULL
  */
-static PolarScan_t* RaveFmiImageInternal_fmiImageToScan(FmiImage* image, double offset, double gain, const char* quantity)
+static PolarScan_t* RaveFmiImageInternal_fmiImageToScan(FmiImage* image, double offset, double gain, const char* quantity, int datatype)
 {
   PolarScan_t* scan = NULL;
   PolarScan_t* result = NULL;
@@ -240,21 +308,33 @@ static PolarScan_t* RaveFmiImageInternal_fmiImageToScan(FmiImage* image, double
 
   PolarScanParam_setGain(param, gain);
   PolarScanParam_setOffset(param, offset);
-  PolarScanParam_setNodata(param, 255.0);
-  PolarScanParam_setUndetect(param, 0.0);
+  PolarScanParam_setNodata(param, image->original_nodata);
+  PolarScanParam_setUndetect(param, image->original_undetect);
   if (quantity != NULL) {
     PolarScanParam_setQuantity(param, quantity);
   } else {
     PolarScanParam_setQuantity(param, "DBZH");
   }
-  if (!PolarScanParam_createData(param, image->width, image->height, RaveDataType_UCHAR)) {
-    RAVE_CRITICAL0("Failed to allocate memory for data");
-    goto done;
+
+  if (datatype == 0) {
+    if (!PolarScanParam_createData(param, image->width, image->height, image->original_type)) {
+      RAVE_CRITICAL0("Failed to allocate memory for data");
+      goto done;
+    }
+  } else {
+    if (!PolarScanParam_createData(param, image->width, image->height, RaveDataType_UCHAR)) {
+      RAVE_CRITICAL0("Failed to allocate memory for data");
+      goto done;
+    }
   }
 
   for (ray = 0; ray < image->height; ray++) {
     for (bin = 0; bin < image->width; bin++) {
-      PolarScanParam_setValue(param, bin, ray, (double)get_pixel(image, bin, ray, 0));
+      if (datatype != 2 && (image->original_type == RaveDataType_CHAR || image->original_type == RaveDataType_UCHAR || datatype == 1)) {
+        PolarScanParam_setValue(param, bin, ray, (double)get_pixel(image, bin, ray, 0));
+      } else {
+        PolarScanParam_setValue(param, bin, ray, (double)get_pixel_orig(image, bin, ray, 0));
+      }
     }
   }
   if (!PolarScan_addParameter(scan, param)) {
@@ -278,7 +358,7 @@ done:
  * @param[in] quantity - the quantity of the parameter. If null, default is DBZH.
  * @return the rave field on success otherwise NULL
  */
-static RaveField_t* RaveFmiImageInternal_fmiImageToField(FmiImage* image)
+static RaveField_t* RaveFmiImageInternal_fmiImageToField(FmiImage* image, int datatype)
 {
   RaveField_t* field = NULL;
   RaveField_t* result = NULL;
@@ -291,14 +371,25 @@ static RaveField_t* RaveFmiImageInternal_fmiImageToField(FmiImage* image)
     goto done;
   }
 
-  if (!RaveField_createData(field, image->width, image->height, RaveDataType_UCHAR)) {
-    RAVE_CRITICAL0("Failed to allocate memory for data");
-    goto done;
+  if (datatype == 0) {
+    if (!RaveField_createData(field, image->width, image->height, image->original_type)) {
+      RAVE_CRITICAL0("Failed to allocate memory for data");
+      goto done;
+    }
+  } else {
+    if (!RaveField_createData(field, image->width, image->height, RaveDataType_UCHAR)) {
+      RAVE_CRITICAL0("Failed to allocate memory for data");
+      goto done;
+    }
   }
 
   for (ray = 0; ray < image->height; ray++) {
     for (bin = 0; bin < image->width; bin++) {
-      RaveField_setValue(field, bin, ray, (double)get_pixel(image, bin, ray, 0));
+      if (datatype != 2 && (image->original_type == RaveDataType_CHAR || image->original_type == RaveDataType_UCHAR || datatype == 1)) {
+        RaveField_setValue(field, bin, ray, (double)get_pixel(image, bin, ray, 0));
+      } else {
+        RaveField_setValue(field, bin, ray, (double)get_pixel_orig(image, bin, ray, 0));
+      }
     }
   }
 
@@ -337,6 +428,26 @@ void RaveFmiImage_fill(RaveFmiImage_t* self, unsigned char v)
   RAVE_ASSERT((self != NULL), "self == NULL");
   if (self->image != NULL) {
     fill_image(self->image, (Byte)v);
+    if (self->image->original != NULL) {
+      int i;
+      for (i = 0; i < self->image->volume; i++) {
+        self->image->original[i] = (double)v;
+      }
+    }
+  }
+}
+
+void RaveFmiImage_fillOriginal(RaveFmiImage_t* self, double v)
+{
+  RAVE_ASSERT((self != NULL), "self == NULL");
+  if (self->image != NULL) {
+    if (self->image->original != NULL) {
+      int i;
+      int nv = self->image->width*self->image->height*self->image->channels;
+      for (i = 0; i < nv; i++) {
+        self->image->original[i] = (double)v;
+      }
+    }
   }
 }
 
@@ -346,7 +457,7 @@ FmiImage* RaveFmiImage_getImage(RaveFmiImage_t* self)
   return self->image;
 }
 
-PolarScan_t* RaveFmiImage_toPolarScan(RaveFmiImage_t* self, const char* quantity)
+PolarScan_t* RaveFmiImage_toPolarScan(RaveFmiImage_t* self, const char* quantity, int datatype)
 {
   PolarScan_t* result = NULL;
   PolarScan_t* scan = NULL;
@@ -357,7 +468,7 @@ PolarScan_t* RaveFmiImage_toPolarScan(RaveFmiImage_t* self, const char* quantity
 
   RAVE_ASSERT((self != NULL), "self == NULL");
 
-  scan = RaveFmiImageInternal_fmiImageToScan(&self->image[0], self->offset, self->gain, quantity);
+  scan = RaveFmiImageInternal_fmiImageToScan(&self->image[0], self->offset, self->gain, quantity, datatype);
   if (scan == NULL) {
     RAVE_CRITICAL0("Failed to convert image to scan");
     goto done;
@@ -382,7 +493,7 @@ done:
   return result;
 }
 
-RaveField_t* RaveFmiImage_toRaveField(RaveFmiImage_t* self)
+RaveField_t* RaveFmiImage_toRaveField(RaveFmiImage_t* self, int datatype)
 {
   RaveField_t* result = NULL;
   RaveField_t* field = NULL;
@@ -393,7 +504,7 @@ RaveField_t* RaveFmiImage_toRaveField(RaveFmiImage_t* self)
 
   RAVE_ASSERT((self != NULL), "self == NULL");
 
-  field = RaveFmiImageInternal_fmiImageToField(&self->image[0]);
+  field = RaveFmiImageInternal_fmiImageToField(&self->image[0], datatype);
   if (field == NULL) {
     RAVE_CRITICAL0("Failed to convert image to field");
     goto done;
@@ -487,6 +598,30 @@ double RaveFmiImage_getOffset(RaveFmiImage_t* self)
   return self->offset;
 }
 
+void RaveFmiImage_setNodata(RaveFmiImage_t* self, double v)
+{
+  RAVE_ASSERT((self != NULL), "self == NULL");
+  self->image->original_nodata = v;
+}
+
+double RaveFmiImage_getNodata(RaveFmiImage_t* self)
+{
+  RAVE_ASSERT((self != NULL), "self == NULL");
+  return self->image->original_nodata;
+}
+
+void RaveFmiImage_setUndetect(RaveFmiImage_t* self, double v)
+{
+  RAVE_ASSERT((self != NULL), "self == NULL");
+  self->image->original_undetect = v;
+}
+
+double RaveFmiImage_getUndetect(RaveFmiImage_t* self)
+{
+  RAVE_ASSERT((self != NULL), "self == NULL");
+  return self->image->original_undetect;
+}
+
 RaveFmiImage_t* RaveFmiImage_new(int width, int height)
 {
   RaveFmiImage_t* result = RAVE_OBJECT_NEW(&RaveFmiImage_TYPE);
index 4d97367..728b133 100644 (file)
@@ -60,6 +60,13 @@ int RaveFmiImage_initialize(RaveFmiImage_t* self, int width, int height);
 void RaveFmiImage_fill(RaveFmiImage_t* self, unsigned char v);
 
 /**
+ * Fills the original image with the provided value.
+ * @param[in] self - self
+ * @param[in] v - the pixel value to set for all pixels
+ */
+void RaveFmiImage_fillOriginal(RaveFmiImage_t* self, double v);
+
+/**
  * Returns the internal FmiImage.
  * @param[in] self - self
  * @return the internal fmi image (NOTE, THIS IS AN INTERNAL POINTER SO DO NOT RELEASE)
@@ -70,16 +77,18 @@ FmiImage* RaveFmiImage_getImage(RaveFmiImage_t* self);
  * Creates a polar scan from a fmi image
  * @param[in] self - self
  * @param[in] quantity - the quantity to be set for the parameters (may be NULL)
+ * @param[in] datatype - how the data field should be defined. 0 = same as when created, 1 = force unsigned char and use data from 8 bit array, 2 = force unsigned char but use data from original data array
  * @return a polar volume on success otherwise NULL
  */
-PolarScan_t* RaveFmiImage_toPolarScan(RaveFmiImage_t* self, const char* quantity);
+PolarScan_t* RaveFmiImage_toPolarScan(RaveFmiImage_t* self, const char* quantity, int datatype);
 
 /**
  * Creates a rave field from a fmi image
  * @param[in] self - self
+ * @param[in] datatype - how the data field should be defined. 0 = same as when created, 1 = force unsigned char and use data from 8 bit array, 2 = force unsigned char but use data from original data array
  * @return a polar volume on success otherwise NULL
  */
-RaveField_t* RaveFmiImage_toRaveField(RaveFmiImage_t* self);
+RaveField_t* RaveFmiImage_toRaveField(RaveFmiImage_t* self, int datatype);
 
 /**
  * Adds a rave attribute to the image.
@@ -137,6 +146,34 @@ void RaveFmiImage_setOffset(RaveFmiImage_t* self, double offset);
 double RaveFmiImage_getOffset(RaveFmiImage_t* self);
 
 /**
+ * Sets the nodata for the data in the image.
+ * @param[in] self - self
+ * @param[in] v - the nodata value
+ */
+void RaveFmiImage_setNodata(RaveFmiImage_t* self, double v);
+
+/**
+ * Returns the nodata for the data in the image
+ * @param[in] self - self
+ * @return the nodata value
+ */
+double RaveFmiImage_getNodata(RaveFmiImage_t* self);
+
+/**
+ * Sets the undetect for the data in the image
+ * @param[in] self - self
+ * @param[in] v - the undetect value
+ */
+void RaveFmiImage_setUndetect(RaveFmiImage_t* self, double v);
+
+/**
+ * Returns the undetect for the data in the image.
+ * @param[in] self - self
+ * @return the undetect value
+ */
+double RaveFmiImage_getUndetect(RaveFmiImage_t* self);
+
+/**
  * Creates a rave fmi image with specified dimension
  * @param[in] width - the width
  * @param[in] height - the height
index 24c3678..a9dd946 100644 (file)
@@ -64,6 +64,9 @@ static void RaveFmiVolumeInternal_resetImage(RaveFmiVolume_t* img)
       if (img->image[i].array != NULL) {
         RAVE_FREE(img->image[i].array);
       }
+      if (img->image[i].original != NULL) {
+        RAVE_FREE(img->image[i].original);
+      }
     }
     RAVE_FREE(img->image);
   }
index 15b9f59..31766f0 100644 (file)
@@ -425,6 +425,7 @@ static int RaveRopoGeneratorInternal_createProbabilityField(
     goto done;
   }
   RaveFmiImage_fill(outprob, CLEAR);
+  RaveFmiImage_fillOriginal(outprob, (double)CLEAR);
 
   if (!RaveRopoGeneratorInternal_addTask(outprob, task) ||
       !RaveRopoGeneratorInternal_addTaskArgs(outprob, fmtstring)) {
@@ -947,6 +948,7 @@ RaveFmiImage_t* RaveRopoGenerator_restore(RaveRopoGenerator_t* self, int thresho
   }
 
   RaveFmiImage_fill(restored, CLEAR);
+  RaveFmiImage_fillOriginal(restored, (double)CLEAR);
 
   if (!RaveRopoGeneratorInternal_addTask(restored, "fi.fmi.ropo.restore") ||
       !RaveRopoGeneratorInternal_addProbabilityTaskArgs(restored, self->probabilities, "RESTORE_THRESH: %d",threshold)) {
@@ -982,6 +984,7 @@ RaveFmiImage_t* RaveRopoGenerator_restore2(RaveRopoGenerator_t* self, int thresh
   }
 
   RaveFmiImage_fill(restored, CLEAR);
+  RaveFmiImage_fillOriginal(restored, (double)CLEAR);
 
   if (!RaveRopoGeneratorInternal_addTask(restored, "fi.fmi.ropo.restore2") ||
       !RaveRopoGeneratorInternal_addProbabilityTaskArgs(restored, self->probabilities, "RESTORE2_THRESH: %d",threshold)) {
index 4dccfc4..bed3bec 100644 (file)
@@ -8,10 +8,12 @@ import string
 import _raveio
 import _fmiimage
 import _rave
+import numpy
 
 class FmiImageTest(unittest.TestCase):
   PVOL_TESTFILE="fixtures/pvol_seang_20090501T120000Z.h5"
-
+  SCAN16_TESTFILE="fixtures/sebaa-scan_0_5_20190613.h5"
+  
   def setUp(self):
     pass
 
@@ -30,6 +32,18 @@ class FmiImageTest(unittest.TestCase):
     a.gain = 2.5
     self.assertAlmostEqual(2.5, a.gain, 4)
 
+  def test_undetect(self):
+    a = _fmiimage.new()
+    self.assertAlmostEqual(0.0, a.undetect, 4)
+    a.undetect = 20.0
+    self.assertAlmostEqual(20.0, a.undetect, 4)
+
+  def test_nodata(self):
+    a = _fmiimage.new()
+    self.assertAlmostEqual(255.0, a.nodata, 4)
+    a.nodata = 1.0
+    self.assertAlmostEqual(1, a.nodata, 4)
+
   def testAttributes(self):
     a = _fmiimage.new()
     names = a.getAttributeNames()
@@ -40,6 +54,13 @@ class FmiImageTest(unittest.TestCase):
     self.assertEqual(1, len(names))
     self.assertTrue("how/slask" in names)
 
+  def testSetValue(self):
+    a = _fmiimage.new(10,10)
+    a.setValue(1,1,1)
+    a.setOriginalValue(1,1,2.0)
+    self.assertEqual(1, a.getValue(1,1))
+    self.assertAlmostEqual(2.0, a.getOriginalValue(1,1), 4)
+
   def testFromRave_scan(self):
     a = _raveio.open(self.PVOL_TESTFILE)
     scan = a.object.getScan(0)
@@ -47,6 +68,23 @@ class FmiImageTest(unittest.TestCase):
     self.assertNotEqual(-1, str(type(b)).find("FmiImageCore"))
     self.assertAlmostEqual(scan.getParameter("DBZH").offset, b.offset, 4)
     self.assertAlmostEqual(scan.getParameter("DBZH").gain, b.gain, 4)
+    self.assertAlmostEqual(scan.getParameter("DBZH").nodata, b.nodata, 4)
+    self.assertAlmostEqual(scan.getParameter("DBZH").undetect, b.undetect, 4)
+
+  def testFromRave_scan_different_nodataUndetect(self):
+    a = _raveio.open(self.PVOL_TESTFILE)
+    scan = a.object.getScan(0)
+    p = scan.getParameter("DBZH")
+    p.offset = 1.0
+    p.gain = 2.0
+    p.nodata = 3.0
+    p.undetect = 4.0
+    b = _fmiimage.fromRave(scan)
+    self.assertNotEqual(-1, str(type(b)).find("FmiImageCore"))
+    self.assertAlmostEqual(1.0, b.offset, 4)
+    self.assertAlmostEqual(2.0, b.gain, 4)
+    self.assertAlmostEqual(3.0, b.nodata, 4)
+    self.assertAlmostEqual(4.0, b.undetect, 4)
 
   def testFromRave_withQuantity_scan(self):
     a = _raveio.open(self.PVOL_TESTFILE)
@@ -80,12 +118,75 @@ class FmiImageTest(unittest.TestCase):
     self.assertAlmostEqual(a.getScan(0).elangle, c.elangle, 4)
     self.assertTrue(c.hasParameter("DBZH"))
 
+  def testToPolarScan_sametype(self):
+    a = _raveio.open(self.SCAN16_TESTFILE).object
+    b = _fmiimage.fromRave(a, "DBZH")
+    b.setValue(1,1,10)
+    b.setOriginalValue(1,1,20)
+    c = b.toPolarScan("DBZH")
+    self.assertAlmostEqual(a.elangle, c.elangle, 4)
+    self.assertTrue(c.hasParameter("DBZH"))
+    self.assertEqual(numpy.int16, c.getParameter("DBZH").getData().dtype)
+    self.assertAlmostEqual(20.0, c.getParameter("DBZH").getValue(1,1)[1], 4)
+
+  def testToPolarScan_datatype1(self):
+    a = _raveio.open(self.SCAN16_TESTFILE).object
+    b = _fmiimage.fromRave(a, "DBZH")
+    b.setValue(1,1,10)
+    b.setOriginalValue(1,1,20)
+    c = b.toPolarScan("DBZH", 1)
+    self.assertAlmostEqual(a.elangle, c.elangle, 4)
+    self.assertTrue(c.hasParameter("DBZH"))
+    self.assertEqual(numpy.uint8, c.getParameter("DBZH").getData().dtype)
+    self.assertAlmostEqual(10.0, c.getParameter("DBZH").getValue(1,1)[1], 4)
+
+  def testToPolarScan_datatype2(self):
+    a = _raveio.open(self.SCAN16_TESTFILE).object
+    b = _fmiimage.fromRave(a, "DBZH")
+    b.setValue(1,1,10)
+    b.setOriginalValue(1,1,20)
+    c = b.toPolarScan("DBZH", 2)
+    self.assertAlmostEqual(a.elangle, c.elangle, 4)
+    self.assertTrue(c.hasParameter("DBZH"))
+    self.assertEqual(numpy.uint8, c.getParameter("DBZH").getData().dtype)
+    self.assertAlmostEqual(20.0, c.getParameter("DBZH").getValue(1,1)[1], 4)
+
   def testToRaveField(self):
     a = _raveio.open(self.PVOL_TESTFILE).object
     b = _fmiimage.fromRave(a.getScan(0), "DBZH")
     c = b.toRaveField()
     self.assertNotEqual(-1, str(type(c)).find("RaveFieldCore"))
-    
+
+  def testToRaveField_sametype(self):
+    a = _raveio.open(self.SCAN16_TESTFILE).object
+    b = _fmiimage.fromRave(a, "DBZH")
+    b.setValue(1,1,10)
+    b.setOriginalValue(1,1,20)
+    c = b.toRaveField()
+    self.assertNotEqual(-1, str(type(c)).find("RaveFieldCore"))
+    self.assertEqual(numpy.int16, c.getData().dtype)
+    self.assertAlmostEqual(20.0, c.getValue(1,1)[1], 4)
+
+  def testToRaveField_datatype1(self):
+    a = _raveio.open(self.SCAN16_TESTFILE).object
+    b = _fmiimage.fromRave(a, "DBZH")
+    b.setValue(1,1,10)
+    b.setOriginalValue(1,1,20)
+    c = b.toRaveField(1)
+    self.assertNotEqual(-1, str(type(c)).find("RaveFieldCore"))
+    self.assertEqual(numpy.uint8, c.getData().dtype)
+    self.assertAlmostEqual(10.0, c.getValue(1,1)[1], 4)
+
+  def testToRaveField_datatype2(self):
+    a = _raveio.open(self.SCAN16_TESTFILE).object
+    b = _fmiimage.fromRave(a, "DBZH")
+    b.setValue(1,1,10)
+    b.setOriginalValue(1,1,20)
+    c = b.toRaveField(2)
+    self.assertNotEqual(-1, str(type(c)).find("RaveFieldCore"))
+    self.assertEqual(numpy.uint8, c.getData().dtype)
+    self.assertAlmostEqual(20.0, c.getValue(1,1)[1], 4)
+        
 if __name__ == "__main__":
   #import sys;sys.argv = ['', 'Test.testName']
   unittest.main()
\ No newline at end of file
index 4dd4fb2..77ed473 100644 (file)
@@ -8,15 +8,17 @@ import unittest
 import _fmiimage
 import _raveio
 import _ropogenerator
-import os, string
+import os, string, sys
 import _rave
 import _polarscanparam
 import _polarscan
 import _ravefield
 import ropo_realtime
+import numpy
 
 class PyRopoGeneratorTest(unittest.TestCase):
   PVOL_TESTFILE="fixtures/pvol_seang_20090501T120000Z.h5"
+  SCAN16_TESTFILE="fixtures/sebaa-scan_0_5_20190613.h5"
   PVOL_RIX_TESTFILE="fixtures/pvol_rix.h5"
   
   TEMPORARY_FILE="ropotest_file1.h5"
@@ -34,7 +36,7 @@ class PyRopoGeneratorTest(unittest.TestCase):
       os.unlink(self.TEMPORARY_FILE)
     if os.path.isfile(self.TEMPORARY_FILE2):
      os.unlink(self.TEMPORARY_FILE2)  
-  
+
   def testNew(self):
     a = _ropogenerator.new()
     self.assertNotEqual(-1, str(type(a)).find("RopoGeneratorCore"))
@@ -60,12 +62,19 @@ class PyRopoGeneratorTest(unittest.TestCase):
     a = _raveio.open(self.PVOL_RIX_TESTFILE).object.getScan(0)
     b = _ropogenerator.new(_fmiimage.fromRave(a, "DBZH"))
     b.threshold(80)
-  
+
   def testSpeck(self):
     a = _raveio.open(self.PVOL_RIX_TESTFILE).object.getScan(0)
     b = _ropogenerator.new(_fmiimage.fromRave(a, "DBZH"))
     b.speck(-20, 5)
 
+  def testSpeck_differentUndetect_restore(self):
+    a = _raveio.open(self.PVOL_RIX_TESTFILE).object.getScan(0)
+    a.getParameter("DBZH").undetect = 254.0
+    b = _ropogenerator.new(_fmiimage.fromRave(a, "DBZH"))
+    c=b.speck(-20, 5).restore(50).toPolarScan().getParameter("DBZH")
+    self.assertAlmostEqual(254.0, c.undetect, 4)
+
   def testSpeckNormOld(self):
     a = _raveio.open(self.PVOL_RIX_TESTFILE).object.getScan(0)
     b = _ropogenerator.new(_fmiimage.fromRave(a, "DBZH"))
@@ -273,6 +282,59 @@ class PyRopoGeneratorTest(unittest.TestCase):
       self.assertEqual(unwrapped.getParameter("DBZH").getData().tolist(), 
                         data.tolist())
 
+  def testSpeck_16bit(self):
+    a = _raveio.open(self.SCAN16_TESTFILE).object
+    b = _ropogenerator.new(_fmiimage.fromRave(a, "DBZH"))
+    c = b.speck(-30,12).restore(108).toRaveField().getData()
+    self.assertEqual(numpy.int16, c.dtype)
+
+  def testChainCompare_8bit_and_16bit_Restore(self):
+    a = _raveio.open(self.PVOL_RIX_TESTFILE).object.getScan(0)
+    b = _ropogenerator.new(_fmiimage.fromRave(a, "DBZH"))
+    result8bit = b.speckNormOld(-20,24,8).emitter2(-30,3,3).softcut(5,170,180).ship(20,8).speck(-30,12).restore(108).toPolarScan().getParameter("DBZH").getData()
+    
+    # Adjust the 8 bit data to be 16 bit instead
+    p = a.getParameter("DBZH")
+    d = p.getData().astype(numpy.int16)
+    p.setData(d)
+    p.undetect = 0.0
+    p.nodata = 255.0
+    a.addParameter(p)
+    
+    b = _ropogenerator.new(_fmiimage.fromRave(a, "DBZH"))
+    result16bit = b.speckNormOld(-20,24,8).emitter2(-30,3,3).softcut(5,170,180).ship(20,8).speck(-30,12).restore(108).toPolarScan().getParameter("DBZH").getData()
+    
+    self.assertEqual(numpy.int16, result16bit.dtype) # Result should be same type as when created
+    
+    result16bit=result16bit.astype(numpy.uint8)
+    
+    self.assertTrue(numpy.array_equal(result8bit,result16bit))
+
+  def testChainCompare_8bit_and_16bit_Restore2(self):
+    a = _raveio.open(self.PVOL_RIX_TESTFILE).object.getScan(0)
+    b = _ropogenerator.new(_fmiimage.fromRave(a, "DBZH"))
+    result8bit = b.speckNormOld(-20,24,8).emitter2(-30,3,3).softcut(5,170,180).ship(20,8).speck(-30,12).restore2(108).toPolarScan().getParameter("DBZH").getData()
+    
+    # Adjust the 8 bit data to be 16 bit instead
+    p = a.getParameter("DBZH")
+    d = p.getData().astype(numpy.int16)
+    p.setData(d)
+    p.undetect = 0.0
+    p.nodata = 255.0
+    a.addParameter(p)
+    
+    b = _ropogenerator.new(_fmiimage.fromRave(a, "DBZH"))
+    result16bit = b.speckNormOld(-20,24,8).emitter2(-30,3,3).softcut(5,170,180).ship(20,8).speck(-30,12).restore2(108).toPolarScan().getParameter("DBZH").getData()
+    
+    self.assertEqual(numpy.int16, result16bit.dtype) # Result should be same type as when created
+    
+    result16bit=result16bit.astype(numpy.uint8)
+    
+    # Averaging might be different since the procedure is different for determining mean. atol means that avg different should be max 3
+    # Verifications is done as absolute(a - b) <= (atol + rtol * absolute(b)) which means that this verifies that
+    # abs(8bit - 16bit) is < 3 
+    self.assertTrue(numpy.allclose(result8bit, result16bit, atol=3.0, rtol=0.0))
+  
   # Simple way to ensure that a file is exported properly
   #
   def exportFile(self, object):
index 9c17e31..7bd6270 100644 (file)
Binary files a/test/pytest/fixtures/pvol_rix.h5 and b/test/pytest/fixtures/pvol_rix.h5 differ
diff --git a/test/pytest/fixtures/sebaa-scan_0_5_20190613.h5 b/test/pytest/fixtures/sebaa-scan_0_5_20190613.h5
new file mode 100644 (file)
index 0000000..aba8921
Binary files /dev/null and b/test/pytest/fixtures/sebaa-scan_0_5_20190613.h5 differ