1 /* load nifti from a file
2 *
3 * 29/6/18
4 * - from fitsload.c
5 * 9/9/19
6 * - use double for all floating point scalar metadata, like other loaders
7 * - remove stray use of "n" property
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 /*
38 #define DEBUG
39 #define VIPS_DEBUG
40 */
41
42 /* TODO
43 *
44 * - for uncompressed images, we could do direct mapping of the input
45 * - perhaps we could stream compressed images? but only if ext is defined at
46 * the start of the file
47 * (yes, file format is magic number, 348-byte header, extension data,
48 * pixel data, then all gz'd)
49 * - we could use the much faster byteswap in glib?
50 * - I have not been able to test the ext stuff :(
51 *
52 * There should be at least a x2 speedup possible.
53 */
54
55 #ifdef HAVE_CONFIG_H
56 #include <config.h>
57 #endif /*HAVE_CONFIG_H*/
58 #include <vips/intl.h>
59
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63
64 #include <vips/vips.h>
65 #include <vips/debug.h>
66 #include <vips/internal.h>
67
68 #ifdef HAVE_NIFTI
69
70 #include <nifti1_io.h>
71
72 #include "pforeign.h"
73
74 typedef struct _VipsForeignLoadNifti {
75 VipsForeignLoad parent_object;
76
77 /* Source to load from (set by subclasses).
78 */
79 VipsSource *source;
80
81 /* Filename from source.
82 */
83 const char *filename;
84
85 /* The NIFTI image loaded to memory.
86 */
87 nifti_image *nim;
88
89 /* Wrap this VipsImage around the NIFTI pointer, then redirect read
90 * requests to that. Saves a copy.
91 */
92 VipsImage *memory;
93
94 } VipsForeignLoadNifti;
95
96 typedef VipsForeignLoadClass VipsForeignLoadNiftiClass;
97
98 G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadNifti, vips_foreign_load_nifti,
99 VIPS_TYPE_FOREIGN_LOAD );
100
101 static void
vips_foreign_load_nifti_dispose(GObject * gobject)102 vips_foreign_load_nifti_dispose( GObject *gobject )
103 {
104 VipsForeignLoadNifti *nifti = (VipsForeignLoadNifti *) gobject;
105
106 VIPS_UNREF( nifti->source );
107 VIPS_UNREF( nifti->memory );
108 VIPS_FREEF( nifti_image_free, nifti->nim );
109
110 G_OBJECT_CLASS( vips_foreign_load_nifti_parent_class )->
111 dispose( gobject );
112 }
113
114 static int
vips_foreign_load_nifti_build(VipsObject * object)115 vips_foreign_load_nifti_build( VipsObject *object )
116 {
117 VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
118 VipsForeignLoadNifti *nifti = (VipsForeignLoadNifti *) object;
119
120 /* We can only open source which have an associated filename, since
121 * the nifti library works in terms of filenames.
122 */
123 if( nifti->source ) {
124 VipsConnection *connection = VIPS_CONNECTION( nifti->source );
125
126 const char *filename;
127
128 if( !vips_source_is_file( nifti->source ) ||
129 !(filename = vips_connection_filename( connection )) ) {
130 vips_error( class->nickname,
131 "%s", _( "no filename available" ) );
132 return( -1 );
133 }
134
135 nifti->filename = filename;
136 }
137
138 if( VIPS_OBJECT_CLASS( vips_foreign_load_nifti_parent_class )->
139 build( object ) )
140 return( -1 );
141
142 return( 0 );
143 }
144
145 /* Map DT_* datatype values to VipsBandFormat.
146 */
147 typedef struct _VipsForeignDT2Vips {
148 int datatype;
149 VipsBandFormat fmt;
150 } VipsForeignDT2Vips ;
151
152 static VipsForeignDT2Vips vips_foreign_nifti_DT2Vips[] = {
153 { DT_UINT8, VIPS_FORMAT_UCHAR },
154 { DT_INT8, VIPS_FORMAT_CHAR },
155 { DT_UINT16, VIPS_FORMAT_USHORT },
156 { DT_INT16, VIPS_FORMAT_SHORT },
157 { DT_UINT32, VIPS_FORMAT_UINT },
158 { DT_INT32, VIPS_FORMAT_INT },
159 { DT_FLOAT32, VIPS_FORMAT_FLOAT },
160 { DT_FLOAT64, VIPS_FORMAT_DOUBLE },
161 { DT_COMPLEX64, VIPS_FORMAT_COMPLEX },
162 { DT_COMPLEX128, VIPS_FORMAT_DPCOMPLEX },
163 { DT_RGB, VIPS_FORMAT_UCHAR },
164 { DT_RGBA32, VIPS_FORMAT_UCHAR }
165 };
166
167 VipsBandFormat
vips__foreign_nifti_datatype2BandFmt(int datatype)168 vips__foreign_nifti_datatype2BandFmt( int datatype )
169 {
170 int i;
171
172 for( i = 0; i < VIPS_NUMBER( vips_foreign_nifti_DT2Vips ); i++ )
173 if( vips_foreign_nifti_DT2Vips[i].datatype == datatype )
174 return( vips_foreign_nifti_DT2Vips[i].fmt );
175
176 return( VIPS_FORMAT_NOTSET );
177 }
178
179 int
vips__foreign_nifti_BandFmt2datatype(VipsBandFormat fmt)180 vips__foreign_nifti_BandFmt2datatype( VipsBandFormat fmt )
181 {
182 int i;
183
184 for( i = 0; i < VIPS_NUMBER( vips_foreign_nifti_DT2Vips ); i++ )
185 if( vips_foreign_nifti_DT2Vips[i].fmt == fmt )
186 return( vips_foreign_nifti_DT2Vips[i].datatype );
187
188 return( -1 );
189 }
190
191 /* All the header fields we attach as metadata.
192 */
193 typedef struct _VipsForeignNiftiFields {
194 char *name;
195 GType type;
196 glong offset;
197 } VipsForeignNiftiFields;
198
199 static VipsForeignNiftiFields vips_foreign_nifti_fields[] = {
200 /* The first 8 must be the dims[] fields, see
201 * vips_foreign_save_nifti_make_nim().
202 */
203 { "ndim", G_TYPE_INT, G_STRUCT_OFFSET( nifti_image, ndim ) },
204 { "nx", G_TYPE_INT, G_STRUCT_OFFSET( nifti_image, nx ) },
205 { "ny", G_TYPE_INT, G_STRUCT_OFFSET( nifti_image, ny ) },
206 { "nz", G_TYPE_INT, G_STRUCT_OFFSET( nifti_image, nz ) },
207 { "nt", G_TYPE_INT, G_STRUCT_OFFSET( nifti_image, nt ) },
208 { "nu", G_TYPE_INT, G_STRUCT_OFFSET( nifti_image, nu ) },
209 { "nv", G_TYPE_INT, G_STRUCT_OFFSET( nifti_image, nv ) },
210 { "nw", G_TYPE_INT, G_STRUCT_OFFSET( nifti_image, nw ) },
211
212 { "dx", G_TYPE_DOUBLE, G_STRUCT_OFFSET( nifti_image, dx ) },
213 { "dy", G_TYPE_DOUBLE, G_STRUCT_OFFSET( nifti_image, dy ) },
214 { "dz", G_TYPE_DOUBLE, G_STRUCT_OFFSET( nifti_image, dz ) },
215 { "dt", G_TYPE_DOUBLE, G_STRUCT_OFFSET( nifti_image, dt ) },
216 { "du", G_TYPE_DOUBLE, G_STRUCT_OFFSET( nifti_image, du ) },
217 { "dv", G_TYPE_DOUBLE, G_STRUCT_OFFSET( nifti_image, dv ) },
218 { "dw", G_TYPE_DOUBLE, G_STRUCT_OFFSET( nifti_image, dw ) },
219
220 { "scl_slope", G_TYPE_DOUBLE,
221 G_STRUCT_OFFSET( nifti_image, scl_slope ) },
222 { "scl_inter", G_TYPE_DOUBLE,
223 G_STRUCT_OFFSET( nifti_image, scl_inter ) },
224
225 { "cal_min", G_TYPE_DOUBLE,
226 G_STRUCT_OFFSET( nifti_image, cal_min ) },
227 { "cal_max", G_TYPE_DOUBLE,
228 G_STRUCT_OFFSET( nifti_image, cal_max ) },
229
230 { "qform_code", G_TYPE_INT,
231 G_STRUCT_OFFSET( nifti_image, qform_code ) },
232 { "sform_code", G_TYPE_INT,
233 G_STRUCT_OFFSET( nifti_image, sform_code ) },
234
235 { "freq_dim", G_TYPE_INT,
236 G_STRUCT_OFFSET( nifti_image, freq_dim ) },
237 { "phase_dim", G_TYPE_INT,
238 G_STRUCT_OFFSET( nifti_image, phase_dim ) },
239 { "slice_dim", G_TYPE_INT,
240 G_STRUCT_OFFSET( nifti_image, slice_dim ) },
241
242 { "slice_code", G_TYPE_INT,
243 G_STRUCT_OFFSET( nifti_image, slice_code ) },
244 { "slice_start", G_TYPE_INT,
245 G_STRUCT_OFFSET( nifti_image, slice_start ) },
246 { "slice_end", G_TYPE_INT,
247 G_STRUCT_OFFSET( nifti_image, slice_end ) },
248 { "slice_duration", G_TYPE_DOUBLE,
249 G_STRUCT_OFFSET( nifti_image, slice_duration ) },
250
251 { "quatern_b", G_TYPE_DOUBLE,
252 G_STRUCT_OFFSET( nifti_image, quatern_b ) },
253 { "quatern_c", G_TYPE_DOUBLE,
254 G_STRUCT_OFFSET( nifti_image, quatern_c ) },
255 { "quatern_d", G_TYPE_DOUBLE,
256 G_STRUCT_OFFSET( nifti_image, quatern_d ) },
257 { "qoffset_x", G_TYPE_DOUBLE,
258 G_STRUCT_OFFSET( nifti_image, qoffset_x ) },
259 { "qoffset_y", G_TYPE_DOUBLE,
260 G_STRUCT_OFFSET( nifti_image, qoffset_y ) },
261 { "qoffset_z", G_TYPE_DOUBLE,
262 G_STRUCT_OFFSET( nifti_image, qoffset_z ) },
263 { "qfac", G_TYPE_DOUBLE,
264 G_STRUCT_OFFSET( nifti_image, qfac ) },
265
266 { "sto_xyz00", G_TYPE_DOUBLE,
267 G_STRUCT_OFFSET( nifti_image, sto_xyz.m[0][0] ) },
268 { "sto_xyz01", G_TYPE_DOUBLE,
269 G_STRUCT_OFFSET( nifti_image, sto_xyz.m[0][1] ) },
270 { "sto_xyz02", G_TYPE_DOUBLE,
271 G_STRUCT_OFFSET( nifti_image, sto_xyz.m[0][2] ) },
272 { "sto_xyz03", G_TYPE_DOUBLE,
273 G_STRUCT_OFFSET( nifti_image, sto_xyz.m[0][3] ) },
274
275 { "sto_xyz10", G_TYPE_DOUBLE,
276 G_STRUCT_OFFSET( nifti_image, sto_xyz.m[1][0] ) },
277 { "sto_xyz11", G_TYPE_DOUBLE,
278 G_STRUCT_OFFSET( nifti_image, sto_xyz.m[1][1] ) },
279 { "sto_xyz12", G_TYPE_DOUBLE,
280 G_STRUCT_OFFSET( nifti_image, sto_xyz.m[1][2] ) },
281 { "sto_xyz13", G_TYPE_DOUBLE,
282 G_STRUCT_OFFSET( nifti_image, sto_xyz.m[1][3] ) },
283
284 { "sto_xyz20", G_TYPE_DOUBLE,
285 G_STRUCT_OFFSET( nifti_image, sto_xyz.m[2][0] ) },
286 { "sto_xyz21", G_TYPE_DOUBLE,
287 G_STRUCT_OFFSET( nifti_image, sto_xyz.m[2][1] ) },
288 { "sto_xyz22", G_TYPE_DOUBLE,
289 G_STRUCT_OFFSET( nifti_image, sto_xyz.m[2][2] ) },
290 { "sto_xyz23", G_TYPE_DOUBLE,
291 G_STRUCT_OFFSET( nifti_image, sto_xyz.m[2][3] ) },
292
293 { "sto_xyz30", G_TYPE_DOUBLE,
294 G_STRUCT_OFFSET( nifti_image, sto_xyz.m[3][0] ) },
295 { "sto_xyz31", G_TYPE_DOUBLE,
296 G_STRUCT_OFFSET( nifti_image, sto_xyz.m[3][1] ) },
297 { "sto_xyz32", G_TYPE_DOUBLE,
298 G_STRUCT_OFFSET( nifti_image, sto_xyz.m[3][2] ) },
299 { "sto_xyz33", G_TYPE_DOUBLE,
300 G_STRUCT_OFFSET( nifti_image, sto_xyz.m[3][3] ) },
301
302 { "toffset", G_TYPE_DOUBLE,
303 G_STRUCT_OFFSET( nifti_image, toffset ) },
304
305 { "xyz_units", G_TYPE_INT,
306 G_STRUCT_OFFSET( nifti_image, xyz_units ) },
307 { "time_units", G_TYPE_INT,
308 G_STRUCT_OFFSET( nifti_image, time_units ) },
309
310 { "nifti_type", G_TYPE_INT,
311 G_STRUCT_OFFSET( nifti_image, nifti_type ) },
312 { "intent_code", G_TYPE_INT,
313 G_STRUCT_OFFSET( nifti_image, intent_code ) },
314 { "intent_p1", G_TYPE_DOUBLE,
315 G_STRUCT_OFFSET( nifti_image, intent_p1 ) },
316 { "intent_p2", G_TYPE_DOUBLE,
317 G_STRUCT_OFFSET( nifti_image, intent_p2 ) },
318 { "intent_p3", G_TYPE_DOUBLE,
319 G_STRUCT_OFFSET( nifti_image, intent_p3 ) },
320 };
321
322 void *
vips__foreign_nifti_map(VipsNiftiMapFn fn,void * a,void * b)323 vips__foreign_nifti_map( VipsNiftiMapFn fn, void *a, void *b )
324 {
325 int i;
326 void *result;
327
328 for( i = 0; i < VIPS_NUMBER( vips_foreign_nifti_fields ); i++ ) {
329 GValue value = { 0 };
330
331 g_value_init( &value, vips_foreign_nifti_fields[i].type );
332 result = fn( vips_foreign_nifti_fields[i].name, &value,
333 vips_foreign_nifti_fields[i].offset, a, b );
334 g_value_unset( &value );
335
336 if( result )
337 return( result );
338 }
339
340 return( NULL );
341 }
342
343 /* How I wish glib had something like this :( Just implement the ones we need
344 * for vips_foreign_nifti_fields above.
345 */
346 static void
vips_gvalue_read(GValue * value,void * p)347 vips_gvalue_read( GValue *value, void *p )
348 {
349 switch( G_VALUE_TYPE( value ) ) {
350 case G_TYPE_INT:
351 g_value_set_int( value, *((int *) p) );
352 break;
353
354 case G_TYPE_DOUBLE:
355 /* We set as double rather than float, as things like pyvips
356 * expect double for metadata items.
357 */
358 g_value_set_double( value, *((float *) p) );
359 break;
360
361 default:
362 g_warning( "vips_gvalue_read: unsupported GType %s",
363 g_type_name( G_VALUE_TYPE( value ) ) );
364 }
365 }
366
367 static void *
vips_foreign_load_nifti_set(const char * name,GValue * value,glong offset,void * a,void * b)368 vips_foreign_load_nifti_set( const char *name, GValue *value, glong offset,
369 void *a, void *b )
370 {
371 nifti_image *nim = (nifti_image *) a;
372 VipsImage *out = VIPS_IMAGE( b );
373
374 char vips_name[256];
375
376 vips_gvalue_read( value, (gpointer) nim + offset );
377 vips_snprintf( vips_name, 256, "nifti-%s", name );
378 vips_image_set( out, vips_name, value );
379
380 return( NULL );
381 }
382
383 static int
vips_foreign_load_nifti_set_header(VipsForeignLoadNifti * nifti,nifti_image * nim,VipsImage * out)384 vips_foreign_load_nifti_set_header( VipsForeignLoadNifti *nifti,
385 nifti_image *nim, VipsImage *out )
386 {
387 VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( nifti );
388
389 guint width;
390 guint height;
391 guint bands;
392 VipsBandFormat fmt;
393 double xres;
394 double yres;
395 int i;
396 char txt[256];
397
398 if( nim->ndim < 1 ||
399 nim->ndim > 7 ) {
400 vips_error( class->nickname,
401 _( "%d-dimensional images not supported" ),
402 nim->ndim );
403 return( 0 );
404 }
405 for( i = 1; i < 8 && i < nim->ndim + 1; i++ ) {
406 if( nim->dim[i] <= 0 ) {
407 vips_error( class->nickname,
408 "%s", _( "invalid dimension" ) );
409 return( 0 );
410 }
411
412 /* If we have several images in a dimension, the spacing must
413 * be non-zero or we'll get a /0 error in resolution
414 * calculation.
415 */
416 if( nim->dim[i] > 1 &&
417 nim->pixdim[i] == 0 ) {
418 vips_error( class->nickname,
419 "%s", _( "invalid resolution" ) );
420 return( 0 );
421 }
422 }
423
424 /* Unfold higher dimensions vertically. bands is updated below for
425 * DT_RGB. Be careful to avoid height going over 2^31.
426 */
427 bands = 1;
428 width = (guint) nim->nx;
429 height = (guint) nim->ny;
430 for( i = 3; i < 8 && i < nim->ndim + 1; i++ )
431 if( !g_uint_checked_mul( &height, height, nim->dim[i] ) ) {
432 vips_error( class->nickname,
433 "%s", _( "dimension overflow" ) );
434 return( 0 );
435 }
436 if( height > INT_MAX ) {
437 vips_error( class->nickname, "%s", _( "dimension overflow" ) );
438 return( 0 );
439 }
440
441 fmt = vips__foreign_nifti_datatype2BandFmt( nim->datatype );
442 if( fmt == VIPS_FORMAT_NOTSET ) {
443 vips_error( class->nickname,
444 _( "datatype %d not supported" ), nim->datatype );
445 return( -1 );
446 }
447
448 if( nim->datatype == DT_RGB )
449 bands = 3;
450 if( nim->datatype == DT_RGBA32 )
451 bands = 4;
452
453 /* We fold y and z together, so they must have the same resolution..
454 */
455 xres = 1.0;
456 yres = 1.0;
457 if( nim->nz == 1 ||
458 nim->dz == nim->dy )
459 switch( nim->xyz_units ) {
460 case NIFTI_UNITS_METER:
461 xres = 1000.0 / nim->dx;
462 yres = 1000.0 / nim->dy;
463 break;
464 case NIFTI_UNITS_MM:
465 xres = 1.0 / nim->dx;
466 yres = 1.0 / nim->dy;
467 break;
468
469 case NIFTI_UNITS_MICRON:
470 xres = 1.0 / (1000.0 * nim->dx);
471 yres = 1.0 / (1000.0 * nim->dy);
472 break;
473
474 default:
475 break;
476 }
477
478 #ifdef DEBUG
479 printf( "get_vips_properties: width = %d\n", width );
480 printf( "get_vips_properties: height = %d\n", height );
481 printf( "get_vips_properties: bands = %d\n", bands );
482 printf( "get_vips_properties: fmt = %d\n", fmt );
483 #endif /*DEBUG*/
484
485 vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL );
486 vips_image_init_fields( out,
487 width, height, bands, fmt,
488 VIPS_CODING_NONE,
489 bands == 1 ?
490 VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_sRGB,
491 xres, yres );
492
493 /* Set some vips metadata for every nifti header field.
494 */
495 if( vips__foreign_nifti_map( vips_foreign_load_nifti_set, nim, out ) )
496 return( -1 );
497
498 /* One byte longer than the spec to leave space for any extra
499 * '\0' termination.
500 */
501 vips_strncpy( txt, nim->intent_name, 17 );
502 vips_image_set_string( out, "nifti-intent_name", txt );
503 vips_strncpy( txt, nim->descrip, 81 );
504 vips_image_set_string( out, "nifti-descrip", txt );
505
506 for( i = 0; i < nim->num_ext; i++ ) {
507 nifti1_extension *ext = &nim->ext_list[i];
508
509 vips_snprintf( txt, 256, "nifti-ext-%d-%d", i, ext->ecode );
510 vips_image_set_blob_copy( out, txt, ext->edata, ext->esize );
511 }
512
513 vips_image_set_int( out, VIPS_META_PAGE_HEIGHT, nim->ny );
514
515 return( 0 );
516 }
517
518 static int
vips_foreign_load_nifti_header(VipsForeignLoad * load)519 vips_foreign_load_nifti_header( VipsForeignLoad *load )
520 {
521 VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
522 VipsForeignLoadNifti *nifti = (VipsForeignLoadNifti *) load;
523
524 /* We can't use the (much faster) nifti_read_header() since it just
525 * reads the 348 bytes of the analyze struct and does not read any of
526 * the extension fields.
527 */
528
529 /* FALSE means don't read data, just the header. Use
530 * nifti_image_load() later to pull the data in.
531 */
532 if( !(nifti->nim = nifti_image_read( nifti->filename, FALSE )) ) {
533 vips_error( class->nickname,
534 "%s", _( "unable to read NIFTI header" ) );
535 return( 0 );
536 }
537
538 if( vips_foreign_load_nifti_set_header( nifti,
539 nifti->nim, load->out ) ) {
540 return( -1 );
541 }
542
543 VIPS_SETSTR( load->out->filename, nifti->filename );
544
545 return( 0 );
546 }
547
548 static int
vips_foreign_load_nifti_load(VipsForeignLoad * load)549 vips_foreign_load_nifti_load( VipsForeignLoad *load )
550 {
551 VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
552 VipsForeignLoadNifti *nifti = (VipsForeignLoadNifti *) load;
553
554 #ifdef DEBUG
555 printf( "vips_foreign_load_nifti_load: loading image\n" );
556 #endif /*DEBUG*/
557
558 /* We just read the entire image to memory.
559 */
560 if( nifti_image_load( nifti->nim ) ) {
561 vips_error( class->nickname,
562 "%s", _( "unable to load NIFTI file" ) );
563 return( -1 );
564 }
565
566 if( !(nifti->memory = vips_image_new_from_memory(
567 nifti->nim->data, VIPS_IMAGE_SIZEOF_IMAGE( load->out ),
568 load->out->Xsize, load->out->Ysize,
569 load->out->Bands, load->out->BandFmt )) )
570 return( -1 );
571
572 if( vips_image_write( nifti->memory, load->real ) )
573 return( -1 );
574
575 return( 0 );
576 }
577
578 static void
vips_foreign_load_nifti_class_init(VipsForeignLoadNiftiClass * class)579 vips_foreign_load_nifti_class_init( VipsForeignLoadNiftiClass *class )
580 {
581 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
582 VipsObjectClass *object_class = (VipsObjectClass *) class;
583 VipsForeignClass *foreign_class = (VipsForeignClass *) class;
584 VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
585
586 gobject_class->dispose = vips_foreign_load_nifti_dispose;
587 gobject_class->set_property = vips_object_set_property;
588 gobject_class->get_property = vips_object_get_property;
589
590 object_class->nickname = "niftiload_base";
591 object_class->description = _( "load a NIFTI image" );
592 object_class->build = vips_foreign_load_nifti_build;
593
594 /* is_a() is not that quick ... lower the priority.
595 */
596 foreign_class->priority = -50;
597
598 load_class->header = vips_foreign_load_nifti_header;
599 load_class->load = vips_foreign_load_nifti_load;
600 }
601
602 static void
vips_foreign_load_nifti_init(VipsForeignLoadNifti * nifti)603 vips_foreign_load_nifti_init( VipsForeignLoadNifti *nifti )
604 {
605 }
606
607 typedef struct _VipsForeignLoadNiftiFile {
608 VipsForeignLoadNifti parent_object;
609
610 /* Filename for load.
611 */
612 char *filename;
613
614 } VipsForeignLoadNiftiFile;
615
616 typedef VipsForeignLoadNiftiClass VipsForeignLoadNiftiFileClass;
617
618 G_DEFINE_TYPE( VipsForeignLoadNiftiFile, vips_foreign_load_nifti_file,
619 vips_foreign_load_nifti_get_type() );
620
621 static int
vips_foreign_load_nifti_file_build(VipsObject * object)622 vips_foreign_load_nifti_file_build( VipsObject *object )
623 {
624 VipsForeignLoadNifti *nifti = (VipsForeignLoadNifti *) object;
625 VipsForeignLoadNiftiFile *file = (VipsForeignLoadNiftiFile *) object;
626
627 if( file->filename &&
628 !(nifti->source =
629 vips_source_new_from_file( file->filename )) )
630 return( -1 );
631
632 if( VIPS_OBJECT_CLASS( vips_foreign_load_nifti_file_parent_class )->
633 build( object ) )
634 return( -1 );
635
636 return( 0 );
637 }
638
639 const char *vips_foreign_nifti_suffs[] = {
640 ".nii", ".nii.gz",
641 ".hdr", ".hdr.gz",
642 ".img", ".img.gz",
643 ".nia", ".nia.gz",
644 NULL
645 };
646
647 static int
vips_foreign_load_nifti_is_a(const char * filename)648 vips_foreign_load_nifti_is_a( const char *filename )
649 {
650 char *hfile;
651 znzFile fp;
652 nifti_1_header nhdr;
653
654 /* Unfortunately is_nifti_file() is very slow and produces lots of
655 * output. We have to make our own.
656 */
657
658 if( !(hfile = nifti_findhdrname( filename )) )
659 return( 0 );
660
661 fp = znzopen( hfile, "rb", nifti_is_gzfile( hfile ));
662 if( znz_isnull( fp ) ) {
663 free( hfile );
664 return( 0 );
665 }
666 free( hfile );
667
668 (void) znzread( &nhdr, 1, sizeof( nhdr ), fp );
669
670 znzclose( fp );
671
672 /* Test for sanity both ways around. There's a thing to test for byte
673 * order in niftilib, but it's static :(
674 */
675 if( nifti_hdr_looks_good( &nhdr ) )
676 return( 1 );
677 swap_nifti_header( &nhdr, FALSE );
678 if( nifti_hdr_looks_good( &nhdr ) )
679 return( 1 );
680
681 return( 0 );
682 }
683
684 static void
vips_foreign_load_nifti_file_class_init(VipsForeignLoadNiftiFileClass * class)685 vips_foreign_load_nifti_file_class_init(
686 VipsForeignLoadNiftiFileClass *class )
687 {
688 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
689 VipsObjectClass *object_class = (VipsObjectClass *) class;
690 VipsForeignClass *foreign_class = (VipsForeignClass *) class;
691 VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
692
693 gobject_class->set_property = vips_object_set_property;
694 gobject_class->get_property = vips_object_get_property;
695
696 object_class->nickname = "niftiload";
697 object_class->description = _( "load NIfTI volume" );
698 object_class->build = vips_foreign_load_nifti_file_build;
699
700 foreign_class->suffs = vips_foreign_nifti_suffs;
701
702 load_class->is_a = vips_foreign_load_nifti_is_a;
703
704 VIPS_ARG_STRING( class, "filename", 1,
705 _( "Filename" ),
706 _( "Filename to load from" ),
707 VIPS_ARGUMENT_REQUIRED_INPUT,
708 G_STRUCT_OFFSET( VipsForeignLoadNiftiFile, filename ),
709 NULL );
710
711 }
712
713 static void
vips_foreign_load_nifti_file_init(VipsForeignLoadNiftiFile * nifti)714 vips_foreign_load_nifti_file_init( VipsForeignLoadNiftiFile *nifti )
715 {
716 }
717
718 typedef struct _VipsForeignLoadNiftiSource {
719 VipsForeignLoadNifti parent_object;
720
721 /* Load from a source.
722 */
723 VipsSource *source;
724
725 } VipsForeignLoadNiftiSource;
726
727 typedef VipsForeignLoadNiftiClass VipsForeignLoadNiftiSourceClass;
728
729 G_DEFINE_TYPE( VipsForeignLoadNiftiSource, vips_foreign_load_nifti_source,
730 vips_foreign_load_nifti_get_type() );
731
732 static int
vips_foreign_load_nifti_source_build(VipsObject * object)733 vips_foreign_load_nifti_source_build( VipsObject *object )
734 {
735 VipsForeignLoadNifti *nifti = (VipsForeignLoadNifti *) object;
736 VipsForeignLoadNiftiSource *source =
737 (VipsForeignLoadNiftiSource *) object;
738
739 if( source->source ) {
740 nifti->source = source->source;
741 g_object_ref( nifti->source );
742 }
743
744 if( VIPS_OBJECT_CLASS(
745 vips_foreign_load_nifti_source_parent_class )->
746 build( object ) )
747 return( -1 );
748
749 return( 0 );
750 }
751
752 static gboolean
vips_foreign_load_nifti_source_is_a_source(VipsSource * source)753 vips_foreign_load_nifti_source_is_a_source( VipsSource *source )
754 {
755 VipsConnection *connection = VIPS_CONNECTION( source );
756
757 const char *filename;
758
759 return( vips_source_is_file( source ) &&
760 (filename = vips_connection_filename( connection )) &&
761 vips_foreign_load_nifti_is_a( filename ) );
762 }
763
764 static void
vips_foreign_load_nifti_source_class_init(VipsForeignLoadNiftiSourceClass * class)765 vips_foreign_load_nifti_source_class_init(
766 VipsForeignLoadNiftiSourceClass *class )
767 {
768 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
769 VipsObjectClass *object_class = (VipsObjectClass *) class;
770 VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
771 VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
772
773 gobject_class->set_property = vips_object_set_property;
774 gobject_class->get_property = vips_object_get_property;
775
776 object_class->nickname = "niftiload_source";
777 object_class->description = _( "load NIfTI volumes" );
778 object_class->build = vips_foreign_load_nifti_source_build;
779
780 operation_class->flags = VIPS_OPERATION_NOCACHE;
781
782 load_class->is_a_source =
783 vips_foreign_load_nifti_source_is_a_source;
784
785 VIPS_ARG_OBJECT( class, "source", 1,
786 _( "Source" ),
787 _( "Source to load from" ),
788 VIPS_ARGUMENT_REQUIRED_INPUT,
789 G_STRUCT_OFFSET( VipsForeignLoadNiftiSource, source ),
790 VIPS_TYPE_SOURCE );
791
792 }
793
794 static void
vips_foreign_load_nifti_source_init(VipsForeignLoadNiftiSource * nifti)795 vips_foreign_load_nifti_source_init(
796 VipsForeignLoadNiftiSource *nifti )
797 {
798 }
799
800 #endif /*HAVE_NIFTI*/
801
802 /**
803 * vips_niftiload:
804 * @filename: file to load
805 * @out: (out): decompressed image
806 * @...: %NULL-terminated list of optional named arguments
807 *
808 * Read a NIFTI image file into a VIPS image.
809 *
810 * NIFTI metadata is attached with the "nifti-" prefix.
811 *
812 * See also: vips_image_new_from_file().
813 *
814 * Returns: 0 on success, -1 on error.
815 */
816 int
vips_niftiload(const char * filename,VipsImage ** out,...)817 vips_niftiload( const char *filename, VipsImage **out, ... )
818 {
819 va_list ap;
820 int result;
821
822 va_start( ap, out );
823 result = vips_call_split( "niftiload", ap, filename, out );
824 va_end( ap );
825
826 return( result );
827 }
828
829 /**
830 * vips_niftiload_source:
831 * @source: source to load from
832 * @out: (out): decompressed image
833 * @...: %NULL-terminated list of optional named arguments
834 *
835 * Exactly as vips_niftiload(), but read from a source.
836 *
837 * Returns: 0 on success, -1 on error.
838 */
839 int
vips_niftiload_source(VipsSource * source,VipsImage ** out,...)840 vips_niftiload_source( VipsSource *source, VipsImage **out, ... )
841 {
842 va_list ap;
843 int result;
844
845 va_start( ap, out );
846 result = vips_call_split( "niftiload_source", ap, source, out );
847 va_end( ap );
848
849 return( result );
850 }
851