1 /* load matrix from a file
2 *
3 * 5/12/11
4 * - from csvload.c
5 * 22/2/20
6 * - rewrite for source API
7 */
8
9 /*
10
11 This file is part of VIPS.
12
13 VIPS is free software; you can redistribute it and/or modify
14 it under the terms of the GNU Lesser General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU Lesser General Public License for more details.
22
23 You should have received a copy of the GNU Lesser General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 02110-1301 USA
27
28 */
29
30 /*
31
32 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
33
34 */
35
36 /*
37 #define DEBUG
38 */
39
40 #ifdef HAVE_CONFIG_H
41 #include <config.h>
42 #endif /*HAVE_CONFIG_H*/
43 #include <vips/intl.h>
44
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48
49 #include <vips/vips.h>
50 #include <vips/buf.h>
51 #include <vips/internal.h>
52
53 #include "pforeign.h"
54
55 typedef struct _VipsForeignLoadMatrix {
56 VipsForeignLoad parent_object;
57
58 /* Set by subclasses.
59 */
60 VipsSource *source;
61
62 /* Buffered source.
63 */
64 VipsSbuf *sbuf;
65
66 /* A line of pixels.
67 */
68 double *linebuf;
69
70 } VipsForeignLoadMatrix;
71
72 typedef VipsForeignLoadClass VipsForeignLoadMatrixClass;
73
74 G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadMatrix, vips_foreign_load_matrix,
75 VIPS_TYPE_FOREIGN_LOAD );
76
77 static void
vips_foreign_load_matrix_dispose(GObject * gobject)78 vips_foreign_load_matrix_dispose( GObject *gobject )
79 {
80 VipsForeignLoadMatrix *matrix = (VipsForeignLoadMatrix *) gobject;
81
82 VIPS_UNREF( matrix->source );
83 VIPS_UNREF( matrix->sbuf );
84 VIPS_FREE( matrix->linebuf );
85
86 G_OBJECT_CLASS( vips_foreign_load_matrix_parent_class )->
87 dispose( gobject );
88 }
89
90 static int
vips_foreign_load_matrix_build(VipsObject * object)91 vips_foreign_load_matrix_build( VipsObject *object )
92 {
93 VipsForeignLoadMatrix *matrix = (VipsForeignLoadMatrix *) object;
94
95 if( !(matrix->sbuf = vips_sbuf_new_from_source( matrix->source )) )
96 return( -1 );
97
98 if( VIPS_OBJECT_CLASS( vips_foreign_load_matrix_parent_class )->
99 build( object ) )
100 return( -1 );
101
102 return( 0 );
103 }
104
105 static VipsForeignFlags
vips_foreign_load_matrix_get_flags(VipsForeignLoad * load)106 vips_foreign_load_matrix_get_flags( VipsForeignLoad *load )
107 {
108 return( 0 );
109 }
110
111 /* Parse a header line. Two numbers for width and height, and two optional
112 * numbers for scale and offset.
113 *
114 * We can have scale and no offset, in which case we assume offset = 0.
115 */
116 static int
parse_matrix_header(char * line,int * width,int * height,double * scale,double * offset)117 parse_matrix_header( char *line,
118 int *width, int *height, double *scale, double *offset )
119 {
120 double header[4];
121 char *p, *q;
122 int i;
123
124 for( i = 0, p = line;
125 (q = vips_break_token( p, " \t" )) &&
126 i < 4;
127 i++, p = q )
128 if( vips_strtod( p, &header[i] ) ) {
129 vips_error( "matload",
130 _( "bad number \"%s\"" ), p );
131 return( -1 );
132 }
133
134 if( i < 4 )
135 header[3] = 0.0;
136 if( i < 3 )
137 header[2] = 1.0;
138 if( i < 2 ) {
139 vips_error( "matload", "%s", _( "no width / height" ) );
140 return( -1 );
141 }
142
143 if( VIPS_FLOOR( header[0] ) != header[0] ||
144 VIPS_FLOOR( header[1] ) != header[1] ) {
145 vips_error( "mask2vips", "%s", _( "width / height not int" ) );
146 return( -1 );
147 }
148
149 /* Width / height can be 65536 for a 16-bit LUT, for example.
150 */
151 *width = header[0];
152 *height = header[1];
153 if( *width <= 0 ||
154 *width > 100000 ||
155 *height <= 0 ||
156 *height > 100000 ) {
157 vips_error( "mask2vips",
158 "%s", _( "width / height out of range" ) );
159 return( -1 );
160 }
161 if( header[2] == 0.0 ) {
162 vips_error( "mask2vips", "%s", _( "zero scale" ) );
163 return( -1 );
164 }
165
166 *scale = header[2];
167 *offset = header[3];
168
169 return( 0 );
170 }
171
172 static int
vips_foreign_load_matrix_header(VipsForeignLoad * load)173 vips_foreign_load_matrix_header( VipsForeignLoad *load )
174 {
175 VipsForeignLoadMatrix *matrix = (VipsForeignLoadMatrix *) load;
176
177 char *line;
178 int width;
179 int height;
180 double scale;
181 double offset;
182 int result;
183
184 /* Rewind.
185 */
186 vips_sbuf_unbuffer( matrix->sbuf );
187 if( vips_source_rewind( matrix->source ) )
188 return( -1 );
189
190 line = vips_sbuf_get_line_copy( matrix->sbuf );
191 result = parse_matrix_header( line, &width, &height, &scale, &offset );
192 g_free( line );
193 if( result )
194 return( -1 );
195
196 if( vips_image_pipelinev( load->out,
197 VIPS_DEMAND_STYLE_THINSTRIP, NULL ) )
198 return( -1 );
199 vips_image_init_fields( load->out,
200 width, height, 1,
201 VIPS_FORMAT_DOUBLE,
202 VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 );
203
204 vips_image_set_double( load->out, "scale", scale );
205 vips_image_set_double( load->out, "offset", offset );
206
207 VIPS_SETSTR( load->out->filename,
208 vips_connection_filename( VIPS_CONNECTION( matrix->source ) ) );
209
210 if( !(matrix->linebuf = VIPS_ARRAY( NULL, width, double )) )
211 return( -1 );
212
213 return( 0 );
214 }
215
216 static int
vips_foreign_load_matrix_load(VipsForeignLoad * load)217 vips_foreign_load_matrix_load( VipsForeignLoad *load )
218 {
219 VipsForeignLoadMatrix *matrix = (VipsForeignLoadMatrix *) load;
220 VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
221
222 int x, y;
223
224 if( vips_image_pipelinev( load->real,
225 VIPS_DEMAND_STYLE_THINSTRIP, NULL ) )
226 return( -1 );
227 vips_image_init_fields( load->real,
228 load->out->Xsize, load->out->Ysize, 1,
229 VIPS_FORMAT_DOUBLE,
230 VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 );
231
232 for( y = 0; y < load->real->Ysize; y++ ) {
233 char *line;
234 char *p, *q;
235
236 line = vips_sbuf_get_line_copy( matrix->sbuf );
237
238 for( x = 0, p = line;
239 (q = vips_break_token( p, " \t" )) &&
240 x < load->out->Xsize;
241 x++, p = q )
242 if( vips_strtod( p, &matrix->linebuf[x] ) ) {
243 vips_error( class->nickname,
244 _( "bad number \"%s\"" ), p );
245 g_free( line );
246 return( -1 );
247 }
248
249 g_free( line );
250
251 if( x != load->out->Xsize ) {
252 vips_error( class->nickname,
253 _( "line %d too short" ), y );
254 return( -1 );
255 }
256
257 if( vips_image_write_line( load->real, y,
258 (VipsPel *) matrix->linebuf ) )
259 return( -1 );
260 }
261
262 return( 0 );
263 }
264
265 static void
vips_foreign_load_matrix_class_init(VipsForeignLoadMatrixClass * class)266 vips_foreign_load_matrix_class_init( VipsForeignLoadMatrixClass *class )
267 {
268 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
269 VipsObjectClass *object_class = (VipsObjectClass *) class;
270 VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
271
272 gobject_class->dispose = vips_foreign_load_matrix_dispose;
273
274 object_class->nickname = "matrixload_base";
275 object_class->description = _( "load matrix" );
276 object_class->build = vips_foreign_load_matrix_build;
277
278 load_class->get_flags = vips_foreign_load_matrix_get_flags;
279 load_class->header = vips_foreign_load_matrix_header;
280 load_class->load = vips_foreign_load_matrix_load;
281
282 }
283
284 static void
vips_foreign_load_matrix_init(VipsForeignLoadMatrix * matrix)285 vips_foreign_load_matrix_init( VipsForeignLoadMatrix *matrix )
286 {
287 }
288
289 typedef struct _VipsForeignLoadMatrixFile {
290 VipsForeignLoadMatrix parent_object;
291
292 /* Filename for load.
293 */
294 char *filename;
295
296 } VipsForeignLoadMatrixFile;
297
298 typedef VipsForeignLoadMatrixClass VipsForeignLoadMatrixFileClass;
299
300 G_DEFINE_TYPE( VipsForeignLoadMatrixFile, vips_foreign_load_matrix_file,
301 vips_foreign_load_matrix_get_type() );
302
303 static VipsForeignFlags
vips_foreign_load_matrix_file_get_flags_filename(const char * filename)304 vips_foreign_load_matrix_file_get_flags_filename( const char *filename )
305 {
306 return( 0 );
307 }
308
309 static int
vips_foreign_load_matrix_file_build(VipsObject * object)310 vips_foreign_load_matrix_file_build( VipsObject *object )
311 {
312 VipsForeignLoadMatrix *matrix = (VipsForeignLoadMatrix *) object;
313 VipsForeignLoadMatrixFile *file = (VipsForeignLoadMatrixFile *) object;
314
315 if( file->filename )
316 if( !(matrix->source =
317 vips_source_new_from_file( file->filename )) )
318 return( -1 );
319
320 if( VIPS_OBJECT_CLASS( vips_foreign_load_matrix_file_parent_class )->
321 build( object ) )
322 return( -1 );
323
324 return( 0 );
325 }
326
327 static const char *vips_foreign_load_matrix_suffs[] = {
328 ".mat",
329 NULL
330 };
331
332 static gboolean
vips_foreign_load_matrix_file_is_a(const char * filename)333 vips_foreign_load_matrix_file_is_a( const char *filename )
334 {
335 unsigned char line[80];
336 guint64 bytes;
337 int width;
338 int height;
339 double scale;
340 double offset;
341 int result;
342
343 if( (bytes = vips__get_bytes( filename, line, 79 )) <= 0 )
344 return( FALSE );
345 line[bytes] = '\0';
346
347 vips_error_freeze();
348 result = parse_matrix_header( (char *) line,
349 &width, &height, &scale, &offset );
350 vips_error_thaw();
351
352 return( result == 0 );
353 }
354
355 static void
vips_foreign_load_matrix_file_class_init(VipsForeignLoadMatrixFileClass * class)356 vips_foreign_load_matrix_file_class_init(
357 VipsForeignLoadMatrixFileClass *class )
358 {
359 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
360 VipsObjectClass *object_class = (VipsObjectClass *) class;
361 VipsForeignClass *foreign_class = (VipsForeignClass *) class;
362 VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
363
364 gobject_class->set_property = vips_object_set_property;
365 gobject_class->get_property = vips_object_get_property;
366
367 object_class->nickname = "matrixload";
368 object_class->build = vips_foreign_load_matrix_file_build;
369
370 foreign_class->suffs = vips_foreign_load_matrix_suffs;
371
372 load_class->is_a = vips_foreign_load_matrix_file_is_a;
373 load_class->get_flags_filename =
374 vips_foreign_load_matrix_file_get_flags_filename;
375
376 VIPS_ARG_STRING( class, "filename", 1,
377 _( "Filename" ),
378 _( "Filename to load from" ),
379 VIPS_ARGUMENT_REQUIRED_INPUT,
380 G_STRUCT_OFFSET( VipsForeignLoadMatrixFile, filename ),
381 NULL );
382
383 }
384
385 static void
vips_foreign_load_matrix_file_init(VipsForeignLoadMatrixFile * file)386 vips_foreign_load_matrix_file_init( VipsForeignLoadMatrixFile *file )
387 {
388 }
389
390 typedef struct _VipsForeignLoadMatrixSource {
391 VipsForeignLoadMatrix parent_object;
392
393 VipsSource *source;
394
395 } VipsForeignLoadMatrixSource;
396
397 typedef VipsForeignLoadMatrixClass VipsForeignLoadMatrixSourceClass;
398
399 G_DEFINE_TYPE( VipsForeignLoadMatrixSource, vips_foreign_load_matrix_source,
400 vips_foreign_load_matrix_get_type() );
401
402 static int
vips_foreign_load_matrix_source_build(VipsObject * object)403 vips_foreign_load_matrix_source_build( VipsObject *object )
404 {
405 VipsForeignLoadMatrix *matrix = (VipsForeignLoadMatrix *) object;
406 VipsForeignLoadMatrixSource *source =
407 (VipsForeignLoadMatrixSource *) object;
408
409 if( source->source ) {
410 matrix->source = source->source;
411 g_object_ref( matrix->source );
412 }
413
414 if( VIPS_OBJECT_CLASS( vips_foreign_load_matrix_source_parent_class )->
415 build( object ) )
416 return( -1 );
417
418 return( 0 );
419 }
420
421 static int
vips_foreign_load_matrix_source_is_a_source(VipsSource * source)422 vips_foreign_load_matrix_source_is_a_source( VipsSource *source )
423 {
424 unsigned char *data;
425 size_t bytes_read;
426 char line[80];
427 int width;
428 int height;
429 double scale;
430 double offset;
431 int result;
432
433 if( (bytes_read = vips_source_sniff_at_most( source,
434 &data, 79 )) <= 0 )
435 return( FALSE );
436 vips_strncpy( line, (const char *) data, 80 );
437
438 vips_error_freeze();
439 result = parse_matrix_header( line,
440 &width, &height, &scale, &offset );
441 vips_error_thaw();
442
443 return( result == 0 );
444 }
445
446 static void
vips_foreign_load_matrix_source_class_init(VipsForeignLoadMatrixFileClass * class)447 vips_foreign_load_matrix_source_class_init(
448 VipsForeignLoadMatrixFileClass *class )
449 {
450 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
451 VipsObjectClass *object_class = (VipsObjectClass *) class;
452 VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
453 VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
454
455 gobject_class->set_property = vips_object_set_property;
456 gobject_class->get_property = vips_object_get_property;
457
458 object_class->nickname = "matrixload_source";
459 object_class->build = vips_foreign_load_matrix_source_build;
460
461 operation_class->flags = VIPS_OPERATION_NOCACHE;
462
463 load_class->is_a_source = vips_foreign_load_matrix_source_is_a_source;
464
465 VIPS_ARG_OBJECT( class, "source", 1,
466 _( "Source" ),
467 _( "Source to load from" ),
468 VIPS_ARGUMENT_REQUIRED_INPUT,
469 G_STRUCT_OFFSET( VipsForeignLoadMatrixSource, source ),
470 VIPS_TYPE_SOURCE );
471
472 }
473
474 static void
vips_foreign_load_matrix_source_init(VipsForeignLoadMatrixSource * source)475 vips_foreign_load_matrix_source_init( VipsForeignLoadMatrixSource *source )
476 {
477 }
478
479 /**
480 * vips_matrixload:
481 * @filename: file to load
482 * @out: (out): output image
483 * @...: %NULL-terminated list of optional named arguments
484 *
485 * Reads a matrix from a file.
486 *
487 * Matrix files have a simple format that's supposed to be easy to create with
488 * a text editor or a spreadsheet.
489 *
490 * The first line has four numbers for width, height, scale and
491 * offset (scale and offset may be omitted, in which case they default to 1.0
492 * and 0.0). Scale must be non-zero. Width and height must be positive
493 * integers. The numbers are separated by any mixture of spaces, commas,
494 * tabs and quotation marks ("). The scale and offset fields may be
495 * floating-point, and must use '.'
496 * as a decimal separator.
497 *
498 * Subsequent lines each hold one row of matrix data, with numbers again
499 * separated by any mixture of spaces, commas,
500 * tabs and quotation marks ("). The numbers may be floating-point, and must
501 * use '.'
502 * as a decimal separator.
503 *
504 * Extra characters at the ends of lines or at the end of the file are
505 * ignored.
506 *
507 * See also: vips_matrixload().
508 *
509 * Returns: 0 on success, -1 on error.
510 */
511 int
vips_matrixload(const char * filename,VipsImage ** out,...)512 vips_matrixload( const char *filename, VipsImage **out, ... )
513 {
514 va_list ap;
515 int result;
516
517 va_start( ap, out );
518 result = vips_call_split( "matrixload", ap, filename, out );
519 va_end( ap );
520
521 return( result );
522 }
523
524 /**
525 * vips_matrixload_source:
526 * @source: source to load
527 * @out: (out): output image
528 * @...: %NULL-terminated list of optional named arguments
529 *
530 * Exactly as vips_matrixload(), but read from a source.
531 *
532 * See also: vips_matrixload().
533 *
534 * Returns: 0 on success, -1 on error.
535 */
536 int
vips_matrixload_source(VipsSource * source,VipsImage ** out,...)537 vips_matrixload_source( VipsSource *source, VipsImage **out, ... )
538 {
539 va_list ap;
540 int result;
541
542 va_start( ap, out );
543 result = vips_call_split( "matrixload_source", ap, source, out );
544 va_end( ap );
545
546 return( result );
547 }
548
549