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