Merge branch 'add-nifti-support'
This commit is contained in:
commit
3565808281
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
.pytest_cache
|
||||
compile
|
||||
.pytest_cache
|
||||
a.out
|
||||
|
@ -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]
|
||||
|
26
configure.ac
26
configure.ac
@ -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)
|
||||
|
@ -2,6 +2,8 @@ noinst_LTLIBRARIES = libforeign.la
|
||||
|
||||
libforeign_la_SOURCES = \
|
||||
pforeign.h \
|
||||
niftiload.c \
|
||||
niftisave.c \
|
||||
quantise.c \
|
||||
exif.c \
|
||||
gifload.c \
|
||||
|
@ -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
652
libvips/foreign/niftiload.c
Normal 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
486
libvips/foreign/niftisave.c
Normal 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 );
|
||||
}
|
@ -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*/
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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
122
m4/nifti.m4
Normal 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
|
@ -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,
|
||||
|
BIN
test/test-suite/images/avg152T1_LR_nifti.nii.gz
Normal file
BIN
test/test-suite/images/avg152T1_LR_nifti.nii.gz
Normal file
Binary file not shown.
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user