Coverage Report

Created: 2025-01-28 06:34

/src/libvips/libvips/arithmetic/linear.c
Line
Count
Source (jump to first uncovered line)
1
/* im_lintra.c -- linear transform
2
 *
3
 * Copyright: 1990, N. Dessipris, based on im_powtra()
4
 * Author: Nicos Dessipris
5
 * Written on: 02/05/1990
6
 * Modified on:
7
 * 23/4/93 JC
8
 *  - adapted to work with partial images
9
 * 1/7/93 JC
10
 *  - adapted for partial v2
11
 * 7/10/94 JC
12
 *  - new IM_NEW()
13
 *  - more typedefs
14
 * 9/2/95 JC
15
 *  - adapted for im_wrap...
16
 *  - operations on complex images now just transform the real channel
17
 * 29/9/95 JC
18
 *  - complex was broken
19
 * 15/4/97 JC
20
 *  - return(0) missing from generate, arrgh!
21
 * 1/7/98 JC
22
 *  - im_lintra_vec added
23
 * 3/8/02 JC
24
 *  - fall back to im_copy() for a == 1, b == 0
25
 * 10/10/02 JC
26
 *  - auug, failing to multiply imag for complex! (thanks matt)
27
 * 10/12/02 JC
28
 *  - removed im_copy() fallback ... meant that output format could change
29
 *    with value :-( very confusing
30
 * 30/6/04
31
 *  - added 1 band image * n band vector case
32
 * 8/12/06
33
 *  - add liboil support
34
 * 9/9/09
35
 *  - gtkdoc comment, minor reformat
36
 * 31/7/10
37
 *  - remove liboil
38
 * 31/10/11
39
 *  - rework as a class
40
 *  - removed the 1-ary constant path, no faster
41
 * 30/11/13
42
 *  - 1ary is back, faster with gcc 4.8
43
 * 14/1/14
44
 *  - add uchar output option
45
 * 30/9/17
46
 *  - squash constants with all elements equal so we use 1ary path more
47
 *    often
48
 */
49
50
/*
51
52
  Copyright (C) 1991-2005 The National Gallery
53
54
  This library is free software; you can redistribute it and/or
55
  modify it under the terms of the GNU Lesser General Public
56
  License as published by the Free Software Foundation; either
57
  version 2.1 of the License, or (at your option) any later version.
58
59
  This library is distributed in the hope that it will be useful,
60
  but WITHOUT ANY WARRANTY; without even the implied warranty of
61
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
62
  Lesser General Public License for more details.
63
64
  You should have received a copy of the GNU Lesser General Public
65
  License along with this library; if not, write to the Free Software
66
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
67
  02110-1301  USA
68
69
 */
70
71
/*
72
73
  These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
74
75
 */
76
77
/*
78
#define DEBUG
79
 */
80
81
#ifdef HAVE_CONFIG_H
82
#include <config.h>
83
#endif /*HAVE_CONFIG_H*/
84
#include <glib/gi18n-lib.h>
85
86
#include <stdio.h>
87
#include <stdlib.h>
88
#include <math.h>
89
90
#include <vips/vips.h>
91
92
#include "unary.h"
93
94
typedef struct _VipsLinear {
95
  VipsUnary parent_instance;
96
97
  /* Our constants: multiply by a, add b.
98
   */
99
  VipsArea *a;
100
  VipsArea *b;
101
102
  /* uchar output.
103
   */
104
  gboolean uchar;
105
106
  /* Our constants expanded to match arith->ready in size.
107
   */
108
  int n;
109
  double *a_ready;
110
  double *b_ready;
111
112
} VipsLinear;
113
114
typedef VipsUnaryClass VipsLinearClass;
115
116
G_DEFINE_TYPE(VipsLinear, vips_linear, VIPS_TYPE_UNARY);
117
118
static int
119
vips_linear_build(VipsObject *object)
120
8.75k
{
121
8.75k
  VipsObjectClass *class = VIPS_OBJECT_GET_CLASS(object);
122
8.75k
  VipsArithmetic *arithmetic = VIPS_ARITHMETIC(object);
123
8.75k
  VipsUnary *unary = (VipsUnary *) object;
124
8.75k
  VipsLinear *linear = (VipsLinear *) object;
125
126
8.75k
  int i;
127
128
  /* If we have a three-element vector, we need to bandup the image to
129
   * match.
130
   */
131
8.75k
  linear->n = 1;
132
8.75k
  if (linear->a)
133
8.75k
    linear->n = VIPS_MAX(linear->n, linear->a->n);
134
8.75k
  if (linear->b)
135
8.75k
    linear->n = VIPS_MAX(linear->n, linear->b->n);
136
8.75k
  if (unary->in) {
137
8.75k
    int bands;
138
139
8.75k
    vips_image_decode_predict(unary->in, &bands, NULL);
140
8.75k
    linear->n = VIPS_MAX(linear->n, bands);
141
8.75k
  }
142
8.75k
  arithmetic->base_bands = linear->n;
143
144
8.75k
  if (unary->in &&
145
8.75k
    linear->a &&
146
8.75k
    linear->b) {
147
8.75k
    if (vips_check_vector(class->nickname,
148
8.75k
        linear->a->n, unary->in) ||
149
8.75k
      vips_check_vector(class->nickname,
150
8.75k
        linear->b->n, unary->in))
151
0
      return -1;
152
8.75k
  }
153
154
  /* If all elements of the constants are equal, we can shrink them down
155
   * to a single element.
156
   */
157
8.75k
  if (linear->a) {
158
8.75k
    double *ary = (double *) linear->a->data;
159
8.75k
    gboolean all_equal;
160
161
8.75k
    all_equal = TRUE;
162
8.75k
    for (i = 1; i < linear->a->n; i++)
163
0
      if (ary[i] != ary[0]) {
164
0
        all_equal = FALSE;
165
0
        break;
166
0
      }
167
168
8.75k
    if (all_equal)
169
8.75k
      linear->a->n = 1;
170
8.75k
  }
171
8.75k
  if (linear->b) {
172
8.75k
    double *ary = (double *) linear->b->data;
173
8.75k
    gboolean all_equal;
174
175
8.75k
    all_equal = TRUE;
176
8.75k
    for (i = 1; i < linear->b->n; i++)
177
0
      if (ary[i] != ary[0]) {
178
0
        all_equal = FALSE;
179
0
        break;
180
0
      }
181
182
8.75k
    if (all_equal)
183
8.75k
      linear->b->n = 1;
184
8.75k
  }
185
186
  /* Make up-banded versions of our constants.
187
   */
188
8.75k
  linear->a_ready = VIPS_ARRAY(linear, linear->n, double);
189
8.75k
  linear->b_ready = VIPS_ARRAY(linear, linear->n, double);
190
191
17.5k
  for (i = 0; i < linear->n; i++) {
192
8.77k
    if (linear->a) {
193
8.77k
      double *ary = (double *) linear->a->data;
194
8.77k
      int j = VIPS_MIN(i, linear->a->n - 1);
195
196
8.77k
      linear->a_ready[i] = ary[j];
197
8.77k
    }
198
199
8.77k
    if (linear->b) {
200
8.77k
      double *ary = (double *) linear->b->data;
201
8.77k
      int j = VIPS_MIN(i, linear->b->n - 1);
202
203
8.77k
      linear->b_ready[i] = ary[j];
204
8.77k
    }
205
8.77k
  }
206
207
8.75k
  if (linear->uchar)
208
0
    arithmetic->format = VIPS_FORMAT_UCHAR;
209
210
8.75k
  if (VIPS_OBJECT_CLASS(vips_linear_parent_class)->build(object))
211
0
    return -1;
212
213
8.75k
  return 0;
214
8.75k
}
215
216
/* Non-complex input, any output, all bands of the constant equal.
217
 */
218
#define LOOP1(IN, OUT) \
219
8.75k
  { \
220
8.75k
    IN *restrict p = (IN *) in[0]; \
221
8.75k
    OUT *restrict q = (OUT *) out; \
222
8.75k
    OUT a1 = a[0]; \
223
8.75k
    OUT b1 = b[0]; \
224
8.75k
    int sz = width * nb; \
225
8.75k
\
226
17.5k
    for (x = 0; x < sz; x++) \
227
8.77k
      q[x] = a1 * (OUT) p[x] + b1; \
228
8.75k
  }
229
230
/* Non-complex input, any output.
231
 */
232
#define LOOPN(IN, OUT) \
233
0
  { \
234
0
    IN *restrict p = (IN *) in[0]; \
235
0
    OUT *restrict q = (OUT *) out; \
236
0
\
237
0
    for (i = 0, x = 0; x < width; x++) \
238
0
      for (k = 0; k < nb; k++, i++) \
239
0
        q[i] = a[k] * (OUT) p[i] + b[k]; \
240
0
  }
241
242
#define LOOP(IN, OUT) \
243
8.75k
  { \
244
8.75k
    if (linear->a->n == 1 && linear->b->n == 1) { \
245
8.75k
      LOOP1(IN, OUT); \
246
8.75k
    } \
247
8.75k
    else { \
248
0
      LOOPN(IN, OUT); \
249
0
    } \
250
8.75k
  }
251
252
/* Complex input, complex output.
253
 */
254
#define LOOPCMPLXN(IN, OUT) \
255
0
  { \
256
0
    IN *restrict p = (IN *) in[0]; \
257
0
    OUT *restrict q = (OUT *) out; \
258
0
\
259
0
    for (x = 0; x < width; x++) \
260
0
      for (k = 0; k < nb; k++) { \
261
0
        q[0] = a[k] * p[0] + b[k]; \
262
0
        q[1] = p[1]; \
263
0
        q += 2; \
264
0
        p += 2; \
265
0
      } \
266
0
  }
267
268
/* Non-complex input, any output, all bands of the constant equal, uchar
269
 * output.
270
 */
271
#define LOOP1uc(IN) \
272
0
  { \
273
0
    IN *restrict p = (IN *) in[0]; \
274
0
    VipsPel *restrict q = (VipsPel *) out; \
275
0
    float a1 = a[0]; \
276
0
    float b1 = b[0]; \
277
0
    int sz = width * nb; \
278
0
\
279
0
    for (x = 0; x < sz; x++) { \
280
0
      float t = a1 * p[x] + b1; \
281
0
\
282
0
      q[x] = VIPS_FCLIP(0, t, 255); \
283
0
    } \
284
0
  }
285
286
/* Non-complex input, uchar output.
287
 */
288
#define LOOPNuc(IN) \
289
0
  { \
290
0
    IN *restrict p = (IN *) in[0]; \
291
0
    VipsPel *restrict q = (VipsPel *) out; \
292
0
\
293
0
    for (i = 0, x = 0; x < width; x++) \
294
0
      for (k = 0; k < nb; k++, i++) { \
295
0
        double t = a[k] * p[i] + b[k]; \
296
0
\
297
0
        q[i] = VIPS_FCLIP(0, t, 255); \
298
0
      } \
299
0
  }
300
301
#define LOOPuc(IN) \
302
0
  { \
303
0
    if (linear->a->n == 1 && linear->b->n == 1) { \
304
0
      LOOP1uc(IN); \
305
0
    } \
306
0
    else { \
307
0
      LOOPNuc(IN); \
308
0
    } \
309
0
  }
310
311
/* Complex input, uchar output.
312
 */
313
#define LOOPCMPLXNuc(IN) \
314
0
  { \
315
0
    IN *restrict p = (IN *) in[0]; \
316
0
    VipsPel *restrict q = (VipsPel *) out; \
317
0
\
318
0
    for (i = 0, x = 0; x < width; x++) \
319
0
      for (k = 0; k < nb; k++, i++) { \
320
0
        double t = a[k] * p[0] + b[k]; \
321
0
\
322
0
        q[i] = VIPS_FCLIP(0, t, 255); \
323
0
        p += 2; \
324
0
      } \
325
0
  }
326
327
/* Lintra a buffer, n set of scale/offset.
328
 */
329
static void
330
vips_linear_buffer(VipsArithmetic *arithmetic,
331
  VipsPel *out, VipsPel **in, int width)
332
8.75k
{
333
8.75k
  VipsImage *im = arithmetic->ready[0];
334
8.75k
  VipsLinear *linear = (VipsLinear *) arithmetic;
335
8.75k
  double *restrict a = linear->a_ready;
336
8.75k
  double *restrict b = linear->b_ready;
337
8.75k
  int nb = im->Bands;
338
339
8.75k
  int i, x, k;
340
341
8.75k
  if (linear->uchar)
342
0
    switch (vips_image_get_format(im)) {
343
0
    case VIPS_FORMAT_UCHAR:
344
0
      LOOPuc(unsigned char);
345
0
      break;
346
0
    case VIPS_FORMAT_CHAR:
347
0
      LOOPuc(signed char);
348
0
      break;
349
0
    case VIPS_FORMAT_USHORT:
350
0
      LOOPuc(unsigned short);
351
0
      break;
352
0
    case VIPS_FORMAT_SHORT:
353
0
      LOOPuc(signed short);
354
0
      break;
355
0
    case VIPS_FORMAT_UINT:
356
0
      LOOPuc(unsigned int);
357
0
      break;
358
0
    case VIPS_FORMAT_INT:
359
0
      LOOPuc(signed int);
360
0
      break;
361
0
    case VIPS_FORMAT_FLOAT:
362
0
      LOOPuc(float);
363
0
      break;
364
0
    case VIPS_FORMAT_DOUBLE:
365
0
      LOOPuc(double);
366
0
      break;
367
0
    case VIPS_FORMAT_COMPLEX:
368
0
      LOOPCMPLXNuc(float);
369
0
      break;
370
0
    case VIPS_FORMAT_DPCOMPLEX:
371
0
      LOOPCMPLXNuc(double);
372
0
      break;
373
374
0
    default:
375
0
      g_assert_not_reached();
376
0
    }
377
8.75k
  else
378
8.75k
    switch (vips_image_get_format(im)) {
379
8.75k
    case VIPS_FORMAT_UCHAR:
380
8.75k
      LOOP(unsigned char, float);
381
8.75k
      break;
382
0
    case VIPS_FORMAT_CHAR:
383
0
      LOOP(signed char, float);
384
0
      break;
385
0
    case VIPS_FORMAT_USHORT:
386
0
      LOOP(unsigned short, float);
387
0
      break;
388
0
    case VIPS_FORMAT_SHORT:
389
0
      LOOP(signed short, float);
390
0
      break;
391
0
    case VIPS_FORMAT_UINT:
392
0
      LOOP(unsigned int, float);
393
0
      break;
394
0
    case VIPS_FORMAT_INT:
395
0
      LOOP(signed int, float);
396
0
      break;
397
0
    case VIPS_FORMAT_FLOAT:
398
0
      LOOP(float, float);
399
0
      break;
400
0
    case VIPS_FORMAT_DOUBLE:
401
0
      LOOP(double, double);
402
0
      break;
403
0
    case VIPS_FORMAT_COMPLEX:
404
0
      LOOPCMPLXN(float, float);
405
0
      break;
406
0
    case VIPS_FORMAT_DPCOMPLEX:
407
0
      LOOPCMPLXN(double, double);
408
0
      break;
409
410
0
    default:
411
0
      g_assert_not_reached();
412
8.75k
    }
413
8.75k
}
414
415
/* Save a bit of typing.
416
 */
417
#define UC VIPS_FORMAT_UCHAR
418
#define C VIPS_FORMAT_CHAR
419
#define US VIPS_FORMAT_USHORT
420
#define S VIPS_FORMAT_SHORT
421
#define UI VIPS_FORMAT_UINT
422
#define I VIPS_FORMAT_INT
423
#define F VIPS_FORMAT_FLOAT
424
#define X VIPS_FORMAT_COMPLEX
425
#define D VIPS_FORMAT_DOUBLE
426
#define DX VIPS_FORMAT_DPCOMPLEX
427
428
/* Format doesn't change with linear.
429
 */
430
static const VipsBandFormat vips_linear_format_table[10] = {
431
  /* Band format:  UC C  US S  UI I  F  X  D  DX */
432
  /* Promotion: */ F, F, F, F, F, F, F, X, D, DX
433
};
434
435
static void
436
vips_linear_class_init(VipsLinearClass *class)
437
1
{
438
1
  GObjectClass *gobject_class = G_OBJECT_CLASS(class);
439
1
  VipsObjectClass *object_class = (VipsObjectClass *) class;
440
1
  VipsArithmeticClass *aclass = VIPS_ARITHMETIC_CLASS(class);
441
442
1
  gobject_class->set_property = vips_object_set_property;
443
1
  gobject_class->get_property = vips_object_get_property;
444
445
1
  object_class->nickname = "linear";
446
1
  object_class->description = _("calculate (a * in + b)");
447
1
  object_class->build = vips_linear_build;
448
449
1
  aclass->process_line = vips_linear_buffer;
450
451
1
  vips_arithmetic_set_format_table(aclass, vips_linear_format_table);
452
453
1
  VIPS_ARG_BOXED(class, "a", 110,
454
1
    _("a"),
455
1
    _("Multiply by this"),
456
1
    VIPS_ARGUMENT_REQUIRED_INPUT,
457
1
    G_STRUCT_OFFSET(VipsLinear, a),
458
1
    VIPS_TYPE_ARRAY_DOUBLE);
459
460
1
  VIPS_ARG_BOXED(class, "b", 111,
461
1
    _("b"),
462
1
    _("Add this"),
463
1
    VIPS_ARGUMENT_REQUIRED_INPUT,
464
1
    G_STRUCT_OFFSET(VipsLinear, b),
465
1
    VIPS_TYPE_ARRAY_DOUBLE);
466
467
1
  VIPS_ARG_BOOL(class, "uchar", 112,
468
1
    _("uchar"),
469
1
    _("Output should be uchar"),
470
1
    VIPS_ARGUMENT_OPTIONAL_INPUT,
471
1
    G_STRUCT_OFFSET(VipsLinear, uchar),
472
1
    FALSE);
473
1
}
474
475
static void
476
vips_linear_init(VipsLinear *linear)
477
8.75k
{
478
8.75k
}
479
480
static int
481
vips_linearv(VipsImage *in, VipsImage **out,
482
  const double *a, const double *b, int n, va_list ap)
483
8.75k
{
484
8.75k
  VipsArea *area_a;
485
8.75k
  VipsArea *area_b;
486
8.75k
  int result;
487
488
8.75k
  area_a = VIPS_AREA(vips_array_double_new(a, n));
489
8.75k
  area_b = VIPS_AREA(vips_array_double_new(b, n));
490
491
8.75k
  result = vips_call_split("linear", ap, in, out, area_a, area_b);
492
493
8.75k
  vips_area_unref(area_a);
494
8.75k
  vips_area_unref(area_b);
495
496
8.75k
  return result;
497
8.75k
}
498
499
/**
500
 * vips_linear: (method)
501
 * @in: image to transform
502
 * @out: (out): output image
503
 * @a: (array length=n): array of constants for multiplication
504
 * @b: (array length=n): array of constants for addition
505
 * @n: length of constant arrays
506
 * @...: %NULL-terminated list of optional named arguments
507
 *
508
 * Optional arguments:
509
 *
510
 * * @uchar: output uchar pixels
511
 *
512
 * Pass an image through a linear transform, ie. (@out = @in * @a + @b). Output
513
 * is float for integer input, double for double input, complex for
514
 * complex input and double complex for double complex input. Set @uchar to
515
 * output uchar pixels.
516
 *
517
 * If the arrays of constants have just one element, that constant is used for
518
 * all image bands. If the arrays have more than one element and they have
519
 * the same number of elements as there are bands in the image, then
520
 * one array element is used for each band. If the arrays have more than one
521
 * element and the image only has a single band, the result is a many-band
522
 * image where each band corresponds to one array element.
523
 *
524
 * See also: vips_linear1(), vips_add().
525
 *
526
 * Returns: 0 on success, -1 on error
527
 */
528
int
529
vips_linear(VipsImage *in, VipsImage **out,
530
  const double *a, const double *b, int n, ...)
531
8.75k
{
532
8.75k
  va_list ap;
533
8.75k
  int result;
534
535
8.75k
  va_start(ap, n);
536
8.75k
  result = vips_linearv(in, out, a, b, n, ap);
537
8.75k
  va_end(ap);
538
539
8.75k
  return result;
540
8.75k
}
541
542
/**
543
 * vips_linear1: (method)
544
 * @in: image to transform
545
 * @out: (out): output image
546
 * @a: constant for multiplication
547
 * @b: constant for addition
548
 * @...: %NULL-terminated list of optional named arguments
549
 *
550
 * Optional arguments:
551
 *
552
 * * @uchar: output uchar pixels
553
 *
554
 * Run vips_linear() with a single constant.
555
 *
556
 * See also: vips_linear().
557
 *
558
 * Returns: 0 on success, -1 on error
559
 */
560
int
561
vips_linear1(VipsImage *in, VipsImage **out, double a, double b, ...)
562
0
{
563
0
  va_list ap;
564
0
  int result;
565
566
0
  va_start(ap, b);
567
0
  result = vips_linearv(in, out, &a, &b, 1, ap);
568
0
  va_end(ap);
569
570
0
  return result;
571
0
}