nifi header read done

This commit is contained in:
John Cupitt 2018-06-30 21:57:00 +01:00
parent 6dc6b4f722
commit 7716fa957b
2 changed files with 300 additions and 15 deletions

View File

@ -516,6 +516,14 @@ PKG_CHECK_MODULES(WIN32_GET_COMMAND_LINE, glib-2.0 >= 2.40,
]
)
# from 2.48 we have g_uint_checked_mul() etc.
PKG_CHECK_MODULES(HAVE_CHECKED_MUL, glib-2.0 >= 2.48,
[AC_DEFINE(HAVE_CHECKED_MUL,1,[define if your glib has checked multiply.])
],
[:
]
)
# check for gtk-doc
GTK_DOC_CHECK([1.14],[--flavour no-tmpl])

View File

@ -34,6 +34,7 @@
/*
#define DEBUG
*/
#define VIPS_DEBUG
#ifdef HAVE_CONFIG_H
#include <config.h>
@ -68,22 +69,291 @@ typedef VipsForeignLoadClass VipsForeignLoadNiftiClass;
G_DEFINE_TYPE( VipsForeignLoadNifti, vips_foreign_load_nifti,
VIPS_TYPE_FOREIGN_LOAD );
static int
vips_foreign_load_nifti_is_a( const char *filename )
/* Map DT_* datatype values to VipsBandFormat.
*/
typedef struct _DT2Vips {
int datatype;
VipsBandFormat fmt;
} DT2Vips;
static DT2Vips vips_DT2Vips[] = {
{ DT_UNSIGNED_CHAR, VIPS_FORMAT_UCHAR },
{ DT_SIGNED_SHORT, VIPS_FORMAT_SHORT },
{ DT_SIGNED_INT, VIPS_FORMAT_INT },
{ DT_FLOAT, VIPS_FORMAT_FLOAT },
{ DT_COMPLEX, VIPS_FORMAT_COMPLEX },
{ DT_DOUBLE, VIPS_FORMAT_DOUBLE },
{ DT_RGB, VIPS_FORMAT_UCHAR }
};
/* Slow and horrid version if there's no recent glib.
*/
#ifndef HAVE_CHECKED_MUL
#define g_uint_checked_mul( dest, a, b ) ( \
((guint64) a * b) > UINT_MAX ? \
(*dest = UINT_MAX, FALSE) : \
(*dest = a * b, TRUE) \
)
#endif /*HAVE_CHECKED_MUL*/
/* All the header fields we attach as metadata.
*/
typedef struct _Field {
char *name;
GType type;
glong offset;
} Field;
static Field other_fields[] = {
{ "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_FLOAT, G_STRUCT_OFFSET( nifti_image, dx ) },
{ "dy", G_TYPE_FLOAT, G_STRUCT_OFFSET( nifti_image, dy ) },
{ "dz", G_TYPE_FLOAT, G_STRUCT_OFFSET( nifti_image, dz ) },
{ "dt", G_TYPE_FLOAT, G_STRUCT_OFFSET( nifti_image, dt ) },
{ "du", G_TYPE_FLOAT, G_STRUCT_OFFSET( nifti_image, du ) },
{ "dv", G_TYPE_FLOAT, G_STRUCT_OFFSET( nifti_image, dv ) },
{ "dw", G_TYPE_FLOAT, G_STRUCT_OFFSET( nifti_image, dw ) },
{ "scl_slope", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, scl_slope ) },
{ "scl_inter", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, scl_inter ) },
{ "cal_min", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, cal_min ) },
{ "cal_max", G_TYPE_FLOAT,
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_FLOAT,
G_STRUCT_OFFSET( nifti_image, slice_duration ) },
{ "quatern_b", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, quatern_b ) },
{ "quatern_c", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, quatern_c ) },
{ "quatern_d", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, quatern_d ) },
{ "qoffset_x", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, qoffset_x ) },
{ "qoffset_y", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, qoffset_y ) },
{ "qoffset_z", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, qoffset_z ) },
{ "qfac", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, qfac ) },
{ "sto_xyz00", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[0][0] ) },
{ "sto_xyz01", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[0][1] ) },
{ "sto_xyz02", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[0][2] ) },
{ "sto_xyz03", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[0][3] ) },
{ "sto_xyz10", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[1][0] ) },
{ "sto_xyz11", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[1][1] ) },
{ "sto_xyz12", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[1][2] ) },
{ "sto_xyz13", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[1][3] ) },
{ "sto_xyz20", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[2][0] ) },
{ "sto_xyz21", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[2][1] ) },
{ "sto_xyz22", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[2][2] ) },
{ "sto_xyz23", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[2][3] ) },
{ "sto_xyz30", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[3][0] ) },
{ "sto_xyz31", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[3][1] ) },
{ "sto_xyz32", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[3][2] ) },
{ "sto_xyz33", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, sto_xyz.m[3][3] ) },
{ "toffset", G_TYPE_FLOAT,
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_FLOAT,
G_STRUCT_OFFSET( nifti_image, intent_p1 ) },
{ "intent_p2", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, intent_p2 ) },
{ "intent_p3", G_TYPE_FLOAT,
G_STRUCT_OFFSET( nifti_image, intent_p3 ) },
};
/* How I wish glib had something like this :( Just implement the ones we need
* for Field above.
*/
static void
vips_gvalue_read( GValue *value, void *p )
{
nifti_image *nim;
switch( G_VALUE_TYPE( value ) ) {
case G_TYPE_INT:
g_value_set_int( value, *((int *) p) );
break;
gboolean result;
case G_TYPE_FLOAT:
g_value_set_float( value, *((float *) p) );
break;
VIPS_DEBUG_MSG( "nifti_is_a: testing \"%s\"\n", filename );
default:
g_warning( "vips_gvalue_read: unsupported GType %s",
g_type_name( G_VALUE_TYPE( value ) ) );
}
}
result = FALSE;
if( (nim = nifti_image_read( filename, FALSE )) ) {
nifti_image_free( nim );
result = TRUE;
static int
vips_foreign_load_nifti_get_header( VipsForeignLoadNifti *nifti,
nifti_image *nim, VipsImage *out )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( nifti );
guint width;
guint height;
guint bands;
VipsBandFormat fmt;
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++ )
if( nim->dim[i] <= 0 ) {
vips_error( class->nickname,
"%s", _( "invalid dimension" ) );
return( 0 );
}
return( result );
/* 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++ )
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 );
}
/* Decode voxel format.
*/
fmt = VIPS_FORMAT_UCHAR;
for( i = 0; i < VIPS_NUMBER( vips_DT2Vips ); i++ )
if( nim->datatype == vips_DT2Vips[i].datatype ) {
fmt = vips_DT2Vips[i].fmt;
break;
}
if( i == VIPS_NUMBER( vips_DT2Vips ) ) {
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;
#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_init_fields( out,
width, height, bands, fmt,
VIPS_CODING_NONE,
bands == 1 ?
VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_sRGB,
1.0, 1.0 );
for( i = 0; i < VIPS_NUMBER( other_fields ); i++ ) {
GValue value = { 0 };
g_value_init( &value, other_fields[i].type );
vips_gvalue_read( &value,
(gpointer) nim + other_fields[i].offset );
vips_snprintf( txt, 256, "nifti-%s", other_fields[i].name );
vips_image_set( out, txt, &value );
g_value_unset( &value );
}
vips_strncpy( txt, nim->intent_name, 16 );
txt[16] = '\0';
vips_image_set_string( out, "nifti-intent_name", txt );
vips_strncpy( txt, nim->descrip, 80 );
txt[80] = '\0';
vips_image_set_string( out, "nifti-descrip", txt );
for( i = 0; i < nim->num_ext; i++ ) {
nifti1_extension *ext = &nim->ext_list[i];
char *data_copy;
vips_snprintf( txt, 256, "nifti-ext-%d-%d", i, ext->ecode );
if( !(data_copy = vips_malloc( NULL, ext->esize )) )
return( -1 );
memcpy( data_copy, ext->edata, ext->esize );
vips_image_set_blob( out, txt,
(VipsCallbackFn) vips_free, data_copy, ext->esize );
}
return( 0 );
}
static int
@ -103,8 +373,10 @@ vips_foreign_load_nifti_header( VipsForeignLoad *load )
return( 0 );
}
/* Set load->out.
*/
if( vips_foreign_load_nifti_get_header( nifti, nim, load->out ) ) {
nifti_image_free( nim );
return( -1 );
}
nifti_image_free( nim );
@ -113,7 +385,12 @@ vips_foreign_load_nifti_header( VipsForeignLoad *load )
return( 0 );
}
const char *vips__nifti_suffs[] = { ".nii", ".nii.gz", NULL };
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 )
@ -127,7 +404,7 @@ vips_foreign_load_nifti_class_init( VipsForeignLoadNiftiClass *class )
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "niftiload";
object_class->description = _( "load a FITS image" );
object_class->description = _( "load a NIFTI image" );
/* is_a() is not that quick ... lower the priority.
*/
@ -135,7 +412,7 @@ vips_foreign_load_nifti_class_init( VipsForeignLoadNiftiClass *class )
foreign_class->suffs = vips__nifti_suffs;
load_class->is_a = vips_foreign_load_nifti_is_a;
load_class->is_a = is_nifti_file;
load_class->header = vips_foreign_load_nifti_header;
VIPS_ARG_STRING( class, "filename", 1,