Coverage Report

Created: 2025-08-28 07:16

/src/libvips/libvips/convolution/spcor.c
Line
Count
Source (jump to first uncovered line)
1
/* spcor
2
 *
3
 * Copyright: 1990, N. Dessipris; 2006, 2007 Nottingham Trent University.
4
 *
5
 *
6
 * Author: Nicos Dessipris
7
 * Written on: 02/05/1990
8
 * Modified on :
9
 * 20/2/95 JC
10
 *  - updated
11
 *  - ANSIfied, a little
12
 * 21/2/95 JC
13
 *  - rewritten
14
 *  - partialed
15
 *  - speed-ups
16
 *  - new correlation coefficient (see above), from Niblack "An
17
 *    Introduction to Digital Image Processing", Prentice/Hall, pp 138.
18
 * 4/9/97 JC
19
 *  - now does short/ushort as well
20
 * 13/2/03 JC
21
 *  - oops, could segv for short images
22
 * 14/4/04 JC
23
 *  - sets Xoffset / Yoffset
24
 * 8/3/06 JC
25
 *  - use im_embed() with edge stretching on the input, not the output
26
 *
27
 * 2006-10-24 tcv
28
 *      - add im_spcor2
29
 *
30
 * 2007-11-12 tcv
31
 *      - make im_spcor a wrapper selecting either im__spcor or im__spcor2
32
 * 2008-09-09 JC
33
 *  - roll back the windowed version for now, it has some tile edge effects
34
 * 3/2/10
35
 *  - gtkdoc
36
 *  - cleanups
37
 * 7/11/13
38
 *  - redone as a class
39
 * 8/4/15
40
 *  - avoid /0 for constant reference or zero image
41
 */
42
43
/*
44
45
  This file is part of VIPS.
46
47
  VIPS is free software; you can redistribute it and/or modify
48
  it under the terms of the GNU Lesser General Public License as published by
49
  the Free Software Foundation; either version 2 of the License, or
50
  (at your option) any later version.
51
52
  This program is distributed in the hope that it will be useful,
53
  but WITHOUT ANY WARRANTY; without even the implied warranty of
54
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
55
  GNU Lesser General Public License for more details.
56
57
  You should have received a copy of the GNU Lesser General Public License
58
  along with this program; if not, write to the Free Software
59
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
60
  02110-1301  USA
61
62
 */
63
64
/*
65
66
  These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
67
68
 */
69
70
#ifdef HAVE_CONFIG_H
71
#include <config.h>
72
#endif /*HAVE_CONFIG_H*/
73
#include <glib/gi18n-lib.h>
74
75
#include <stdio.h>
76
#include <math.h>
77
78
#include <vips/vips.h>
79
80
#include "pconvolution.h"
81
#include "correlation.h"
82
83
typedef struct _VipsSpcor {
84
  VipsCorrelation parent_instance;
85
86
  /* Per-band mean of ref images.
87
   */
88
  double *rmean;
89
90
  /* Per band sqrt(sumij (ref(i,j)-mean(ref))^2)
91
   */
92
  double *c1;
93
} VipsSpcor;
94
95
typedef VipsCorrelationClass VipsSpcorClass;
96
97
G_DEFINE_TYPE(VipsSpcor, vips_spcor, VIPS_TYPE_CORRELATION);
98
99
static int
100
vips_spcor_pre_generate(VipsCorrelation *correlation)
101
0
{
102
0
  VipsObjectClass *class = VIPS_OBJECT_GET_CLASS(correlation);
103
0
  VipsSpcor *spcor = (VipsSpcor *) correlation;
104
0
  VipsImage *ref = correlation->ref_ready;
105
0
  int bands = ref->Bands;
106
0
  VipsImage **b = (VipsImage **)
107
0
    vips_object_local_array(VIPS_OBJECT(spcor), bands);
108
0
  VipsImage **t = (VipsImage **)
109
0
    vips_object_local_array(VIPS_OBJECT(spcor), 2);
110
0
  VipsImage **b2 = (VipsImage **)
111
0
    vips_object_local_array(VIPS_OBJECT(spcor), bands);
112
113
0
  int i;
114
0
  double *offset;
115
0
  double *scale;
116
117
0
  if (vips_check_noncomplex(class->nickname, ref))
118
0
    return -1;
119
120
  /* Per-band mean.
121
   */
122
0
  if (!(spcor->rmean = VIPS_ARRAY(spcor, bands, double)) ||
123
0
    !(spcor->c1 = VIPS_ARRAY(spcor, bands, double)))
124
0
    return -1;
125
0
  for (i = 0; i < bands; i++)
126
0
    if (vips_extract_band(ref, &b[i], i, NULL) ||
127
0
      vips_avg(b[i], &spcor->rmean[i], NULL))
128
0
      return -1;
129
130
  /* Per band sqrt(sumij (ref(i,j)-mean(ref))^2)
131
   */
132
0
  if (!(offset = VIPS_ARRAY(spcor, bands, double)) ||
133
0
    !(scale = VIPS_ARRAY(spcor, bands, double)))
134
0
    return -1;
135
0
  for (i = 0; i < bands; i++) {
136
0
    offset[i] = -spcor->rmean[i];
137
0
    scale[i] = 1.0;
138
0
  }
139
0
  if (vips_linear(ref, &t[0], scale, offset, bands, NULL) ||
140
0
    vips_multiply(t[0], t[0], &t[1], NULL))
141
0
    return -1;
142
0
  for (i = 0; i < bands; i++)
143
0
    if (vips_extract_band(t[1], &b2[i], i, NULL) ||
144
0
      vips_avg(b2[i], &spcor->c1[i], NULL))
145
0
      return -1;
146
0
  for (i = 0; i < bands; i++) {
147
0
    spcor->c1[i] *= ref->Xsize * ref->Ysize;
148
0
    spcor->c1[i] = sqrt(spcor->c1[i]);
149
0
  }
150
151
0
  return 0;
152
0
}
153
154
#define LOOP(IN) \
155
0
  { \
156
0
    IN *r1 = ((IN *) ref->data) + b; \
157
0
    IN *p1 = ((IN *) p) + b; \
158
0
    int in_lsk = lsk / sizeof(IN); \
159
0
    IN *r1a; \
160
0
    IN *p1a; \
161
0
\
162
0
    /* Mean of area of in corresponding to ref. \
163
0
     */ \
164
0
    p1a = p1; \
165
0
    sum1 = 0.0; \
166
0
    for (j = 0; j < ref->Ysize; j++) { \
167
0
      for (i = 0; i < sz; i += bands) \
168
0
        sum1 += p1a[i]; \
169
0
      p1a += in_lsk; \
170
0
    } \
171
0
    imean = sum1 / VIPS_IMAGE_N_PELS(ref); \
172
0
\
173
0
    /* Calculate sum-of-squares-of-differences for this window on \
174
0
     * in, and also sum-of-products-of-differences from mean. \
175
0
     */ \
176
0
    p1a = p1; \
177
0
    r1a = r1; \
178
0
    sum2 = 0.0; \
179
0
    sum3 = 0.0; \
180
0
    for (j = 0; j < ref->Ysize; j++) { \
181
0
      for (i = 0; i < sz; i += bands) { \
182
0
        /* Reference pel and input pel. \
183
0
         */ \
184
0
        IN ip = p1a[i]; \
185
0
        IN rp = r1a[i]; \
186
0
\
187
0
        /* Accumulate sum-of-squares-of-differences for \
188
0
         * input image. \
189
0
         */ \
190
0
        double t = ip - imean; \
191
0
        sum2 += t * t; \
192
0
\
193
0
        /* Accumulate product-of-difference from mean. \
194
0
         */ \
195
0
        sum3 += (rp - spcor->rmean[b]) * t; \
196
0
      } \
197
0
\
198
0
      p1a += in_lsk; \
199
0
      r1a += sz; \
200
0
    } \
201
0
  }
202
203
static void
204
vips_spcor_correlation(VipsCorrelation *correlation,
205
  VipsRegion *in, VipsRegion *out)
206
0
{
207
0
  VipsSpcor *spcor = (VipsSpcor *) correlation;
208
0
  VipsRect *r = &out->valid;
209
0
  VipsImage *ref = correlation->ref_ready;
210
0
  int bands = vips_band_format_iscomplex(ref->BandFmt)
211
0
    ? ref->Bands * 2
212
0
    : ref->Bands;
213
0
  int sz = ref->Xsize * bands;
214
0
  int lsk = VIPS_REGION_LSKIP(in);
215
216
0
  int x, y, b, j, i;
217
218
0
  double imean;
219
0
  double sum1;
220
0
  double sum2, sum3;
221
0
  double c2, cc;
222
223
0
  for (y = 0; y < r->height; y++) {
224
0
    float *q = (float *)
225
0
      VIPS_REGION_ADDR(out, r->left, r->top + y);
226
227
0
    for (x = 0; x < r->width; x++) {
228
0
      VipsPel *p =
229
0
        VIPS_REGION_ADDR(in, r->left + x, r->top + y);
230
231
0
      for (b = 0; b < bands; b++) {
232
0
        switch (vips_image_get_format(ref)) {
233
0
        case VIPS_FORMAT_UCHAR:
234
0
          LOOP(unsigned char);
235
0
          break;
236
237
0
        case VIPS_FORMAT_CHAR:
238
0
          LOOP(signed char);
239
0
          break;
240
241
0
        case VIPS_FORMAT_USHORT:
242
0
          LOOP(unsigned short);
243
0
          break;
244
245
0
        case VIPS_FORMAT_SHORT:
246
0
          LOOP(signed short);
247
0
          break;
248
249
0
        case VIPS_FORMAT_UINT:
250
0
          LOOP(unsigned int);
251
0
          break;
252
253
0
        case VIPS_FORMAT_INT:
254
0
          LOOP(signed int);
255
0
          break;
256
257
0
        case VIPS_FORMAT_FLOAT:
258
0
        case VIPS_FORMAT_COMPLEX:
259
0
          LOOP(float);
260
0
          break;
261
262
0
        case VIPS_FORMAT_DOUBLE:
263
0
        case VIPS_FORMAT_DPCOMPLEX:
264
0
          LOOP(double);
265
0
          break;
266
267
0
        default:
268
0
          g_assert_not_reached();
269
270
          /* Stop compiler warnings.
271
           */
272
0
          sum2 = 0;
273
0
          sum3 = 0;
274
0
        }
275
276
0
        c2 = spcor->c1[b] * sqrt(sum2);
277
278
0
        if (c2 == 0.0)
279
          /* Something like constant ref.
280
           * We regard this as uncorrelated.
281
           */
282
0
          cc = 0.0;
283
0
        else
284
0
          cc = sum3 / c2;
285
286
0
        *q++ = cc;
287
0
      }
288
0
    }
289
0
  }
290
0
}
291
292
/* Save a bit of typing.
293
 */
294
#define UC VIPS_FORMAT_UCHAR
295
#define C VIPS_FORMAT_CHAR
296
#define US VIPS_FORMAT_USHORT
297
#define S VIPS_FORMAT_SHORT
298
#define UI VIPS_FORMAT_UINT
299
#define I VIPS_FORMAT_INT
300
#define F VIPS_FORMAT_FLOAT
301
#define X VIPS_FORMAT_COMPLEX
302
#define D VIPS_FORMAT_DOUBLE
303
#define DX VIPS_FORMAT_DPCOMPLEX
304
305
static const VipsBandFormat vips_spcor_format_table[10] = {
306
  /* Band format:  UC C  US S  UI I  F  X  D  DX */
307
  /* Promotion: */ F, F, F, F, F, F, F, F, F, F
308
};
309
310
static void
311
vips_spcor_class_init(VipsSpcorClass *class)
312
17
{
313
17
  VipsObjectClass *object_class = (VipsObjectClass *) class;
314
17
  VipsCorrelationClass *cclass = VIPS_CORRELATION_CLASS(class);
315
316
17
  object_class->nickname = "spcor";
317
17
  object_class->description = _("spatial correlation");
318
319
17
  cclass->format_table = vips_spcor_format_table;
320
17
  cclass->pre_generate = vips_spcor_pre_generate;
321
17
  cclass->correlation = vips_spcor_correlation;
322
17
}
323
324
static void
325
vips_spcor_init(VipsSpcor *spcor)
326
0
{
327
0
}
328
329
/**
330
 * vips_spcor: (method)
331
 * @in: input image
332
 * @ref: reference image
333
 * @out: (out): output image
334
 * @...: `NULL`-terminated list of optional named arguments
335
 *
336
 * Calculate a correlation surface.
337
 *
338
 * @ref is placed at every position in @in and the correlation coefficient
339
 * calculated. The output
340
 * image is always float.
341
 *
342
 * The output
343
 * image is the same size as the input. Extra input edge pixels are made by
344
 * copying the existing edges outwards.
345
 *
346
 * The correlation coefficient is calculated as:
347
 *
348
 * ```
349
 *          sumij (ref(i,j)-mean(ref))(inkl(i,j)-mean(inkl))
350
 * c(k,l) = ------------------------------------------------
351
 *          sqrt(sumij (ref(i,j)-mean(ref))^2) *
352
 *                      sqrt(sumij (inkl(i,j)-mean(inkl))^2)
353
 * ```
354
 *
355
 * where inkl is the area of @in centred at position (k,l).
356
 *
357
 * from Niblack "An Introduction to Digital Image Processing",
358
 * Prentice/Hall, pp 138.
359
 *
360
 * If the number of bands differs, one of the images
361
 * must have one band. In this case, an n-band image is formed from the
362
 * one-band image by joining n copies of the one-band image together, and then
363
 * the two n-band images are operated upon.
364
 *
365
 * The output image is always float, unless either of the two inputs is
366
 * double, in which case the output is also double.
367
 *
368
 * ::: seealso
369
 *     [method@Image.fastcor].
370
 *
371
 * Returns: 0 on success, -1 on error
372
 */
373
int
374
vips_spcor(VipsImage *in, VipsImage *ref, VipsImage **out, ...)
375
0
{
376
0
  va_list ap;
377
0
  int result;
378
379
0
  va_start(ap, out);
380
0
  result = vips_call_split("spcor", ap, in, ref, out);
381
0
  va_end(ap);
382
383
0
  return result;
384
0
}