VISIR Pipeline Reference Manual  4.1.0
visir_util_clip_body.c
1 /* $Id: visir_util_clip_body.c,v 1.18 2012-02-02 10:09:30 jtaylor Exp $
2  *
3  * This file is part of the irplib package
4  * Copyright (C) 2011 European Southern Observatory
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program 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 General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA
19  */
20 
21 /*
22  * $Author: jtaylor $
23  * $Date: 2012-02-02 10:09:30 $
24  * $Revision: 1.18 $
25  * $Name: not supported by cvs2svn $
26  */
27 
28 #include <math.h>
29 
30 #define TYPE_ADD(a) CONCAT2X(a, PIXEL_TYPE)
31 #define TYPE_ADD_CONST(a) CONCAT2X(a, CONCAT2X(PIXEL_TYPE, const))
32 
33 /* Swap macro */
34 #define PIXEL_TYPE_SWAP(a, b) \
35  { register const PIXEL_TYPE t=(a); (a)=(b); (b)=t; }
36 
37 static PIXEL_TYPE TYPE_ADD(cpl_tools_get_kth)(PIXEL_TYPE *, int, int);
38 
39 static
40 double TYPE_ADD(cpl_tools_get_variancesum)(const PIXEL_TYPE *, int, double *,
41  const double *);
42 
43 
44 /*----------------------------------------------------------------------------*/
56 /*----------------------------------------------------------------------------*/
57 double TYPE_ADD(cpl_tools_get_variancesum)(const PIXEL_TYPE * a,
58  int n, double * pmean,
59  const double * cache)
60 {
61  double varsum = 0.0;
62  double mean = 0.0;
63  int i;
64 
65  cpl_ensure(a != NULL, CPL_ERROR_NULL_INPUT, 0.0);
66  cpl_ensure(n >= 0, CPL_ERROR_ILLEGAL_INPUT, 0.0);
67 
68  for (i=0; i < n; i++) {
69  const double delta = (double)a[i] - mean;
70  double ri = cache[i];
71  //assert(ri == 1./(i + 1));
72 
73  varsum += i * delta * delta * ri;
74  mean += delta * ri;
75  }
76 
77  if (pmean != NULL) *pmean = mean;
78 
79  return varsum;
80 }
81 
82 /*----------------------------------------------------------------------------*/
97 /*----------------------------------------------------------------------------*/
98 static
99 PIXEL_TYPE TYPE_ADD(cpl_tools_get_kth)(PIXEL_TYPE * self,
100  int n,
101  int k)
102 {
103  register int l = 0;
104  register int m = n - 1;
105  register int i = l;
106  register int j = m;
107 
108  cpl_ensure(self != NULL, CPL_ERROR_NULL_INPUT, (PIXEL_TYPE)0);
109  cpl_ensure(k >= 0, CPL_ERROR_ILLEGAL_INPUT, (PIXEL_TYPE)0);
110  cpl_ensure(k < n, CPL_ERROR_ACCESS_OUT_OF_RANGE, (PIXEL_TYPE)0);
111 
112  while (l < m) {
113  register const PIXEL_TYPE x = self[k];
114 
115  do {
116  while (self[i] < x) i++;
117  while (x < self[j]) j--;
118  if (i <= j) {
119  PIXEL_TYPE_SWAP(self[i], self[j]);
120  i++; j--;
121  }
122  } while (i <= j);
123 
124  /* assert( j < i ); */
125 
126  /* The original implementation has two index comparisons and
127  two, three or four index assignments. This has been reduced
128  to one or two index comparisons and two index assignments.
129  */
130 
131  if (k <= j) {
132  /* assert( k < i ); */
133  m = j;
134  i = l;
135  } else {
136  if (k < i) {
137  m = j;
138  } else {
139  j = m;
140  }
141  l = i;
142  }
143  }
144  return self[k];
145 }
146 
147 
148 /*----------------------------------------------------------------------------*/
159 /*----------------------------------------------------------------------------*/
160 static
161 cpl_error_code TYPE_ADD(visir_util_clip_kappa_sigma)(cpl_imagelist * self,
162  cpl_imagelist * devlist,
163  double keepfrac,
164  double kappa, int maxite,
165  const int * shifts)
166 {
167  const int nz = cpl_imagelist_get_size(self);
168  const cpl_image * img = cpl_imagelist_get_const(self, 0);
169  const int nx = cpl_image_get_size_x(img);
170  const int ny = cpl_image_get_size_y(img);
171  /* Reject this many of the minimum (and maximum) values */
172  const int minrej = (int)((double)nz * 0.5 * (1.0 - keepfrac) + 0.5);
173  PIXEL_TYPE * pvalues = (PIXEL_TYPE*)cpl_malloc(nz * sizeof(*pvalues));
174  /* Pointers to the nz pixel buffers */
175  const PIXEL_TYPE ** pimg = cpl_malloc((size_t)nz * sizeof(PIXEL_TYPE *));
176  cpl_binary ** pbpm = cpl_malloc((size_t)nz * sizeof(cpl_binary *));
177  cpl_image * imgstdev0 = cpl_image_new(nx, ny, STDEV_TYPE);
178  cpl_image * imgstdevn = cpl_image_new(nx, ny, STDEV_TYPE);
179 
180  /* precompute the quotient of the standard deviation computation
181  * improves performance by about 10%-20%
182  * typical burst nz size is around 1000 so it fits easily into the cache
183  * for larger stacks it needs to be reevaluated if the cache misses cost
184  * more than recomputing
185  * the variable is declared globally in the calling file for cleanup */
186 OMP_PRAGMA(omp critical(clip_rnok_precompute))
187  if (prnok == NULL || prnok_nz < nz) {
188  if (prnok)
189  cpl_free(prnok);
190  prnok = cpl_malloc(nz * sizeof(prnok[0]));
191  for (int i = 0; i < nz; i++) {
192  prnok[i] = 1. / (i + 1);
193  }
194  prnok_nz = nz;
195  }
196 
197 
198  bug_if(cpl_image_get_type(img) != PIXEL_TYPE_CPL);
199 
200  //error_if(nz < 3, CPL_ERROR_DATA_NOT_FOUND, "nz = %d < 3", nz);
201 
202  error_if(keepfrac < 0.0, CPL_ERROR_ILLEGAL_INPUT, "Parameter keepfrac = "
203  "%g < 0.0", keepfrac);
204  error_if(keepfrac > 1.0, CPL_ERROR_ILLEGAL_INPUT, "Parameter keepfrac = "
205  "%g > 1.0", keepfrac);
206  error_if(kappa < 0.0, CPL_ERROR_ILLEGAL_INPUT, "Parameter kappa = "
207  "%g < 0.0", kappa);
208  error_if(maxite < 0, CPL_ERROR_ILLEGAL_INPUT, "Parameter maxite = "
209  "%d < 0", maxite);
210 
211  for (int k = 0; k < nz; k++) {
212  cpl_image * imgk = cpl_imagelist_get(self, k);
213  cpl_mask * bpm = cpl_image_get_bpm(imgk);
214 
215  pimg[k] = TYPE_ADD_CONST(cpl_image_get_data)(imgk);
216  pbpm[k] = cpl_mask_get_data(bpm);
217 
218  bug_if(pimg[k] == NULL);
219  bug_if(pbpm[k] == NULL);
220 
221  }
222 
223  for (int j = 0; j < ny; j++) {
224  for (int i = 0; i < nx; i++) {
225  int nok = 0;
226 
227  for (int k = 0; k < nz; k++) {
228  const int ii = (i + shifts[k * 2]) +
229  (j + shifts[k * 2 + 1]) * nx;
230  if (i + shifts[k * 2] >= nx || i + shifts[k * 2] < 0 ||
231  j + shifts[k * 2 + 1] >= ny || j + shifts[k * 2 + 1] < 0)
232  continue;
233 
234  if (!pbpm[k][ii]) {
235  pvalues[nok++] = pimg[k][ii];
236  }
237  }
238 
239  if (nok > 1) { /* Cannot clip with just one value */
240  /* Index of 1st value to skip */
241  const int ithmin = minrej - (nz - nok) / 2 - 1;
242  double median, mean, stdev, varsum;
243  PIXEL_TYPE * pmedian = pvalues;
244  int nmedian = nok;
245 
246  if (0 <= ithmin && 2 * ithmin + 3 <= nok) {
247  /* Need at least 3 values for a proper median + stdev */
248  /* Index of last value to skip */
249  const int ithmax = nok - ithmin - 1;
250  (void)TYPE_ADD(cpl_tools_get_kth)(pvalues, nok, ithmax);
251  (void)TYPE_ADD(cpl_tools_get_kth)(pvalues, ithmax, ithmin);
252  /* Already ignored values are skipped in median */
253  pmedian += ithmin + 1;
254  nmedian -= ithmin * 2 + 2;
255  }
256  bug_if( nmedian <= 1 );
257 
258  median = (double)TYPE_ADD(cpl_tools_get_kth)(pmedian, nmedian,
259  (nmedian - 1) / 2);
260  if (!(nmedian & 1)) {
261  /* Even number of samples, use mean of two central values */
262  /* Already did lower half */
263  median += (double)TYPE_ADD(cpl_tools_get_kth)(pmedian +
264  nmedian / 2,
265  nmedian / 2,
266  0);
267  median *= 0.5;
268  }
269 
270  varsum = TYPE_ADD(cpl_tools_get_variancesum)(pmedian, nmedian,
271  NULL, prnok);
272  stdev = sqrt(varsum / (double)(nmedian - 1));
273 
274  bug_if(cpl_image_set(imgstdev0, 1 + i, 1 + j, stdev));
275 
276  for (int nite = 0; nite < maxite; nite++) {
277  /* For nite > 0 use mean of previous iteration */
278  const double center = nite ? mean : median;
279  const double lolim = center - kappa * stdev;
280  const double hilim = center + kappa * stdev;
281  const int prevok = nok;
282 
283  nok = 0;
284  mean = 0.0;
285  varsum = 0.0;
286 
287  for (int k = 0; k < nz; k++) {
288  const int ii = (i + shifts[k * 2]) +
289  (j + shifts[k * 2 + 1]) * nx;
290  if (i + shifts[k * 2] >= nx || i + shifts[k * 2] < 0 ||
291  j + shifts[k * 2 + 1] >= ny || j + shifts[k * 2 + 1] < 0) {
292  continue;
293  }
294  /*
295  cpl_msg_debug(cpl_func, "it %d z%d x%d(%d) y%d(%d): "
296  "%.2f <= %.2f <= %.2f, bad %d", nite, k,
297  i + shifts[k * 2],
298  shifts[k * 2], j + shifts[k * 2 + 1],
299  shifts[k * 2 + 1], lolim,
300  (double)pimg[k][ii + ishift], hilim,
301  pbpm[k][ii + ishift]);
302  */
303 
304  if (!pbpm[k][ii]) {
305  if (lolim <= pimg[k][ii] &&
306  pimg[k][ii] <= hilim) {
307  /* Compute also for last iteration, for final
308  standard deviation */
309  const double delta =
310  (double)pimg[k][ii] - mean;
311  double rnok = prnok[nok];
312  //assert(rnok == 1./(nok + 1));
313 
314  varsum += nok * delta * delta * rnok;
315  mean += delta * rnok;
316 
317  nok++;
318  } else {
319  /* Reject outlier */
320  pbpm[k][ii] = CPL_BINARY_1;
321  }
322  }
323  }
324 
325  if (nok < 2) {
326  stdev = 0.0;
327  break; /* No more values */
328  }
329  if (nok == prevok) break; /* Convergence */
330  stdev = sqrt(varsum / (double)(nok - 1));
331  }
332  bug_if(cpl_image_set(imgstdevn, 1 + i, 1 + j, stdev));
333  }
334  }
335  }
336 
337  bug_if(cpl_imagelist_set(devlist, imgstdev0, 0));
338  imgstdev0 = NULL;
339  bug_if(cpl_imagelist_set(devlist, imgstdevn, 1));
340  imgstdevn = NULL;
341 
342  end_skip;
343 
344  cpl_image_delete(imgstdev0);
345  cpl_image_delete(imgstdevn);
346  cpl_free(pvalues);
347  cpl_free(pimg);
348  cpl_free(pbpm);
349 
350  return cpl_error_get_code();
351 }
352 
353 #undef PIXEL_TYPE_SWAP