7b2729adfa
- use double for all floating point scalar metadata, like other loaders - remove use of stray "n" property
653 lines
17 KiB
C
653 lines
17 KiB
C
/* load nifti from a file
|
|
*
|
|
* 29/6/18
|
|
* - from fitsload.c
|
|
* 9/9/19
|
|
* - use double for all floating point scalar metadata, like other loaders
|
|
* - remove stray use of "n" property
|
|
*/
|
|
|
|
/*
|
|
|
|
This file is part of VIPS.
|
|
|
|
VIPS is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
02110-1301 USA
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
|
|
|
*/
|
|
|
|
/*
|
|
#define DEBUG
|
|
#define VIPS_DEBUG
|
|
*/
|
|
|
|
/* TODO
|
|
*
|
|
* - for uncompressed images, we could do direct mapping of the input
|
|
* - perhaps we could stream compressed images? but only if ext is defined at
|
|
* the start of the file
|
|
* (yes, file format is magic number, 348-byte header, extension data,
|
|
* pixel data, then all gz'd)
|
|
* - we could use the much faster byteswap in glib?
|
|
* - I have not been able to test the ext stuff :(
|
|
*
|
|
* There should be at least a x2 speedup possible.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif /*HAVE_CONFIG_H*/
|
|
#include <vips/intl.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <vips/vips.h>
|
|
#include <vips/debug.h>
|
|
#include <vips/internal.h>
|
|
|
|
#ifdef HAVE_NIFTI
|
|
|
|
#include <nifti1_io.h>
|
|
|
|
#include "pforeign.h"
|
|
|
|
typedef struct _VipsForeignLoadNifti {
|
|
VipsForeignLoad parent_object;
|
|
|
|
/* Filename for load.
|
|
*/
|
|
char *filename;
|
|
|
|
/* The NIFTI image loaded to memory.
|
|
*/
|
|
nifti_image *nim;
|
|
|
|
/* Wrap this VipsImage around the NIFTI pointer, then redirect read
|
|
* requests to that. Saves a copy.
|
|
*/
|
|
VipsImage *memory;
|
|
|
|
} VipsForeignLoadNifti;
|
|
|
|
typedef VipsForeignLoadClass VipsForeignLoadNiftiClass;
|
|
|
|
G_DEFINE_TYPE( VipsForeignLoadNifti, vips_foreign_load_nifti,
|
|
VIPS_TYPE_FOREIGN_LOAD );
|
|
|
|
static void
|
|
vips_foreign_load_nifti_dispose( GObject *gobject )
|
|
{
|
|
VipsForeignLoadNifti *nifti = (VipsForeignLoadNifti *) gobject;
|
|
|
|
VIPS_UNREF( nifti->memory );
|
|
VIPS_FREEF( nifti_image_free, nifti->nim );
|
|
|
|
G_OBJECT_CLASS( vips_foreign_load_nifti_parent_class )->
|
|
dispose( gobject );
|
|
}
|
|
|
|
static int
|
|
vips_foreign_load_nifti_is_a( const char *filename )
|
|
{
|
|
char *hfile;
|
|
znzFile fp;
|
|
nifti_1_header nhdr;
|
|
|
|
/* Unfortunately is_nifti_file() is very slow and produces lots of
|
|
* output. We have to make our own.
|
|
*/
|
|
|
|
if( !(hfile = nifti_findhdrname( filename )) )
|
|
return( 0 );
|
|
|
|
fp = znzopen( hfile, "rb", nifti_is_gzfile( hfile ));
|
|
if( znz_isnull( fp ) ) {
|
|
free( hfile );
|
|
return( 0 );
|
|
}
|
|
free( hfile );
|
|
|
|
(void) znzread( &nhdr, 1, sizeof( nhdr ), fp );
|
|
|
|
znzclose( fp );
|
|
|
|
/* Test for sanity both ways around. There's a thing to test for byte
|
|
* order in niftilib, but it's static :(
|
|
*/
|
|
if( nifti_hdr_looks_good( &nhdr ) )
|
|
return( 1 );
|
|
swap_nifti_header( &nhdr, FALSE );
|
|
if( nifti_hdr_looks_good( &nhdr ) )
|
|
return( 1 );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Map DT_* datatype values to VipsBandFormat.
|
|
*/
|
|
typedef struct _VipsForeignDT2Vips {
|
|
int datatype;
|
|
VipsBandFormat fmt;
|
|
} VipsForeignDT2Vips ;
|
|
|
|
static VipsForeignDT2Vips vips_foreign_nifti_DT2Vips[] = {
|
|
{ DT_UINT8, VIPS_FORMAT_UCHAR },
|
|
{ DT_INT8, VIPS_FORMAT_CHAR },
|
|
{ DT_UINT16, VIPS_FORMAT_USHORT },
|
|
{ DT_INT16, VIPS_FORMAT_SHORT },
|
|
{ DT_UINT32, VIPS_FORMAT_UINT },
|
|
{ DT_INT32, VIPS_FORMAT_INT },
|
|
{ DT_FLOAT32, VIPS_FORMAT_FLOAT },
|
|
{ DT_FLOAT64, VIPS_FORMAT_DOUBLE },
|
|
{ DT_COMPLEX64, VIPS_FORMAT_COMPLEX },
|
|
{ DT_COMPLEX128, VIPS_FORMAT_DPCOMPLEX },
|
|
{ DT_RGB, VIPS_FORMAT_UCHAR },
|
|
{ DT_RGBA32, VIPS_FORMAT_UCHAR }
|
|
};
|
|
|
|
VipsBandFormat
|
|
vips__foreign_nifti_datatype2BandFmt( int datatype )
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < VIPS_NUMBER( vips_foreign_nifti_DT2Vips ); i++ )
|
|
if( vips_foreign_nifti_DT2Vips[i].datatype == datatype )
|
|
return( vips_foreign_nifti_DT2Vips[i].fmt );
|
|
|
|
return( VIPS_FORMAT_NOTSET );
|
|
}
|
|
|
|
int
|
|
vips__foreign_nifti_BandFmt2datatype( VipsBandFormat fmt )
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < VIPS_NUMBER( vips_foreign_nifti_DT2Vips ); i++ )
|
|
if( vips_foreign_nifti_DT2Vips[i].fmt == fmt )
|
|
return( vips_foreign_nifti_DT2Vips[i].datatype );
|
|
|
|
return( -1 );
|
|
}
|
|
|
|
/* All the header fields we attach as metadata.
|
|
*/
|
|
typedef struct _VipsForeignNiftiFields {
|
|
char *name;
|
|
GType type;
|
|
glong offset;
|
|
} VipsForeignNiftiFields;
|
|
|
|
static VipsForeignNiftiFields vips_foreign_nifti_fields[] = {
|
|
/* The first 8 must be the dims[] fields, see
|
|
* vips_foreign_save_nifti_make_nim().
|
|
*/
|
|
{ "ndim", G_TYPE_INT, G_STRUCT_OFFSET( nifti_image, ndim ) },
|
|
{ "nx", G_TYPE_INT, G_STRUCT_OFFSET( nifti_image, nx ) },
|
|
{ "ny", G_TYPE_INT, G_STRUCT_OFFSET( nifti_image, ny ) },
|
|
{ "nz", G_TYPE_INT, G_STRUCT_OFFSET( nifti_image, nz ) },
|
|
{ "nt", G_TYPE_INT, G_STRUCT_OFFSET( nifti_image, nt ) },
|
|
{ "nu", G_TYPE_INT, G_STRUCT_OFFSET( nifti_image, nu ) },
|
|
{ "nv", G_TYPE_INT, G_STRUCT_OFFSET( nifti_image, nv ) },
|
|
{ "nw", G_TYPE_INT, G_STRUCT_OFFSET( nifti_image, nw ) },
|
|
|
|
{ "dx", G_TYPE_DOUBLE, G_STRUCT_OFFSET( nifti_image, dx ) },
|
|
{ "dy", G_TYPE_DOUBLE, G_STRUCT_OFFSET( nifti_image, dy ) },
|
|
{ "dz", G_TYPE_DOUBLE, G_STRUCT_OFFSET( nifti_image, dz ) },
|
|
{ "dt", G_TYPE_DOUBLE, G_STRUCT_OFFSET( nifti_image, dt ) },
|
|
{ "du", G_TYPE_DOUBLE, G_STRUCT_OFFSET( nifti_image, du ) },
|
|
{ "dv", G_TYPE_DOUBLE, G_STRUCT_OFFSET( nifti_image, dv ) },
|
|
{ "dw", G_TYPE_DOUBLE, G_STRUCT_OFFSET( nifti_image, dw ) },
|
|
|
|
{ "scl_slope", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, scl_slope ) },
|
|
{ "scl_inter", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, scl_inter ) },
|
|
|
|
{ "cal_min", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, cal_min ) },
|
|
{ "cal_max", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, cal_max ) },
|
|
|
|
{ "qform_code", G_TYPE_INT,
|
|
G_STRUCT_OFFSET( nifti_image, qform_code ) },
|
|
{ "sform_code", G_TYPE_INT,
|
|
G_STRUCT_OFFSET( nifti_image, sform_code ) },
|
|
|
|
{ "freq_dim", G_TYPE_INT,
|
|
G_STRUCT_OFFSET( nifti_image, freq_dim ) },
|
|
{ "phase_dim", G_TYPE_INT,
|
|
G_STRUCT_OFFSET( nifti_image, phase_dim ) },
|
|
{ "slice_dim", G_TYPE_INT,
|
|
G_STRUCT_OFFSET( nifti_image, slice_dim ) },
|
|
|
|
{ "slice_code", G_TYPE_INT,
|
|
G_STRUCT_OFFSET( nifti_image, slice_code ) },
|
|
{ "slice_start", G_TYPE_INT,
|
|
G_STRUCT_OFFSET( nifti_image, slice_start ) },
|
|
{ "slice_end", G_TYPE_INT,
|
|
G_STRUCT_OFFSET( nifti_image, slice_end ) },
|
|
{ "slice_duration", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, slice_duration ) },
|
|
|
|
{ "quatern_b", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, quatern_b ) },
|
|
{ "quatern_c", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, quatern_c ) },
|
|
{ "quatern_d", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, quatern_d ) },
|
|
{ "qoffset_x", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, qoffset_x ) },
|
|
{ "qoffset_y", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, qoffset_y ) },
|
|
{ "qoffset_z", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, qoffset_z ) },
|
|
{ "qfac", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, qfac ) },
|
|
|
|
{ "sto_xyz00", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[0][0] ) },
|
|
{ "sto_xyz01", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[0][1] ) },
|
|
{ "sto_xyz02", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[0][2] ) },
|
|
{ "sto_xyz03", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[0][3] ) },
|
|
|
|
{ "sto_xyz10", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[1][0] ) },
|
|
{ "sto_xyz11", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[1][1] ) },
|
|
{ "sto_xyz12", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[1][2] ) },
|
|
{ "sto_xyz13", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[1][3] ) },
|
|
|
|
{ "sto_xyz20", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[2][0] ) },
|
|
{ "sto_xyz21", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[2][1] ) },
|
|
{ "sto_xyz22", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[2][2] ) },
|
|
{ "sto_xyz23", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[2][3] ) },
|
|
|
|
{ "sto_xyz30", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[3][0] ) },
|
|
{ "sto_xyz31", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[3][1] ) },
|
|
{ "sto_xyz32", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[3][2] ) },
|
|
{ "sto_xyz33", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[3][3] ) },
|
|
|
|
{ "toffset", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, toffset ) },
|
|
|
|
{ "xyz_units", G_TYPE_INT,
|
|
G_STRUCT_OFFSET( nifti_image, xyz_units ) },
|
|
{ "time_units", G_TYPE_INT,
|
|
G_STRUCT_OFFSET( nifti_image, time_units ) },
|
|
|
|
{ "nifti_type", G_TYPE_INT,
|
|
G_STRUCT_OFFSET( nifti_image, nifti_type ) },
|
|
{ "intent_code", G_TYPE_INT,
|
|
G_STRUCT_OFFSET( nifti_image, intent_code ) },
|
|
{ "intent_p1", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, intent_p1 ) },
|
|
{ "intent_p2", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, intent_p2 ) },
|
|
{ "intent_p3", G_TYPE_DOUBLE,
|
|
G_STRUCT_OFFSET( nifti_image, intent_p3 ) },
|
|
};
|
|
|
|
void *
|
|
vips__foreign_nifti_map( VipsNiftiMapFn fn, void *a, void *b )
|
|
{
|
|
int i;
|
|
void *result;
|
|
|
|
for( i = 0; i < VIPS_NUMBER( vips_foreign_nifti_fields ); i++ ) {
|
|
GValue value = { 0 };
|
|
|
|
g_value_init( &value, vips_foreign_nifti_fields[i].type );
|
|
result = fn( vips_foreign_nifti_fields[i].name, &value,
|
|
vips_foreign_nifti_fields[i].offset, a, b );
|
|
g_value_unset( &value );
|
|
|
|
if( result )
|
|
return( result );
|
|
}
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
/* How I wish glib had something like this :( Just implement the ones we need
|
|
* for vips_foreign_nifti_fields above.
|
|
*/
|
|
static void
|
|
vips_gvalue_read( GValue *value, void *p )
|
|
{
|
|
switch( G_VALUE_TYPE( value ) ) {
|
|
case G_TYPE_INT:
|
|
g_value_set_int( value, *((int *) p) );
|
|
break;
|
|
|
|
case G_TYPE_DOUBLE:
|
|
/* We set as double rather than float, as things like pyvips
|
|
* expect double for metadata items.
|
|
*/
|
|
g_value_set_double( value, *((float *) p) );
|
|
break;
|
|
|
|
default:
|
|
g_warning( "vips_gvalue_read: unsupported GType %s",
|
|
g_type_name( G_VALUE_TYPE( value ) ) );
|
|
}
|
|
}
|
|
|
|
static void *
|
|
vips_foreign_load_nifti_set( const char *name, GValue *value, glong offset,
|
|
void *a, void *b )
|
|
{
|
|
nifti_image *nim = (nifti_image *) a;
|
|
VipsImage *out = VIPS_IMAGE( b );
|
|
|
|
char vips_name[256];
|
|
|
|
vips_gvalue_read( value, (gpointer) nim + offset );
|
|
vips_snprintf( vips_name, 256, "nifti-%s", name );
|
|
vips_image_set( out, vips_name, value );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
static int
|
|
vips_foreign_load_nifti_set_header( VipsForeignLoadNifti *nifti,
|
|
nifti_image *nim, VipsImage *out )
|
|
{
|
|
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( nifti );
|
|
|
|
guint width;
|
|
guint height;
|
|
guint bands;
|
|
VipsBandFormat fmt;
|
|
double xres;
|
|
double yres;
|
|
int i;
|
|
char txt[256];
|
|
|
|
if( nim->ndim < 1 ||
|
|
nim->ndim > 7 ) {
|
|
vips_error( class->nickname,
|
|
_( "%d-dimensional images not supported" ),
|
|
nim->ndim );
|
|
return( 0 );
|
|
}
|
|
for( i = 1; i < 8 && i < nim->ndim + 1; i++ ) {
|
|
if( nim->dim[i] <= 0 ) {
|
|
vips_error( class->nickname,
|
|
"%s", _( "invalid dimension" ) );
|
|
return( 0 );
|
|
}
|
|
|
|
/* If we have several images in a dimension, the spacing must
|
|
* be non-zero or we'll get a /0 error in resolution
|
|
* calculation.
|
|
*/
|
|
if( nim->dim[i] > 1 &&
|
|
nim->pixdim[i] == 0 ) {
|
|
vips_error( class->nickname,
|
|
"%s", _( "invalid resolution" ) );
|
|
return( 0 );
|
|
}
|
|
}
|
|
|
|
/* Unfold higher dimensions vertically. bands is updated below for
|
|
* DT_RGB. Be careful to avoid height going over 2^31.
|
|
*/
|
|
bands = 1;
|
|
width = (guint) nim->nx;
|
|
height = (guint) nim->ny;
|
|
for( i = 3; i < 8 && i < nim->ndim + 1; i++ )
|
|
if( !g_uint_checked_mul( &height, height, nim->dim[i] ) ) {
|
|
vips_error( class->nickname,
|
|
"%s", _( "dimension overflow" ) );
|
|
return( 0 );
|
|
}
|
|
if( height > INT_MAX ) {
|
|
vips_error( class->nickname, "%s", _( "dimension overflow" ) );
|
|
return( 0 );
|
|
}
|
|
|
|
fmt = vips__foreign_nifti_datatype2BandFmt( nim->datatype );
|
|
if( fmt == VIPS_FORMAT_NOTSET ) {
|
|
vips_error( class->nickname,
|
|
_( "datatype %d not supported" ), nim->datatype );
|
|
return( -1 );
|
|
}
|
|
|
|
if( nim->datatype == DT_RGB )
|
|
bands = 3;
|
|
if( nim->datatype == DT_RGBA32 )
|
|
bands = 4;
|
|
|
|
/* We fold y and z together, so they must have the same resolution..
|
|
*/
|
|
xres = 1.0;
|
|
yres = 1.0;
|
|
if( nim->nz == 1 ||
|
|
nim->dz == nim->dy )
|
|
switch( nim->xyz_units ) {
|
|
case NIFTI_UNITS_METER:
|
|
xres = 1000.0 / nim->dx;
|
|
yres = 1000.0 / nim->dy;
|
|
break;
|
|
case NIFTI_UNITS_MM:
|
|
xres = 1.0 / nim->dx;
|
|
yres = 1.0 / nim->dy;
|
|
break;
|
|
|
|
case NIFTI_UNITS_MICRON:
|
|
xres = 1.0 / (1000.0 * nim->dx);
|
|
yres = 1.0 / (1000.0 * nim->dy);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf( "get_vips_properties: width = %d\n", width );
|
|
printf( "get_vips_properties: height = %d\n", height );
|
|
printf( "get_vips_properties: bands = %d\n", bands );
|
|
printf( "get_vips_properties: fmt = %d\n", fmt );
|
|
#endif /*DEBUG*/
|
|
|
|
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL );
|
|
vips_image_init_fields( out,
|
|
width, height, bands, fmt,
|
|
VIPS_CODING_NONE,
|
|
bands == 1 ?
|
|
VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_sRGB,
|
|
xres, yres );
|
|
|
|
/* Set some vips metadata for every nifti header field.
|
|
*/
|
|
if( vips__foreign_nifti_map( vips_foreign_load_nifti_set, nim, out ) )
|
|
return( -1 );
|
|
|
|
/* One byte longer than the spec to leave space for any extra
|
|
* '\0' termination.
|
|
*/
|
|
vips_strncpy( txt, nim->intent_name, 17 );
|
|
vips_image_set_string( out, "nifti-intent_name", txt );
|
|
vips_strncpy( txt, nim->descrip, 81 );
|
|
vips_image_set_string( out, "nifti-descrip", txt );
|
|
|
|
for( i = 0; i < nim->num_ext; i++ ) {
|
|
nifti1_extension *ext = &nim->ext_list[i];
|
|
|
|
vips_snprintf( txt, 256, "nifti-ext-%d-%d", i, ext->ecode );
|
|
vips_image_set_blob_copy( out, txt, ext->edata, ext->esize );
|
|
}
|
|
|
|
vips_image_set_int( out, VIPS_META_PAGE_HEIGHT, nim->ny );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
static int
|
|
vips_foreign_load_nifti_header( VipsForeignLoad *load )
|
|
{
|
|
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
|
|
VipsForeignLoadNifti *nifti = (VipsForeignLoadNifti *) load;
|
|
|
|
/* We can't use the (much faster) nifti_read_header() since it just
|
|
* reads the 348 bytes of the analyze struct and does not read any of
|
|
* the extension fields.
|
|
*/
|
|
|
|
/* FALSE means don't read data, just the header. Use
|
|
* nifti_image_load() later to pull the data in.
|
|
*/
|
|
if( !(nifti->nim = nifti_image_read( nifti->filename, FALSE )) ) {
|
|
vips_error( class->nickname,
|
|
"%s", _( "unable to read NIFTI header" ) );
|
|
return( 0 );
|
|
}
|
|
|
|
if( vips_foreign_load_nifti_set_header( nifti,
|
|
nifti->nim, load->out ) ) {
|
|
return( -1 );
|
|
}
|
|
|
|
VIPS_SETSTR( load->out->filename, nifti->filename );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
static int
|
|
vips_foreign_load_nifti_load( VipsForeignLoad *load )
|
|
{
|
|
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
|
|
VipsForeignLoadNifti *nifti = (VipsForeignLoadNifti *) load;
|
|
|
|
#ifdef DEBUG
|
|
printf( "vips_foreign_load_nifti_load: loading image\n" );
|
|
#endif /*DEBUG*/
|
|
|
|
/* We just read the entire image to memory.
|
|
*/
|
|
if( nifti_image_load( nifti->nim ) ) {
|
|
vips_error( class->nickname,
|
|
"%s", _( "unable to load NIFTI file" ) );
|
|
return( -1 );
|
|
}
|
|
|
|
if( !(nifti->memory = vips_image_new_from_memory(
|
|
nifti->nim->data, VIPS_IMAGE_SIZEOF_IMAGE( load->out ),
|
|
load->out->Xsize, load->out->Ysize,
|
|
load->out->Bands, load->out->BandFmt )) )
|
|
return( -1 );
|
|
|
|
if( vips_image_write( nifti->memory, load->real ) )
|
|
return( -1 );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
const char *vips__nifti_suffs[] = {
|
|
".nii", ".nii.gz",
|
|
".hdr", ".hdr.gz",
|
|
".img", ".img.gz",
|
|
".nia", ".nia.gz",
|
|
NULL
|
|
};
|
|
|
|
static void
|
|
vips_foreign_load_nifti_class_init( VipsForeignLoadNiftiClass *class )
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
|
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
|
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
|
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
|
|
|
gobject_class->dispose = vips_foreign_load_nifti_dispose;
|
|
gobject_class->set_property = vips_object_set_property;
|
|
gobject_class->get_property = vips_object_get_property;
|
|
|
|
object_class->nickname = "niftiload";
|
|
object_class->description = _( "load a NIFTI image" );
|
|
|
|
/* is_a() is not that quick ... lower the priority.
|
|
*/
|
|
foreign_class->priority = -50;
|
|
|
|
foreign_class->suffs = vips__nifti_suffs;
|
|
|
|
load_class->is_a = vips_foreign_load_nifti_is_a;
|
|
load_class->header = vips_foreign_load_nifti_header;
|
|
load_class->load = vips_foreign_load_nifti_load;
|
|
|
|
VIPS_ARG_STRING( class, "filename", 1,
|
|
_( "Filename" ),
|
|
_( "Filename to load from" ),
|
|
VIPS_ARGUMENT_REQUIRED_INPUT,
|
|
G_STRUCT_OFFSET( VipsForeignLoadNifti, filename ),
|
|
NULL );
|
|
}
|
|
|
|
static void
|
|
vips_foreign_load_nifti_init( VipsForeignLoadNifti *nifti )
|
|
{
|
|
}
|
|
|
|
#endif /*HAVE_NIFTI*/
|
|
|
|
/**
|
|
* vips_niftiload:
|
|
* @filename: file to load
|
|
* @out: (out): decompressed image
|
|
* @...: %NULL-terminated list of optional named arguments
|
|
*
|
|
* Read a NIFTI image file into a VIPS image.
|
|
*
|
|
* NIFTI metadata is attached with the "nifti-" prefix.
|
|
*
|
|
* See also: vips_image_new_from_file().
|
|
*
|
|
* Returns: 0 on success, -1 on error.
|
|
*/
|
|
int
|
|
vips_niftiload( const char *filename, VipsImage **out, ... )
|
|
{
|
|
va_list ap;
|
|
int result;
|
|
|
|
va_start( ap, out );
|
|
result = vips_call_split( "niftiload", ap, filename, out );
|
|
va_end( ap );
|
|
|
|
return( result );
|
|
}
|