Merge branch 'add-nifti-support'

This commit is contained in:
John Cupitt 2018-07-20 18:20:52 +01:00
commit 3565808281
15 changed files with 1373 additions and 8 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
.pytest_cache
compile
.pytest_cache
a.out

View File

@ -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
- support writing string-valued fields via libexif
- paste in the test suite from pyvips
- get EXIF tag names from tag plus ifd [@Nan619]

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])
@ -842,6 +850,19 @@ if test x"$with_OpenEXR" != x"no"; then
)
fi
# nifti
AC_ARG_WITH([nifti],
AS_HELP_STRING([--without-nifti], [build without nifti (default: test)]))
if test x"$with_nifti" != x"no"; then
FIND_NIFTI([
with_nifti=yes
],[
with_nifti=no
]
)
fi
# pdfium
AC_ARG_WITH([pdfium],
AS_HELP_STRING([--without-pdfium], [build without pdfium (default: test)]))
@ -1284,8 +1305,8 @@ do
done | sort -ru`
VIPS_CFLAGS=`echo $VIPS_CFLAGS`
VIPS_CFLAGS="$VIPS_DEBUG_FLAGS $VIPS_CFLAGS"
VIPS_INCLUDES="$ZLIB_INCLUDES $PNG_INCLUDES $TIFF_INCLUDES $JPEG_INCLUDES"
VIPS_LIBS="$ZLIB_LIBS $MAGICK_LIBS $PNG_LIBS $IMAGEQUANT_LIBS $TIFF_LIBS $JPEG_LIBS $GTHREAD_LIBS $REQUIRED_LIBS $EXPAT_LIBS $PANGOFT2_LIBS $GSF_LIBS $FFTW_LIBS $ORC_LIBS $LCMS_LIBS $GIFLIB_LIBS $RSVG_LIBS $PDFIUM_LIBS $POPPLER_LIBS $OPENEXR_LIBS $OPENSLIDE_LIBS $CFITSIO_LIBS $LIBWEBP_LIBS $LIBWEBPMUX_LIBS $MATIO_LIBS $EXIF_LIBS -lm"
VIPS_INCLUDES="$ZLIB_INCLUDES $PNG_INCLUDES $TIFF_INCLUDES $JPEG_INCLUDES $NIFTI_INCLUDES"
VIPS_LIBS="$ZLIB_LIBS $MAGICK_LIBS $PNG_LIBS $IMAGEQUANT_LIBS $TIFF_LIBS $JPEG_LIBS $GTHREAD_LIBS $REQUIRED_LIBS $EXPAT_LIBS $PANGOFT2_LIBS $GSF_LIBS $FFTW_LIBS $ORC_LIBS $LCMS_LIBS $GIFLIB_LIBS $RSVG_LIBS $NIFTI_LIBS $PDFIUM_LIBS $POPPLER_LIBS $OPENEXR_LIBS $OPENSLIDE_LIBS $CFITSIO_LIBS $LIBWEBP_LIBS $LIBWEBPMUX_LIBS $MATIO_LIBS $EXIF_LIBS -lm"
AC_SUBST(VIPS_LIBDIR)
@ -1375,6 +1396,7 @@ save with libMagick: $enable_magicksave
accelerate loops with orc: $with_orc
(requires orc-0.4.11 or later)
ICC profile support with lcms: $with_lcms
file import with niftiio: $with_nifti
file import with OpenEXR: $with_OpenEXR
file import with OpenSlide: $with_openslide
(requires openslide-3.3.0 or later)

View File

@ -2,6 +2,8 @@ noinst_LTLIBRARIES = libforeign.la
libforeign_la_SOURCES = \
pforeign.h \
niftiload.c \
niftisave.c \
quantise.c \
exif.c \
gifload.c \

View File

@ -1836,6 +1836,8 @@ vips_foreign_operation_init( void )
extern GType vips_foreign_load_svg_get_type( void );
extern GType vips_foreign_load_svg_file_get_type( void );
extern GType vips_foreign_load_svg_buffer_get_type( void );
extern GType vips_foreign_load_nifti_get_type( void );
extern GType vips_foreign_save_nifti_get_type( void );
extern GType vips_foreign_load_gif_get_type( void );
extern GType vips_foreign_load_gif_file_get_type( void );
extern GType vips_foreign_load_gif_buffer_get_type( void );
@ -1958,6 +1960,11 @@ vips_foreign_operation_init( void )
vips_foreign_load_openexr_get_type();
#endif /*HAVE_OPENEXR*/
#ifdef HAVE_NIFTI
vips_foreign_load_nifti_get_type();
vips_foreign_save_nifti_get_type();
#endif /*HAVE_NIFTI*/
vips__foreign_load_operation =
g_quark_from_static_string( "vips-foreign-load-operation" );
}

652
libvips/foreign/niftiload.c Normal file
View File

@ -0,0 +1,652 @@
/* load nifti from a file
*
* 29/6/18
* - from fitsload.c
*/
/*
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_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 ) },
};
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_FLOAT:
g_value_set_float( 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];
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 );
}
if( nim->ny > 1 )
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_copy( nifti->memory, &load->real, NULL ) )
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_CFITSIO*/
/**
* 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 );
}

486
libvips/foreign/niftisave.c Normal file
View File

@ -0,0 +1,486 @@
/* save to nifti
*
* 5/7/18
* - from fitssave.c
*/
/*
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_VERBOSE
#define DEBUG
*/
#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>
#ifdef HAVE_NIFTI
#include <nifti1_io.h>
#include "pforeign.h"
typedef struct _VipsForeignSaveNifti {
VipsForeignSave parent_object;
/* Filename for save.
*/
char *filename;
nifti_image *nim;
} VipsForeignSaveNifti;
typedef VipsForeignSaveClass VipsForeignSaveNiftiClass;
G_DEFINE_TYPE( VipsForeignSaveNifti, vips_foreign_save_nifti,
VIPS_TYPE_FOREIGN_SAVE );
static void
vips_foreign_save_nifti_dispose( GObject *gobject )
{
VipsForeignSaveNifti *nifti = (VipsForeignSaveNifti *) gobject;
VIPS_FREEF( nifti_image_free, nifti->nim );
G_OBJECT_CLASS( vips_foreign_save_nifti_parent_class )->
dispose( gobject );
}
/* Make ->nim from the vips header fields.
*/
static int
vips_foreign_save_nifti_header_vips( VipsForeignSaveNifti *nifti,
VipsImage *image )
{
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 );
}
typedef struct _VipsNdimInfo {
VipsImage *image;
nifti_image *nim;
int *dims;
int n;
} VipsNdimInfo;
static void *
vips_foreign_save_nifti_set_dims( const char *name,
GValue *value, glong offset, void *a, void *b )
{
VipsNdimInfo *info = (VipsNdimInfo *) a;
/* The first 8 members are the dims fields.
*/
if( info->n < 8 ) {
char vips_name[256];
int i;
vips_snprintf( vips_name, 256, "nifti-%s", name );
if( vips_image_get_int( info->image, vips_name, &i ) ||
i <= 0 ||
i > VIPS_MAX_COORD )
return( info );
info->dims[info->n] = i;
}
info->n += 1;
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_write( GValue *value, void *p )
{
switch( G_VALUE_TYPE( value ) ) {
case G_TYPE_INT:
*((int *) p) = g_value_get_int( value );
break;
case G_TYPE_FLOAT:
*((float *) p) = g_value_get_float( value );
break;
default:
g_warning( "vips_gvalue_write: unsupported GType %s",
g_type_name( G_VALUE_TYPE( value ) ) );
}
}
static void *
vips_foreign_save_nifti_set_fields( const char *name,
GValue *value, glong offset, void *a, void *b )
{
VipsNdimInfo *info = (VipsNdimInfo *) a;
/* The first 8 members are the dims fields. We set them above ^^^ --
* do the others in this pass.
*/
if( info->n >= 8 ) {
char vips_name[256];
GValue value_copy = { 0 };
vips_snprintf( vips_name, 256, "nifti-%s", name );
if( vips_image_get( info->image, vips_name, &value_copy ) )
return( info );
vips_gvalue_write( &value_copy, (gpointer) info->nim + offset );
g_value_unset( &value_copy );
}
info->n += 1;
return( NULL );
}
static void *
vips_foreign_save_nifti_ext( VipsImage *image,
const char *field, GValue *value, void *a )
{
nifti_image *nim = (nifti_image *) a;
int i;
int ecode;
char *data;
size_t length;
if( !vips_isprefix( "nifti-ext-", field ) )
return( NULL );
/* The name is "nifti-ext-N-XX" where N is the index (discard this)
* and XX is the nifti ext ecode.
*/
if( sscanf( field, "nifti-ext-%d-%d", &i, &ecode ) != 2 ) {
vips_error( "niftisave",
"%s", _( "bad nifti-ext- field name" ) );
return( image );
}
if( vips_image_get_blob( image, field, (void *) &data, &length ) )
return( image );
if( nifti_add_extension( nim, data, length, ecode ) ) {
vips_error( "niftisave",
"%s", _( "unable to attach nifti ext" ) );
return( image );
}
return( NULL );
}
/* Make ->nim from the nifti- fields.
*/
static int
vips_foreign_save_nifti_header_nifti( VipsForeignSaveNifti *nifti,
VipsImage *image )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( nifti );
VipsNdimInfo info;
int dims[8];
int datatype;
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;
if( vips__foreign_nifti_map(
vips_foreign_save_nifti_set_dims, &info, NULL ) )
return( -1 );
/* 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++ )
if( !g_uint_checked_mul( &height, height, dims[i] ) ) {
vips_error( class->nickname,
"%s", _( "dimension overflow" ) );
return( 0 );
}
if( image->Xsize != dims[1] ||
image->Ysize != height ) {
vips_error( class->nickname,
"%s", _( "bad image dimensions" ) );
return( -1 );
}
datatype = vips__foreign_nifti_BandFmt2datatype( image->BandFmt );
if( datatype == -1 ) {
vips_error( class->nickname,
"%s", _( "unsupported libvips image type" ) );
return( -1 );
}
if( !(nifti->nim = nifti_make_new_nim( dims, datatype, FALSE )) )
return( -1 );
info.image = image;
info.nim = nifti->nim;
info.n = 0;
if( vips__foreign_nifti_map(
vips_foreign_save_nifti_set_fields, &info, NULL ) )
return( -1 );
/* Attach any ext blocks.
*/
if( vips_image_map( image,
(VipsImageMapFn) vips_foreign_save_nifti_ext, nifti->nim ) )
return( -1 );
return( 0 );
}
static int
vips_foreign_save_nifti_build( VipsObject *object )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
VipsForeignSave *save = (VipsForeignSave *) object;
VipsForeignSaveNifti *nifti = (VipsForeignSaveNifti *) object;
if( VIPS_OBJECT_CLASS( vips_foreign_save_nifti_parent_class )->
build( object ) )
return( -1 );
/* This could be an image (indirectly) from niftiload, or something
* like OME_TIFF, which does not have all the "nifti-ndim" fields.
*
* If it doesn't look like a nifti, try to make a nifti header from
* what we have.
*/
if( vips_image_get_typeof( save->ready, "nifti-ndim" ) ) {
if( vips_foreign_save_nifti_header_nifti( nifti, save->ready ) )
return( -1 );
}
else {
if( vips_foreign_save_nifti_header_vips( nifti, save->ready ) )
return( -1 );
}
/* set ext, plus other stuff
*/
if( nifti_set_filenames( nifti->nim, nifti->filename, FALSE, TRUE ) ) {
vips_error( class->nickname,
"%s", _( "unable to set nifti filename" ) );
return( -1 );
}
if( !(nifti->nim->data =
vips_image_write_to_memory( save->ready, NULL )) )
return( -1 );
/* No return code!??!?!!
*/
nifti_image_write( nifti->nim );
/* We must free and NULL the pointer or nifti will try to free it for
* us.
*/
VIPS_FREE( nifti->nim->data );
return( 0 );
}
/* Save a bit of typing.
*/
#define UC VIPS_FORMAT_UCHAR
#define C VIPS_FORMAT_CHAR
#define US VIPS_FORMAT_USHORT
#define S VIPS_FORMAT_SHORT
#define UI VIPS_FORMAT_UINT
#define I VIPS_FORMAT_INT
#define F VIPS_FORMAT_FLOAT
#define X VIPS_FORMAT_COMPLEX
#define D VIPS_FORMAT_DOUBLE
#define DX VIPS_FORMAT_DPCOMPLEX
static int vips_nifti_bandfmt[10] = {
/* UC C US S UI I F X D DX */
UC, C, US, S, UI, I, F, X, D, DX
};
static void
vips_foreign_save_nifti_class_init( VipsForeignSaveNiftiClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class;
gobject_class->dispose = vips_foreign_save_nifti_dispose;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "niftisave";
object_class->description = _( "save image to nifti file" );
object_class->build = vips_foreign_save_nifti_build;
foreign_class->suffs = vips__nifti_suffs;
save_class->saveable = VIPS_SAVEABLE_ANY;
save_class->format_table = vips_nifti_bandfmt;
VIPS_ARG_STRING( class, "filename", 1,
_( "Filename" ),
_( "Filename to save to" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveNifti, filename ),
NULL );
}
static void
vips_foreign_save_nifti_init( VipsForeignSaveNifti *nifti )
{
}
#endif /*HAVE_NIFTI*/
/**
* vips_niftisave: (method)
* @in: image to save
* @filename: file to write to
* @...: %NULL-terminated list of optional named arguments
*
* Write a VIPS image to a file in NIFTI format.
*
* Use the various NIFTI suffixes to pick the nifti save format.
*
* See also: vips_image_write_to_file(), vips_niftiload().
*
* Returns: 0 on success, -1 on error.
*/
int
vips_niftisave( VipsImage *in, const char *filename, ... )
{
va_list ap;
int result;
va_start( ap, filename );
result = vips_call_split( "niftisave", ap, in, filename );
va_end( ap );
return( result );
}

View File

@ -35,6 +35,16 @@
extern "C" {
#endif /*__cplusplus*/
/* 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*/
void vips__tiff_init( void );
int vips__tiff_write( VipsImage *in, const char *filename,
@ -256,6 +266,15 @@ int vips__quantise_image( VipsImage *in,
VipsImage **index_out, VipsImage **palette_out,
int colours, int Q, double dither );
extern const char *vips__nifti_suffs[];
VipsBandFormat vips__foreign_nifti_datatype2BandFmt( int datatype );
int vips__foreign_nifti_BandFmt2datatype( VipsBandFormat fmt );
typedef void *(*VipsNiftiMapFn)( const char *name, GValue *value, glong offset,
void *a, void *b );
void *vips__foreign_nifti_map( VipsNiftiMapFn fn, void *a, void *b );
#ifdef __cplusplus
}
#endif /*__cplusplus*/

View File

@ -560,6 +560,11 @@ int vips_gifload( const char *filename, VipsImage **out, ... )
int vips_gifload_buffer( void *buf, size_t len, VipsImage **out, ... )
__attribute__((sentinel));
int vips_niftiload( const char *filename, VipsImage **out, ... )
__attribute__((sentinel));
int vips_niftisave( VipsImage *in, const char *filename, ... )
__attribute__((sentinel));
/**
* VipsForeignDzLayout:
* @VIPS_FOREIGN_DZ_LAYOUT_DZ: use DeepZoom directory layout

View File

@ -454,6 +454,26 @@ transform_save_string_double( const GValue *src_value, GValue *dest_value )
NULL ) );
}
static void
transform_float_save_string( const GValue *src_value, GValue *dest_value )
{
char buf[G_ASCII_DTOSTR_BUF_SIZE];
/* Need to be locale independent.
*/
g_ascii_dtostr( buf, G_ASCII_DTOSTR_BUF_SIZE,
g_value_get_float( src_value ) );
vips_value_set_save_string( dest_value, buf );
}
static void
transform_save_string_float( const GValue *src_value, GValue *dest_value )
{
g_value_set_float( dest_value,
g_ascii_strtod( vips_value_get_save_string( src_value ),
NULL ) );
}
/* Save meta fields to the header. We have a new string type for header fields
* to save to XML and define transform functions to go from our meta types to
* this string type.
@ -1847,4 +1867,8 @@ vips__meta_init_types( void )
transform_double_save_string );
g_value_register_transform_func( VIPS_TYPE_SAVE_STRING, G_TYPE_DOUBLE,
transform_save_string_double );
g_value_register_transform_func( G_TYPE_FLOAT, VIPS_TYPE_SAVE_STRING,
transform_float_save_string );
g_value_register_transform_func( VIPS_TYPE_SAVE_STRING, G_TYPE_FLOAT,
transform_save_string_float );
}

View File

@ -840,6 +840,10 @@ build_xml_meta( VipsMeta *meta, VipsDbuf *dbuf )
g_value_unset( &save_value );
}
else {
printf( "unable to convert %s for save\n",
g_type_name( type ) );
}
return( NULL );
}

122
m4/nifti.m4 Normal file
View File

@ -0,0 +1,122 @@
dnl From FIND_MOTIF and ACX_PTHREAD, without much understanding
dnl
dnl FIND_NIFTI[ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]
dnl ------------------------------------------------
dnl
dnl Find NIFTI libraries and headers
dnl
dnl Put compile stuff in NIFTI_INCLUDES
dnl Put link stuff in NIFTI_LIBS
dnl Define HAVE_NIFTI if found
dnl
AC_DEFUN([FIND_NIFTI], [
AC_REQUIRE([AC_PATH_XTRA])
NIFTI_INCLUDES=""
NIFTI_LIBS=""
AC_ARG_WITH(nifti,
AS_HELP_STRING([--without-nifti], [build without nifti (default: test)]))
# Treat --without-nifti like --without-nifti-includes --without-nifti-libraries.
if test "$with_nifti" = "no"; then
NIFTI_INCLUDES=no
NIFTI_LIBS=no
fi
AC_ARG_WITH(nifti-includes,
AS_HELP_STRING([--with-nifti-includes=DIR], [libniftiio includes are in DIR]),
NIFTI_INCLUDES="-I$withval"
)
AC_ARG_WITH(nifti-libraries,
AS_HELP_STRING([--with-nifti-libraries=DIR],
[libniftiio libraries are in DIR]),
NIFTI_LIBS="-L$withval -lniftiio"
)
AC_MSG_CHECKING(for NIFTI)
# Look for nifti1_io.h ... usually in /usr/include/nifti
if test "$NIFTI_INCLUDES" = ""; then
nifti_save_CFLAGS="$CFLAGS"
# annoyingly, the header must be unqualified, so we have to add to the
# search path
CFLAGS="-I/usr/include/nifti $nifti_save_CFLAGS"
AC_TRY_COMPILE([#include <nifti1_io.h>],[int a;],[
NIFTI_INCLUDES="-I/usr/include/nifti"
], [
# not in the standard search path, try $prefix
CFLAGS="-I${prefix}/include/nifti $nifti_save_CFLAGS"
AC_TRY_COMPILE([#include <nifti1_io.h>],[int a;],[
NIFTI_INCLUDES="-I${prefix}/include/nifti"
], [
NIFTI_INCLUDES="no"
])
])
CFLAGS="$nifti_save_CFLAGS"
fi
# Now for the libraries
if test "$NIFTI_LIBS" = ""; then
nifti_save_LIBS="$LIBS"
nifti_save_CFLAGS="$CFLAGS"
LIBS="-lniftiio -lm $nifti_save_LIBS"
CFLAGS="$NIFTI_INCLUDES $CFLAGS"
# Try the standard search path first
AC_TRY_LINK([#include <nifti1_io.h>],[is_nifti_file("")], [
NIFTI_LIBS="-lniftiio"
], [
# libniftiio is not in the standard search path, try $prefix
LIBS="-L${prefix}/lib $LIBS"
AC_TRY_LINK([#include <nifti1_io.h>],[is_nifti_file("")], [
NIFTI_LIBS="-L${prefix}/lib -lniftiio"
], [
NIFTI_LIBS=no
])
])
LIBS="$nifti_save_LIBS"
CFLAGS="$nifti_save_CFLAGS"
fi
AC_SUBST(NIFTI_LIBS)
AC_SUBST(NIFTI_INCLUDES)
# Print a helpful message
nifti_libraries_result="$NIFTI_LIBS"
nifti_includes_result="$NIFTI_INCLUDES"
if test x"$nifti_libraries_result" = x""; then
nifti_libraries_result="in default path"
fi
if test x"$nifti_includes_result" = x""; then
nifti_includes_result="in default path"
fi
if test "$nifti_libraries_result" = "no"; then
nifti_libraries_result="(none)"
fi
if test "$nifti_includes_result" = "no"; then
nifti_includes_result="(none)"
fi
AC_MSG_RESULT([libraries $nifti_libraries_result, headers $nifti_includes_result])
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
if test "$NIFTI_INCLUDES" != "no" && test "$NIFTI_LIBS" != "no"; then
AC_DEFINE(HAVE_NIFTI,1,[Define if you have nifti libraries and header files.])
$1
else
NIFTI_INCLUDES=""
NIFTI_LIBS=""
$2
fi
])dnl

View File

@ -27,6 +27,7 @@ SVG_GZ_FILE = os.path.join(IMAGES, "vips-profile.svg.gz")
GIF_ANIM_FILE = os.path.join(IMAGES, "cogs.gif")
DICOM_FILE = os.path.join(IMAGES, "dicom_test_image.dcm")
BMP_FILE = os.path.join(IMAGES, "MARBLES.BMP")
NIFTI_FILE = os.path.join(IMAGES, "avg152T1_LR_nifti.nii.gz")
unsigned_formats = [pyvips.BandFormat.UCHAR,
pyvips.BandFormat.USHORT,

Binary file not shown.

View File

@ -6,12 +6,12 @@ import tempfile
import pytest
import pyvips
from helpers import JPEG_FILE, SRGB_FILE, \
MATLAB_FILE, PNG_FILE, TIF_FILE, OME_FILE, ANALYZE_FILE, \
GIF_FILE, WEBP_FILE, EXR_FILE, FITS_FILE, OPENSLIDE_FILE, \
PDF_FILE, SVG_FILE, SVGZ_FILE, SVG_GZ_FILE, GIF_ANIM_FILE, \
DICOM_FILE, BMP_FILE, temp_filename, assert_almost_equal_objects, have, \
skip_if_no
from helpers import \
JPEG_FILE, SRGB_FILE, MATLAB_FILE, PNG_FILE, TIF_FILE, OME_FILE, \
ANALYZE_FILE, GIF_FILE, WEBP_FILE, EXR_FILE, FITS_FILE, OPENSLIDE_FILE, \
PDF_FILE, SVG_FILE, SVGZ_FILE, SVG_GZ_FILE, GIF_ANIM_FILE, DICOM_FILE, \
BMP_FILE, NIFTI_FILE, \
temp_filename, assert_almost_equal_objects, have, skip_if_no
class TestForeign:
@ -532,6 +532,25 @@ class TestForeign:
self.file_loader("fitsload", FITS_FILE, fits_valid)
self.save_load("%s.fits", self.mono)
@skip_if_no("niftiload")
def test_niftiload(self):
def nifti_valid(im):
a = im(30, 26)
assert_almost_equal_objects(a, [131])
assert im.width == 91
assert im.height == 9919
assert im.bands == 1
print("NIFTI_FILE =", NIFTI_FILE)
im = pyvips.Operation.call("niftiload", NIFTI_FILE)
print("width = ", im.width)
im = pyvips.Operation.call("niftiload", "test/images/avg152T1_LR_nifti.nii.gz")
print("width = ", im.width)
self.file_loader("niftiload", NIFTI_FILE, nifti_valid)
self.save_load("%s.nii.gz", self.mono)
@skip_if_no("openslideload")
def test_openslideload(self):
def openslide_valid(im):