Coverage Report

Created: 2025-01-28 06:34

/src/libvips/libvips/arithmetic/profile.c
Line
Count
Source (jump to first uncovered line)
1
/* find image profiles
2
 *
3
 * 11/8/99 JC
4
 *  - from im_cntlines()
5
 * 22/4/04
6
 *  - now outputs horizontal/vertical image
7
 * 9/11/10
8
 *  - any image format, any number of bands
9
 *  - gtk-doc
10
 * 21/9/13
11
 *  - rewrite as a class
12
 *  - output h and v profile in one pass
13
 *  - partial
14
 *  - output is int rather than ushort
15
 */
16
17
/*
18
19
  This file is part of VIPS.
20
21
  VIPS is free software; you can redistribute it and/or modify
22
  it under the terms of the GNU Lesser General Public License as published by
23
  the Free Software Foundation; either version 2 of the License, or
24
  (at your option) any later version.
25
26
  This program is distributed in the hope that it will be useful,
27
  but WITHOUT ANY WARRANTY; without even the implied warranty of
28
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29
  GNU Lesser General Public License for more details.
30
31
  You should have received a copy of the GNU Lesser General Public License
32
  along with this program; if not, write to the Free Software
33
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
34
  02110-1301  USA
35
36
 */
37
38
/*
39
40
  These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
41
42
 */
43
44
#ifdef HAVE_CONFIG_H
45
#include <config.h>
46
#endif /*HAVE_CONFIG_H*/
47
#include <glib/gi18n-lib.h>
48
49
#include <stdio.h>
50
#include <stdlib.h>
51
#include <string.h>
52
53
#include <vips/vips.h>
54
55
#include "statistic.h"
56
57
struct _Edges;
58
59
typedef struct {
60
  /* Horizontal array: Ys of top-most non-zero pixel.
61
   */
62
  int *column_edges;
63
64
  /* Vertical array: Xs of left-most non-zero pixel.
65
   */
66
  int *row_edges;
67
68
} Edges;
69
70
typedef struct _VipsProfile {
71
  VipsStatistic parent_instance;
72
73
  /* Main edge set. Threads accumulate to this.
74
   */
75
  Edges *edges;
76
77
  /* Write profiles here.
78
   */
79
  VipsImage *columns;
80
  VipsImage *rows;
81
82
} VipsProfile;
83
84
typedef VipsStatisticClass VipsProfileClass;
85
86
G_DEFINE_TYPE(VipsProfile, vips_profile, VIPS_TYPE_STATISTIC);
87
88
static Edges *
89
edges_new(VipsProfile *profile)
90
0
{
91
0
  VipsStatistic *statistic = VIPS_STATISTIC(profile);
92
0
  VipsImage *in = statistic->ready;
93
94
0
  Edges *edges;
95
0
  int i;
96
97
0
  if (!(edges = VIPS_NEW(profile, Edges)))
98
0
    return NULL;
99
0
  edges->column_edges = VIPS_ARRAY(profile, in->Xsize * in->Bands, int);
100
0
  edges->row_edges = VIPS_ARRAY(profile, in->Ysize * in->Bands, int);
101
0
  if (!edges->column_edges ||
102
0
    !edges->row_edges)
103
0
    return NULL;
104
105
0
  for (i = 0; i < in->Xsize * in->Bands; i++)
106
0
    edges->column_edges[i] = in->Ysize;
107
0
  for (i = 0; i < in->Ysize * in->Bands; i++)
108
0
    edges->row_edges[i] = in->Xsize;
109
110
0
  return edges;
111
0
}
112
113
static int
114
vips_profile_build(VipsObject *object)
115
0
{
116
0
  VipsObjectClass *class = VIPS_OBJECT_GET_CLASS(object);
117
0
  VipsStatistic *statistic = VIPS_STATISTIC(object);
118
0
  VipsProfile *profile = (VipsProfile *) object;
119
120
0
  int y;
121
122
0
  if (statistic->in &&
123
0
    vips_check_noncomplex(class->nickname, statistic->in))
124
0
    return -1;
125
126
0
  g_object_set(object,
127
0
    "columns", vips_image_new(),
128
0
    "rows", vips_image_new(),
129
0
    NULL);
130
131
  /* main edge set made on first thread start.
132
   */
133
134
0
  if (VIPS_OBJECT_CLASS(vips_profile_parent_class)->build(object))
135
0
    return -1;
136
137
  /* Make the output image.
138
   */
139
0
  if (vips_image_pipelinev(profile->columns,
140
0
      VIPS_DEMAND_STYLE_ANY, statistic->ready, NULL) ||
141
0
    vips_image_pipelinev(profile->rows,
142
0
      VIPS_DEMAND_STYLE_ANY, statistic->ready, NULL))
143
0
    return -1;
144
0
  profile->columns->Ysize = 1;
145
0
  profile->columns->BandFmt = VIPS_FORMAT_INT;
146
0
  profile->columns->Type = VIPS_INTERPRETATION_HISTOGRAM;
147
0
  profile->rows->Xsize = 1;
148
0
  profile->rows->BandFmt = VIPS_FORMAT_INT;
149
0
  profile->rows->Type = VIPS_INTERPRETATION_HISTOGRAM;
150
151
0
  if (vips_image_write_line(profile->columns, 0,
152
0
      (VipsPel *) profile->edges->column_edges))
153
0
    return -1;
154
0
  for (y = 0; y < profile->rows->Ysize; y++)
155
0
    if (vips_image_write_line(profile->rows, y,
156
0
        (VipsPel *) profile->edges->row_edges +
157
0
          y * VIPS_IMAGE_SIZEOF_PEL(profile->rows)))
158
0
      return -1;
159
160
0
  return 0;
161
0
}
162
163
/* New edge accumulator.
164
 */
165
static void *
166
vips_profile_start(VipsStatistic *statistic)
167
0
{
168
0
  VipsProfile *profile = (VipsProfile *) statistic;
169
170
  /* Make the main hist, if necessary.
171
   */
172
0
  if (!profile->edges)
173
0
    profile->edges = edges_new(profile);
174
175
0
  return (void *) edges_new(profile);
176
0
}
177
178
/* We do this a lot.
179
 */
180
0
#define MINBANG(V, C) ((V) = VIPS_MIN(V, C))
181
182
/* Add a line of pixels.
183
 */
184
#define ADD_PIXELS(TYPE) \
185
0
  { \
186
0
    TYPE *p; \
187
0
    int *column_edges; \
188
0
    int *row_edges; \
189
0
\
190
0
    p = (TYPE *) in; \
191
0
    column_edges = edges->column_edges + x * nb; \
192
0
    row_edges = edges->row_edges + y * nb; \
193
0
    for (i = 0; i < n; i++) { \
194
0
      for (j = 0; j < nb; j++) { \
195
0
        if (p[j]) { \
196
0
          MINBANG(column_edges[j], y); \
197
0
          MINBANG(row_edges[j], x + i); \
198
0
        } \
199
0
      } \
200
0
\
201
0
      p += nb; \
202
0
      column_edges += nb; \
203
0
    } \
204
0
  }
205
206
/* Add a region to a profile.
207
 */
208
static int
209
vips_profile_scan(VipsStatistic *statistic, void *seq,
210
  int x, int y, void *in, int n)
211
0
{
212
0
  int nb = statistic->ready->Bands;
213
0
  Edges *edges = (Edges *) seq;
214
0
  int i, j;
215
216
0
  switch (statistic->ready->BandFmt) {
217
0
  case VIPS_FORMAT_UCHAR:
218
0
    ADD_PIXELS(guchar);
219
0
    break;
220
221
0
  case VIPS_FORMAT_CHAR:
222
0
    ADD_PIXELS(char);
223
0
    break;
224
225
0
  case VIPS_FORMAT_USHORT:
226
0
    ADD_PIXELS(gushort);
227
0
    break;
228
229
0
  case VIPS_FORMAT_SHORT:
230
0
    ADD_PIXELS(short);
231
0
    break;
232
233
0
  case VIPS_FORMAT_UINT:
234
0
    ADD_PIXELS(guint);
235
0
    break;
236
237
0
  case VIPS_FORMAT_INT:
238
0
    ADD_PIXELS(int);
239
0
    break;
240
241
0
  case VIPS_FORMAT_FLOAT:
242
0
    ADD_PIXELS(float);
243
0
    break;
244
245
0
  case VIPS_FORMAT_DOUBLE:
246
0
    ADD_PIXELS(double);
247
0
    break;
248
249
0
  default:
250
0
    g_assert_not_reached();
251
0
  }
252
253
0
  return 0;
254
0
}
255
256
/* Join a sub-profile onto the main profile.
257
 */
258
static int
259
vips_profile_stop(VipsStatistic *statistic, void *seq)
260
0
{
261
0
  VipsProfile *profile = (VipsProfile *) statistic;
262
0
  Edges *edges = profile->edges;
263
0
  Edges *sub_edges = (Edges *) seq;
264
0
  VipsImage *in = statistic->ready;
265
266
0
  int i;
267
268
0
  for (i = 0; i < in->Xsize * in->Bands; i++)
269
0
    MINBANG(edges->column_edges[i], sub_edges->column_edges[i]);
270
271
0
  for (i = 0; i < in->Ysize * in->Bands; i++)
272
0
    MINBANG(edges->row_edges[i], sub_edges->row_edges[i]);
273
274
  /* Blank out sub-profile to make sure we can't add it again.
275
   */
276
0
  sub_edges->row_edges = NULL;
277
0
  sub_edges->column_edges = NULL;
278
279
0
  return 0;
280
0
}
281
282
static void
283
vips_profile_class_init(VipsProfileClass *class)
284
1
{
285
1
  GObjectClass *gobject_class = (GObjectClass *) class;
286
1
  VipsObjectClass *object_class = (VipsObjectClass *) class;
287
1
  VipsStatisticClass *sclass = VIPS_STATISTIC_CLASS(class);
288
289
1
  gobject_class->set_property = vips_object_set_property;
290
1
  gobject_class->get_property = vips_object_get_property;
291
292
1
  object_class->nickname = "profile";
293
1
  object_class->description = _("find image profiles");
294
1
  object_class->build = vips_profile_build;
295
296
1
  sclass->start = vips_profile_start;
297
1
  sclass->scan = vips_profile_scan;
298
1
  sclass->stop = vips_profile_stop;
299
300
1
  VIPS_ARG_IMAGE(class, "columns", 100,
301
1
    _("Columns"),
302
1
    _("First non-zero pixel in column"),
303
1
    VIPS_ARGUMENT_REQUIRED_OUTPUT,
304
1
    G_STRUCT_OFFSET(VipsProfile, columns));
305
306
1
  VIPS_ARG_IMAGE(class, "rows", 101,
307
1
    _("Rows"),
308
1
    _("First non-zero pixel in row"),
309
1
    VIPS_ARGUMENT_REQUIRED_OUTPUT,
310
1
    G_STRUCT_OFFSET(VipsProfile, rows));
311
1
}
312
313
static void
314
vips_profile_init(VipsProfile *profile)
315
0
{
316
0
}
317
318
/**
319
 * vips_profile: (method)
320
 * @in: input image
321
 * @columns: (out): distances from top edge
322
 * @rows: (out): distances from left edge
323
 * @...: %NULL-terminated list of optional named arguments
324
 *
325
 * vips_profile() searches inward from the edge of @in and finds the
326
 * first non-zero pixel. Pixels in @columns have the distance from the top edge
327
 * to the first non-zero pixel in that column, @rows has the distance from the
328
 * left edge to the first non-zero pixel in that row.
329
 *
330
 * See also: vips_project(), vips_hist_find().
331
 *
332
 * Returns: 0 on success, -1 on error
333
 */
334
int
335
vips_profile(VipsImage *in, VipsImage **columns, VipsImage **rows, ...)
336
0
{
337
0
  va_list ap;
338
0
  int result;
339
340
0
  va_start(ap, rows);
341
0
  result = vips_call_split("profile", ap, in, columns, rows);
342
0
  va_end(ap);
343
344
0
  return result;
345
0
}