Coverage Report

Created: 2026-10-28 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libvips/libvips/convolution/convsep.c
Line
Count
Source
1
/* convolve twice, rotating the mask
2
 *
3
 * 23/10/13
4
 *  - from vips_convsep()
5
 * 8/5/17
6
 *      - default to float ... int will often lose precision and should not be
7
 *        the default
8
 */
9
10
/*
11
12
  This file is part of VIPS.
13
14
  VIPS is free software; you can redistribute it and/or modify
15
  it under the terms of the GNU Lesser General Public License as published by
16
  the Free Software Foundation; either version 2 of the License, or
17
  (at your option) any later version.
18
19
  This program is distributed in the hope that it will be useful,
20
  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
  GNU Lesser General Public License for more details.
23
24
  You should have received a copy of the GNU Lesser General Public License
25
  along with this program; if not, write to the Free Software
26
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27
  02110-1301  USA
28
29
 */
30
31
/*
32
33
  These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
34
35
 */
36
37
#ifdef HAVE_CONFIG_H
38
#include <config.h>
39
#endif /*HAVE_CONFIG_H*/
40
#include <glib/gi18n-lib.h>
41
42
#include <stdio.h>
43
44
#include <vips/vips.h>
45
46
#include "pconvolution.h"
47
48
typedef struct {
49
  VipsConvolution parent_instance;
50
51
  VipsPrecision precision;
52
  int layers;
53
  int cluster;
54
} VipsConvsep;
55
56
typedef VipsConvolutionClass VipsConvsepClass;
57
58
40
G_DEFINE_TYPE(VipsConvsep, vips_convsep, VIPS_TYPE_CONVOLUTION);
59
40
60
40
static int
61
40
vips_convsep_build(VipsObject *object)
62
46.5k
{
63
46.5k
  VipsObjectClass *class = VIPS_OBJECT_GET_CLASS(object);
64
46.5k
  VipsConvolution *convolution = (VipsConvolution *) object;
65
46.5k
  VipsConvsep *convsep = (VipsConvsep *) object;
66
46.5k
  VipsImage **t = (VipsImage **) vips_object_local_array(object, 4);
67
68
46.5k
  VipsImage *in;
69
70
46.5k
  g_object_set(convsep, "out", vips_image_new(), NULL);
71
72
46.5k
  if (VIPS_OBJECT_CLASS(vips_convsep_parent_class)->build(object))
73
1
    return -1;
74
75
46.5k
  if (vips_check_separable(class->nickname, convolution->M))
76
1
    return -1;
77
78
46.5k
  in = convolution->in;
79
80
46.5k
  if (convsep->precision == VIPS_PRECISION_APPROXIMATE) {
81
7
    if (vips_convasep(in, &t[0], convolution->M,
82
7
        "layers", convsep->layers,
83
7
        NULL))
84
0
      return -1;
85
7
    in = t[0];
86
7
  }
87
46.5k
  else {
88
    /* Take a copy, since we must set the offset.
89
     */
90
46.5k
    if (vips_rot(convolution->M, &t[0], VIPS_ANGLE_D90, NULL) ||
91
46.5k
      vips_copy(t[0], &t[3], NULL))
92
0
      return -1;
93
46.5k
    vips_image_set_double(t[3], "offset", 0);
94
95
46.5k
    if (vips_conv(in, &t[1], convolution->M,
96
46.5k
        "precision", convsep->precision,
97
46.5k
        "layers", convsep->layers,
98
46.5k
        "cluster", convsep->cluster,
99
46.5k
        NULL) ||
100
46.5k
      vips_conv(t[1], &t[2], t[3],
101
46.5k
        "precision", convsep->precision,
102
46.5k
        "layers", convsep->layers,
103
46.5k
        "cluster", convsep->cluster,
104
46.5k
        NULL))
105
0
      return -1;
106
46.5k
    in = t[2];
107
46.5k
  }
108
109
46.5k
  if (vips_image_write(in, convolution->out))
110
0
    return -1;
111
112
46.5k
  return 0;
113
46.5k
}
114
115
static void
116
vips_convsep_class_init(VipsConvsepClass *class)
117
20
{
118
20
  GObjectClass *gobject_class = G_OBJECT_CLASS(class);
119
20
  VipsObjectClass *object_class = (VipsObjectClass *) class;
120
121
20
  gobject_class->set_property = vips_object_set_property;
122
20
  gobject_class->get_property = vips_object_get_property;
123
124
20
  object_class->nickname = "convsep";
125
20
  object_class->description = _("separable convolution operation");
126
20
  object_class->build = vips_convsep_build;
127
128
20
  VIPS_ARG_ENUM(class, "precision", 203,
129
20
    _("Precision"),
130
20
    _("Convolve with this precision"),
131
20
    VIPS_ARGUMENT_OPTIONAL_INPUT,
132
20
    G_STRUCT_OFFSET(VipsConvsep, precision),
133
20
    VIPS_TYPE_PRECISION, VIPS_PRECISION_FLOAT);
134
135
20
  VIPS_ARG_INT(class, "layers", 204,
136
20
    _("Layers"),
137
20
    _("Use this many layers in approximation"),
138
20
    VIPS_ARGUMENT_OPTIONAL_INPUT,
139
20
    G_STRUCT_OFFSET(VipsConvsep, layers),
140
20
    1, 1000, 5);
141
142
20
  VIPS_ARG_INT(class, "cluster", 205,
143
20
    _("Cluster"),
144
20
    _("Cluster lines closer than this in approximation"),
145
20
    VIPS_ARGUMENT_OPTIONAL_INPUT,
146
20
    G_STRUCT_OFFSET(VipsConvsep, cluster),
147
20
    1, 100, 1);
148
20
}
149
150
static void
151
vips_convsep_init(VipsConvsep *convsep)
152
46.5k
{
153
46.5k
  convsep->precision = VIPS_PRECISION_FLOAT;
154
46.5k
  convsep->layers = 5;
155
46.5k
  convsep->cluster = 1;
156
46.5k
}
157
158
/**
159
 * vips_convsep: (method)
160
 * @in: input image
161
 * @out: (out): output image
162
 * @mask: convolution mask
163
 * @...: `NULL`-terminated list of optional named arguments
164
 *
165
 * Perform a separable convolution of @in with @mask.
166
 * See [method@Image.conv] for a detailed description.
167
 *
168
 * The mask must be 1xn or nx1 elements.
169
 *
170
 * The image is convolved twice: once with @mask and then again with @mask
171
 * rotated by 90 degrees. This is much faster for certain types of mask
172
 * (gaussian blur, for example) than doing a full 2D convolution.
173
 *
174
 * ::: tip "Optional arguments"
175
 *     * @precision: [enum@Precision], calculation accuracy
176
 *     * @layers: `gint`, number of layers for approximation
177
 *     * @cluster: `gint`, cluster lines closer than this distance
178
 *
179
 * ::: seealso
180
 *     [method@Image.conv], [ctor@Image.gaussmat].
181
 *
182
 * Returns: 0 on success, -1 on error
183
 */
184
int
185
vips_convsep(VipsImage *in, VipsImage **out, VipsImage *mask, ...)
186
46.5k
{
187
46.5k
  va_list ap;
188
46.5k
  int result;
189
190
46.5k
  va_start(ap, mask);
191
46.5k
  result = vips_call_split("convsep", ap, in, out, mask);
192
46.5k
  va_end(ap);
193
194
46.5k
  return result;
195
46.5k
}