File: pool.c

package info (click to toggle)
gnuastro 0.24-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 44,360 kB
  • sloc: ansic: 185,444; sh: 15,785; makefile: 1,303; cpp: 9
file content (362 lines) | stat: -rw-r--r-- 11,589 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
/*********************************************************************
Pool -- Pool input data and create a new dataset.
This is part of GNU Astronomy Utilities (Gnuastro) package.

Original author:
     Faezeh Bidjarchian <fbidjarchian@gmail.com>
Contributing author(s):
     Mohammad Akhlaghi <mohammad@akhlaghi.org>
Copyright (C) 2023-2025 Free Software Foundation, Inc.

Gnuastro is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.

Gnuastro is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with Gnuastro. If not, see <http://www.gnu.org/licenses/>.
**********************************************************************/
#include <config.h>

#include <stdio.h>
#include <errno.h>
#include <error.h>
#include <stdlib.h>
#include <string.h>

#include <gnuastro/wcs.h>
#include <gnuastro/type.h>
#include <gnuastro/fits.h>
#include <gnuastro/pool.h>
#include <gnuastro/pointer.h>
#include <gnuastro/threads.h>
#include <gnuastro/dimension.h>
#include <gnuastro/statistics.h>

#include <gnuastro-internal/checkset.h>





/* Identifiers for each operator. */
enum pool_operators
{
  POOL_MAX,      /* Maximum the desired pixels.  */
  POOL_MIN,      /* Minimum the desired pixels.  */
  POOL_SUM,      /* Sum of desired pixels.       */
  POOL_MEAN,     /* Mean the desired pixel.      */
  POOL_MEDIAN,   /* Median the desired pixels.   */
};





/**********************************************************************/
/****************             Pooling                 *****************/
/**********************************************************************/
#define POOLING_DIM 10

/* Main input/output parameters. */
struct pooling
{
  int       operator;     /* The type of pooling.             */
  size_t    poolsize;     /* The size of pooling.             */
  size_t     pstride;     /* The stride of pooling.           */
  size_t      *osize;     /* The output size.                 */
  gal_data_t  *input;     /* Dataset to print values of.      */
  gal_data_t    *out;     /* Output data structure.           */
};





/* Do the pooling on each thread.

   Current assumptions:

   - The size of pooling can be every single number (the pooling window
     is a square).
   - The width and height of the input are not necessarily divisible
     by the size of the pooling. In other words, the image can be both
     square and rectangular.
   - We apply pooling to our input with any stride length that may be
     differ from the poolsize. Remember that, the size of the poolsize
     must be greater than the stride size. */
static void *
pool_type_on_thread(void *in_prm)
{
  struct gal_threads_params *tprm=(struct gal_threads_params *)in_prm;
  struct pooling *pp=(struct pooling *)tprm->params;
  gal_data_t *input=pp->input;

  size_t strd=pp->pstride;
  size_t ndim=input->ndim;
  size_t pools=pp->poolsize;
  size_t w=input->dsize[1], wr;
  gal_data_t *statv=NULL, *result=NULL;
  size_t i, a, b, oind, iind, vc, numpixs, coord[POOLING_DIM], index;

  /* All number of pixels that we selected each time par the pooling. */
  numpixs=pools*pools;

  /* Allocated memory for selected pixels of input to feed into the
     statisitical operation. */
  statv=gal_data_alloc(NULL, input->type, 1, &numpixs, NULL, 0,
                       input->minmapsize, input->quietmmap,
                       NULL, NULL, NULL);

  /* Go over all the pixels that were assigned to this thread. */
  for(i=0; tprm->indexs[i] != GAL_BLANK_SIZE_T; ++i)
    {
      /* For easy reading, put the index in 'oind'. */
      oind=tprm->indexs[i];

      /* Get the coordinate of the pixel. */
      gal_dimension_index_to_coord(oind, ndim, pp->osize, coord);

      /* Convert the pixel coordinate to the desired pixel that we must
         select to set the pooling's starting pointer. */
      iind=strd*w*coord[0]+strd*coord[1];
      wr=iind%w;

      /* In some cases, the pooling window doesn't cover a whole squared
         window and only has maybe one pixel. So since we initialize the
         statv by Null (=0 in C), some of these values fill with input
         values and others remain zero. So these zeros will affect the
         outputs. Therefore we initialize the statv by blank value. */
      gal_blank_initialize(statv);

      /* Set the sorted and blank check flags to 0 so the statistical
         operators re-check everytime for sorted or blank elements. */
      statv->flag=0;

      /* Depending on the size of pooling, we condider a set of pixels
	 and fill the temporary 'values' array. Then we do the required
         operation on them. */
      vc=0;
      for(a=0;a<pools;++a)
	for(b=0;b<pools && wr+b<input->dsize[1];++b)
	  {
	    index=iind + a*w + b;
	    if (index<input->size)
	      {
                memcpy(gal_pointer_increment(statv->array, vc,
                                             statv->type),
                       gal_pointer_increment(input->array, index,
                                             input->type),
                       gal_type_sizeof(input->type));
                vc++;
	      }
	  }

      /* Do the necessary calculation. */
      switch(pp->operator)
        {
        case POOL_MAX:    result=gal_statistics_maximum(statv);   break;
        case POOL_MIN:    result=gal_statistics_minimum(statv);   break;
        case POOL_SUM:    result=gal_statistics_sum(statv);       break;
        case POOL_MEAN:   result=gal_statistics_mean(statv);      break;
        case POOL_MEDIAN: result=gal_statistics_median(statv, 1); break;
        default:
          error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s "
                "to fix the problem. 'operator' code %d is not "
                "recognized.", PACKAGE_BUGREPORT, __func__,
                pp->operator);
        }

      /* Make sure the output array type and the type of result are the
         same. */
      if(result->type!=pp->out->type)
        result=gal_data_copy_to_new_type_free(result, pp->out->type);

      /* Copy the result into the output array. */
      memcpy(gal_pointer_increment(pp->out->array, oind, pp->out->type),
             result->array, gal_type_sizeof(pp->out->type));

      /* Clean up. */
      gal_data_free(result);
    }

  /* Clean up. */
  gal_data_free(statv);

  /* Wait for all the other threads to finish, then return. */
  if(tprm->b) pthread_barrier_wait(tprm->b);
  return NULL;
}





static gal_data_t *
pool_generic(gal_data_t *input, size_t psize, size_t stride, int operator,
             size_t numthreads)
{
  struct pooling pp={0};

  int otype=GAL_TYPE_INVALID;
  size_t outndim=input->ndim, i, r, diff;

  /* Print a warning if the psize or the stride have a wrong value. It
     happens when the user writes a negative value for them. */
  if(psize>(size_t)(-1)/2 || psize==0)
    error(EXIT_FAILURE, 0, "the value of poolsize must be positive, and "
          "non zero)");
  if(stride>(size_t)(-1)/2 || stride==0)
    error(EXIT_FAILURE, 0, "the value of stride must be positive, and "
          "non zero)");

  /* Make sure the given poolsize is lower than the input's width or
     height. */
  if(psize>input->dsize[0] && psize>input->dsize[1])
    error(EXIT_FAILURE, 0, "%s: the pool size along dimension must be "
         "lower than the input's width or height in that dimension",
         __func__);

  /* Make sure the size of the stride is lower than the poolsize. If not,
     some pixels may not be considered during the pooling process. */
  if(stride>psize)
    error(EXIT_FAILURE, 0, "%s: the size of the stride must be lower "
          "than the poolsize. Otherwise, there are some pixels that are "
          "not in any of the pooling windows.", __func__);

  /* Set the pointers in the structure of the parameter. */
  pp.input=input;
  pp.pstride=stride;
  pp.poolsize=psize;
  pp.operator=operator;

  if(pp.input->size==1) pp.out=pp.input;
  else
    {
      /* Resize output when calculating pooling on it and the remainder
         is not zero. So we must calculate the pooling one more time for
         the remaining pixels. */
      pp.osize=gal_pointer_allocate(GAL_TYPE_SIZE_T, input->ndim, 0,
                                    __func__, "osize");

      /* if the width is not divisible by the poolsize, we have to add one
         dimension to the output dimension for these remaining pixels. */
      for(i=0;i<input->ndim;++i)
        {
          r=(input->dsize[i])%stride;
          diff=((r==0)?0:1);
          pp.osize[i]=(input->dsize[i]/stride)+diff;
        }

      /* Set the type of the output dataset. */
      switch(pp.operator)
        {
        case POOL_MAX:
        case POOL_MIN:
        case POOL_MEDIAN:
          otype=pp.input->type;
          break;

        case POOL_SUM:
        case POOL_MEAN:
          otype=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);
        }

      /* Allocating an struct for the output data. */
      pp.out=gal_data_alloc(NULL, otype, outndim, pp.osize, NULL, 0,
                            pp.input->minmapsize, pp.input->quietmmap, NULL,
                            NULL, NULL);

      /* Spin-off the threads and do the processing on each thread. */
      gal_threads_spin_off(pool_type_on_thread, &pp, pp.out->size, numthreads,
                           pp.input->minmapsize, pp.input->quietmmap);
    }

  /* Correct the WCS (if it has one). */
  if(input->wcs)
    {
      /* We currently assume that a 'cdelt' exists (due to a lack of
         time)! */
      if(pp.input->wcs->cdelt==NULL)
        error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at '%s' to "
              "fix the problem. The input WCS has no 'cdelt' component",
              __func__, PACKAGE_BUGREPORT);

      /* Copy the input WCS to the output and correct the values. */
      pp.out->wcs=gal_wcs_copy(pp.input->wcs);
      pp.out->wcs->crpix[0]/=psize;
      pp.out->wcs->crpix[1]/=psize;
      pp.out->wcs->cdelt[0]*=psize;
      pp.out->wcs->cdelt[1]*=psize;
    }

  /* Clean up and return. */
  free(pp.osize);
  return pp.out;
}





gal_data_t *
gal_pool_max(gal_data_t *input, size_t psize, size_t stride,
             size_t numthreads)
{
  return pool_generic(input, psize, stride, POOL_MAX, numthreads);
}





gal_data_t *
gal_pool_min(gal_data_t *input, size_t psize, size_t stride,
             size_t numthreads)
{
  return pool_generic(input, psize, stride, POOL_MIN, numthreads);
}





gal_data_t *
gal_pool_sum(gal_data_t *input, size_t psize, size_t stride,
             size_t numthreads)
{
  return pool_generic(input, psize, stride, POOL_SUM, numthreads);
}





gal_data_t *
gal_pool_mean(gal_data_t *input, size_t psize, size_t stride,
              size_t numthreads)
{
  return pool_generic(input, psize, stride, POOL_MEAN, numthreads);
}





gal_data_t *
gal_pool_median(gal_data_t *input, size_t psize, size_t stride,
                size_t numthreads)
{
  return pool_generic(input, psize, stride, POOL_MEDIAN, numthreads);
}