1 /* switch between an array of images
2 *
3 * 28/7/19
4 * - from maplut.c
5 */
6
7 /*
8
9 This file is part of VIPS.
10
11 VIPS is free software; you can redistribute it and/or modify
12 it under the terms of the GNU Lesser General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU Lesser General Public License for more details.
20
21 You should have received a copy of the GNU Lesser General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 02110-1301 USA
25
26 */
27
28 /*
29
30 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
31
32 */
33
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif /*HAVE_CONFIG_H*/
37 #include <vips/intl.h>
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42
43 #include <vips/vips.h>
44 #include <vips/internal.h>
45
46 typedef struct _VipsSwitch {
47 VipsOperation parent_instance;
48
49 VipsArrayImage *tests;
50 VipsImage *out;
51
52 int n;
53
54 } VipsSwitch;
55
56 typedef VipsOperationClass VipsSwitchClass;
57
58 G_DEFINE_TYPE( VipsSwitch, vips_switch, VIPS_TYPE_OPERATION );
59
60 static int
vips_switch_gen(VipsRegion * or,void * seq,void * a,void * b,gboolean * stop)61 vips_switch_gen( VipsRegion *or, void *seq, void *a, void *b,
62 gboolean *stop )
63 {
64 VipsRegion **ar = (VipsRegion **) seq;
65 VipsSwitch *swit = (VipsSwitch *) b;
66 VipsRect *r = &or->valid;
67
68 int x, y, i;
69 VipsPel * restrict q;
70 size_t qls;
71 VipsPel * restrict p[256];
72 size_t ls[256];
73
74 if( vips_reorder_prepare_many( or->im, ar, r ) )
75 return( -1 );
76
77 g_assert( ar[0]->im->BandFmt == VIPS_FORMAT_UCHAR );
78 g_assert( ar[0]->im->Bands == 1 );
79
80 for( i = 0; i < swit->n; i++ ) {
81 p[i] = VIPS_REGION_ADDR( ar[i], r->left, r->top );
82 ls[i] = VIPS_REGION_LSKIP( ar[i] );
83 }
84
85 q = VIPS_REGION_ADDR( or, r->left, r->top );
86 qls = VIPS_REGION_LSKIP( or );
87 for( y = 0; y < r->height; y++ ) {
88 for( x = 0; x < r->width; x++ ) {
89 for( i = 0; i < swit->n; i++ )
90 if( p[i][x] )
91 break;
92
93 q[x] = i;
94 }
95
96 q += qls;
97 for( i = 0; i < swit->n; i++ )
98 p[i] += ls[i];
99 }
100
101 return( 0 );
102 }
103
104 static int
vips_switch_build(VipsObject * object)105 vips_switch_build( VipsObject *object )
106 {
107 VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
108 VipsSwitch *swit = (VipsSwitch *) object;
109
110 VipsImage **tests;
111 VipsImage **decode;
112 VipsImage **format;
113 VipsImage **band;
114 VipsImage **size;
115 int i;
116
117 g_object_set( object, "out", vips_image_new(), NULL );
118
119 if( VIPS_OBJECT_CLASS( vips_switch_parent_class )->build( object ) )
120 return( -1 );
121
122 /* 255 rather than 256, since we want to reserve +1 as the no
123 * match value.
124 */
125 tests = vips_area_get_data( &swit->tests->area,
126 NULL, &swit->n, NULL, NULL );
127 if( swit->n > 255 ||
128 swit->n < 1 ) {
129 vips_error( class->nickname, "%s", _( "bad number of tests" ) );
130 return( -1 );
131 }
132
133 decode = (VipsImage **) vips_object_local_array( object, swit->n );
134 format = (VipsImage **) vips_object_local_array( object, swit->n );
135 band = (VipsImage **) vips_object_local_array( object, swit->n + 1 );
136 size = (VipsImage **) vips_object_local_array( object, swit->n + 1 );
137
138 /* Decode RAD/LABQ etc.
139 */
140 for( i = 0; i < swit->n; i++ )
141 if( vips_image_decode( tests[i], &decode[i] ) )
142 return( -1 );
143 tests = decode;
144
145 /* Must be uchar.
146 */
147 for( i = 0; i < swit->n; i++ )
148 if( vips_cast_uchar( tests[i], &format[i], NULL ) )
149 return( -1 );
150 tests = format;
151
152 /* Images must match in size and bands.
153 */
154 if( vips__bandalike_vec( class->nickname, tests, band, swit->n, 1 ) ||
155 vips__sizealike_vec( band, size, swit->n ) )
156 return( -1 );
157 tests = size;
158
159 if( tests[0]->Bands > 1 ) {
160 vips_error( class->nickname,
161 "%s", _( "test images not 1-band" ) );
162 return( -1 );
163 }
164
165 if( vips_image_pipeline_array( swit->out,
166 VIPS_DEMAND_STYLE_THINSTRIP, tests ) )
167 return( -1 );
168
169 if( vips_image_generate( swit->out,
170 vips_start_many, vips_switch_gen, vips_stop_many,
171 tests, swit ) )
172 return( -1 );
173
174 return( 0 );
175 }
176
177 static void
vips_switch_class_init(VipsSwitchClass * class)178 vips_switch_class_init( VipsSwitchClass *class )
179 {
180 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
181 VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class );
182 VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
183
184 gobject_class->set_property = vips_object_set_property;
185 gobject_class->get_property = vips_object_get_property;
186
187 object_class->nickname = "switch";
188 object_class->description =
189 _( "find the index of the first non-zero pixel in tests" );
190 object_class->build = vips_switch_build;
191
192 operation_class->flags = VIPS_OPERATION_SEQUENTIAL;
193
194 VIPS_ARG_BOXED( class, "tests", 1,
195 _( "Tests" ),
196 _( "Table of images to test" ),
197 VIPS_ARGUMENT_REQUIRED_INPUT,
198 G_STRUCT_OFFSET( VipsSwitch, tests ),
199 VIPS_TYPE_ARRAY_IMAGE );
200
201 VIPS_ARG_IMAGE( class, "out", 2,
202 _( "Output" ),
203 _( "Output image" ),
204 VIPS_ARGUMENT_REQUIRED_OUTPUT,
205 G_STRUCT_OFFSET( VipsSwitch, out ) );
206
207 }
208
209 static void
vips_switch_init(VipsSwitch * swit)210 vips_switch_init( VipsSwitch *swit )
211 {
212 }
213
214 static int
vips_switchv(VipsImage ** tests,VipsImage ** out,int n,va_list ap)215 vips_switchv( VipsImage **tests, VipsImage **out, int n, va_list ap )
216 {
217 VipsArrayImage *tests_array;
218 int result;
219
220 tests_array = vips_array_image_new( tests, n );
221 result = vips_call_split( "switch", ap, tests_array, out );
222 vips_area_unref( VIPS_AREA( tests_array ) );
223
224 return( result );
225 }
226
227 /**
228 * vips_switch: (method)
229 * @tests: (array length=n): test these images
230 * @out: (out): output index image
231 * @n: number of input images
232 * @...: %NULL-terminated list of optional named arguments
233 *
234 * The @tests images are evaluated and at each point the index of the first
235 * non-zero value is written to @out. If all @tests are false, the value
236 * (@n + 1) is written.
237 *
238 * Images in @tests must have one band. They are expanded to the
239 * bounding box of the set of images in @tests, and that size is used for
240 * @out. @tests can have up to 255 elements.
241 *
242 * Combine with vips_case() to make an efficient multi-way vips_ifthenelse().
243 *
244 * See also: vips_maplut(), vips_case(), vips_ifthenelse().
245 *
246 * Returns: 0 on success, -1 on error
247 */
248 int
vips_switch(VipsImage ** tests,VipsImage ** out,int n,...)249 vips_switch( VipsImage **tests, VipsImage **out, int n, ... )
250 {
251 va_list ap;
252 int result;
253
254 va_start( ap, n );
255 result = vips_switchv( tests, out, n, ap );
256 va_end( ap );
257
258 return( result );
259 }
260
261