1 #ifndef _NIFTI_IMAGE_H_ 2 #define _NIFTI_IMAGE_H_ 11 #define R_NegInf -INFINITY 27 #include "niftilib/nifti1_io.h" 64 : image(image), dimension(dimension), index(index)
66 if (dimension != image->ndim)
67 throw std::runtime_error(
"Blocks must be along the last dimension in the image");
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");
87 blockSize *= image->
dim[i];
89 if (blockSize != source->nvox)
90 throw std::runtime_error(
"New data does not have the same size as the target block");
92 blockSize *= image->nbyper;
93 memcpy(static_cast<char*>(image->data) + blockSize*index, source->data, blockSize);
100 template <
typename TargetType>
101 std::vector<TargetType>
getData ()
const;
113 if (sexpType == INTSXP || sexpType == LGLSXP)
115 else if (sexpType == REALSXP)
118 throw std::runtime_error(
"Array elements must be numeric");
129 int icode, jcode, kcode;
130 nifti_mat44_to_orientation(matrix, &icode, &jcode, &kcode);
132 int codes[3] = { icode, jcode, kcode };
133 std::string result(
"---");
134 for (
int i=0; i<3; i++)
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;
158 void copy (
const nifti_image *source);
180 void initFromNiftiS4 (
const Rcpp::RObject &
object,
const bool copyData =
true);
187 void initFromMriImage (
const Rcpp::RObject &
object,
const bool copyData =
true);
200 void initFromArray (
const Rcpp::RObject &
object,
const bool copyData =
true);
214 void setPixunits (
const std::vector<std::string> &pixunits);
221 : image(NULL), persistent(false) {}
228 : image(NULL), persistent(false)
232 Rprintf(
"Creating NiftiImage with pointer %p (from NiftiImage)\n", this->image);
241 : image(NULL), persistent(false)
245 Rprintf(
"Creating NiftiImage with pointer %p (from Block)\n", this->image);
256 : image(NULL), persistent(false)
263 Rprintf(
"Creating NiftiImage with pointer %p (from pointer)\n", this->image);
273 NiftiImage (
const std::string &path,
const bool readData =
true)
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);
280 Rprintf(
"Creating NiftiImage with pointer %p (from string)\n", this->image);
290 NiftiImage (
const SEXP
object,
const bool readData =
true);
301 Rprintf(
"Freeing NiftiImage with pointer %p\n", this->image);
303 nifti_image_free(image);
310 operator const nifti_image* ()
const {
return image; }
315 operator nifti_image* () {
return image; }
335 Rprintf(
"Creating NiftiImage with pointer %p (from NiftiImage)\n", this->image);
349 Rprintf(
"Creating NiftiImage with pointer %p (from Block)\n", this->image);
363 Rprintf(
"Setting NiftiImage with pointer %p to be persistent\n", this->image);
371 bool isNull ()
const {
return (image == NULL); }
381 bool isDataScaled ()
const {
return (image != NULL && image->scl_slope != 0.0 && (image->scl_slope != 1.0 || image->scl_inter != 0.0)); }
398 std::vector<int>
dim ()
const 401 return std::vector<int>();
403 return std::vector<int>(image->dim+1, image->dim+image->ndim+1);
413 return std::vector<float>();
415 return std::vector<float>(image->pixdim+1, image->pixdim+image->ndim+1);
426 int ndim = image->ndim;
427 while (image->dim[ndim] < 2)
429 image->
dim[0] = image->ndim = ndim;
437 template <
typename TargetType>
438 std::vector<TargetType>
getData ()
const;
459 template <
typename SourceType>
467 nifti_image_unload(image);
509 mat44
xform (
const bool preferQuaternion =
true)
const;
519 return image->dim[image->ndim];
573 void toFile (
const std::string fileName,
const short datatype = DT_NONE)
const;
580 void toFile (
const std::string fileName,
const std::string &datatype)
const;
588 Rcpp::RObject
toArray ()
const;
595 Rcpp::RObject
toPointer (
const std::string label)
const;
603 Rcpp::RObject
toArrayOrPointer (
const bool internal,
const std::string label)
const;
616 #include "lib/NiftiImage_internal.h" 621 nifti_image_free(
image);
627 image = nifti_copy_nim_info(source);
628 if (source->data != NULL)
630 size_t dataSize = nifti_get_volsize(source);
631 image->data = calloc(1, dataSize);
632 memcpy(
image->data, source->data, dataSize);
641 const nifti_image *sourceStruct = source;
648 nifti_image_free(
image);
650 const nifti_image *sourceStruct = source.
image;
651 if (sourceStruct == NULL)
655 image = nifti_copy_nim_info(sourceStruct);
659 nifti_update_dims_from_array(
image);
661 if (sourceStruct->data != NULL)
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);
677 nifti_1_header header;
678 header.sizeof_hdr = 348;
680 const std::vector<short> dims =
object.slot(
"dim_");
681 for (
int i=0; i<8; i++)
682 header.dim[i] = dims[i];
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");
689 header.datatype =
object.slot(
"datatype");
690 header.bitpix =
object.slot(
"bitpix");
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");
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"));
702 header.vox_offset =
object.slot(
"vox_offset");
704 header.scl_slope =
object.slot(
"scl_slope");
705 header.scl_inter =
object.slot(
"scl_inter");
706 header.toffset =
object.slot(
"toffset");
708 header.cal_max =
object.slot(
"cal_max");
709 header.cal_min =
object.slot(
"cal_min");
710 header.glmax = header.glmin = 0;
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';
721 header.qform_code =
object.slot(
"qform_code");
722 header.sform_code =
object.slot(
"sform_code");
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");
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++)
736 header.srow_x[i] = srow_x[i];
737 header.srow_y[i] = srow_y[i];
738 header.srow_z[i] = srow_z[i];
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;
746 throw std::runtime_error(
"Data type is not supported");
748 this->
image = nifti_convert_nhdr2nim(header, NULL);
750 const SEXP data = PROTECT(
object.slot(
".Data"));
751 if (!copyData || Rf_length(data) <= 1)
752 this->
image->data = NULL;
755 const size_t dataSize = nifti_get_volsize(this->
image);
756 this->
image->data = calloc(1, dataSize);
757 if (header.datatype == DT_INT32)
759 Rcpp::IntegerVector intData(data);
760 std::copy(intData.begin(), intData.end(),
static_cast<int32_t*
>(this->
image->data));
764 Rcpp::DoubleVector doubleData(data);
765 std::copy(doubleData.begin(), doubleData.end(),
static_cast<double*
>(this->
image->data));
773 Rcpp::Reference mriImage(
object);
774 Rcpp::Function getXform = mriImage.field(
"getXform");
775 Rcpp::NumericMatrix
xform = getXform();
779 if (Rf_length(mriImage.field(
"tags")) > 0)
782 Rcpp::RObject data = mriImage.field(
"data");
783 if (data.inherits(
"SparseArray"))
785 Rcpp::Language call(
"as.array", data);
789 const short datatype = (Rf_isNull(data) ? DT_INT32 :
sexpTypeToNiftiType(data.sexp_type()));
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()));
796 for (
int i=0; i<
nDims; i++)
798 dims[i+1] = dimVector[i];
799 nVoxels *= dimVector[i];
802 if (this->
image == NULL)
803 this->
image = nifti_make_new_nim(dims, datatype, FALSE);
806 std::copy(dims, dims+8, this->
image->
dim);
807 this->
image->datatype = datatype;
808 nifti_datatype_sizes(
image->datatype, &
image->nbyper, NULL);
811 if (copyData && !Rf_isNull(data))
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);
819 memcpy(this->
image->data, REAL(data), dataSize);
822 this->
image->data = NULL;
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]);
829 const std::vector<std::string> pixunitsVector = mriImage.field(
"voxelDimUnits");
832 if (xform.rows() != 4 || xform.cols() != 4)
833 this->
image->qform_code = this->
image->sform_code = 0;
837 for (
int i=0; i<4; i++)
839 for (
int j=0; j<4; j++)
840 matrix.m[i][j] = static_cast<float>(xform(i,j));
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);
847 this->
image->sto_xyz = matrix;
848 this->
image->sto_ijk = nifti_mat44_inverse(
image->sto_xyz);
850 this->
image->qform_code = this->
image->sform_code = 2;
856 Rcpp::List list(
object);
857 nifti_1_header *header = nifti_make_new_header(NULL, DT_FLOAT64);
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));
864 internal::copyIfPresent(list, names,
"sizeof_hdr", header->sizeof_hdr);
866 internal::copyIfPresent(list, names,
"dim_info", header->dim_info);
867 if (names.count(
"dim") == 1)
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];
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);
879 internal::copyIfPresent(list, names,
"datatype", header->datatype);
880 internal::copyIfPresent(list, names,
"bitpix", header->bitpix);
882 internal::copyIfPresent(list, names,
"slice_start", header->slice_start);
883 if (names.count(
"pixdim") == 1)
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];
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);
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());
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);
914 if (names.count(
"srow_x") == 1)
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];
920 if (names.count(
"srow_y") == 1)
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];
926 if (names.count(
"srow_z") == 1)
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];
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());
938 this->
image = nifti_convert_nhdr2nim(*header, NULL);
939 this->
image->data = NULL;
945 int dims[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
946 const std::vector<int> dimVector =
object.attr(
"dim");
948 const int nDims = std::min(7,
int(dimVector.size()));
950 for (
int i=0; i<
nDims; i++)
951 dims[i+1] = dimVector[i];
954 this->
image = nifti_make_new_nim(dims, datatype,
int(copyData));
958 const size_t dataSize = nifti_get_volsize(
image);
959 if (datatype == DT_INT32)
960 memcpy(this->
image->data, INTEGER(
object), dataSize);
962 memcpy(this->
image->data, REAL(
object), dataSize);
965 this->
image->data = NULL;
967 if (
object.hasAttribute(
"pixdim"))
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++)
975 if (
object.hasAttribute(
"pixunits"))
977 const std::vector<std::string> pixunitsVector =
object.attr(
"pixunits");
985 Rcpp::RObject imageObject(
object);
986 bool resolved =
false;
988 if (imageObject.hasAttribute(
".nifti_image_ptr"))
990 Rcpp::XPtr<NiftiImage> imagePtr(SEXP(imageObject.attr(
".nifti_image_ptr")));
991 if (imagePtr.get() != NULL)
993 this->
image = imagePtr->image;
997 if (imageObject.hasAttribute(
"dim"))
1000 else if (Rf_isString(
object))
1001 throw std::runtime_error(
"Internal image is not valid");
1003 Rf_warning(
"Ignoring invalid internal pointer");
1008 if (Rf_isNull(
object))
1010 else if (Rf_isString(
object))
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);
1017 else if (imageObject.inherits(
"nifti"))
1019 else if (imageObject.inherits(
"anlz"))
1020 throw std::runtime_error(
"Cannot currently convert objects of class \"anlz\"");
1021 else if (imageObject.inherits(
"MriImage"))
1023 else if (Rf_isVectorList(
object))
1025 else if (imageObject.hasAttribute(
"dim"))
1028 throw std::runtime_error(
"Cannot convert object of class \"" + Rcpp::as<std::string>(imageObject.attr(
"class")) +
"\" to a nifti_image");
1031 if (this->
image != NULL)
1032 nifti_update_dims_from_array(this->
image);
1035 Rprintf(
"Creating NiftiImage with pointer %p (from SEXP)\n", this->
image);
1044 const std::vector<float> origPixdim(
image->pixdim+1,
image->pixdim+4);
1046 for (
int i=1; i<8; i++)
1047 image->pixdim[i] = 0.0;
1049 const int pixdimLength = pixdim.size();
1050 for (
int i=0; i<std::min(pixdimLength,nDims); i++)
1051 image->pixdim[i+1] = pixdim[i];
1053 if (!std::equal(origPixdim.begin(), origPixdim.begin() + std::min(3,nDims), pixdim.begin()))
1056 for (
int i=0; i<3; i++)
1058 for (
int j=0; j<3; j++)
1061 scaleMatrix.m[i][j] = 0.0;
1062 else if (i >= nDims)
1063 scaleMatrix.m[i][j] = 1.0;
1065 scaleMatrix.m[i][j] = pixdim[i] / origPixdim[i];
1069 if (
image->qform_code > 0)
1071 mat33 prod = nifti_mat33_mul(scaleMatrix, internal::topLeftCorner(
image->qto_xyz));
1072 for (
int i=0; i<3; i++)
1074 for (
int j=0; j<3; j++)
1075 image->qto_xyz.m[i][j] = prod.m[i][j];
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);
1081 if (
image->sform_code > 0)
1083 mat33 prod = nifti_mat33_mul(scaleMatrix, internal::topLeftCorner(
image->sto_xyz));
1084 for (
int i=0; i<3; i++)
1086 for (
int j=0; j<3; j++)
1087 image->sto_xyz.m[i][j] = prod.m[i][j];
1089 image->sto_ijk = nifti_mat44_inverse(
image->sto_xyz);
1096 for (
size_t i=0; i<pixunits.size(); i++)
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;
1123 for (
int i=0; i<std::min(3,
int(scales.size())); i++)
1125 if (scales[i] != 1.0)
1127 pixdim[i] /= scales[i];
1128 image->dim[i+1] =
static_cast<int>(std::floor(
image->dim[i+1] * scales[i]));
1133 nifti_update_dims_from_array(
image);
1137 nifti_image_unload(
image);
1139 image->scl_slope = 0.0;
1140 image->scl_inter = 0.0;
1149 if (
image->qform_code == 0 &&
image->sform_code == 0)
1151 Rf_warning(
"Image qform and sform codes are both zero, so it cannot be reoriented");
1155 int used[6] = { 0, 0, 0, 0, 0, 0 };
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");
1162 const int codes[3] = { icode, jcode, kcode };
1163 const mat44 native = this->
xform();
1167 for (
int j=0; j<3; j++)
1169 for (
int i=0; i<3; i++)
1170 target.m[i][j] = 0.0;
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;
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++)
1190 for (
int j=0; j<3; j++)
1191 nativeAxesTransposed.m[i][j] = 0.0;
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;
1205 if (icode == nicode && jcode == njcode && kcode == nkcode)
1211 mat33 transform = nifti_mat33_mul(nativeAxesTransposed, target);
1213 for (
int i=0; i<4; i++)
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];
1218 result.m[i][3] = native.m[i][3];
1222 if (
image->qform_code > 0)
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);
1228 if (
image->sform_code > 0)
1230 image->sto_xyz = result;
1231 image->sto_ijk = nifti_mat44_inverse(
image->sto_xyz);
1235 int locs[3], signs[3], newdim[3];
1237 double maxes[3] = { R_NegInf, R_NegInf, R_NegInf };
1238 for (
int j=0; j<3; j++)
1240 for (
int i=0; i<3; i++)
1242 const double value =
static_cast<double>(transform.m[i][j]);
1243 if (fabs(value) > maxes[j])
1245 maxes[j] = fabs(value);
1246 signs[j] = value > 0.0 ? 1 : -1;
1252 newdim[j] =
image->dim[locs[j]+1];
1253 newpixdim[j] =
image->pixdim[locs[j]+1];
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];
1262 if (
image->data != NULL)
1265 size_t nVolumes = std::max(
size_t(1),
image->nvox / volSize);
1267 const std::vector<double> oldData = this->getData<double>();
1268 std::vector<double> newData(
image->nvox);
1271 size_t volStart = 0;
1272 for (
int i=0; i<3; i++)
1275 volStart += (
image->dim[i+1] - 1) * strides[i];
1279 std::vector<double>::const_iterator it = oldData.begin();
1280 for (
size_t v=0; v<nVolumes; v++)
1282 for (
int k=0; k<
image->nz; k++)
1284 ptrdiff_t offset = k * strides[2] * signs[2];
1285 for (
int j=0; j<
image->ny; j++)
1287 for (
int i=0; i<
image->nx; i++)
1289 newData[volStart + offset] = *it++;
1290 offset += strides[0] * signs[0];
1292 offset += strides[1] * signs[1] -
image->nx * strides[0] * signs[0];
1295 volStart += volSize;
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);
1313 if (orientation.length() != 3)
1314 throw std::runtime_error(
"Orientation string should have exactly three characters");
1317 for (
int i=0; i<3; i++)
1319 switch(orientation[i])
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;
1329 throw std::runtime_error(
"Orientation string is invalid");
1333 return reorient(codes[0], codes[1], codes[2]);
1340 Rcpp::RObject object(array);
1341 if (!
object.hasAttribute(
"dim"))
1344 for (
int i=0; i<8; i++)
1346 const std::vector<int> dimVector =
object.attr(
"dim");
1348 const int nDims = std::min(7,
int(dimVector.size()));
1350 for (
int i=0; i<
nDims; i++)
1351 image->dim[i+1] = dimVector[i];
1353 if (
object.hasAttribute(
"pixdim"))
1355 const std::vector<float> pixdimVector =
object.attr(
"pixdim");
1359 if (
object.hasAttribute(
"pixunits"))
1361 const std::vector<std::string> pixunitsVector =
object.attr(
"pixunits");
1366 nifti_update_dims_from_array(
image);
1370 nifti_datatype_sizes(
image->datatype, &
image->nbyper, NULL);
1373 nifti_image_unload(
image);
1375 const size_t dataSize = nifti_get_volsize(
image);
1376 image->data = calloc(1, dataSize);
1377 if (
image->datatype == DT_INT32)
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));
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));
1390 image->scl_slope = 0.0;
1391 image->scl_inter = 0.0;
1403 for (
int i=0; i<4; i++)
1405 for (
int j=0; j<4; j++)
1406 matrix.m[i][j] = 0.0;
1410 else if (
image->qform_code <= 0 &&
image->sform_code <= 0)
1414 for (
int i=0; i<4; i++)
1416 for (
int j=0; j<4; j++)
1419 matrix.m[i][j] = 0.0;
1421 matrix.m[3][3] = 1.0;
1423 matrix.m[i][j] = (
image->pixdim[i+1]==0.0 ? 1.0 :
image->pixdim[i+1]);
1428 else if ((preferQuaternion &&
image->qform_code > 0) ||
image->sform_code <= 0)
1429 return image->qto_xyz;
1431 return image->sto_xyz;
1434 template <
typename TargetType>
1438 return std::vector<TargetType>();
1440 size_t blockSize = 1;
1441 for (
int i=1; i<dimension; i++)
1442 blockSize *=
image->dim[i];
1444 std::vector<TargetType> data(blockSize);
1445 internal::convertData<TargetType>(
image->data,
image->datatype, blockSize, data.begin(), blockSize*index);
1447 if (
image.isDataScaled())
1448 std::transform(data.begin(), data.end(), data.begin(), internal::DataRescaler<TargetType>(
image->scl_slope,
image->scl_inter));
1453 template <
typename TargetType>
1457 return std::vector<TargetType>();
1459 std::vector<TargetType> data(
image->nvox);
1460 internal::convertData<TargetType>(
image->data,
image->datatype,
image->nvox, data.begin());
1463 std::transform(data.begin(), data.end(), data.begin(), internal::DataRescaler<TargetType>(
image->scl_slope,
image->scl_inter));
1470 if (this->
isNull() ||
image->datatype == datatype)
1473 if (
image->data != NULL)
1476 nifti_datatype_sizes(datatype, &bytesPerPixel, NULL);
1477 void *data = calloc(
image->nvox, bytesPerPixel);
1482 internal::convertData<uint8_t>(
image->data,
image->datatype,
image->nvox,
static_cast<uint8_t *
>(data));
1486 internal::convertData<int16_t>(
image->data,
image->datatype,
image->nvox,
static_cast<int16_t *
>(data));
1490 internal::convertData<int32_t>(
image->data,
image->datatype,
image->nvox,
static_cast<int32_t *
>(data));
1494 internal::convertData<float>(
image->data,
image->datatype,
image->nvox,
static_cast<float *
>(data));
1498 internal::convertData<double>(
image->data,
image->datatype,
image->nvox,
static_cast<double *
>(data));
1502 internal::convertData<int8_t>(
image->data,
image->datatype,
image->nvox,
static_cast<int8_t *
>(data));
1506 internal::convertData<uint16_t>(
image->data,
image->datatype,
image->nvox,
static_cast<uint16_t *
>(data));
1510 internal::convertData<uint32_t>(
image->data,
image->datatype,
image->nvox,
static_cast<uint32_t *
>(data));
1514 internal::convertData<int64_t>(
image->data,
image->datatype,
image->nvox,
static_cast<int64_t *
>(data));
1518 internal::convertData<uint64_t>(
image->data,
image->datatype,
image->nvox,
static_cast<uint64_t *
>(data));
1522 throw std::runtime_error(
"Unsupported data type (" + std::string(nifti_datatype_string(datatype)) +
")");
1525 nifti_image_unload(
image);
1529 image->datatype = datatype;
1530 nifti_datatype_sizes(datatype, &
image->nbyper, &
image->swapsize);
1540 template <
typename SourceType>
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");
1548 if (datatype != DT_NONE)
1550 nifti_image_unload(
image);
1551 image->datatype = datatype;
1552 nifti_datatype_sizes(datatype, &
image->nbyper, &
image->swapsize);
1555 if (
image->data == NULL)
1557 internal::replaceData<SourceType>(data.begin(), data.end(),
image->data,
image->datatype);
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()));
1572 if (datatype == DT_NONE)
1577 const int status = nifti_set_filenames(imageToWrite, fileName.c_str(),
false,
true);
1579 throw std::runtime_error(
"Failed to set filenames for NIfTI object");
1580 nifti_image_write(imageToWrite);
1585 toFile(fileName, internal::stringToDatatype(datatype));
1592 Rcpp::RObject array;
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));
1603 switch (
image->datatype)
1613 array = internal::imageDataToArray<INTSXP>(
image);
1618 array = internal::imageDataToArray<REALSXP>(
image);
1622 throw std::runtime_error(
"Unsupported data type (" + std::string(nifti_datatype_string(
image->datatype)) +
")");
1626 internal::addAttributes(array,
image);
1627 array.attr(
"class") = Rcpp::CharacterVector::create(
"niftiImage",
"array");
1634 return Rcpp::RObject();
1637 Rcpp::RObject
string = Rcpp::wrap(label);
1638 internal::addAttributes(
string,
image,
false);
1639 string.attr(
"class") = Rcpp::CharacterVector::create(
"internalImage",
"niftiImage");
1651 if (this->
image == NULL)
1652 return Rcpp::RObject();
1654 nifti_1_header header = nifti_convert_nim2nhdr(this->
image);
1657 result[
"sizeof_hdr"] = header.sizeof_hdr;
1659 result[
"dim_info"] = int(header.dim_info);
1660 result[
"dim"] = std::vector<short>(header.dim, header.dim+8);
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;
1667 result[
"datatype"] = header.datatype;
1668 result[
"bitpix"] = header.bitpix;
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);
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);
1697 result[
"intent_name"] = std::string(header.intent_name, 16);
1698 result[
"magic"] = std::string(header.magic, 4);
1700 result.attr(
"class") = Rcpp::CharacterVector::create(
"niftiHeader");
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
NiftiImage()
Default constructor.
Definition: NiftiImage.h:220
const int index
The location along dimension.
Definition: NiftiImage.h:54
static std::string xformToString(const mat44 matrix)
Convert a 4x4 xform matrix to a string describing its canonical axes.
Definition: NiftiImage.h:127
std::vector< int > dim() const
Return the dimensions of the image.
Definition: NiftiImage.h:398
const nifti_image * operator->() const
Allows a NiftiImage object to be treated as a pointer to a const nifti_image.
Definition: NiftiImage.h:320
Rcpp::RObject headerToList() const
Create an R list containing raw image metadata.
Definition: NiftiImage.h:1649
bool persistent
Marker of persistence, which determines whether the nifti_image should be freed on destruction...
Definition: NiftiImage.h:152
std::vector< float > pixdim() const
Return the dimensions of the pixels or voxels in the image.
Definition: NiftiImage.h:410
NiftiImage(const std::string &path, const bool readData=true)
Initialise using a path string.
Definition: NiftiImage.h:273
NiftiImage & rescale(const std::vector< float > &scales)
Rescale the image, changing its image dimensions and pixel dimensions.
Definition: NiftiImage.h:1119
NiftiImage(nifti_image *const image, const bool copy=false)
Initialise using an existing nifti_image pointer.
Definition: NiftiImage.h:255
Definition: NiftiImage.h:37
void initFromMriImage(const Rcpp::RObject &object, const bool copyData=true)
Initialise the object from a reference object of class "MriImage".
Definition: NiftiImage.h:771
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
bool isNull() const
Determine whether or not the internal pointer is NULL.
Definition: NiftiImage.h:371
Inner class referring to a subset of an image.
Definition: NiftiImage.h:50
Block slice(const int i)
Extract a slice block from a 3D image.
Definition: NiftiImage.h:552
Rcpp::RObject toPointer(const std::string label) const
Create an internal image to pass back to R.
Definition: NiftiImage.h:1631
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
std::vector< TargetType > getData() const
Extract a vector of data from the image, casting it to any required element type. ...
Definition: NiftiImage.h:1454
void initFromArray(const Rcpp::RObject &object, const bool copyData=true)
Initialise the object from an R array.
Definition: NiftiImage.h:943
bool isDataScaled() const
Determine whether nontrivial scale and slope parameters are set.
Definition: NiftiImage.h:381
void initFromNiftiS4(const Rcpp::RObject &object, const bool copyData=true)
Initialise the object from an S4 object of class "nifti".
Definition: NiftiImage.h:675
const Block slice(const int i) const
Extract a slice block from a 3D image.
Definition: NiftiImage.h:545
const Block volume(const int i) const
Extract a volume block from a 4D image.
Definition: NiftiImage.h:559
bool isPersistent() const
Determine whether or not the image is marked as persistent.
Definition: NiftiImage.h:376
void setPixunits(const std::vector< std::string > &pixunits)
Modify the pixel dimension units.
Definition: NiftiImage.h:1094
NiftiImage(const Block &source)
Initialise from a block, copying in the data.
Definition: NiftiImage.h:240
virtual ~NiftiImage()
Destructor which frees the wrapped pointer, unless the object is marked as persistent.
Definition: NiftiImage.h:296
Thin wrapper around a C-style nifti_image struct that allows C++-style destruction.
Definition: NiftiImage.h:43
NiftiImage(const NiftiImage &source)
Copy constructor.
Definition: NiftiImage.h:227
int nBlocks() const
Return the number of blocks in the image.
Definition: NiftiImage.h:514
NiftiImage & update(const SEXP array)
Update the image from an R array.
Definition: NiftiImage.h:1338
const int dimension
The dimension along which the block applies (which should be the last)
Definition: NiftiImage.h:53
const NiftiImage & image
The parent image.
Definition: NiftiImage.h:52
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
void toFile(const std::string fileName, const short datatype=DT_NONE) const
Write the image to a NIfTI-1 file.
Definition: NiftiImage.h:1567
void updatePixdim(const std::vector< float > &pixdim)
Modify the pixel dimensions, and potentially the xform matrices to match.
Definition: NiftiImage.h:1041
const Block block(const int i) const
Extract a block from the image.
Definition: NiftiImage.h:529
NiftiImage & setPersistence(const bool persistent)
Marked the image as persistent, so that it can be passed back to R.
Definition: NiftiImage.h:358
void copy(const nifti_image *source)
Copy the contents of a nifti_image to create a new image.
Definition: NiftiImage.h:618
mat44 xform(const bool preferQuaternion=true) const
Obtain an xform matrix, indicating the orientation of the image.
Definition: NiftiImage.h:1398
nifti_image * image
The wrapped nifti_image pointer.
Definition: NiftiImage.h:151
NiftiImage & changeDatatype(const short datatype)
Change the datatype of the image, casting the pixel data if present.
Definition: NiftiImage.h:1468
std::vector< TargetType > getData() const
Extract a vector of data from a block, casting it to any required element type.
Definition: NiftiImage.h:1435
Rcpp::RObject toArrayOrPointer(const bool internal, const std::string label) const
A conditional method that calls either toArray or toPointer.
Definition: NiftiImage.h:1644
static short sexpTypeToNiftiType(const int sexpType)
Convert between R SEXP object type and nifti_image datatype codes.
Definition: NiftiImage.h:111
int nDims() const
Return the number of dimensions in the image.
Definition: NiftiImage.h:386
Block volume(const int i)
Extract a volume block from a 4D image.
Definition: NiftiImage.h:566
NiftiImage & drop()
Drop unitary dimensions.
Definition: NiftiImage.h:424
Rcpp::RObject toArray() const
Create an R array from the image.
Definition: NiftiImage.h:1590
Block(const NiftiImage &image, const int dimension, const int index)
Standard constructor for this class.
Definition: NiftiImage.h:63
NiftiImage & dropData()
Drop the data from the image, retaining only the metadata.
Definition: NiftiImage.h:465
Block block(const int i)
Extract a block from the image.
Definition: NiftiImage.h:538