RNifti: inst/include/lib/NiftiImage.h Source File

RNifti Fast R and C++ Access to NIfTI Images

NiftiImage.h
1 #ifndef _NIFTI_IMAGE_H_
2 #define _NIFTI_IMAGE_H_
3
4
5 #ifndef _NO_R__
6
7 #include <Rcpp.h>
8
9 #else
10
11 #define R_NegInf -INFINITY
12
13 #include <stdint.h>
14 #include <cstddef>
15 #include <cmath>
16 #include <string>
17 #include <sstream>
18 #include <vector>
19 #include <stdexcept>
20 #include <algorithm>
21 #include <map>
22 #include <locale>
23
24 #endif
25
26
27 #include "niftilib/nifti1_io.h"
28
37 namespace RNifti {
38
43 class NiftiImage
44 {
45 public:
50 struct Block
51 {
52 const NiftiImage &image;
53 const int dimension;
54 const int index;
63 Block (const NiftiImage &image, const int dimension, const int index)
64 : image(image), dimension(dimension), index(index)
65 {
66 if (dimension != image->ndim)
67 throw std::runtime_error("Blocks must be along the last dimension in the image");
68 }
69
78 Block & operator= (const NiftiImage &source)
79 {
80 if (source->datatype != image->datatype)
81 throw std::runtime_error("New data does not have the same datatype as the target block");
82 if (source->scl_slope != image->scl_slope || source->scl_inter != image->scl_inter)
83 throw std::runtime_error("New data does not have the same scale parameters as the target block");
84
85 size_t blockSize = 1;
86 for (int i=1; i<dimension; i++)
87 blockSize *= image->dim[i];
88
89 if (blockSize != source->nvox)
90 throw std::runtime_error("New data does not have the same size as the target block");
91
92 blockSize *= image->nbyper;
93 memcpy(static_cast<char*>(image->data) + blockSize*index, source->data, blockSize);
94 return *this;
95 }
96
100 template <typename TargetType>
101 std::vector<TargetType> getData () const;
102 };
103
104 #ifndef _NO_R__
105
111 static short sexpTypeToNiftiType (const int sexpType)
112 {
113 if (sexpType == INTSXP || sexpType == LGLSXP)
114 return DT_INT32;
115 else if (sexpType == REALSXP)
116 return DT_FLOAT64;
117 else
118 throw std::runtime_error("Array elements must be numeric");
119 }
120 #endif
121
127 static std::string xformToString (const mat44 matrix)
128 {
129 int icode, jcode, kcode;
130 nifti_mat44_to_orientation(matrix, &icode, &jcode, &kcode);
131
132 int codes[3] = { icode, jcode, kcode };
133 std::string result("---");
134 for (int i=0; i<3; i++)
135 {
136 switch (codes[i])
137 {
138 case NIFTI_L2R: result[i] = 'R'; break;
139 case NIFTI_R2L: result[i] = 'L'; break;
140 case NIFTI_P2A: result[i] = 'A'; break;
141 case NIFTI_A2P: result[i] = 'P'; break;
142 case NIFTI_I2S: result[i] = 'S'; break;
143 case NIFTI_S2I: result[i] = 'I'; break;
144 }
145 }
146 return result;
147 }
148
149
150 protected:
151 nifti_image *image;
152 bool persistent;
158 void copy (const nifti_image *source);
159
164 void copy (const NiftiImage &source);
165
170 void copy (const Block &source);
171
172
173 #ifndef _NO_R__
174
180 void initFromNiftiS4 (const Rcpp::RObject &object, const bool copyData = true);
181
187 void initFromMriImage (const Rcpp::RObject &object, const bool copyData = true);
188
193 void initFromList (const Rcpp::RObject &object);
194
200 void initFromArray (const Rcpp::RObject &object, const bool copyData = true);
201
202 #endif
203
208 void updatePixdim (const std::vector<float> &pixdim);
209
214 void setPixunits (const std::vector<std::string> &pixunits);
215
216 public:
220 NiftiImage ()
221 : image(NULL), persistent(false) {}
222
227 NiftiImage (const NiftiImage &source)
228 : image(NULL), persistent(false)
229 {
230 this->copy(source);
231 #ifndef NDEBUG
232 Rprintf("Creating NiftiImage with pointer %p (from NiftiImage)\n", this->image);
233 #endif
234 }
235
240 NiftiImage (const Block &source)
241 : image(NULL), persistent(false)
242 {
243 this->copy(source);
244 #ifndef NDEBUG
245 Rprintf("Creating NiftiImage with pointer %p (from Block)\n", this->image);
246 #endif
247 }
248
255 NiftiImage (nifti_image * const image, const bool copy = false)
256 : image(NULL), persistent(false)
257 {
258 if (copy)
259 this->copy(image);
260 else
261 this->image = image;
262 #ifndef NDEBUG
263 Rprintf("Creating NiftiImage with pointer %p (from pointer)\n", this->image);
264 #endif
265 }
266
273 NiftiImage (const std::string &path, const bool readData = true)
274 : persistent(false)
275 {
276 this->image = nifti_image_read(path.c_str(), readData);
277 if (this->image == NULL)
278 throw std::runtime_error("Failed to read image from path " + path);
279 #ifndef NDEBUG
280 Rprintf("Creating NiftiImage with pointer %p (from string)\n", this->image);
281 #endif
282 }
283
284 #ifndef _NO_R__
285
290 NiftiImage (const SEXP object, const bool readData = true);
291 #endif
292
296 virtual ~NiftiImage ()
297 {
298 if (!persistent)
299 {
300 #ifndef NDEBUG
301 Rprintf("Freeing NiftiImage with pointer %p\n", this->image);
302 #endif
303 nifti_image_free(image);
304 }
305 }
306
310 operator const nifti_image* () const { return image; }
311
315 operator nifti_image* () { return image; }
316
320 const nifti_image * operator-> () const { return image; }
321
325 nifti_image * operator-> () { return image; }
326
331 NiftiImage & operator= (const NiftiImage &source)
332 {
333 copy(source);
334 #ifndef NDEBUG
335 Rprintf("Creating NiftiImage with pointer %p (from NiftiImage)\n", this->image);
336 #endif
337 return *this;
338 }
339
345 NiftiImage & operator= (const Block &source)
346 {
347 copy(source);
348 #ifndef NDEBUG
349 Rprintf("Creating NiftiImage with pointer %p (from Block)\n", this->image);
350 #endif
351 return *this;
352 }
353
358 NiftiImage & setPersistence (const bool persistent)
359 {
360 this->persistent = persistent;
361 #ifndef NDEBUG
362 if (persistent)
363 Rprintf("Setting NiftiImage with pointer %p to be persistent\n", this->image);
364 #endif
365 return *this;
366 }
367
371 bool isNull () const { return (image == NULL); }
372
376 bool isPersistent () const { return persistent; }
377
381 bool isDataScaled () const { return (image != NULL && image->scl_slope != 0.0 && (image->scl_slope != 1.0 || image->scl_inter != 0.0)); }
382
386 int nDims () const
387 {
388 if (image == NULL)
389 return 0;
390 else
391 return image->ndim;
392 }
393
398 std::vector<int> dim () const
399 {
400 if (image == NULL)
401 return std::vector<int>();
402 else
403 return std::vector<int>(image->dim+1, image->dim+image->ndim+1);
404 }
405
410 std::vector<float> pixdim () const
411 {
412 if (image == NULL)
413 return std::vector<float>();
414 else
415 return std::vector<float>(image->pixdim+1, image->pixdim+image->ndim+1);
416 }
417
424 NiftiImage & drop ()
425 {
426 int ndim = image->ndim;
427 while (image->dim[ndim] < 2)
428 ndim--;
429 image->dim[0] = image->ndim = ndim;
430
431 return *this;
432 }
433
437 template <typename TargetType>
438 std::vector<TargetType> getData () const;
439
444 NiftiImage & changeDatatype (const short datatype);
445
450 NiftiImage & changeDatatype (const std::string &datatype);
451
459 template <typename SourceType>
460 NiftiImage & replaceData (const std::vector<SourceType> &data, const short datatype = DT_NONE);
461
465 NiftiImage & dropData ()
466 {
467 nifti_image_unload(image);
468 return *this;
469 }
470
476 NiftiImage & rescale (const std::vector<float> &scales);
477
485 NiftiImage & reorient (const int i, const int j, const int k);
486
494 NiftiImage & reorient (const std::string &orientation);
495
496 #ifndef _NO_R__
497
501 NiftiImage & update (const SEXP array);
502 #endif
503
509 mat44 xform (const bool preferQuaternion = true) const;
510
514 int nBlocks () const
515 {
516 if (image == NULL)
517 return 0;
518 else
519 return image->dim[image->ndim];
520 }
521
529 const Block block (const int i) const { return Block(*this, nDims(), i); }
530
538 Block block (const int i) { return Block(*this, nDims(), i); }
539
545 const Block slice (const int i) const { return Block(*this, 3, i); }
546
552 Block slice (const int i) { return Block(*this, 3, i); }
553
559 const Block volume (const int i) const { return Block(*this, 4, i); }
560
566 Block volume (const int i) { return Block(*this, 4, i); }
567
573 void toFile (const std::string fileName, const short datatype = DT_NONE) const;
574
580 void toFile (const std::string fileName, const std::string &datatype) const;
581
582 #ifndef _NO_R__
583
588 Rcpp::RObject toArray () const;
589
595 Rcpp::RObject toPointer (const std::string label) const;
596
603 Rcpp::RObject toArrayOrPointer (const bool internal, const std::string label) const;
604
609 Rcpp::RObject headerToList () const;
610
611 #endif
612
613 };
614
615 // Include helper functions
616 #include "lib/NiftiImage_internal.h"
617
618 inline void NiftiImage::copy (const nifti_image *source)
619 {
620 if (image != NULL && !persistent)
621 nifti_image_free(image);
622
623 if (source == NULL)
624 image = NULL;
625 else
626 {
627 image = nifti_copy_nim_info(source);
628 if (source->data != NULL)
629 {
630 size_t dataSize = nifti_get_volsize(source);
631 image->data = calloc(1, dataSize);
632 memcpy(image->data, source->data, dataSize);
633 }
634 }
635
636 persistent = false;
637 }
638
639 inline void NiftiImage::copy (const NiftiImage &source)
640 {
641 const nifti_image *sourceStruct = source;
642 copy(sourceStruct);
643 }
644
645 inline void NiftiImage::copy (const Block &source)
646 {
647 if (image != NULL && !persistent)
648 nifti_image_free(image);
649
650 const nifti_image *sourceStruct = source.image;
651 if (sourceStruct == NULL)
652 image = NULL;
653 else
654 {
655 image = nifti_copy_nim_info(sourceStruct);
656 image->dim[0] = source.image->dim[0] - 1;
657 image->dim[source.dimension] = 1;
658 image->pixdim[source.dimension] = 1.0;
659 nifti_update_dims_from_array(image);
660
661 if (sourceStruct->data != NULL)
662 {
663 size_t blockSize = nifti_get_volsize(image);
664 image->data = calloc(1, blockSize);
665 memcpy(image->data, static_cast<char*>(source.image->data) + blockSize*source.index, blockSize);
666 }
667 }
668
669 persistent = false;
670 }
671
672 #ifndef _NO_R__
673
674 // Convert an S4 "nifti" object, as defined in the oro.nifti package, to a "nifti_image" struct
675 inline void NiftiImage::initFromNiftiS4 (const Rcpp::RObject &object, const bool copyData)
676 {
677 nifti_1_header header;
678 header.sizeof_hdr = 348;
679
680 const std::vector<short> dims = object.slot("dim_");
681 for (int i=0; i<8; i++)
682 header.dim[i] = dims[i];
683
684 header.intent_p1 = object.slot("intent_p1");
685 header.intent_p2 = object.slot("intent_p2");
686 header.intent_p3 = object.slot("intent_p3");
687 header.intent_code = object.slot("intent_code");
688
689 header.datatype = object.slot("datatype");
690 header.bitpix = object.slot("bitpix");
691
692 header.slice_start = object.slot("slice_start");
693 header.slice_end = object.slot("slice_end");
694 header.slice_code = Rcpp::as<int>(object.slot("slice_code"));
695 header.slice_duration = object.slot("slice_duration");
696
697 const std::vector<float> pixdims = object.slot("pixdim");
698 for (int i=0; i<8; i++)
699 header.pixdim[i] = pixdims[i];
700 header.xyzt_units = Rcpp::as<int>(object.slot("xyzt_units"));
701
702 header.vox_offset = object.slot("vox_offset");
703
704 header.scl_slope = object.slot("scl_slope");
705 header.scl_inter = object.slot("scl_inter");
706 header.toffset = object.slot("toffset");
707
708 header.cal_max = object.slot("cal_max");
709 header.cal_min = object.slot("cal_min");
710 header.glmax = header.glmin = 0;
711
712 strncpy(header.descrip, Rcpp::as<std::string>(object.slot("descrip")).c_str(), 79);
713 header.descrip[79] = '\0';
714 strncpy(header.aux_file, Rcpp::as<std::string>(object.slot("aux_file")).c_str(), 23);
715 header.aux_file[23] = '\0';
716 strncpy(header.intent_name, Rcpp::as<std::string>(object.slot("intent_name")).c_str(), 15);
717 header.intent_name[15] = '\0';
718 strncpy(header.magic, Rcpp::as<std::string>(object.slot("magic")).c_str(), 3);
719 header.magic[3] = '\0';
720
721 header.qform_code = object.slot("qform_code");
722 header.sform_code = object.slot("sform_code");
723
724 header.quatern_b = object.slot("quatern_b");
725 header.quatern_c = object.slot("quatern_c");
726 header.quatern_d = object.slot("quatern_d");
727 header.qoffset_x = object.slot("qoffset_x");
728 header.qoffset_y = object.slot("qoffset_y");
729 header.qoffset_z = object.slot("qoffset_z");
730
731 const std::vector<float> srow_x = object.slot("srow_x");
732 const std::vector<float> srow_y = object.slot("srow_y");
733 const std::vector<float> srow_z = object.slot("srow_z");
734 for (int i=0; i<4; i++)
735 {
736 header.srow_x[i] = srow_x[i];
737 header.srow_y[i] = srow_y[i];
738 header.srow_z[i] = srow_z[i];
739 }
740
741 if (header.datatype == DT_UINT8 || header.datatype == DT_INT16 || header.datatype == DT_INT32 || header.datatype == DT_INT8 || header.datatype == DT_UINT16 || header.datatype == DT_UINT32)
742 header.datatype = DT_INT32;
743 else if (header.datatype == DT_FLOAT32 || header.datatype == DT_FLOAT64)
744 header.datatype = DT_FLOAT64; // This assumes that sizeof(double) == 8
745 else
746 throw std::runtime_error("Data type is not supported");
747
748 this->image = nifti_convert_nhdr2nim(header, NULL);
749
750 const SEXP data = PROTECT(object.slot(".Data"));
751 if (!copyData || Rf_length(data) <= 1)
752 this->image->data = NULL;
753 else
754 {
755 const size_t dataSize = nifti_get_volsize(this->image);
756 this->image->data = calloc(1, dataSize);
757 if (header.datatype == DT_INT32)
758 {
759 Rcpp::IntegerVector intData(data);
760 std::copy(intData.begin(), intData.end(), static_cast<int32_t*>(this->image->data));
761 }
762 else
763 {
764 Rcpp::DoubleVector doubleData(data);
765 std::copy(doubleData.begin(), doubleData.end(), static_cast<double*>(this->image->data));
766 }
767 }
768 UNPROTECT(1);
769 }
770
771 inline void NiftiImage::initFromMriImage (const Rcpp::RObject &object, const bool copyData)
772 {
773 Rcpp::Reference mriImage(object);
774 Rcpp::Function getXform = mriImage.field("getXform");
775 Rcpp::NumericMatrix xform = getXform();
776
777 this->image = NULL;
778
779 if (Rf_length(mriImage.field("tags")) > 0)
780 initFromList(mriImage.field("tags"));
781
782 Rcpp::RObject data = mriImage.field("data");
783 if (data.inherits("SparseArray"))
784 {
785 Rcpp::Language call("as.array", data);
786 data = call.eval();
787 }
788
789 const short datatype = (Rf_isNull(data) ? DT_INT32 : sexpTypeToNiftiType(data.sexp_type()));
790
791 int dims[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
792 const std::vector<int> dimVector = mriImage.field("imageDims");
793 const int nDims = std::min(7, int(dimVector.size()));
794 dims[0] = nDims;
795 size_t nVoxels = 1;
796 for (int i=0; i<nDims; i++)
797 {
798 dims[i+1] = dimVector[i];
799 nVoxels *= dimVector[i];
800 }
801
802 if (this->image == NULL)
803 this->image = nifti_make_new_nim(dims, datatype, FALSE);
804 else
805 {
806 std::copy(dims, dims+8, this->image->dim);
807 this->image->datatype = datatype;
808 nifti_datatype_sizes(image->datatype, &image->nbyper, NULL);
809 }
810
811 if (copyData && !Rf_isNull(data))
812 {
813 // NB: nifti_get_volsize() will not be right here if there were tags
814 const size_t dataSize = nVoxels * image->nbyper;
815 this->image->data = calloc(1, dataSize);
816 if (datatype == DT_INT32)
817 memcpy(this->image->data, INTEGER(data), dataSize);
818 else
819 memcpy(this->image->data, REAL(data), dataSize);
820 }
821 else
822 this->image->data = NULL;
823
824 const std::vector<float> pixdimVector = mriImage.field("voxelDims");
825 const int pixdimLength = pixdimVector.size();
826 for (int i=0; i<std::min(pixdimLength,nDims); i++)
827 this->image->pixdim[i+1] = std::abs(pixdimVector[i]);
828
829 const std::vector<std::string> pixunitsVector = mriImage.field("voxelDimUnits");
830 setPixunits(pixunitsVector);
831
832 if (xform.rows() != 4 || xform.cols() != 4)
833 this->image->qform_code = this->image->sform_code = 0;
834 else
835 {
836 mat44 matrix;
837 for (int i=0; i<4; i++)
838 {
839 for (int j=0; j<4; j++)
840 matrix.m[i][j] = static_cast<float>(xform(i,j));
841 }
842
843 this->image->qto_xyz = matrix;
844 this->image->qto_ijk = nifti_mat44_inverse(image->qto_xyz);
845 nifti_mat44_to_quatern(image->qto_xyz, &image->quatern_b, &image->quatern_c, &image->quatern_d, &image->qoffset_x, &image->qoffset_y, &image->qoffset_z, NULL, NULL, NULL, &image->qfac);
846
847 this->image->sto_xyz = matrix;
848 this->image->sto_ijk = nifti_mat44_inverse(image->sto_xyz);
849
850 this->image->qform_code = this->image->sform_code = 2;
851 }
852 }
853
854 inline void NiftiImage::initFromList (const Rcpp::RObject &object)
855 {
856 Rcpp::List list(object);
857 nifti_1_header *header = nifti_make_new_header(NULL, DT_FLOAT64);
858
859 const Rcpp::CharacterVector _names = list.names();
860 std::set<std::string> names;
861 for (Rcpp::CharacterVector::const_iterator it=_names.begin(); it!=_names.end(); it++)
862 names.insert(Rcpp::as<std::string>(*it));
863
864 internal::copyIfPresent(list, names, "sizeof_hdr", header->sizeof_hdr);
865
866 internal::copyIfPresent(list, names, "dim_info", header->dim_info);
867 if (names.count("dim") == 1)
868 {
869 std::vector<short> dim = list["dim"];
870 for (size_t i=0; i<std::min(dim.size(),size_t(8)); i++)
871 header->dim[i] = dim[i];
872 }
873
874 internal::copyIfPresent(list, names, "intent_p1", header->intent_p1);
875 internal::copyIfPresent(list, names, "intent_p2", header->intent_p2);
876 internal::copyIfPresent(list, names, "intent_p3", header->intent_p3);
877 internal::copyIfPresent(list, names, "intent_code", header->intent_code);
878
879 internal::copyIfPresent(list, names, "datatype", header->datatype);
880 internal::copyIfPresent(list, names, "bitpix", header->bitpix);
881
882 internal::copyIfPresent(list, names, "slice_start", header->slice_start);
883 if (names.count("pixdim") == 1)
884 {
885 std::vector<float> pixdim = list["pixdim"];
886 for (size_t i=0; i<std::min(pixdim.size(),size_t(8)); i++)
887 header->pixdim[i] = pixdim[i];
888 }
889 internal::copyIfPresent(list, names, "vox_offset", header->vox_offset);
890 internal::copyIfPresent(list, names, "scl_slope", header->scl_slope);
891 internal::copyIfPresent(list, names, "scl_inter", header->scl_inter);
892 internal::copyIfPresent(list, names, "slice_end", header->slice_end);
893 internal::copyIfPresent(list, names, "slice_code", header->slice_code);
894 internal::copyIfPresent(list, names, "xyzt_units", header->xyzt_units);
895 internal::copyIfPresent(list, names, "cal_max", header->cal_max);
896 internal::copyIfPresent(list, names, "cal_min", header->cal_min);
897 internal::copyIfPresent(list, names, "slice_duration", header->slice_duration);
898 internal::copyIfPresent(list, names, "toffset", header->toffset);
899
900 if (names.count("descrip") == 1)
901 strcpy(header->descrip, Rcpp::as<std::string>(list["descrip"]).substr(0,79).c_str());
902 if (names.count("aux_file") == 1)
903 strcpy(header->aux_file, Rcpp::as<std::string>(list["aux_file"]).substr(0,23).c_str());
904
905 internal::copyIfPresent(list, names, "qform_code", header->qform_code);
906 internal::copyIfPresent(list, names, "sform_code", header->sform_code);
907 internal::copyIfPresent(list, names, "quatern_b", header->quatern_b);
908 internal::copyIfPresent(list, names, "quatern_c", header->quatern_c);
909 internal::copyIfPresent(list, names, "quatern_d", header->quatern_d);
910 internal::copyIfPresent(list, names, "qoffset_x", header->qoffset_x);
911 internal::copyIfPresent(list, names, "qoffset_y", header->qoffset_y);
912 internal::copyIfPresent(list, names, "qoffset_z", header->qoffset_z);
913
914 if (names.count("srow_x") == 1)
915 {
916 std::vector<float> srow_x = list["srow_x"];
917 for (size_t i=0; i<std::min(srow_x.size(),size_t(4)); i++)
918 header->srow_x[i] = srow_x[i];
919 }
920 if (names.count("srow_y") == 1)
921 {
922 std::vector<float> srow_y = list["srow_y"];
923 for (size_t i=0; i<std::min(srow_y.size(),size_t(4)); i++)
924 header->srow_y[i] = srow_y[i];
925 }
926 if (names.count("srow_z") == 1)
927 {
928 std::vector<float> srow_z = list["srow_z"];
929 for (size_t i=0; i<std::min(srow_z.size(),size_t(4)); i++)
930 header->srow_z[i] = srow_z[i];
931 }
932
933 if (names.count("intent_name") == 1)
934 strcpy(header->intent_name, Rcpp::as<std::string>(list["intent_name"]).substr(0,15).c_str());
935 if (names.count("magic") == 1)
936 strcpy(header->magic, Rcpp::as<std::string>(list["magic"]).substr(0,3).c_str());
937
938 this->image = nifti_convert_nhdr2nim(*header, NULL);
939 this->image->data = NULL;
940 free(header);
941 }
942
943 inline void NiftiImage::initFromArray (const Rcpp::RObject &object, const bool copyData)
944 {
945 int dims[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
946 const std::vector<int> dimVector = object.attr("dim");
947
948 const int nDims = std::min(7, int(dimVector.size()));
949 dims[0] = nDims;
950 for (int i=0; i<nDims; i++)
951 dims[i+1] = dimVector[i];
952
953 const short datatype = sexpTypeToNiftiType(object.sexp_type());
954 this->image = nifti_make_new_nim(dims, datatype, int(copyData));
955
956 if (copyData)
957 {
958 const size_t dataSize = nifti_get_volsize(image);
959 if (datatype == DT_INT32)
960 memcpy(this->image->data, INTEGER(object), dataSize);
961 else
962 memcpy(this->image->data, REAL(object), dataSize);
963 }
964 else
965 this->image->data = NULL;
966
967 if (object.hasAttribute("pixdim"))
968 {
969 const std::vector<float> pixdimVector = object.attr("pixdim");
970 const int pixdimLength = pixdimVector.size();
971 for (int i=0; i<std::min(pixdimLength,nDims); i++)
972 this->image->pixdim[i+1] = pixdimVector[i];
973 }
974
975 if (object.hasAttribute("pixunits"))
976 {
977 const std::vector<std::string> pixunitsVector = object.attr("pixunits");
978 setPixunits(pixunitsVector);
979 }
980 }
981
982 inline NiftiImage::NiftiImage (const SEXP object, const bool readData)
983 : persistent(false)
984 {
985 Rcpp::RObject imageObject(object);
986 bool resolved = false;
987
988 if (imageObject.hasAttribute(".nifti_image_ptr"))
989 {
990 Rcpp::XPtr<NiftiImage> imagePtr(SEXP(imageObject.attr(".nifti_image_ptr")));
991 if (imagePtr.get() != NULL)
992 {
993 this->image = imagePtr->image;
994 this->persistent = true;
995 resolved = true;
996
997 if (imageObject.hasAttribute("dim"))
998 update(object);
999 }
1000 else if (Rf_isString(object))
1001 throw std::runtime_error("Internal image is not valid");
1002 else
1003 Rf_warning("Ignoring invalid internal pointer");
1004 }
1005
1006 if (!resolved)
1007 {
1008 if (Rf_isNull(object))
1009 this->image = NULL;
1010 else if (Rf_isString(object))
1011 {
1012 const std::string path = Rcpp::as<std::string>(object);
1013 this->image = nifti_image_read(path.c_str(), readData);
1014 if (this->image == NULL)
1015 throw std::runtime_error("Failed to read image from path " + path);
1016 }
1017 else if (imageObject.inherits("nifti"))
1018 initFromNiftiS4(imageObject, readData);
1019 else if (imageObject.inherits("anlz"))
1020 throw std::runtime_error("Cannot currently convert objects of class \"anlz\"");
1021 else if (imageObject.inherits("MriImage"))
1022 initFromMriImage(imageObject, readData);
1023 else if (Rf_isVectorList(object))
1024 initFromList(imageObject);
1025 else if (imageObject.hasAttribute("dim"))
1026 initFromArray(imageObject, readData);
1027 else
1028 throw std::runtime_error("Cannot convert object of class \"" + Rcpp::as<std::string>(imageObject.attr("class")) + "\" to a nifti_image");
1029 }
1030
1031 if (this->image != NULL)
1032 nifti_update_dims_from_array(this->image);
1033
1034 #ifndef NDEBUG
1035 Rprintf("Creating NiftiImage with pointer %p (from SEXP)\n", this->image);
1036 #endif
1037 }
1038
1039 #endif // _NO_R__
1040
1041 inline void NiftiImage::updatePixdim (const std::vector<float> &pixdim)
1042 {
1043 const int nDims = image->dim[0];
1044 const std::vector<float> origPixdim(image->pixdim+1, image->pixdim+4);
1045
1046 for (int i=1; i<8; i++)
1047 image->pixdim[i] = 0.0;
1048
1049 const int pixdimLength = pixdim.size();
1050 for (int i=0; i<std::min(pixdimLength,nDims); i++)
1051 image->pixdim[i+1] = pixdim[i];
1052
1053 if (!std::equal(origPixdim.begin(), origPixdim.begin() + std::min(3,nDims), pixdim.begin()))
1054 {
1055 mat33 scaleMatrix;
1056 for (int i=0; i<3; i++)
1057 {
1058 for (int j=0; j<3; j++)
1059 {
1060 if (i != j)
1061 scaleMatrix.m[i][j] = 0.0;
1062 else if (i >= nDims)
1063 scaleMatrix.m[i][j] = 1.0;
1064 else
1065 scaleMatrix.m[i][j] = pixdim[i] / origPixdim[i];
1066 }
1067 }
1068
1069 if (image->qform_code > 0)
1070 {
1071 mat33 prod = nifti_mat33_mul(scaleMatrix, internal::topLeftCorner(image->qto_xyz));
1072 for (int i=0; i<3; i++)
1073 {
1074 for (int j=0; j<3; j++)
1075 image->qto_xyz.m[i][j] = prod.m[i][j];
1076 }
1077 image->qto_ijk = nifti_mat44_inverse(image->qto_xyz);
1078 nifti_mat44_to_quatern(image->qto_xyz, &image->quatern_b, &image->quatern_c, &image->quatern_d, &image->qoffset_x, &image->qoffset_y, &image->qoffset_z, NULL, NULL, NULL, &image->qfac);
1079 }
1080
1081 if (image->sform_code > 0)
1082 {
1083 mat33 prod = nifti_mat33_mul(scaleMatrix, internal::topLeftCorner(image->sto_xyz));
1084 for (int i=0; i<3; i++)
1085 {
1086 for (int j=0; j<3; j++)
1087 image->sto_xyz.m[i][j] = prod.m[i][j];
1088 }
1089 image->sto_ijk = nifti_mat44_inverse(image->sto_xyz);
1090 }
1091 }
1092 }
1093
1094 inline void NiftiImage::setPixunits (const std::vector<std::string> &pixunits)
1095 {
1096 for (size_t i=0; i<pixunits.size(); i++)
1097 {
1098 if (pixunits[i] == "m")
1099 image->xyz_units = NIFTI_UNITS_METER;
1100 else if (pixunits[i] == "mm")
1101 image->xyz_units = NIFTI_UNITS_MM;
1102 else if (pixunits[i] == "um")
1103 image->xyz_units = NIFTI_UNITS_MICRON;
1104 else if (pixunits[i] == "s")
1105 image->time_units = NIFTI_UNITS_SEC;
1106 else if (pixunits[i] == "ms")
1107 image->time_units = NIFTI_UNITS_MSEC;
1108 else if (pixunits[i] == "us")
1109 image->time_units = NIFTI_UNITS_USEC;
1110 else if (pixunits[i] == "Hz")
1111 image->time_units = NIFTI_UNITS_HZ;
1112 else if (pixunits[i] == "ppm")
1113 image->time_units = NIFTI_UNITS_PPM;
1114 else if (pixunits[i] == "rad/s")
1115 image->time_units = NIFTI_UNITS_RADS;
1116 }
1117 }
1118
1119 inline NiftiImage & NiftiImage::rescale (const std::vector<float> &scales)
1120 {
1121 std::vector<float> pixdim(image->pixdim+1, image->pixdim+4);
1122
1123 for (int i=0; i<std::min(3, int(scales.size())); i++)
1124 {
1125 if (scales[i] != 1.0)
1126 {
1127 pixdim[i] /= scales[i];
1128 image->dim[i+1] = static_cast<int>(std::floor(image->dim[i+1] * scales[i]));
1129 }
1130 }
1131
1132 updatePixdim(pixdim);
1133 nifti_update_dims_from_array(image);
1134
1135 // Data vector is now the wrong size, so drop it
1136 if (!persistent)
1137 nifti_image_unload(image);
1138
1139 image->scl_slope = 0.0;
1140 image->scl_inter = 0.0;
1141
1142 return *this;
1143 }
1144
1145 inline NiftiImage & NiftiImage::reorient (const int icode, const int jcode, const int kcode)
1146 {
1147 if (this->isNull())
1148 return *this;
1149 if (image->qform_code == 0 && image->sform_code == 0)
1150 {
1151 Rf_warning("Image qform and sform codes are both zero, so it cannot be reoriented");
1152 return *this;
1153 }
1154
1155 int used[6] = { 0, 0, 0, 0, 0, 0 };
1156 used[icode-1] = 1;
1157 used[jcode-1] = 1;
1158 used[kcode-1] = 1;
1159 if (used[0]+used[1] != 1 || used[2]+used[3] != 1 || used[4]+used[5] != 1)
1160 throw std::runtime_error("Each canonical axis should be used exactly once");
1161
1162 const int codes[3] = { icode, jcode, kcode };
1163 const mat44 native = this->xform();
1164
1165 // Create a target xform (rotation matrix only)
1166 mat33 target;
1167 for (int j=0; j<3; j++)
1168 {
1169 for (int i=0; i<3; i++)
1170 target.m[i][j] = 0.0;
1171
1172 switch (codes[j])
1173 {
1174 case NIFTI_L2R: target.m[0][j] = 1.0; break;
1175 case NIFTI_R2L: target.m[0][j] = -1.0; break;
1176 case NIFTI_P2A: target.m[1][j] = 1.0; break;
1177 case NIFTI_A2P: target.m[1][j] = -1.0; break;
1178 case NIFTI_I2S: target.m[2][j] = 1.0; break;
1179 case NIFTI_S2I: target.m[2][j] = -1.0; break;
1180 }
1181 }
1182
1183 // Extract (inverse of) canonical axis matrix from native xform
1184 int nicode, njcode, nkcode;
1185 nifti_mat44_to_orientation(native, &nicode, &njcode, &nkcode);
1186 int ncodes[3] = { nicode, njcode, nkcode };
1187 mat33 nativeAxesTransposed;
1188 for (int i=0; i<3; i++)
1189 {
1190 for (int j=0; j<3; j++)
1191 nativeAxesTransposed.m[i][j] = 0.0;
1192
1193 switch (ncodes[i])
1194 {
1195 case NIFTI_L2R: nativeAxesTransposed.m[i][0] = 1.0; break;
1196 case NIFTI_R2L: nativeAxesTransposed.m[i][0] = -1.0; break;
1197 case NIFTI_P2A: nativeAxesTransposed.m[i][1] = 1.0; break;
1198 case NIFTI_A2P: nativeAxesTransposed.m[i][1] = -1.0; break;
1199 case NIFTI_I2S: nativeAxesTransposed.m[i][2] = 1.0; break;
1200 case NIFTI_S2I: nativeAxesTransposed.m[i][2] = -1.0; break;
1201 }
1202 }
1203
1204 // Check for no-op case
1205 if (icode == nicode && jcode == njcode && kcode == nkcode)
1206 return *this;
1207
1208 // The transform is t(approx_old_xform) %*% target_xform
1209 // The new xform is old_xform %*% transform
1210 // NB: "transform" is really 4x4, but the last row and column are filled implicitly during the multiplication loop
1211 mat33 transform = nifti_mat33_mul(nativeAxesTransposed, target);
1212 mat44 result;
1213 for (int i=0; i<4; i++)
1214 {
1215 for (int j=0; j<3; j++)
1216 result.m[i][j] = native.m[i][0] * transform.m[0][j] + native.m[i][1] * transform.m[1][j] + native.m[i][2] * transform.m[2][j];
1217
1218 result.m[i][3] = native.m[i][3];
1219 }
1220
1221 // Update the xforms with nonzero codes
1222 if (image->qform_code > 0)
1223 {
1224 image->qto_xyz = result;
1225 image->qto_ijk = nifti_mat44_inverse(image->qto_xyz);
1226 nifti_mat44_to_quatern(image->qto_xyz, &image->quatern_b, &image->quatern_c, &image->quatern_d, &image->qoffset_x, &image->qoffset_y, &image->qoffset_z, NULL, NULL, NULL, &image->qfac);
1227 }
1228 if (image->sform_code > 0)
1229 {
1230 image->sto_xyz = result;
1231 image->sto_ijk = nifti_mat44_inverse(image->sto_xyz);
1232 }
1233
1234 // Extract the mapping between dimensions and the signs
1235 int locs[3], signs[3], newdim[3];
1236 float newpixdim[3];
1237 double maxes[3] = { R_NegInf, R_NegInf, R_NegInf };
1238 for (int j=0; j<3; j++)
1239 {
1240 for (int i=0; i<3; i++)
1241 {
1242 const double value = static_cast<double>(transform.m[i][j]);
1243 if (fabs(value) > maxes[j])
1244 {
1245 maxes[j] = fabs(value);
1246 signs[j] = value > 0.0 ? 1 : -1;
1247 locs[j] = i;
1248 }
1249 }
1250
1251 // Permute dim and pixdim
1252 newdim[j] = image->dim[locs[j]+1];
1253 newpixdim[j] = image->pixdim[locs[j]+1];
1254 }
1255
1256 // Calculate strides in target space
1257 ptrdiff_t strides[3];
1258 strides[locs[0]] = 1;
1259 for (int n=1; n<3; n++)
1260 strides[locs[n]] = strides[locs[n-1]] * image->dim[locs[n-1]+1];
1261
1262 if (image->data != NULL)
1263 {
1264 size_t volSize = size_t(image->nx * image->ny * image->nz);
1265 size_t nVolumes = std::max(size_t(1), image->nvox / volSize);
1266
1267 const std::vector<double> oldData = this->getData<double>();
1268 std::vector<double> newData(image->nvox);
1269
1270 // Where the sign is negative we need to start at the end of the dimension
1271 size_t volStart = 0;
1272 for (int i=0; i<3; i++)
1273 {
1274 if (signs[i] < 0)
1275 volStart += (image->dim[i+1] - 1) * strides[i];
1276 }
1277
1278 // Iterate over the data and place it into a new vector
1279 std::vector<double>::const_iterator it = oldData.begin();
1280 for (size_t v=0; v<nVolumes; v++)
1281 {
1282 for (int k=0; k<image->nz; k++)
1283 {
1284 ptrdiff_t offset = k * strides[2] * signs[2];
1285 for (int j=0; j<image->ny; j++)
1286 {
1287 for (int i=0; i<image->nx; i++)
1288 {
1289 newData[volStart + offset] = *it++;
1290 offset += strides[0] * signs[0];
1291 }
1292 offset += strides[1] * signs[1] - image->nx * strides[0] * signs[0];
1293 }
1294 }
1295 volStart += volSize;
1296 }
1297
1298 // Replace the existing data in the image
1299 this->replaceData(newData);
1300 }
1301
1302 // Copy new dims and pixdims in
1303 // NB: Old dims are used above, so this must happen last
1304 std::copy(newdim, newdim+3, image->dim+1);
1305 std::copy(newpixdim, newpixdim+3, image->pixdim+1);
1306 nifti_update_dims_from_array(image);
1307
1308 return *this;
1309 }
1310
1311 inline NiftiImage & NiftiImage::reorient (const std::string &orientation)
1312 {
1313 if (orientation.length() != 3)
1314 throw std::runtime_error("Orientation string should have exactly three characters");
1315
1316 int codes[3];
1317 for (int i=0; i<3; i++)
1318 {
1319 switch(orientation[i])
1320 {
1321 case 'r': case 'R': codes[i] = NIFTI_L2R; break;
1322 case 'l': case 'L': codes[i] = NIFTI_R2L; break;
1323 case 'a': case 'A': codes[i] = NIFTI_P2A; break;
1324 case 'p': case 'P': codes[i] = NIFTI_A2P; break;
1325 case 's': case 'S': codes[i] = NIFTI_I2S; break;
1326 case 'i': case 'I': codes[i] = NIFTI_S2I; break;
1327
1328 default:
1329 throw std::runtime_error("Orientation string is invalid");
1330 }
1331 }
1332
1333 return reorient(codes[0], codes[1], codes[2]);
1334 }
1335
1336 #ifndef _NO_R__
1337
1338 inline NiftiImage & NiftiImage::update (const SEXP array)
1339 {
1340 Rcpp::RObject object(array);
1341 if (!object.hasAttribute("dim"))
1342 return *this;
1343
1344 for (int i=0; i<8; i++)
1345 image->dim[i] = 0;
1346 const std::vector<int> dimVector = object.attr("dim");
1347
1348 const int nDims = std::min(7, int(dimVector.size()));
1349 image->dim[0] = nDims;
1350 for (int i=0; i<nDims; i++)
1351 image->dim[i+1] = dimVector[i];
1352
1353 if (object.hasAttribute("pixdim"))
1354 {
1355 const std::vector<float> pixdimVector = object.attr("pixdim");
1356 updatePixdim(pixdimVector);
1357 }
1358
1359 if (object.hasAttribute("pixunits"))
1360 {
1361 const std::vector<std::string> pixunitsVector = object.attr("pixunits");
1362 setPixunits(pixunitsVector);
1363 }
1364
1365 // This NIfTI-1 library function clobbers dim[0] if the last dimension is unitary; we undo that here
1366 nifti_update_dims_from_array(image);
1367 image->dim[0] = image->ndim = nDims;
1368
1369 image->datatype = NiftiImage::sexpTypeToNiftiType(object.sexp_type());
1370 nifti_datatype_sizes(image->datatype, &image->nbyper, NULL);
1371
1372 if (!persistent)
1373 nifti_image_unload(image);
1374
1375 const size_t dataSize = nifti_get_volsize(image);
1376 image->data = calloc(1, dataSize);
1377 if (image->datatype == DT_INT32)
1378 {
1379 memcpy(image->data, INTEGER(object), dataSize);
1380 image->cal_min = static_cast<float>(*std::min_element(INTEGER(object), INTEGER(object)+image->nvox));
1381 image->cal_max = static_cast<float>(*std::max_element(INTEGER(object), INTEGER(object)+image->nvox));
1382 }
1383 else
1384 {
1385 memcpy(image->data, REAL(object), dataSize);
1386 image->cal_min = static_cast<float>(*std::min_element(REAL(object), REAL(object)+image->nvox));
1387 image->cal_max = static_cast<float>(*std::max_element(REAL(object), REAL(object)+image->nvox));
1388 }
1389
1390 image->scl_slope = 0.0;
1391 image->scl_inter = 0.0;
1392
1393 return *this;
1394 }
1395
1396 #endif// _NO_R__
1397
1398 inline mat44 NiftiImage::xform (const bool preferQuaternion) const
1399 {
1400 if (image == NULL)
1401 {
1402 mat44 matrix;
1403 for (int i=0; i<4; i++)
1404 {
1405 for (int j=0; j<4; j++)
1406 matrix.m[i][j] = 0.0;
1407 }
1408 return matrix;
1409 }
1410 else if (image->qform_code <= 0 && image->sform_code <= 0)
1411 {
1412 // No qform or sform so use pixdim (NB: other software may assume differently)
1413 mat44 matrix;
1414 for (int i=0; i<4; i++)
1415 {
1416 for (int j=0; j<4; j++)
1417 {
1418 if (i != j)
1419 matrix.m[i][j] = 0.0;
1420 else if (i == 3)
1421 matrix.m[3][3] = 1.0;
1422 else
1423 matrix.m[i][j] = (image->pixdim[i+1]==0.0 ? 1.0 : image->pixdim[i+1]);
1424 }
1425 }
1426 return matrix;
1427 }
1428 else if ((preferQuaternion && image->qform_code > 0) || image->sform_code <= 0)
1429 return image->qto_xyz;
1430 else
1431 return image->sto_xyz;
1432 }
1433
1434 template <typename TargetType>
1435 inline std::vector<TargetType> NiftiImage::Block::getData () const
1436 {
1437 if (image.isNull())
1438 return std::vector<TargetType>();
1439
1440 size_t blockSize = 1;
1441 for (int i=1; i<dimension; i++)
1442 blockSize *= image->dim[i];
1443
1444 std::vector<TargetType> data(blockSize);
1445 internal::convertData<TargetType>(image->data, image->datatype, blockSize, data.begin(), blockSize*index);
1446
1447 if (image.isDataScaled())
1448 std::transform(data.begin(), data.end(), data.begin(), internal::DataRescaler<TargetType>(image->scl_slope,image->scl_inter));
1449
1450 return data;
1451 }
1452
1453 template <typename TargetType>
1454 inline std::vector<TargetType> NiftiImage::getData () const
1455 {
1456 if (this->isNull())
1457 return std::vector<TargetType>();
1458
1459 std::vector<TargetType> data(image->nvox);
1460 internal::convertData<TargetType>(image->data, image->datatype, image->nvox, data.begin());
1461
1462 if (this->isDataScaled())
1463 std::transform(data.begin(), data.end(), data.begin(), internal::DataRescaler<TargetType>(image->scl_slope,image->scl_inter));
1464
1465 return data;
1466 }
1467
1468 inline NiftiImage & NiftiImage::changeDatatype (const short datatype)
1469 {
1470 if (this->isNull() || image->datatype == datatype)
1471 return *this;
1472
1473 if (image->data != NULL)
1474 {
1475 int bytesPerPixel;
1476 nifti_datatype_sizes(datatype, &bytesPerPixel, NULL);
1477 void *data = calloc(image->nvox, bytesPerPixel);
1478
1479 switch (datatype)
1480 {
1481 case DT_UINT8:
1482 internal::convertData<uint8_t>(image->data, image->datatype, image->nvox, static_cast<uint8_t *>(data));
1483 break;
1484
1485 case DT_INT16:
1486 internal::convertData<int16_t>(image->data, image->datatype, image->nvox, static_cast<int16_t *>(data));
1487 break;
1488
1489 case DT_INT32:
1490 internal::convertData<int32_t>(image->data, image->datatype, image->nvox, static_cast<int32_t *>(data));
1491 break;
1492
1493 case DT_FLOAT32:
1494 internal::convertData<float>(image->data, image->datatype, image->nvox, static_cast<float *>(data));
1495 break;
1496
1497 case DT_FLOAT64:
1498 internal::convertData<double>(image->data, image->datatype, image->nvox, static_cast<double *>(data));
1499 break;
1500
1501 case DT_INT8:
1502 internal::convertData<int8_t>(image->data, image->datatype, image->nvox, static_cast<int8_t *>(data));
1503 break;
1504
1505 case DT_UINT16:
1506 internal::convertData<uint16_t>(image->data, image->datatype, image->nvox, static_cast<uint16_t *>(data));
1507 break;
1508
1509 case DT_UINT32:
1510 internal::convertData<uint32_t>(image->data, image->datatype, image->nvox, static_cast<uint32_t *>(data));
1511 break;
1512
1513 case DT_INT64:
1514 internal::convertData<int64_t>(image->data, image->datatype, image->nvox, static_cast<int64_t *>(data));
1515 break;
1516
1517 case DT_UINT64:
1518 internal::convertData<uint64_t>(image->data, image->datatype, image->nvox, static_cast<uint64_t *>(data));
1519 break;
1520
1521 default:
1522 throw std::runtime_error("Unsupported data type (" + std::string(nifti_datatype_string(datatype)) + ")");
1523 }
1524
1525 nifti_image_unload(image);
1526 image->data = data;
1527 }
1528
1529 image->datatype = datatype;
1530 nifti_datatype_sizes(datatype, &image->nbyper, &image->swapsize);
1531
1532 return *this;
1533 }
1534
1535 inline NiftiImage & NiftiImage::changeDatatype (const std::string &datatype)
1536 {
1537 return changeDatatype(internal::stringToDatatype(datatype));
1538 }
1539
1540 template <typename SourceType>
1541 inline NiftiImage & NiftiImage::replaceData (const std::vector<SourceType> &data, const short datatype)
1542 {
1543 if (this->isNull())
1544 return *this;
1545 else if (data.size() != image->nvox)
1546 throw std::runtime_error("New data length does not match the number of voxels in the image");
1547
1548 if (datatype != DT_NONE)
1549 {
1550 nifti_image_unload(image);
1551 image->datatype = datatype;
1552 nifti_datatype_sizes(datatype, &image->nbyper, &image->swapsize);
1553 }
1554
1555 if (image->data == NULL)
1556 image->data = calloc(image->nvox, image->nbyper);
1557 internal::replaceData<SourceType>(data.begin(), data.end(), image->data, image->datatype);
1558
1559 image->scl_slope = 0.0;
1560 image->scl_inter = 0.0;
1561 image->cal_min = static_cast<float>(*std::min_element(data.begin(), data.end()));
1562 image->cal_max = static_cast<float>(*std::max_element(data.begin(), data.end()));
1563
1564 return *this;
1565 }
1566
1567 inline void NiftiImage::toFile (const std::string fileName, const short datatype) const
1568 {
1569 // Copy the source image only if the datatype will be changed
1570 NiftiImage imageToWrite(image, datatype != DT_NONE);
1571
1572 if (datatype == DT_NONE)
1573 imageToWrite.setPersistence(true);
1574 else
1575 imageToWrite.changeDatatype(datatype);
1576
1577 const int status = nifti_set_filenames(imageToWrite, fileName.c_str(), false, true);
1578 if (status != 0)
1579 throw std::runtime_error("Failed to set filenames for NIfTI object");
1580 nifti_image_write(imageToWrite);
1581 }
1582
1583 inline void NiftiImage::toFile (const std::string fileName, const std::string &datatype) const
1584 {
1585 toFile(fileName, internal::stringToDatatype(datatype));
1586 }
1587
1588 #ifndef _NO_R__
1589
1590 inline Rcpp::RObject NiftiImage::toArray () const
1591 {
1592 Rcpp::RObject array;
1593
1594 if (this->isNull())
1595 return array;
1596 else if (this->isDataScaled())
1597 {
1598 array = internal::imageDataToArray<REALSXP>(image);
1599 std::transform(REAL(array), REAL(array)+Rf_length(array), REAL(array), internal::DataRescaler<double>(image->scl_slope,image->scl_inter));
1600 }
1601 else
1602 {
1603 switch (image->datatype)
1604 {
1605 case DT_UINT8:
1606 case DT_INT16:
1607 case DT_INT32:
1608 case DT_INT8:
1609 case DT_UINT16:
1610 case DT_UINT32:
1611 case DT_INT64:
1612 case DT_UINT64:
1613 array = internal::imageDataToArray<INTSXP>(image);
1614 break;
1615
1616 case DT_FLOAT32:
1617 case DT_FLOAT64:
1618 array = internal::imageDataToArray<REALSXP>(image);
1619 break;
1620
1621 default:
1622 throw std::runtime_error("Unsupported data type (" + std::string(nifti_datatype_string(image->datatype)) + ")");
1623 }
1624 }
1625
1626 internal::addAttributes(array, image);
1627 array.attr("class") = Rcpp::CharacterVector::create("niftiImage", "array");
1628 return array;
1629 }
1630
1631 inline Rcpp::RObject NiftiImage::toPointer (const std::string label) const
1632 {
1633 if (this->isNull())
1634 return Rcpp::RObject();
1635 else
1636 {
1637 Rcpp::RObject string = Rcpp::wrap(label);
1638 internal::addAttributes(string, image, false);
1639 string.attr("class") = Rcpp::CharacterVector::create("internalImage", "niftiImage");
1640 return string;
1641 }
1642 }
1643
1644 inline Rcpp::RObject NiftiImage::toArrayOrPointer (const bool internal, const std::string label) const
1645 {
1646 return (internal ? toPointer(label) : toArray());
1647 }
1648
1649 inline Rcpp::RObject NiftiImage::headerToList () const
1650 {
1651 if (this->image == NULL)
1652 return Rcpp::RObject();
1653
1654 nifti_1_header header = nifti_convert_nim2nhdr(this->image);
1655 Rcpp::List result;
1656
1657 result["sizeof_hdr"] = header.sizeof_hdr;
1658
1659 result["dim_info"] = int(header.dim_info);
1660 result["dim"] = std::vector<short>(header.dim, header.dim+8);
1661
1662 result["intent_p1"] = header.intent_p1;
1663 result["intent_p2"] = header.intent_p2;
1664 result["intent_p3"] = header.intent_p3;
1665 result["intent_code"] = header.intent_code;
1666
1667 result["datatype"] = header.datatype;
1668 result["bitpix"] = header.bitpix;
1669
1670 result["slice_start"] = header.slice_start;
1671 result["pixdim"] = std::vector<float>(header.pixdim, header.pixdim+8);
1672 result["vox_offset"] = header.vox_offset;
1673 result["scl_slope"] = header.scl_slope;
1674 result["scl_inter"] = header.scl_inter;
1675 result["slice_end"] = header.slice_end;
1676 result["slice_code"] = int(header.slice_code);
1677 result["xyzt_units"] = int(header.xyzt_units);
1678 result["cal_max"] = header.cal_max;
1679 result["cal_min"] = header.cal_min;
1680 result["slice_duration"] = header.slice_duration;
1681 result["toffset"] = header.toffset;
1682 result["descrip"] = std::string(header.descrip, 80);
1683 result["aux_file"] = std::string(header.aux_file, 24);
1684
1685 result["qform_code"] = header.qform_code;
1686 result["sform_code"] = header.sform_code;
1687 result["quatern_b"] = header.quatern_b;
1688 result["quatern_c"] = header.quatern_c;
1689 result["quatern_d"] = header.quatern_d;
1690 result["qoffset_x"] = header.qoffset_x;
1691 result["qoffset_y"] = header.qoffset_y;
1692 result["qoffset_z"] = header.qoffset_z;
1693 result["srow_x"] = std::vector<float>(header.srow_x, header.srow_x+4);
1694 result["srow_y"] = std::vector<float>(header.srow_y, header.srow_y+4);
1695 result["srow_z"] = std::vector<float>(header.srow_z, header.srow_z+4);
1696
1697 result["intent_name"] = std::string(header.intent_name, 16);
1698 result["magic"] = std::string(header.magic, 4);
1699
1700 result.attr("class") = Rcpp::CharacterVector::create("niftiHeader");
1701
1702 return result;
1703 }
1704
1705 #endif // _NO_R__
1706
1707 } // namespace
1708
1709 #endif
RNifti::NiftiImage::reorient
NiftiImage & reorient(const int i, const int j, const int k)
Reorient the image by permuting dimensions and potentially reversing some.
Definition: NiftiImage.h:1145
RNifti::NiftiImage::NiftiImage
NiftiImage()
Default constructor.
Definition: NiftiImage.h:220
RNifti::NiftiImage::Block::index
const int index
The location along dimension.
Definition: NiftiImage.h:54
RNifti::NiftiImage::xformToString
static std::string xformToString(const mat44 matrix)
Convert a 4x4 xform matrix to a string describing its canonical axes.
Definition: NiftiImage.h:127
RNifti::NiftiImage::dim
std::vector< int > dim() const
Return the dimensions of the image.
Definition: NiftiImage.h:398
RNifti::NiftiImage::operator->
const nifti_image * operator->() const
Allows a NiftiImage object to be treated as a pointer to a const nifti_image.
Definition: NiftiImage.h:320
RNifti::NiftiImage::headerToList
Rcpp::RObject headerToList() const
Create an R list containing raw image metadata.
Definition: NiftiImage.h:1649
RNifti::NiftiImage::persistent
bool persistent
Marker of persistence, which determines whether the nifti_image should be freed on destruction...
Definition: NiftiImage.h:152
RNifti::NiftiImage::pixdim
std::vector< float > pixdim() const
Return the dimensions of the pixels or voxels in the image.
Definition: NiftiImage.h:410
RNifti::NiftiImage::NiftiImage
NiftiImage(const std::string &path, const bool readData=true)
Initialise using a path string.
Definition: NiftiImage.h:273
RNifti::NiftiImage::rescale
NiftiImage & rescale(const std::vector< float > &scales)
Rescale the image, changing its image dimensions and pixel dimensions.
Definition: NiftiImage.h:1119
RNifti::NiftiImage::NiftiImage
NiftiImage(nifti_image *const image, const bool copy=false)
Initialise using an existing nifti_image pointer.
Definition: NiftiImage.h:255
RNifti
Definition: NiftiImage.h:37
RNifti::NiftiImage::initFromMriImage
void initFromMriImage(const Rcpp::RObject &object, const bool copyData=true)
Initialise the object from a reference object of class "MriImage".
Definition: NiftiImage.h:771
RNifti::NiftiImage::Block::operator=
Block & operator=(const NiftiImage &source)
Copy assignment operator, which allows a block in one image to be replaced with the contents of anoth...
Definition: NiftiImage.h:78
RNifti::NiftiImage::isNull
bool isNull() const
Determine whether or not the internal pointer is NULL.
Definition: NiftiImage.h:371
RNifti::NiftiImage::Block
Inner class referring to a subset of an image.
Definition: NiftiImage.h:50
RNifti::NiftiImage::slice
Block slice(const int i)
Extract a slice block from a 3D image.
Definition: NiftiImage.h:552
RNifti::NiftiImage::toPointer
Rcpp::RObject toPointer(const std::string label) const
Create an internal image to pass back to R.
Definition: NiftiImage.h:1631
RNifti::NiftiImage::replaceData
NiftiImage & replaceData(const std::vector< SourceType > &data, const short datatype=DT_NONE)
Replace the pixel data in the image with the contents of a vector.
Definition: NiftiImage.h:1541
RNifti::NiftiImage::getData
std::vector< TargetType > getData() const
Extract a vector of data from the image, casting it to any required element type. ...
Definition: NiftiImage.h:1454
RNifti::NiftiImage::initFromArray
void initFromArray(const Rcpp::RObject &object, const bool copyData=true)
Initialise the object from an R array.
Definition: NiftiImage.h:943
RNifti::NiftiImage::isDataScaled
bool isDataScaled() const
Determine whether nontrivial scale and slope parameters are set.
Definition: NiftiImage.h:381
RNifti::NiftiImage::initFromNiftiS4
void initFromNiftiS4(const Rcpp::RObject &object, const bool copyData=true)
Initialise the object from an S4 object of class "nifti".
Definition: NiftiImage.h:675
RNifti::NiftiImage::slice
const Block slice(const int i) const
Extract a slice block from a 3D image.
Definition: NiftiImage.h:545
RNifti::NiftiImage::volume
const Block volume(const int i) const
Extract a volume block from a 4D image.
Definition: NiftiImage.h:559
RNifti::NiftiImage::isPersistent
bool isPersistent() const
Determine whether or not the image is marked as persistent.
Definition: NiftiImage.h:376
RNifti::NiftiImage::setPixunits
void setPixunits(const std::vector< std::string > &pixunits)
Modify the pixel dimension units.
Definition: NiftiImage.h:1094
RNifti::NiftiImage::NiftiImage
NiftiImage(const Block &source)
Initialise from a block, copying in the data.
Definition: NiftiImage.h:240
RNifti::NiftiImage::~NiftiImage
virtual ~NiftiImage()
Destructor which frees the wrapped pointer, unless the object is marked as persistent.
Definition: NiftiImage.h:296
RNifti::NiftiImage
Thin wrapper around a C-style nifti_image struct that allows C++-style destruction.
Definition: NiftiImage.h:43
RNifti::NiftiImage::NiftiImage
NiftiImage(const NiftiImage &source)
Copy constructor.
Definition: NiftiImage.h:227
RNifti::NiftiImage::nBlocks
int nBlocks() const
Return the number of blocks in the image.
Definition: NiftiImage.h:514
RNifti::NiftiImage::update
NiftiImage & update(const SEXP array)
Update the image from an R array.
Definition: NiftiImage.h:1338
RNifti::NiftiImage::Block::dimension
const int dimension
The dimension along which the block applies (which should be the last)
Definition: NiftiImage.h:53
RNifti::NiftiImage::Block::image
const NiftiImage & image
The parent image.
Definition: NiftiImage.h:52
RNifti::NiftiImage::initFromList
void initFromList(const Rcpp::RObject &object)
Initialise the object from an R list with named elements, which can only contain metadata.
Definition: NiftiImage.h:854
RNifti::NiftiImage::toFile
void toFile(const std::string fileName, const short datatype=DT_NONE) const
Write the image to a NIfTI-1 file.
Definition: NiftiImage.h:1567
RNifti::NiftiImage::updatePixdim
void updatePixdim(const std::vector< float > &pixdim)
Modify the pixel dimensions, and potentially the xform matrices to match.
Definition: NiftiImage.h:1041
RNifti::NiftiImage::block
const Block block(const int i) const
Extract a block from the image.
Definition: NiftiImage.h:529
RNifti::NiftiImage::setPersistence
NiftiImage & setPersistence(const bool persistent)
Marked the image as persistent, so that it can be passed back to R.
Definition: NiftiImage.h:358
RNifti::NiftiImage::copy
void copy(const nifti_image *source)
Copy the contents of a nifti_image to create a new image.
Definition: NiftiImage.h:618
RNifti::NiftiImage::xform
mat44 xform(const bool preferQuaternion=true) const
Obtain an xform matrix, indicating the orientation of the image.
Definition: NiftiImage.h:1398
RNifti::NiftiImage::image
nifti_image * image
The wrapped nifti_image pointer.
Definition: NiftiImage.h:151
RNifti::NiftiImage::changeDatatype
NiftiImage & changeDatatype(const short datatype)
Change the datatype of the image, casting the pixel data if present.
Definition: NiftiImage.h:1468
RNifti::NiftiImage::Block::getData
std::vector< TargetType > getData() const
Extract a vector of data from a block, casting it to any required element type.
Definition: NiftiImage.h:1435
RNifti::NiftiImage::toArrayOrPointer
Rcpp::RObject toArrayOrPointer(const bool internal, const std::string label) const
A conditional method that calls either toArray or toPointer.
Definition: NiftiImage.h:1644
RNifti::NiftiImage::sexpTypeToNiftiType
static short sexpTypeToNiftiType(const int sexpType)
Convert between R SEXP object type and nifti_image datatype codes.
Definition: NiftiImage.h:111
RNifti::NiftiImage::nDims
int nDims() const
Return the number of dimensions in the image.
Definition: NiftiImage.h:386
RNifti::NiftiImage::volume
Block volume(const int i)
Extract a volume block from a 4D image.
Definition: NiftiImage.h:566
RNifti::NiftiImage::drop
NiftiImage & drop()
Drop unitary dimensions.
Definition: NiftiImage.h:424
RNifti::NiftiImage::toArray
Rcpp::RObject toArray() const
Create an R array from the image.
Definition: NiftiImage.h:1590
RNifti::NiftiImage::Block::Block
Block(const NiftiImage &image, const int dimension, const int index)
Standard constructor for this class.
Definition: NiftiImage.h:63
RNifti::NiftiImage::dropData
NiftiImage & dropData()
Drop the data from the image, retaining only the metadata.
Definition: NiftiImage.h:465
RNifti::NiftiImage::block
Block block(const int i)
Extract a block from the image.
Definition: NiftiImage.h:538