72854d232ee28c63ec6824d15eac2aa22fcbf296
[bropo.git] / ropo / rave_ropo_generator.c
1 /* --------------------------------------------------------------------
2 Copyright (C) 2011 Swedish Meteorological and Hydrological Institute, SMHI,
3
4 This file is part of bRopo.
5
6 bRopo is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 bRopo is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with bRopo.  If not, see <http://www.gnu.org/licenses/>.
18 ------------------------------------------------------------------------*/
19
20 /**
21  * rave object wrapper for the ropo generator
22  * This object DOES NOT support \ref #RAVE_OBJECT_CLONE.
23  * @file
24  * @author Anders Henja (Swedish Meteorological and Hydrological Institute, SMHI)
25  * @date 2011-09-02
26  */
27 #include <limits.h>
28 #include <math.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdarg.h>
32 #include <fmi_util.h>
33 #include <fmi_image.h>
34 #include <fmi_image_filter.h>
35 #include <fmi_image_filter_line.h>
36 #include <fmi_image_histogram.h>
37 #include <fmi_image_filter_speck.h>
38 #include <fmi_image_filter_morpho.h>
39 #include <fmi_image_restore.h>
40 #include <fmi_meteosat.h>
41 #include <fmi_radar_image.h>
42
43 #include "rave_ropo_generator.h"
44 #include "rave_debug.h"
45 #include "rave_alloc.h"
46 #include "raveobject_list.h"
47
48 /**
49  * Represents a rave fmi image
50  */
51 struct _RaveRopoGenerator_t {
52   RAVE_OBJECT_HEAD       /** Always on top */
53   RaveFmiImage_t* image; /**< the fmi image */
54   RaveObjectList_t* probabilities; /**< a list of probabilities */
55   RaveFmiImage_t* classification; /**< the classification field */
56   RaveFmiImage_t* markers; /**< the markers identifying what type of detector indicating probability */
57 };
58
59 /*@{ Private functions */
60 static const char RopoGenerator_CLEAR_STR[] = "CLEAR:";
61 static const char RopoGenerator_CUTOFF_STR[] = "CUTOFF:";
62 static const char RopoGenerator_BIOMET_STR[] = "BIOMET:";
63 static const char RopoGenerator_SHIP_STR[] = "SHIP:";
64 static const char RopoGenerator_SUN_STR[] = "SUN:";
65 static const char RopoGenerator_EMITTER_STR[] = "EMITTER:";
66 static const char RopoGenerator_EMITTER2_STR[] = "EMITTER2:";
67 static const char RopoGenerator_CLUTTER_STR[] = "CLUTTER:";
68 static const char RopoGenerator_CLUTTER2_STR[] = "CLUTTER2:";
69 static const char RopoGenerator_SPECK_STR[] = "SPECK:";
70 static const char RopoGenerator_SPECKNORMOLD_STR[] = "SPECKNORMOLD:";
71 static const char RopoGenerator_DOPPLER_STR[] = "DOPPLER:";
72 static const char RopoGenerator_GROUND_STR[] = "GROUND:";
73 static const char RopoGenerator_METEOSAT_STR[] = "METEOSAT:";
74 static const char RopoGenerator_THRESH1_STR[] = "THRESH1:";
75 static const char RopoGenerator_EMITTER3_STR[] = "EMITTER3:";
76 static const char RopoGenerator_DATA_MIN_STR[] = "DATA_MIN:";
77 static const char RopoGenerator_DATA_MAX_STR[] = "DATA_MAX:";
78 static const char RopoGenerator_NO_DATA_STR[] = "NO_DATA:";
79
80 struct RopoGenerator_PgmCodeMapping {
81   FmiRadarPGMCode type;  /**< the pgm code */
82   const char* str;       /**< the string representation */
83 };
84
85 /**
86  * The product type mapping table.
87  */
88 static const struct RopoGenerator_PgmCodeMapping PGM_CODE_MAPPING[] =
89 {
90   {CLEAR,  RopoGenerator_CLEAR_STR},
91   {CUTOFF,  RopoGenerator_CUTOFF_STR},
92   {BIOMET,  RopoGenerator_BIOMET_STR},
93   {SHIP,  RopoGenerator_SHIP_STR},
94   {SUN,  RopoGenerator_SUN_STR},
95   {EMITTER,  RopoGenerator_EMITTER_STR},
96   {EMITTER2,  RopoGenerator_EMITTER2_STR},
97   {CLUTTER,  RopoGenerator_CLUTTER_STR},
98   {CLUTTER2,  RopoGenerator_CLUTTER2_STR},
99   {SPECK,  RopoGenerator_SPECK_STR},
100   {SPECK, RopoGenerator_SPECKNORMOLD_STR},
101   {DOPPLER,  RopoGenerator_DOPPLER_STR},
102   {METEOSAT,  RopoGenerator_METEOSAT_STR},
103   {THRESH1,  RopoGenerator_THRESH1_STR},
104   {EMITTER3,  RopoGenerator_EMITTER3_STR},
105   {DATA_MIN,  RopoGenerator_DATA_MIN_STR},
106   {DATA_MAX,  RopoGenerator_DATA_MAX_STR},
107   {NO_DATA,  RopoGenerator_NO_DATA_STR},
108   {-1, NULL}
109 };
110
111 /**
112  * Constructor
113  */
114 static int RaveRopoGenerator_constructor(RaveCoreObject* obj)
115 {
116   RaveRopoGenerator_t* this = (RaveRopoGenerator_t*)obj;
117   this->image = NULL;
118   this->classification = NULL;
119   this->markers = NULL;
120   this->probabilities = RAVE_OBJECT_NEW(&RaveObjectList_TYPE);
121
122   if (this->probabilities == NULL) {
123     goto error;
124   }
125   return 1;
126 error:
127   RAVE_OBJECT_RELEASE(this->probabilities);
128   return 0;
129 }
130
131 /**
132  * Destructor
133  */
134 static void RaveRopoGenerator_destructor(RaveCoreObject* obj)
135 {
136   RaveRopoGenerator_t* src = (RaveRopoGenerator_t*)obj;
137   RAVE_OBJECT_RELEASE(src->image);
138   RAVE_OBJECT_RELEASE(src->probabilities);
139   RAVE_OBJECT_RELEASE(src->classification);
140   RAVE_OBJECT_RELEASE(src->markers);
141 }
142
143 /**
144  * Adds how/task to the image.
145  * @param[in] image - the image
146  * @param[in] taskname - the name of the task executed
147  * @return 1 on success otherwise 0
148  */
149 static int RaveRopoGeneratorInternal_addTask(RaveFmiImage_t* image, const char* taskname)
150 {
151   RaveAttribute_t* attribute = NULL;
152   int result = 0;
153   RAVE_ASSERT((image != NULL), "image == NULL");
154   RAVE_ASSERT((taskname != NULL), "taskname == NULL");
155
156   attribute = RaveAttributeHelp_createString("how/task", taskname);
157   if (attribute == NULL || !RaveFmiImage_addAttribute(image, attribute)) {
158     RAVE_CRITICAL0("Failed to add attribute to image");
159     goto done;
160   }
161   result = 1;
162
163 done:
164   RAVE_OBJECT_RELEASE(attribute);
165   return result;
166 }
167
168 /**
169  * Adds how/task to the image.
170  * @param[in] image - the image
171  * @param[in] format - the var args format
172  * @param[in] ... - the arguments
173  * @return 1 on success otherwise 0
174  */
175 static int RaveRopoGeneratorInternal_addTaskArgs(RaveFmiImage_t* image, const char* fmt, ...)
176 {
177   RaveAttribute_t* attribute = NULL;
178   int result = 0;
179   char fmtstring[1024];
180   va_list ap;
181   int n = 0;
182
183   RAVE_ASSERT((image != NULL), "image == NULL");
184   RAVE_ASSERT((fmt != NULL), "fmt == NULL");
185
186   va_start(ap, fmt);
187   n = vsnprintf(fmtstring, 1024, fmt, ap);
188   va_end(ap);
189   if (n < 0 || n >= 1024) {
190     RAVE_ERROR0("Failed to generate name");
191     goto done;
192   }
193
194   attribute = RaveAttributeHelp_createString("how/task_args", fmtstring);
195   if (attribute == NULL || !RaveFmiImage_addAttribute(image, attribute)) {
196     RAVE_CRITICAL0("Failed to add attribute to image");
197     goto done;
198   }
199   result = 1;
200
201 done:
202   RAVE_OBJECT_RELEASE(attribute);
203   return result;
204 }
205
206 static int RaveRopoGeneratorInternal_addProbabilityTaskArgs(RaveFmiImage_t* image, RaveObjectList_t* probs, const char* fmt, ...)
207 {
208   RaveFmiImage_t* field = NULL;
209   RaveAttribute_t* attr = NULL;
210   int sz = 0;
211   int i = 0;
212   int result = 0;
213   char pstr[1024];
214   char fmtstring[1024];
215   va_list ap;
216   int n = 0;
217
218   RAVE_ASSERT((image != NULL), "image == NULL");
219   RAVE_ASSERT((probs != NULL), "probs == NULL");
220   RAVE_ASSERT((fmt != NULL), "fmt == NULL");
221
222   va_start(ap, fmt);
223   n = vsnprintf(fmtstring, 1024, fmt, ap);
224   va_end(ap);
225   if (n < 0 || n >= 1024) {
226     RAVE_ERROR0("Failed to generate name");
227     goto done;
228   }
229
230   memset(pstr, '\0', 1024);
231
232   sz = RaveObjectList_size(probs);
233   for (i = 0; i < sz; i++) {
234     field = (RaveFmiImage_t*)RaveObjectList_get(probs, i);
235     attr = RaveFmiImage_getAttribute(field, "how/task_args");
236     if (attr != NULL) {
237       char* attrstr = NULL;
238       if (!RaveAttribute_getString(attr, &attrstr) || attrstr == NULL) {
239         goto done;
240       }
241       if (strlen(pstr) > 0) {
242         strcat(pstr, ";");
243         strcat(pstr, attrstr);
244       } else {
245         strcpy(pstr, attrstr);
246       }
247     }
248     RAVE_OBJECT_RELEASE(attr);
249     RAVE_OBJECT_RELEASE(field);
250   }
251
252   if (strlen(fmtstring) > 0) {
253     if (strlen(pstr) > 0) {
254       strcat(pstr, ";");
255       strcat(pstr, fmtstring);
256     } else {
257       strcpy(pstr, fmtstring);
258     }
259   }
260
261   attr = RaveAttributeHelp_createString("how/task_args", pstr);
262   if (attr == NULL || !RaveFmiImage_addAttribute(image, attr)) {
263     RAVE_CRITICAL0("Failed to add attribute to image");
264     goto done;
265   }
266
267   result = 1;
268 done:
269   RAVE_OBJECT_RELEASE(field);
270   RAVE_OBJECT_RELEASE(attr);
271   return result;
272 }
273
274 /**
275  * Gets the attribute how/task_args and atempts to determine a FmiRadarPGMCode from
276  * the string.
277  * @param[in] image - the image
278  * @return the identified PGM code on success or CLEAR if nothing could be identified
279  */
280 static FmiRadarPGMCode RaveRopoGeneratorInternal_getPgmCode(RaveFmiImage_t* image)
281 {
282   RaveAttribute_t* taskargs = NULL;
283   FmiRadarPGMCode result = CLEAR;
284   RAVE_ASSERT((image != NULL), "image == NULL");
285
286   taskargs = RaveFmiImage_getAttribute(image, "how/task_args");
287   if (taskargs != NULL) {
288     char* argument = NULL;
289     int index = 0;
290     if (!RaveAttribute_getString(taskargs, &argument)) {
291       goto done;
292     }
293     if (argument == NULL) {
294       goto done;
295     }
296     while (PGM_CODE_MAPPING[index].str != NULL) {
297       if (strncmp(PGM_CODE_MAPPING[index].str, argument, strlen(PGM_CODE_MAPPING[index].str))==0) {
298         result = PGM_CODE_MAPPING[index].type;
299         goto done;
300       }
301       index++;
302     }
303   }
304 done:
305   RAVE_OBJECT_RELEASE(taskargs);
306   return result;
307 }
308
309 /**
310  * Creates a valid 8 bit value in range 0-255 from the wanted value translated
311  * with the offset and gain.
312  * E.g. If data representation is from -20dBZ and up with 0.5 dBZ intervals you
313  * probably have a offset -20 and gain 0.5. Which means that
314  * 1 in the data is -19.5dBZ.
315  * However, this function inverts these value. value should be actual value, e.g. -20
316  * and then it is converted with the offset and gain by v = (value - offset)/gain.
317  *
318  * @param[in] value - the requested actual value, e.g. -20 (dBZ)
319  * @param[in] image - image (from where offset and gain are fetched)
320  * @return the converted value (0-255) on success and negative value on failure
321  */
322 static int RaveRopoGeneratorInternal_valueToByteRange(int value, RaveFmiImage_t* image)
323 {
324   int result = -1;
325   double offset = 0.0;
326   double gain = 0.0;
327
328   RAVE_ASSERT((image != NULL), "image == NULL");
329
330   offset = RaveFmiImage_getOffset(image);
331   gain = RaveFmiImage_getGain(image);
332
333   if (gain != 0.0) {
334     result = (int)((value - offset)/gain);
335   }
336   if (result <= 0) {
337     result = 0;
338   } else if (result >= 255) {
339     result = 255;
340   }
341   return result;
342 }
343
344 /**
345  * Creates a valid 8 bit value in range 0-255 from the wanted value translated
346  * with the gain. This is assumed to be corresponding to the function \ref rel_dbz_to_byte.
347  *
348  * @param[in] value - the requested relative value, e.g. 20 (dBZ)
349  * @param[in] image - image (from where offset and gain are fetched)
350  * @return the converted rel value (0-255) on success and negative value on failure.
351  */
352 static int RaveRopoGeneratorInternal_relValueToByteRange(int value, RaveFmiImage_t* image)
353 {
354   int result = -1;
355   double gain = 0.0;
356
357   RAVE_ASSERT((image != NULL), "image == NULL");
358
359   gain = RaveFmiImage_getGain(image);
360
361   if (gain != 0.0) {
362     result = (int)(value/gain);
363   }
364   if (result <= 0) {
365     result = 0;
366   } else if (result >= 255) {
367     result = 255;
368   }
369   return result;
370 }
371
372
373 /**
374  * Clears the classification information. Should be run at the end of all
375  * detectors so that proper classficiation information can be returned.
376  * @param[in] self - self
377  */
378 static void RaveRopoGeneratorInternal_removeClassifications(RaveRopoGenerator_t* self)
379 {
380   RAVE_OBJECT_RELEASE(self->classification);
381   RAVE_OBJECT_RELEASE(self->markers);
382 }
383
384 /**
385  * Creates a probability field with the task & task_args filled.
386  * @param[in] self - self
387  * @param[in,out] probability - the created probability field
388  * @param[in] task - the detector how/task
389  * @param[in] fmt - the detector how/task_args format string
390  * @param[in] ... - the varargs list for the fmt string
391  * @return 1 on success otherwise 0
392  */
393 static int RaveRopoGeneratorInternal_createProbabilityField(
394   RaveRopoGenerator_t* self,
395   RaveFmiImage_t** probability,
396   const char* task,
397   const char* fmt, ...)
398 {
399   int result = 0;
400   RaveFmiImage_t* outprob = NULL;
401   char fmtstring[1024];
402   va_list ap;
403   int n = 0;
404
405   RAVE_ASSERT((self != NULL), "self == NULL");
406   RAVE_ASSERT((probability != NULL), "probability == NULL");
407   RAVE_ASSERT((fmt != NULL), "fmt == NULL");
408
409   va_start(ap, fmt);
410   n = vsnprintf(fmtstring, 1024, fmt, ap);
411   va_end(ap);
412   if (n < 0 || n >= 1024) {
413     RAVE_ERROR0("Failed to generate name");
414     goto done;
415   }
416
417   if (self->image == NULL) {
418     RAVE_ERROR0("Creating probability field when there is no image");
419     goto done;
420   }
421
422   outprob = RAVE_OBJECT_CLONE(self->image);
423   if (outprob == NULL) {
424     RAVE_CRITICAL0("Failed to clone image");
425     goto done;
426   }
427   RaveFmiImage_fill(outprob, CLEAR);
428
429   if (!RaveRopoGeneratorInternal_addTask(outprob, task) ||
430       !RaveRopoGeneratorInternal_addTaskArgs(outprob, fmtstring)) {
431     RAVE_CRITICAL0("Failed to add task arguments");
432     goto done;
433   }
434
435   RAVE_OBJECT_RELEASE(*probability);
436   *probability = RAVE_OBJECT_COPY(outprob);
437
438   result = 1;
439 done:
440   RAVE_OBJECT_RELEASE(outprob);
441   return result;
442 }
443
444
445 /*@} End of Private functions */
446
447 /*@{ Interface functions */
448 void RaveRopoGenerator_setImage(RaveRopoGenerator_t* self, RaveFmiImage_t* image)
449 {
450   RAVE_ASSERT((self != NULL), "self == NULL");
451   RAVE_ASSERT((image != NULL), "image == NULL");
452
453   RaveRopoGenerator_declassify(self);
454   RAVE_OBJECT_RELEASE(self->image);
455   self->image = RAVE_OBJECT_COPY(image);
456 }
457
458 RaveFmiImage_t* RaveRopoGenerator_getImage(RaveRopoGenerator_t* self)
459 {
460   RAVE_ASSERT((self != NULL), "self == NULL");
461   return RAVE_OBJECT_COPY(self->image);
462 }
463
464 void RaveRopoGenerator_threshold(RaveRopoGenerator_t* self, int threshold)
465 {
466   RAVE_ASSERT((self != NULL), "self == NULL");
467
468   if (self->image == NULL) {
469     RAVE_ERROR0("Calling threshold when there is no image to operate on?");
470     return;
471   }
472
473   threshold_image(RaveFmiImage_getImage(self->image),
474                   RaveFmiImage_getImage(self->image),
475                   threshold);
476 }
477
478 int RaveRopoGenerator_speck(RaveRopoGenerator_t* self, int minDbz, int maxA)
479 {
480   RaveFmiImage_t* probability = NULL;
481   int result = 0;
482   RAVE_ASSERT((self != NULL), "self == NULL");
483
484   if (!RaveRopoGeneratorInternal_createProbabilityField(self,
485          &probability,
486          "fi.fmi.ropo.detector",
487          "SPECK: %d,%d",minDbz, maxA)) {
488     goto done;
489   }
490
491   detect_specks(RaveFmiImage_getImage(self->image),
492                 RaveFmiImage_getImage(probability),
493                 RaveRopoGeneratorInternal_valueToByteRange(minDbz, self->image),
494                 histogram_area);
495   semisigmoid_image(RaveFmiImage_getImage(probability), maxA);
496   invert_image(RaveFmiImage_getImage(probability));
497   translate_intensity(RaveFmiImage_getImage(probability), 255, 0);
498
499   if (!RaveObjectList_add(self->probabilities, (RaveCoreObject*)probability)) {
500     RAVE_ERROR0("Failed to add probability field to probabilities");
501     goto done;
502   }
503
504   RaveRopoGeneratorInternal_removeClassifications(self);
505
506   result = 1;
507 done:
508   RAVE_OBJECT_RELEASE(probability);
509   return result;
510 }
511
512 int RaveRopoGenerator_speckNormOld(RaveRopoGenerator_t* self, int minDbz, int maxA, int maxN)
513 {
514   RaveFmiImage_t* probability = NULL;
515   int result = 0;
516   RAVE_ASSERT((self != NULL), "self == NULL");
517
518   if (!RaveRopoGeneratorInternal_createProbabilityField(self,
519          &probability,
520          "fi.fmi.ropo.detector",
521          "SPECKNORMOLD: %d,%d,%d",minDbz, maxA, maxN)) {
522     goto done;
523   }
524
525   detect_specks(RaveFmiImage_getImage(self->image),
526                             RaveFmiImage_getImage(probability),
527                             RaveRopoGeneratorInternal_valueToByteRange(minDbz, self->image),
528                             histogram_area);
529   distance_compensation_mul(RaveFmiImage_getImage(probability), maxN);
530   semisigmoid_image(RaveFmiImage_getImage(probability),maxA);
531   invert_image(RaveFmiImage_getImage(probability));
532   translate_intensity(RaveFmiImage_getImage(probability),255,0);
533
534   if (!RaveObjectList_add(self->probabilities, (RaveCoreObject*)probability)) {
535     RAVE_ERROR0("Failed to add probability field to probabilities");
536     goto done;
537   }
538
539   RaveRopoGeneratorInternal_removeClassifications(self);
540
541   result = 1;
542 done:
543   RAVE_OBJECT_RELEASE(probability);
544   return result;
545 }
546
547 int RaveRopoGenerator_emitter(RaveRopoGenerator_t* self, int minDbz, int length)
548 {
549   RaveFmiImage_t* probability = NULL;
550   int result = 0;
551   RAVE_ASSERT((self != NULL), "self == NULL");
552
553   if (!RaveRopoGeneratorInternal_createProbabilityField(self,
554          &probability,
555          "fi.fmi.ropo.detector",
556          "EMITTER: %d,%d",minDbz, length)) {
557     goto done;
558   }
559
560   detect_emitters(RaveFmiImage_getImage(self->image),
561                   RaveFmiImage_getImage(probability),
562                   RaveRopoGeneratorInternal_valueToByteRange(minDbz, self->image), length);
563
564   if (!RaveObjectList_add(self->probabilities, (RaveCoreObject*)probability)) {
565     RAVE_ERROR0("Failed to add probability field to probabilities");
566     goto done;
567   }
568
569   RaveRopoGeneratorInternal_removeClassifications(self);
570
571   result = 1;
572 done:
573   RAVE_OBJECT_RELEASE(probability);
574   return result;
575 }
576
577 int RaveRopoGenerator_emitter2(RaveRopoGenerator_t* self, int minDbz, int length, int width)
578 {
579   RaveFmiImage_t* probability = NULL;
580   int result = 0;
581   RAVE_ASSERT((self != NULL), "self == NULL");
582
583   if (!RaveRopoGeneratorInternal_createProbabilityField(self,
584          &probability,
585          "fi.fmi.ropo.detector",
586          "EMITTER2: %d,%d,%d",minDbz, length, width)) {
587     goto done;
588   }
589
590   detect_emitters2(RaveFmiImage_getImage(self->image),
591                    RaveFmiImage_getImage(probability),
592                    RaveRopoGeneratorInternal_valueToByteRange(minDbz, self->image),
593                    length,
594                    width);
595
596   if (!RaveObjectList_add(self->probabilities, (RaveCoreObject*)probability)) {
597     RAVE_ERROR0("Failed to add probability field to probabilities");
598     goto done;
599   }
600
601   RaveRopoGeneratorInternal_removeClassifications(self);
602
603   result = 1;
604 done:
605   RAVE_OBJECT_RELEASE(probability);
606   return result;
607 }
608
609 int RaveRopoGenerator_clutter(RaveRopoGenerator_t* self, int minDbz, int maxCompactness)
610 {
611   RaveFmiImage_t* probability = NULL;
612   int result = 0;
613   RAVE_ASSERT((self != NULL), "self == NULL");
614
615   if (!RaveRopoGeneratorInternal_createProbabilityField(self,
616          &probability,
617          "fi.fmi.ropo.detector",
618          "CLUTTER: %d,%d",minDbz, maxCompactness)) {
619     goto done;
620   }
621
622   detect_specks(RaveFmiImage_getImage(self->image),
623                 RaveFmiImage_getImage(probability),
624                 RaveRopoGeneratorInternal_valueToByteRange(minDbz, self->image),
625                 histogram_compactness);
626   semisigmoid_image(RaveFmiImage_getImage(probability),maxCompactness);
627   invert_image(RaveFmiImage_getImage(probability));
628   translate_intensity(RaveFmiImage_getImage(probability),255,0);
629
630   if (!RaveObjectList_add(self->probabilities, (RaveCoreObject*)probability)) {
631     RAVE_ERROR0("Failed to add probability field to probabilities");
632     goto done;
633   }
634
635   RaveRopoGeneratorInternal_removeClassifications(self);
636
637   result = 1;
638 done:
639   RAVE_OBJECT_RELEASE(probability);
640   return result;
641 }
642
643 int RaveRopoGenerator_clutter2(RaveRopoGenerator_t* self, int minDbz, int maxSmoothness)
644 {
645   RaveFmiImage_t* probability = NULL;
646   int result = 0;
647   RAVE_ASSERT((self != NULL), "self == NULL");
648
649   if (!RaveRopoGeneratorInternal_createProbabilityField(self,
650          &probability,
651          "fi.fmi.ropo.detector",
652          "CLUTTER2: %d,%d",minDbz, maxSmoothness)) {
653     goto done;
654   }
655
656   detect_specks(RaveFmiImage_getImage(self->image),
657                 RaveFmiImage_getImage(probability),
658                 RaveRopoGeneratorInternal_valueToByteRange(minDbz, self->image),
659                 histogram_smoothness);
660   invert_image(RaveFmiImage_getImage(probability));
661   translate_intensity(RaveFmiImage_getImage(probability),255,0);
662   semisigmoid_image(RaveFmiImage_getImage(probability),255-maxSmoothness);
663
664   if (!RaveObjectList_add(self->probabilities, (RaveCoreObject*)probability)) {
665     RAVE_ERROR0("Failed to add probability field to probabilities");
666     goto done;
667   }
668
669   RaveRopoGeneratorInternal_removeClassifications(self);
670
671   result = 1;
672 done:
673   RAVE_OBJECT_RELEASE(probability);
674   return result;
675 }
676
677 int RaveRopoGenerator_softcut(RaveRopoGenerator_t* self, int maxDbz, int r, int r2)
678 {
679   RaveFmiImage_t* probability = NULL;
680   int result = 0;
681   RAVE_ASSERT((self != NULL), "self == NULL");
682
683   if (!RaveRopoGeneratorInternal_createProbabilityField(self,
684          &probability,
685          "fi.fmi.ropo.detector",
686          "SOFTCUT: %d,%d,%d",maxDbz, r, r2)) {
687     goto done;
688   }
689
690   detect_insect_band(RaveFmiImage_getImage(self->image),
691                      RaveFmiImage_getImage(probability),
692                      RaveRopoGeneratorInternal_valueToByteRange(maxDbz, self->image),
693                      r, r2);
694
695   if (!RaveObjectList_add(self->probabilities, (RaveCoreObject*)probability)) {
696     RAVE_ERROR0("Failed to add probability field to probabilities");
697     goto done;
698   }
699
700   RaveRopoGeneratorInternal_removeClassifications(self);
701
702   result = 1;
703 done:
704   RAVE_OBJECT_RELEASE(probability);
705   return result;
706 }
707
708 int RaveRopoGenerator_biomet(RaveRopoGenerator_t* self, int maxDbz, int dbzDelta, int maxAlt, int altDelta)
709 {
710   RaveFmiImage_t* probability = NULL;
711   int result = 0;
712   RAVE_ASSERT((self != NULL), "self == NULL");
713
714   if (!RaveRopoGeneratorInternal_createProbabilityField(self,
715          &probability,
716          "fi.fmi.ropo.detector",
717          "BIOMET: %d,%d,%d,%d",maxDbz, dbzDelta, maxAlt, altDelta)) {
718     goto done;
719   }
720
721   detect_biomet(RaveFmiImage_getImage(self->image),
722                 RaveFmiImage_getImage(probability),
723                 RaveRopoGeneratorInternal_valueToByteRange(maxDbz, self->image),
724                 RaveRopoGeneratorInternal_relValueToByteRange(dbzDelta, self->image),
725                 maxAlt,
726                 altDelta);
727
728   if (!RaveObjectList_add(self->probabilities, (RaveCoreObject*)probability)) {
729     RAVE_ERROR0("Failed to add probability field to probabilities");
730     goto done;
731   }
732
733   RaveRopoGeneratorInternal_removeClassifications(self);
734
735   result = 1;
736 done:
737   RAVE_OBJECT_RELEASE(probability);
738   return result;
739 }
740
741 int RaveRopoGenerator_ship(RaveRopoGenerator_t* self, int minRelDbz, int minA)
742 {
743   RaveFmiImage_t* probability = NULL;
744   int result = 0;
745   RAVE_ASSERT((self != NULL), "self == NULL");
746
747   if (!RaveRopoGeneratorInternal_createProbabilityField(self,
748          &probability,
749          "fi.fmi.ropo.detector",
750          "SHIP: %d,%d",minRelDbz, minA)) {
751     goto done;
752   }
753
754   detect_ships(RaveFmiImage_getImage(self->image),
755                RaveFmiImage_getImage(probability),
756                RaveRopoGeneratorInternal_relValueToByteRange(minRelDbz, self->image),
757                minA);
758
759   if (!RaveObjectList_add(self->probabilities, (RaveCoreObject*)probability)) {
760     RAVE_ERROR0("Failed to add probability field to probabilities");
761     goto done;
762   }
763
764   RaveRopoGeneratorInternal_removeClassifications(self);
765
766   result = 1;
767 done:
768   RAVE_OBJECT_RELEASE(probability);
769   return result;
770 }
771
772 int RaveRopoGenerator_sun(RaveRopoGenerator_t* self, int minDbz, int minLength, int maxThickness)
773 {
774   RaveFmiImage_t* probability = NULL;
775   int result = 0;
776   RAVE_ASSERT((self != NULL), "self == NULL");
777
778   if (!RaveRopoGeneratorInternal_createProbabilityField(self,
779          &probability,
780          "fi.fmi.ropo.detector",
781          "SUN: %d,%d,%d", minDbz, minLength, maxThickness)) {
782     goto done;
783   }
784
785   detect_sun(RaveFmiImage_getImage(self->image),
786              RaveFmiImage_getImage(probability),
787              RaveRopoGeneratorInternal_valueToByteRange(minDbz, self->image),
788              maxThickness,
789              minLength);
790
791   if (!RaveObjectList_add(self->probabilities, (RaveCoreObject*)probability)) {
792     RAVE_ERROR0("Failed to add probability field to probabilities");
793     goto done;
794   }
795
796   RaveRopoGeneratorInternal_removeClassifications(self);
797
798   result = 1;
799 done:
800   RAVE_OBJECT_RELEASE(probability);
801   return result;
802 }
803
804 int RaveRopoGenerator_sun2(RaveRopoGenerator_t* self, int minDbz, int minLength, int maxThickness, int azimuth, int elevation)
805 {
806   RaveFmiImage_t* probability = NULL;
807   int result = 0;
808   RAVE_ASSERT((self != NULL), "self == NULL");
809
810   if (!RaveRopoGeneratorInternal_createProbabilityField(self,
811          &probability,
812          "fi.fmi.ropo.detector",
813          "SUN2: %d,%d,%d,%d,%d", minDbz, minLength, maxThickness,azimuth,elevation)) {
814     goto done;
815   }
816
817   detect_sun2(RaveFmiImage_getImage(self->image),
818               RaveFmiImage_getImage(probability),
819               RaveRopoGeneratorInternal_valueToByteRange(minDbz, self->image),
820               maxThickness,
821               minLength,
822               azimuth,
823               elevation);
824
825   if (!RaveObjectList_add(self->probabilities, (RaveCoreObject*)probability)) {
826     RAVE_ERROR0("Failed to add probability field to probabilities");
827     goto done;
828   }
829
830   RaveRopoGeneratorInternal_removeClassifications(self);
831
832   result = 1;
833 done:
834   RAVE_OBJECT_RELEASE(probability);
835   return result;
836 }
837
838 int RaveRopoGenerator_classify(RaveRopoGenerator_t* self)
839 {
840   RaveFmiImage_t* probability = NULL;
841   RaveFmiImage_t* markers = NULL;
842   FmiImage* fmiProbImage = NULL;
843   FmiImage* fmiMarkersImage = NULL;
844   int result = 0;
845   int probCount = 0;
846   int i = 0;
847
848   RAVE_ASSERT((self != NULL), "self == NULL");
849
850   if (self->image == NULL) {
851     RAVE_ERROR0("Calling speck when there is no image to operate on?");
852     goto done;
853   }
854
855   probability = RAVE_OBJECT_CLONE(self->image);
856   markers = RAVE_OBJECT_CLONE(self->image);
857   if (probability == NULL || markers == NULL) {
858     RAVE_CRITICAL0("Failed to clone image");
859     goto done;
860   }
861
862   RaveFmiImage_fill(probability, CLEAR);
863   RaveFmiImage_fill(markers, CLEAR);
864
865   probCount = RaveObjectList_size(self->probabilities);
866
867   fmiProbImage = RaveFmiImage_getImage(probability);
868   fmiMarkersImage = RaveFmiImage_getImage(markers);
869
870   for (i = 0; i < probCount; i++) {
871     int j = 0;
872     RaveFmiImage_t* image = (RaveFmiImage_t*)RaveObjectList_get(self->probabilities, i);
873     FmiImage* probImage = RaveFmiImage_getImage(image);
874     FmiRadarPGMCode pgmCode = RaveRopoGeneratorInternal_getPgmCode(image);
875     for (j = 0; j < fmiProbImage->volume; j++) {
876       if (probImage->array[j] >= fmiProbImage->array[j]) {
877         fmiMarkersImage->array[j]=pgmCode;
878         fmiProbImage->array[j]=probImage->array[j];
879       }
880     }
881     RAVE_OBJECT_RELEASE(image);
882   }
883
884   if (!RaveRopoGeneratorInternal_addTask(probability, "fi.fmi.ropo.detector.classification") ||
885       !RaveRopoGeneratorInternal_addProbabilityTaskArgs(probability, self->probabilities, "")) {
886     RAVE_CRITICAL0("Failed to add task arguments");
887     goto done;
888   }
889
890   if (!RaveRopoGeneratorInternal_addTask(markers, "fi.fmi.ropo.detector.classification_marker") ||
891       !RaveRopoGeneratorInternal_addProbabilityTaskArgs(markers, self->probabilities, "")) {
892     RAVE_CRITICAL0("Failed to add task arguments");
893     goto done;
894   }
895
896   RAVE_OBJECT_RELEASE(self->classification);
897   RAVE_OBJECT_RELEASE(self->markers);
898   self->classification = RAVE_OBJECT_COPY(probability);
899   self->markers = RAVE_OBJECT_COPY(markers);
900
901   result = 1;
902 done:
903   RAVE_OBJECT_RELEASE(probability);
904   RAVE_OBJECT_RELEASE(markers);
905   return result;
906 }
907
908 void RaveRopoGenerator_declassify(RaveRopoGenerator_t* self)
909 {
910   RAVE_ASSERT((self != NULL), "self == NULL");
911   RAVE_OBJECT_RELEASE(self->classification);
912   RAVE_OBJECT_RELEASE(self->markers);
913   RaveObjectList_clear(self->probabilities);
914 }
915
916 RaveFmiImage_t* RaveRopoGenerator_restore(RaveRopoGenerator_t* self, int threshold)
917 {
918   RaveFmiImage_t* restored = NULL;
919   RaveFmiImage_t* result = NULL;
920
921   RAVE_ASSERT((self != NULL), "self == NULL");
922
923   if (self->classification == NULL || self->markers == NULL) {
924     RaveRopoGenerator_classify(self);
925   }
926   restored = RAVE_OBJECT_CLONE(self->image);
927   if (restored == NULL) {
928     RAVE_CRITICAL0("Failed to clone image");
929     goto done;
930   }
931
932   RaveFmiImage_fill(restored, CLEAR);
933
934   if (!RaveRopoGeneratorInternal_addTask(restored, "fi.fmi.ropo.restore") ||
935       !RaveRopoGeneratorInternal_addProbabilityTaskArgs(restored, self->probabilities, "RESTORE_THRESH: %d",threshold)) {
936     RAVE_CRITICAL0("Failed to add task arguments");
937     goto done;
938   }
939
940   restore_image(RaveFmiImage_getImage(self->image),
941                 RaveFmiImage_getImage(restored),
942                 RaveFmiImage_getImage(self->classification),
943                 threshold);
944
945   result = RAVE_OBJECT_COPY(restored);
946 done:
947   RAVE_OBJECT_RELEASE(restored);
948   return result;
949 }
950
951 RaveFmiImage_t* RaveRopoGenerator_restore2(RaveRopoGenerator_t* self, int threshold)
952 {
953   RaveFmiImage_t* restored = NULL;
954   RaveFmiImage_t* result = NULL;
955
956   RAVE_ASSERT((self != NULL), "self == NULL");
957
958   if (self->classification == NULL || self->markers == NULL) {
959     RaveRopoGenerator_classify(self);
960   }
961   restored = RAVE_OBJECT_CLONE(self->image);
962   if (restored == NULL) {
963     RAVE_CRITICAL0("Failed to clone image");
964     goto done;
965   }
966
967   RaveFmiImage_fill(restored, CLEAR);
968
969   if (!RaveRopoGeneratorInternal_addTask(restored, "fi.fmi.ropo.restore2") ||
970       !RaveRopoGeneratorInternal_addProbabilityTaskArgs(restored, self->probabilities, "RESTORE2_THRESH: %d",threshold)) {
971     RAVE_CRITICAL0("Failed to add task arguments");
972     goto done;
973   }
974
975   restore_image2(RaveFmiImage_getImage(self->image),
976                  RaveFmiImage_getImage(restored),
977                  RaveFmiImage_getImage(self->classification),
978                  threshold);
979
980   result = RAVE_OBJECT_COPY(restored);
981 done:
982   RAVE_OBJECT_RELEASE(restored);
983   return result;
984 }
985
986 int RaveRopoGenerator_restoreSelf(RaveRopoGenerator_t* self, int threshold)
987 {
988   RaveFmiImage_t* restored = NULL;
989   int result = 0;
990
991   RAVE_ASSERT((self != NULL), "self == NULL");
992
993   restored = RaveRopoGenerator_restore(self, threshold);
994
995   if (restored == NULL) {
996     RAVE_ERROR0("Failed to restore self");
997     goto done;
998   }
999
1000   RAVE_OBJECT_RELEASE(self->image);
1001   self->image = RAVE_OBJECT_COPY(restored);
1002
1003   result = 1;
1004 done:
1005   RAVE_OBJECT_RELEASE(restored);
1006   return result;
1007 }
1008
1009 int RaveRopoGenerator_getProbabilityFieldCount(RaveRopoGenerator_t* self)
1010 {
1011   RAVE_ASSERT((self != NULL), "self == NULL");
1012   return RaveObjectList_size(self->probabilities);
1013 }
1014
1015 RaveFmiImage_t* RaveRopoGenerator_getProbabilityField(RaveRopoGenerator_t* self, int index)
1016 {
1017   RaveCoreObject* object = NULL;
1018
1019   RAVE_ASSERT((self != NULL), "self == NULL");
1020
1021   object = RaveObjectList_get(self->probabilities, index);
1022
1023   return (RaveFmiImage_t*)object;
1024 }
1025
1026 RaveFmiImage_t* RaveRopoGenerator_getClassification(RaveRopoGenerator_t* self)
1027 {
1028   RAVE_ASSERT((self != NULL), "self == NULL");
1029   if (self->classification == NULL || self->markers == NULL) {
1030     RaveRopoGenerator_classify(self);
1031   }
1032   return RAVE_OBJECT_COPY(self->classification);
1033 }
1034
1035 RaveFmiImage_t* RaveRopoGenerator_getMarkers(RaveRopoGenerator_t* self)
1036 {
1037   RAVE_ASSERT((self != NULL), "self == NULL");
1038   if (self->classification == NULL || self->markers == NULL) {
1039     RaveRopoGenerator_classify(self);
1040   }
1041   return RAVE_OBJECT_COPY(self->markers);
1042 }
1043
1044 RaveRopoGenerator_t* RaveRopoGenerator_new(RaveFmiImage_t* image)
1045 {
1046   RaveRopoGenerator_t* result = NULL;
1047   RAVE_ASSERT((image != NULL), "image == NULL");
1048
1049   result = RAVE_OBJECT_NEW(&RaveRopoGenerator_TYPE);
1050
1051   if (result == NULL) {
1052     RAVE_CRITICAL0("Failed to allocate memory for generator");
1053     return NULL;
1054   }
1055   result->image = RAVE_OBJECT_COPY(image);
1056
1057   return result;
1058 }
1059
1060 /*@} End of Interface functions */
1061 RaveCoreObjectType RaveRopoGenerator_TYPE = {
1062     "RaveRopoGenerator",
1063     sizeof(RaveRopoGenerator_t),
1064     RaveRopoGenerator_constructor,
1065     RaveRopoGenerator_destructor,
1066     NULL /* No copy constructor */
1067 };