From d03416d3860ddf2c25e21f112eb771db5bba55e2 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 19 Jul 2018 17:45:54 +0100 Subject: [PATCH] works! just needs some tests could be faster too --- ChangeLog | 1 + libvips/foreign/niftiload.c | 45 +++++++++++++++++-- libvips/foreign/niftisave.c | 89 ++++++++++++++++++++++++++++++++++++- 3 files changed, 129 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2eca4791..2558f3ea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -31,6 +31,7 @@ - add Mitchell kernel - pyramid builders have a choice of 2x2 shrinkers [harukizaemon] - add `palette` option to pngsave [felixbuenemann] +- add basic nifti load/save support 12/3/18 started 8.6.4 - better fitting of fonts with overhanging edges [AdriĆ ] diff --git a/libvips/foreign/niftiload.c b/libvips/foreign/niftiload.c index abfff1ac..eedc0f0c 100644 --- a/libvips/foreign/niftiload.c +++ b/libvips/foreign/niftiload.c @@ -103,6 +103,43 @@ vips_foreign_load_nifti_dispose( GObject *gobject ) 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 { @@ -360,7 +397,7 @@ vips_foreign_load_nifti_set_header( VipsForeignLoadNifti *nifti, nim->ndim ); return( 0 ); } - for( i = 1; i < 8; i++ ) { + for( i = 1; i < 8 && i < nim->ndim + 1; i++ ) { if( nim->dim[i] <= 0 ) { vips_error( class->nickname, "%s", _( "invalid dimension" ) ); @@ -368,7 +405,7 @@ vips_foreign_load_nifti_set_header( VipsForeignLoadNifti *nifti, } /* If we have several images in a dimension, the spacing must - * be non-zero, or we'll get a /0 error in resolution + * be non-zero or we'll get a /0 error in resolution * calculation. */ if( nim->dim[i] > 1 && @@ -385,7 +422,7 @@ vips_foreign_load_nifti_set_header( VipsForeignLoadNifti *nifti, bands = 1; width = (guint) nim->nx; height = (guint) nim->ny; - for( i = 3; i < 8; i++ ) + 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" ) ); @@ -568,7 +605,7 @@ vips_foreign_load_nifti_class_init( VipsForeignLoadNiftiClass *class ) foreign_class->suffs = vips__nifti_suffs; - load_class->is_a = is_nifti_file; + 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; diff --git a/libvips/foreign/niftisave.c b/libvips/foreign/niftisave.c index 0da42ab1..9427630d 100644 --- a/libvips/foreign/niftisave.c +++ b/libvips/foreign/niftisave.c @@ -86,7 +86,73 @@ static int vips_foreign_save_nifti_header_vips( VipsForeignSaveNifti *nifti, VipsImage *image ) { - g_assert( FALSE ); + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( nifti ); + + int dims[8]; + int datatype; + int i; + + /* Most nifti images have this defaulted as 1. + */ + for( i = 0; i < VIPS_NUMBER( dims ); i++ ) + dims[i] = 1; + + dims[0] = 2; + dims[1] = image->Xsize; + dims[2] = image->Ysize; + + if( vips_image_get_typeof( image, VIPS_META_PAGE_HEIGHT ) ) { + int page_height; + + if( vips_image_get_int( image, + VIPS_META_PAGE_HEIGHT, &page_height ) ) + return( -1 ); + + if( image->Ysize % page_height == 0 ) { + dims[0] = 3; + dims[2] = page_height; + dims[3] = image->Ysize / page_height; + } + } + + datatype = vips__foreign_nifti_BandFmt2datatype( image->BandFmt ); + if( datatype == -1 ) { + vips_error( class->nickname, + "%s", _( "unsupported libvips image type" ) ); + return( -1 ); + } + + if( image->Bands > 1 ) { + if( image->BandFmt != VIPS_FORMAT_UCHAR ) { + vips_error( class->nickname, + "%s", _( "8-bit colour images only" ) ); + return( -1 ); + } + + if( image->Bands == 3 ) + datatype = DT_RGB; + else if( image->Bands == 4 ) + datatype = DT_RGBA32; + else { + vips_error( class->nickname, + "%s", _( "3 or 4 band colour images only" ) ); + return( -1 ); + } + } + + if( !(nifti->nim = nifti_make_new_nim( dims, datatype, FALSE )) ) + return( -1 ); + + nifti->nim->dx = 1.0 / image->Xres; + nifti->nim->dy = 1.0 / image->Yres; + nifti->nim->dz = 1.0 / image->Yres; + nifti->nim->xyz_units = NIFTI_UNITS_MM; + + vips_snprintf( nifti->nim->descrip, sizeof( nifti->nim->descrip ), + "libvips-%s", VIPS_VERSION ); + + /* All other fields can stay at their default value. + */ return( 0 ); } @@ -183,6 +249,11 @@ vips_foreign_save_nifti_header_nifti( VipsForeignSaveNifti *nifti, guint height; int i; + /* Most nifti images have this defaulted as 1. + */ + for( i = 0; i < VIPS_NUMBER( dims ); i++ ) + dims[i] = 1; + info.image = image; info.dims = dims; info.n = 0; @@ -190,8 +261,22 @@ vips_foreign_save_nifti_header_nifti( VipsForeignSaveNifti *nifti, vips_foreign_save_nifti_set_dims, &info, NULL ) ) return( -1 ); - /* FIXME what about page-height? should check that too. + /* page-height overrides ny if it makes sense. This might not be + * correct :( */ + if( vips_image_get_typeof( image, VIPS_META_PAGE_HEIGHT ) ) { + int page_height; + + if( vips_image_get_int( image, + VIPS_META_PAGE_HEIGHT, &page_height ) ) + return( -1 ); + + if( image->Ysize % page_height == 0 ) { + dims[0] = 3; + dims[2] = page_height; + dims[3] = image->Ysize / page_height; + } + } height = 1; for( i = 2; i < VIPS_NUMBER( dims ) && i < dims[0] + 1; i++ )