1 #ifndef _NIFTI_IMAGE_H_ 2 #define _NIFTI_IMAGE_H_ 11 #define R_NegInf -INFINITY 12 #define ISNAN(x) (x != x) 29 #include "niftilib/nifti1_io.h" 69 throw std::runtime_error(
"Blocks must be along the last dimension in the image");
82 if (source->datatype !=
image->datatype)
83 throw std::runtime_error(
"New data does not have the same datatype as the target block");
84 if (source->scl_slope !=
image->scl_slope || source->scl_inter !=
image->scl_inter)
85 throw std::runtime_error(
"New data does not have the same scale parameters as the target block");
91 if (blockSize != source->nvox)
92 throw std::runtime_error(
"New data does not have the same size as the target block");
94 blockSize *=
image->nbyper;
95 memcpy(static_cast<char*>(
image->data) + blockSize*
index, source->data, blockSize);
102 template <
typename TargetType>
103 std::vector<TargetType>
getData ()
const;
113 static short sexpTypeToNiftiType (
const int sexpType)
115 if (sexpType == INTSXP || sexpType == LGLSXP)
117 else if (sexpType == REALSXP)
120 throw std::runtime_error(
"Array elements must be numeric");
131 int icode, jcode, kcode;
132 nifti_mat44_to_orientation(matrix, &icode, &jcode, &kcode);
134 int codes[3] = { icode, jcode, kcode };
135 std::string result(
"---");
136 for (
int i=0; i<3; i++)
140 case NIFTI_L2R: result[i] =
'R';
break;
141 case NIFTI_R2L: result[i] =
'L';
break;
142 case NIFTI_P2A: result[i] =
'A';
break;
143 case NIFTI_A2P: result[i] =
'P';
break;
144 case NIFTI_I2S: result[i] =
'S';
break;
145 case NIFTI_S2I: result[i] =
'I';
break;
159 nifti_1_header *header = nifti_read_header(path.c_str(), NULL,
false);
164 int version = NIFTI_VERSION(*header);
168 if (header->sizeof_hdr == 540 || header->sizeof_hdr == 469893120)
171 const char *magic = (
char *) header + 4;
172 if (strncmp(magic,
"ni2",3) == 0 || strncmp(magic,
"n+2",3) == 0)
175 else if (!nifti_hdr_looks_good(header))
194 void copy (
const nifti_image *source);
216 void initFromNiftiS4 (
const Rcpp::RObject &
object,
const bool copyData =
true);
223 void initFromMriImage (
const Rcpp::RObject &
object,
const bool copyData =
true);
229 void initFromList (
const Rcpp::RObject &
object);
236 void initFromArray (
const Rcpp::RObject &
object,
const bool copyData =
true);
250 void setPixunits (
const std::vector<std::string> &pixunits);
268 Rprintf(
"Creating NiftiImage with pointer %p (from NiftiImage)\n", this->image);
281 Rprintf(
"Creating NiftiImage with pointer %p (from Block)\n", this->image);
299 Rprintf(
"Creating NiftiImage with pointer %p (from pointer)\n", this->image);
309 NiftiImage (
const std::string &path,
const bool readData =
true)
312 this->image = nifti_image_read(path.c_str(), readData);
313 if (this->image == NULL)
314 throw std::runtime_error(
"Failed to read image from path " + path);
316 Rprintf(
"Creating NiftiImage with pointer %p (from string)\n", this->image);
326 NiftiImage (
const std::string &path,
const std::vector<int> &volumes);
334 NiftiImage (
const SEXP
object,
const bool readData =
true);
345 Rprintf(
"Freeing NiftiImage with pointer %p\n", this->image);
347 nifti_image_free(
image);
354 operator const nifti_image* ()
const {
return image; }
359 operator nifti_image* () {
return image; }
379 Rprintf(
"Creating NiftiImage with pointer %p (from NiftiImage)\n", this->image);
393 Rprintf(
"Creating NiftiImage with pointer %p (from Block)\n", this->image);
407 Rprintf(
"Setting NiftiImage with pointer %p to be persistent\n", this->image);
442 std::vector<int>
dim ()
const 445 return std::vector<int>();
457 return std::vector<float>();
459 return std::vector<float>(
image->pixdim+1,
image->pixdim+
image->ndim+1);
470 int ndim =
image->ndim;
471 while (
image->dim[ndim] < 2)
481 template <
typename TargetType>
482 std::vector<TargetType>
getData ()
const;
503 template <
typename SourceType>
511 nifti_image_unload(
image);
545 NiftiImage & update (
const Rcpp::RObject &
object);
553 mat44
xform (
const bool preferQuaternion =
true)
const;
617 void toFile (
const std::string fileName,
const short datatype = DT_NONE)
const;
624 void toFile (
const std::string fileName,
const std::string &datatype)
const;
632 Rcpp::RObject toArray ()
const;
639 Rcpp::RObject toPointer (
const std::string label)
const;
647 Rcpp::RObject toArrayOrPointer (
const bool internal,
const std::string label)
const;
653 Rcpp::RObject headerToList ()
const;
660 #include "lib/NiftiImage_internal.h" 665 nifti_image_free(
image);
671 image = nifti_copy_nim_info(source);
672 if (source->data != NULL)
674 size_t dataSize = nifti_get_volsize(source);
675 image->data = calloc(1, dataSize);
676 memcpy(
image->data, source->data, dataSize);
685 const nifti_image *sourceStruct = source;
692 nifti_image_free(
image);
694 const nifti_image *sourceStruct = source.
image;
695 if (sourceStruct == NULL)
699 image = nifti_copy_nim_info(sourceStruct);
703 nifti_update_dims_from_array(
image);
705 if (sourceStruct->data != NULL)
707 size_t blockSize = nifti_get_volsize(
image);
708 image->data = calloc(1, blockSize);
709 memcpy(
image->data, static_cast<char*>(source.
image->data) + blockSize*source.
index, blockSize);
719 inline void NiftiImage::initFromNiftiS4 (
const Rcpp::RObject &
object,
const bool copyData)
721 nifti_1_header header;
722 header.sizeof_hdr = 348;
724 const std::vector<short> dims =
object.slot(
"dim_");
725 for (
int i=0; i<8; i++)
726 header.dim[i] = dims[i];
728 header.intent_p1 =
object.slot(
"intent_p1");
729 header.intent_p2 =
object.slot(
"intent_p2");
730 header.intent_p3 =
object.slot(
"intent_p3");
731 header.intent_code =
object.slot(
"intent_code");
733 header.datatype =
object.slot(
"datatype");
734 header.bitpix =
object.slot(
"bitpix");
736 header.slice_start =
object.slot(
"slice_start");
737 header.slice_end =
object.slot(
"slice_end");
738 header.slice_code = Rcpp::as<int>(
object.slot(
"slice_code"));
739 header.slice_duration =
object.slot(
"slice_duration");
741 const std::vector<float> pixdims =
object.slot(
"pixdim");
742 for (
int i=0; i<8; i++)
743 header.pixdim[i] = pixdims[i];
744 header.xyzt_units = Rcpp::as<int>(
object.slot(
"xyzt_units"));
746 header.vox_offset =
object.slot(
"vox_offset");
748 header.scl_slope =
object.slot(
"scl_slope");
749 header.scl_inter =
object.slot(
"scl_inter");
750 header.toffset =
object.slot(
"toffset");
752 header.cal_max =
object.slot(
"cal_max");
753 header.cal_min =
object.slot(
"cal_min");
754 header.glmax = header.glmin = 0;
756 strncpy(header.descrip, Rcpp::as<std::string>(
object.slot(
"descrip")).c_str(), 79);
757 header.descrip[79] =
'\0';
758 strncpy(header.aux_file, Rcpp::as<std::string>(
object.slot(
"aux_file")).c_str(), 23);
759 header.aux_file[23] =
'\0';
760 strncpy(header.intent_name, Rcpp::as<std::string>(
object.slot(
"intent_name")).c_str(), 15);
761 header.intent_name[15] =
'\0';
762 strncpy(header.magic, Rcpp::as<std::string>(
object.slot(
"magic")).c_str(), 3);
763 header.magic[3] =
'\0';
765 header.qform_code =
object.slot(
"qform_code");
766 header.sform_code =
object.slot(
"sform_code");
768 header.quatern_b =
object.slot(
"quatern_b");
769 header.quatern_c =
object.slot(
"quatern_c");
770 header.quatern_d =
object.slot(
"quatern_d");
771 header.qoffset_x =
object.slot(
"qoffset_x");
772 header.qoffset_y =
object.slot(
"qoffset_y");
773 header.qoffset_z =
object.slot(
"qoffset_z");
775 const std::vector<float> srow_x =
object.slot(
"srow_x");
776 const std::vector<float> srow_y =
object.slot(
"srow_y");
777 const std::vector<float> srow_z =
object.slot(
"srow_z");
778 for (
int i=0; i<4; i++)
780 header.srow_x[i] = srow_x[i];
781 header.srow_y[i] = srow_y[i];
782 header.srow_z[i] = srow_z[i];
785 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)
786 header.datatype = DT_INT32;
787 else if (header.datatype == DT_FLOAT32 || header.datatype == DT_FLOAT64)
788 header.datatype = DT_FLOAT64;
790 throw std::runtime_error(
"Data type is not supported");
792 this->
image = nifti_convert_nhdr2nim(header, NULL);
794 const SEXP data = PROTECT(
object.slot(
".Data"));
795 if (!copyData || Rf_length(data) <= 1)
796 this->
image->data = NULL;
799 const size_t dataSize = nifti_get_volsize(this->
image);
800 this->
image->data = calloc(1, dataSize);
801 if (header.datatype == DT_INT32)
803 Rcpp::IntegerVector intData(data);
804 std::copy(intData.begin(), intData.end(),
static_cast<int32_t*
>(this->
image->data));
808 Rcpp::DoubleVector doubleData(data);
809 std::copy(doubleData.begin(), doubleData.end(),
static_cast<double*
>(this->
image->data));
815 inline void NiftiImage::initFromMriImage (
const Rcpp::RObject &
object,
const bool copyData)
817 Rcpp::Reference mriImage(
object);
818 Rcpp::Function getXform = mriImage.field(
"getXform");
819 Rcpp::NumericMatrix
xform = getXform();
823 if (Rf_length(mriImage.field(
"tags")) > 0)
824 initFromList(mriImage.field(
"tags"));
826 Rcpp::RObject data = mriImage.field(
"data");
827 if (data.inherits(
"SparseArray"))
829 Rcpp::Language call(
"as.array", data);
833 const short datatype = (Rf_isNull(data) ? DT_INT32 : sexpTypeToNiftiType(data.sexp_type()));
835 int dims[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
836 const std::vector<int> dimVector = mriImage.field(
"imageDims");
837 const int nDims = std::min(7,
int(dimVector.size()));
840 for (
int i=0; i<
nDims; i++)
842 dims[i+1] = dimVector[i];
843 nVoxels *= dimVector[i];
846 if (this->
image == NULL)
847 this->
image = nifti_make_new_nim(dims, datatype, FALSE);
850 std::copy(dims, dims+8, this->
image->dim);
851 this->
image->datatype = datatype;
852 nifti_datatype_sizes(
image->datatype, &
image->nbyper, NULL);
855 if (copyData && !Rf_isNull(data))
858 const size_t dataSize = nVoxels *
image->nbyper;
859 this->
image->data = calloc(1, dataSize);
860 if (datatype == DT_INT32)
861 memcpy(this->
image->data, INTEGER(data), dataSize);
863 memcpy(this->
image->data, REAL(data), dataSize);
866 this->
image->data = NULL;
868 const std::vector<float> pixdimVector = mriImage.field(
"voxelDims");
869 const int pixdimLength = pixdimVector.size();
870 for (
int i=0; i<std::min(pixdimLength,
nDims); i++)
871 this->
image->pixdim[i+1] = std::abs(pixdimVector[i]);
873 const std::vector<std::string> pixunitsVector = mriImage.field(
"voxelDimUnits");
877 this->
image->qform_code = this->image->sform_code = 0;
881 for (
int i=0; i<4; i++)
883 for (
int j=0; j<4; j++)
884 matrix.m[i][j] = static_cast<float>(
xform(i,j));
887 this->
image->qto_xyz = matrix;
888 this->
image->qto_ijk = nifti_mat44_inverse(
image->qto_xyz);
889 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);
891 this->
image->sto_xyz = matrix;
892 this->
image->sto_ijk = nifti_mat44_inverse(
image->sto_xyz);
894 this->
image->qform_code = this->
image->sform_code = 2;
898 inline void NiftiImage::initFromList (
const Rcpp::RObject &
object)
900 Rcpp::List list(
object);
901 nifti_1_header *header = nifti_make_new_header(NULL, DT_FLOAT64);
903 internal::updateHeader(header, list);
905 this->image = nifti_convert_nhdr2nim(*header, NULL);
906 this->image->data = NULL;
910 inline void NiftiImage::initFromArray (
const Rcpp::RObject &
object,
const bool copyData)
912 int dims[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
913 const std::vector<int> dimVector =
object.attr(
"dim");
915 const int nDims = std::min(7,
int(dimVector.size()));
917 for (
int i=0; i<nDims; i++)
918 dims[i+1] = dimVector[i];
920 const short datatype = sexpTypeToNiftiType(
object.sexp_type());
921 this->image = nifti_make_new_nim(dims, datatype,
int(copyData));
925 const size_t dataSize = nifti_get_volsize(image);
926 if (datatype == DT_INT32)
927 memcpy(this->image->data, INTEGER(
object), dataSize);
929 memcpy(this->image->data, REAL(
object), dataSize);
932 this->image->data = NULL;
934 if (
object.hasAttribute(
"pixdim"))
936 const std::vector<float> pixdimVector =
object.attr(
"pixdim");
937 const int pixdimLength = pixdimVector.size();
938 for (
int i=0; i<std::min(pixdimLength,nDims); i++)
939 this->image->pixdim[i+1] = pixdimVector[i];
942 if (
object.hasAttribute(
"pixunits"))
944 const std::vector<std::string> pixunitsVector =
object.attr(
"pixunits");
945 setPixunits(pixunitsVector);
949 inline NiftiImage::NiftiImage (
const SEXP
object,
const bool readData)
952 Rcpp::RObject imageObject(
object);
953 bool resolved =
false;
955 if (imageObject.hasAttribute(
".nifti_image_ptr"))
957 Rcpp::XPtr<NiftiImage> imagePtr(SEXP(imageObject.attr(
".nifti_image_ptr")));
961 this->
image = ptr->image;
965 if (imageObject.hasAttribute(
"dim"))
968 else if (Rf_isString(
object))
969 throw std::runtime_error(
"Internal image is not valid");
971 Rf_warning(
"Ignoring invalid internal pointer");
976 if (Rf_isNull(
object))
978 else if (Rf_isString(
object))
980 const std::string path = Rcpp::as<std::string>(object);
981 this->
image = nifti_image_read(path.c_str(), readData);
982 if (this->image == NULL)
983 throw std::runtime_error(
"Failed to read image from path " + path);
985 else if (imageObject.inherits(
"nifti"))
986 initFromNiftiS4(imageObject, readData);
987 else if (imageObject.inherits(
"anlz"))
988 throw std::runtime_error(
"Cannot currently convert objects of class \"anlz\"");
989 else if (imageObject.inherits(
"MriImage"))
990 initFromMriImage(imageObject, readData);
991 else if (Rf_isVectorList(
object))
992 initFromList(imageObject);
993 else if (imageObject.hasAttribute(
"dim"))
994 initFromArray(imageObject, readData);
996 throw std::runtime_error(
"Cannot convert object of class \"" + Rcpp::as<std::string>(imageObject.attr(
"class")) +
"\" to a nifti_image");
999 if (this->image != NULL)
1000 nifti_update_dims_from_array(this->image);
1003 Rprintf(
"Creating NiftiImage with pointer %p (from SEXP)\n", this->image);
1012 if (volumes.empty())
1013 throw std::runtime_error(
"The vector of volumes is empty");
1015 nifti_brick_list brickList;
1016 this->image = nifti_image_read_bricks(path.c_str(), volumes.size(), &volumes[0], &brickList);
1017 if (this->image == NULL)
1018 throw std::runtime_error(
"Failed to read image from path " + path);
1021 image->data = calloc(1, nifti_get_volsize(
image));
1022 for (
int i=0; i<brickList.nbricks; i++)
1023 memcpy((
char *)
image->data + i * brickSize, brickList.bricks[i], brickSize);
1024 nifti_free_NBL(&brickList);
1027 Rprintf(
"Creating NiftiImage with pointer %p (from string and volume vector)\n", this->image);
1034 const std::vector<float> origPixdim(
image->pixdim+1,
image->pixdim+4);
1036 for (
int i=1; i<8; i++)
1037 image->pixdim[i] = 0.0;
1039 const int pixdimLength =
pixdim.size();
1040 for (
int i=0; i<std::min(pixdimLength,
nDims); i++)
1043 if (!std::equal(origPixdim.begin(), origPixdim.begin() + std::min(3,
nDims),
pixdim.begin()))
1046 for (
int i=0; i<3; i++)
1048 for (
int j=0; j<3; j++)
1051 scaleMatrix.m[i][j] = 0.0;
1052 else if (i >=
nDims)
1053 scaleMatrix.m[i][j] = 1.0;
1055 scaleMatrix.m[i][j] =
pixdim[i] / origPixdim[i];
1059 if (
image->qform_code > 0)
1061 mat33 prod = nifti_mat33_mul(scaleMatrix, internal::topLeftCorner(
image->qto_xyz));
1062 for (
int i=0; i<3; i++)
1064 for (
int j=0; j<3; j++)
1065 image->qto_xyz.m[i][j] = prod.m[i][j];
1067 image->qto_ijk = nifti_mat44_inverse(
image->qto_xyz);
1068 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);
1071 if (
image->sform_code > 0)
1073 mat33 prod = nifti_mat33_mul(scaleMatrix, internal::topLeftCorner(
image->sto_xyz));
1074 for (
int i=0; i<3; i++)
1076 for (
int j=0; j<3; j++)
1077 image->sto_xyz.m[i][j] = prod.m[i][j];
1079 image->sto_ijk = nifti_mat44_inverse(
image->sto_xyz);
1086 for (
size_t i=0; i<pixunits.size(); i++)
1088 if (pixunits[i] ==
"m")
1089 image->xyz_units = NIFTI_UNITS_METER;
1090 else if (pixunits[i] ==
"mm")
1091 image->xyz_units = NIFTI_UNITS_MM;
1092 else if (pixunits[i] ==
"um")
1093 image->xyz_units = NIFTI_UNITS_MICRON;
1094 else if (pixunits[i] ==
"s")
1095 image->time_units = NIFTI_UNITS_SEC;
1096 else if (pixunits[i] ==
"ms")
1097 image->time_units = NIFTI_UNITS_MSEC;
1098 else if (pixunits[i] ==
"us")
1099 image->time_units = NIFTI_UNITS_USEC;
1100 else if (pixunits[i] ==
"Hz")
1101 image->time_units = NIFTI_UNITS_HZ;
1102 else if (pixunits[i] ==
"ppm")
1103 image->time_units = NIFTI_UNITS_PPM;
1104 else if (pixunits[i] ==
"rad/s")
1105 image->time_units = NIFTI_UNITS_RADS;
1113 for (
int i=0; i<std::min(3,
int(scales.size())); i++)
1115 if (scales[i] != 1.0)
1118 image->dim[i+1] =
static_cast<int>(std::floor(
image->dim[i+1] * scales[i]));
1123 nifti_update_dims_from_array(
image);
1127 nifti_image_unload(
image);
1129 image->scl_slope = 0.0;
1130 image->scl_inter = 0.0;
1139 if (image->qform_code == 0 &&
image->sform_code == 0)
1141 Rf_warning(
"Image qform and sform codes are both zero, so it cannot be reoriented");
1145 int used[6] = { 0, 0, 0, 0, 0, 0 };
1149 if (used[0]+used[1] != 1 || used[2]+used[3] != 1 || used[4]+used[5] != 1)
1150 throw std::runtime_error(
"Each canonical axis should be used exactly once");
1152 const int codes[3] = { icode, jcode, kcode };
1153 const mat44 native = this->
xform();
1157 for (
int j=0; j<3; j++)
1159 for (
int i=0; i<3; i++)
1160 target.m[i][j] = 0.0;
1164 case NIFTI_L2R: target.m[0][j] = 1.0;
break;
1165 case NIFTI_R2L: target.m[0][j] = -1.0;
break;
1166 case NIFTI_P2A: target.m[1][j] = 1.0;
break;
1167 case NIFTI_A2P: target.m[1][j] = -1.0;
break;
1168 case NIFTI_I2S: target.m[2][j] = 1.0;
break;
1169 case NIFTI_S2I: target.m[2][j] = -1.0;
break;
1174 int nicode, njcode, nkcode;
1175 nifti_mat44_to_orientation(native, &nicode, &njcode, &nkcode);
1176 int ncodes[3] = { nicode, njcode, nkcode };
1177 mat33 nativeAxesTransposed;
1178 for (
int i=0; i<3; i++)
1180 for (
int j=0; j<3; j++)
1181 nativeAxesTransposed.m[i][j] = 0.0;
1185 case NIFTI_L2R: nativeAxesTransposed.m[i][0] = 1.0;
break;
1186 case NIFTI_R2L: nativeAxesTransposed.m[i][0] = -1.0;
break;
1187 case NIFTI_P2A: nativeAxesTransposed.m[i][1] = 1.0;
break;
1188 case NIFTI_A2P: nativeAxesTransposed.m[i][1] = -1.0;
break;
1189 case NIFTI_I2S: nativeAxesTransposed.m[i][2] = 1.0;
break;
1190 case NIFTI_S2I: nativeAxesTransposed.m[i][2] = -1.0;
break;
1195 if (icode == nicode && jcode == njcode && kcode == nkcode)
1201 mat33 transform = nifti_mat33_mul(nativeAxesTransposed, target);
1203 for (
int i=0; i<4; i++)
1205 for (
int j=0; j<3; j++)
1206 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];
1208 result.m[i][3] = native.m[i][3];
1212 if (
image->qform_code > 0)
1214 image->qto_xyz = result;
1215 image->qto_ijk = nifti_mat44_inverse(
image->qto_xyz);
1216 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);
1218 if (
image->sform_code > 0)
1220 image->sto_xyz = result;
1221 image->sto_ijk = nifti_mat44_inverse(
image->sto_xyz);
1225 int locs[3], signs[3], newdim[3];
1227 double maxes[3] = { R_NegInf, R_NegInf, R_NegInf };
1228 for (
int j=0; j<3; j++)
1230 for (
int i=0; i<3; i++)
1232 const double value =
static_cast<double>(transform.m[i][j]);
1233 if (fabs(value) > maxes[j])
1235 maxes[j] = fabs(value);
1236 signs[j] = value > 0.0 ? 1 : -1;
1242 newdim[j] =
image->dim[locs[j]+1];
1243 newpixdim[j] =
image->pixdim[locs[j]+1];
1247 ptrdiff_t strides[3];
1248 strides[locs[0]] = 1;
1249 for (
int n=1; n<3; n++)
1250 strides[locs[n]] = strides[locs[n-1]] *
image->dim[locs[n-1]+1];
1252 if (
image->data != NULL)
1255 size_t nVolumes = std::max(
size_t(1),
image->nvox / volSize);
1257 const std::vector<double> oldData = this->getData<double>();
1258 std::vector<double> newData(
image->nvox);
1261 size_t volStart = 0;
1262 for (
int i=0; i<3; i++)
1265 volStart += (
image->dim[i+1] - 1) * strides[i];
1269 std::vector<double>::const_iterator it = oldData.begin();
1270 for (
size_t v=0; v<nVolumes; v++)
1272 for (
int k=0; k<
image->nz; k++)
1274 ptrdiff_t offset = k * strides[2] * signs[2];
1275 for (
int j=0; j<
image->ny; j++)
1277 for (
int i=0; i<
image->nx; i++)
1279 newData[volStart + offset] = *it++;
1280 offset += strides[0] * signs[0];
1282 offset += strides[1] * signs[1] -
image->nx * strides[0] * signs[0];
1285 volStart += volSize;
1294 std::copy(newdim, newdim+3,
image->dim+1);
1295 std::copy(newpixdim, newpixdim+3,
image->pixdim+1);
1296 nifti_update_dims_from_array(
image);
1303 if (orientation.length() != 3)
1304 throw std::runtime_error(
"Orientation string should have exactly three characters");
1307 for (
int i=0; i<3; i++)
1309 switch(orientation[i])
1311 case 'r':
case 'R': codes[i] = NIFTI_L2R;
break;
1312 case 'l':
case 'L': codes[i] = NIFTI_R2L;
break;
1313 case 'a':
case 'A': codes[i] = NIFTI_P2A;
break;
1314 case 'p':
case 'P': codes[i] = NIFTI_A2P;
break;
1315 case 's':
case 'S': codes[i] = NIFTI_I2S;
break;
1316 case 'i':
case 'I': codes[i] = NIFTI_S2I;
break;
1319 throw std::runtime_error(
"Orientation string is invalid");
1323 return reorient(codes[0], codes[1], codes[2]);
1328 inline NiftiImage & NiftiImage::update (
const Rcpp::RObject &
object)
1330 if (Rf_isVectorList(
object))
1332 Rcpp::List list(
object);
1333 nifti_1_header *header = NULL;
1336 header = nifti_make_new_header(NULL, DT_FLOAT64);
1337 internal::updateHeader(header, list,
true);
1341 header = (nifti_1_header *) calloc(1,
sizeof(nifti_1_header));
1342 *header = nifti_convert_nim2nhdr(
image);
1343 internal::updateHeader(header, list,
true);
1348 nifti_image *newImage = nifti_convert_nhdr2nim(*header, NULL);
1349 if (this->image->data != NULL)
1351 size_t dataSize = nifti_get_volsize(
image);
1352 newImage->data = calloc(1, dataSize);
1353 memcpy(newImage->data,
image->data, dataSize);
1356 nifti_image_free(
image);
1357 this->image = newImage;
1360 else if (
object.hasAttribute(
"dim"))
1362 for (
int i=0; i<8; i++)
1364 const std::vector<int> dimVector =
object.attr(
"dim");
1366 const int nDims = std::min(7,
int(dimVector.size()));
1368 for (
int i=0; i<
nDims; i++)
1369 image->dim[i+1] = dimVector[i];
1371 if (
object.hasAttribute(
"pixdim"))
1373 const std::vector<float> pixdimVector =
object.attr(
"pixdim");
1377 if (
object.hasAttribute(
"pixunits"))
1379 const std::vector<std::string> pixunitsVector =
object.attr(
"pixunits");
1384 nifti_update_dims_from_array(
image);
1387 image->datatype = NiftiImage::sexpTypeToNiftiType(
object.sexp_type());
1388 nifti_datatype_sizes(
image->datatype, &
image->nbyper, NULL);
1391 nifti_image_unload(
image);
1393 const size_t dataSize = nifti_get_volsize(
image);
1394 image->data = calloc(1, dataSize);
1395 if (
image->datatype == DT_INT32)
1396 memcpy(
image->data, INTEGER(
object), dataSize);
1398 memcpy(
image->data, REAL(
object), dataSize);
1400 image->scl_slope = 0.0;
1401 image->scl_inter = 0.0;
1414 for (
int i=0; i<4; i++)
1416 for (
int j=0; j<4; j++)
1417 matrix.m[i][j] = 0.0;
1421 else if (
image->qform_code <= 0 &&
image->sform_code <= 0)
1425 for (
int i=0; i<4; i++)
1427 for (
int j=0; j<4; j++)
1430 matrix.m[i][j] = 0.0;
1432 matrix.m[3][3] = 1.0;
1434 matrix.m[i][j] = (
image->pixdim[i+1]==0.0 ? 1.0 :
image->pixdim[i+1]);
1439 else if ((preferQuaternion &&
image->qform_code > 0) ||
image->sform_code <= 0)
1440 return image->qto_xyz;
1442 return image->sto_xyz;
1445 template <
typename TargetType>
1449 return std::vector<TargetType>();
1451 size_t blockSize = 1;
1455 std::vector<TargetType> data(blockSize);
1456 internal::convertData<TargetType>(
image->data,
image->datatype, blockSize, data.begin(), blockSize*
index);
1459 std::transform(data.begin(), data.end(), data.begin(), internal::DataRescaler<TargetType>(
image->scl_slope,
image->scl_inter));
1464 template <
typename TargetType>
1468 return std::vector<TargetType>();
1470 std::vector<TargetType> data(
image->nvox);
1471 internal::convertData<TargetType>(
image->data,
image->datatype,
image->nvox, data.begin());
1474 std::transform(data.begin(), data.end(), data.begin(), internal::DataRescaler<TargetType>(
image->scl_slope,
image->scl_inter));
1481 if (this->
isNull() || image->datatype == datatype)
1484 if (
image->data != NULL)
1487 nifti_datatype_sizes(datatype, &bytesPerPixel, NULL);
1488 void *data = calloc(
image->nvox, bytesPerPixel);
1493 internal::convertData<uint8_t>(
image->data,
image->datatype,
image->nvox,
static_cast<uint8_t *
>(data));
1497 internal::convertData<int16_t>(
image->data,
image->datatype,
image->nvox,
static_cast<int16_t *
>(data));
1501 internal::convertData<int32_t>(
image->data,
image->datatype,
image->nvox,
static_cast<int32_t *
>(data));
1505 internal::convertData<float>(
image->data,
image->datatype,
image->nvox,
static_cast<float *
>(data));
1509 internal::convertData<double>(
image->data,
image->datatype,
image->nvox,
static_cast<double *
>(data));
1513 internal::convertData<int8_t>(
image->data,
image->datatype,
image->nvox,
static_cast<int8_t *
>(data));
1517 internal::convertData<uint16_t>(
image->data,
image->datatype,
image->nvox,
static_cast<uint16_t *
>(data));
1521 internal::convertData<uint32_t>(
image->data,
image->datatype,
image->nvox,
static_cast<uint32_t *
>(data));
1525 internal::convertData<int64_t>(
image->data,
image->datatype,
image->nvox,
static_cast<int64_t *
>(data));
1529 internal::convertData<uint64_t>(
image->data,
image->datatype,
image->nvox,
static_cast<uint64_t *
>(data));
1533 throw std::runtime_error(
"Unsupported data type (" + std::string(nifti_datatype_string(datatype)) +
")");
1536 nifti_image_unload(
image);
1540 image->datatype = datatype;
1541 nifti_datatype_sizes(datatype, &
image->nbyper, &
image->swapsize);
1551 template <
typename SourceType>
1556 else if (data.size() !=
image->nvox)
1557 throw std::runtime_error(
"New data length does not match the number of voxels in the image");
1559 if (datatype != DT_NONE)
1561 nifti_image_unload(
image);
1562 image->datatype = datatype;
1563 nifti_datatype_sizes(datatype, &
image->nbyper, &
image->swapsize);
1566 if (
image->data == NULL)
1568 internal::replaceData<SourceType>(data.begin(), data.end(),
image->data,
image->datatype);
1570 image->scl_slope = 0.0;
1571 image->scl_inter = 0.0;
1572 image->cal_min =
static_cast<float>(*std::min_element(data.begin(), data.end()));
1573 image->cal_max =
static_cast<float>(*std::max_element(data.begin(), data.end()));
1583 if (datatype == DT_NONE)
1588 const int status = nifti_set_filenames(imageToWrite, fileName.c_str(),
false,
true);
1590 throw std::runtime_error(
"Failed to set filenames for NIfTI object");
1591 nifti_image_write(imageToWrite);
1596 toFile(fileName, internal::stringToDatatype(datatype));
1601 inline Rcpp::RObject NiftiImage::toArray ()
const 1603 Rcpp::RObject array;
1609 array = internal::imageDataToArray<REALSXP>(
image);
1610 std::transform(REAL(array), REAL(array)+Rf_length(array), REAL(array), internal::DataRescaler<double>(
image->scl_slope,
image->scl_inter));
1614 switch (
image->datatype)
1624 array = internal::imageDataToArray<INTSXP>(
image);
1629 array = internal::imageDataToArray<REALSXP>(
image);
1633 throw std::runtime_error(
"Unsupported data type (" + std::string(nifti_datatype_string(
image->datatype)) +
")");
1637 internal::addAttributes(array,
image);
1638 array.attr(
"class") = Rcpp::CharacterVector::create(
"niftiImage",
"array");
1642 inline Rcpp::RObject NiftiImage::toPointer (
const std::string label)
const 1645 return Rcpp::RObject();
1648 Rcpp::RObject
string = Rcpp::wrap(label);
1649 internal::addAttributes(
string,
image,
false);
1650 string.attr(
"class") = Rcpp::CharacterVector::create(
"internalImage",
"niftiImage");
1655 inline Rcpp::RObject NiftiImage::toArrayOrPointer (
const bool internal,
const std::string label)
const 1657 return (
internal ? toPointer(label) : toArray());
1660 inline Rcpp::RObject NiftiImage::headerToList ()
const 1662 if (this->image == NULL)
1663 return Rcpp::RObject();
1665 nifti_1_header header = nifti_convert_nim2nhdr(this->image);
1668 result[
"sizeof_hdr"] = header.sizeof_hdr;
1670 result[
"dim_info"] = int(header.dim_info);
1671 result[
"dim"] = std::vector<short>(header.dim, header.dim+8);
1673 result[
"intent_p1"] = header.intent_p1;
1674 result[
"intent_p2"] = header.intent_p2;
1675 result[
"intent_p3"] = header.intent_p3;
1676 result[
"intent_code"] = header.intent_code;
1678 result[
"datatype"] = header.datatype;
1679 result[
"bitpix"] = header.bitpix;
1681 result[
"slice_start"] = header.slice_start;
1682 result[
"pixdim"] = std::vector<float>(header.pixdim, header.pixdim+8);
1683 result[
"vox_offset"] = header.vox_offset;
1684 result[
"scl_slope"] = header.scl_slope;
1685 result[
"scl_inter"] = header.scl_inter;
1686 result[
"slice_end"] = header.slice_end;
1687 result[
"slice_code"] = int(header.slice_code);
1688 result[
"xyzt_units"] = int(header.xyzt_units);
1689 result[
"cal_max"] = header.cal_max;
1690 result[
"cal_min"] = header.cal_min;
1691 result[
"slice_duration"] = header.slice_duration;
1692 result[
"toffset"] = header.toffset;
1693 result[
"descrip"] = std::string(header.descrip, 80);
1694 result[
"aux_file"] = std::string(header.aux_file, 24);
1696 result[
"qform_code"] = header.qform_code;
1697 result[
"sform_code"] = header.sform_code;
1698 result[
"quatern_b"] = header.quatern_b;
1699 result[
"quatern_c"] = header.quatern_c;
1700 result[
"quatern_d"] = header.quatern_d;
1701 result[
"qoffset_x"] = header.qoffset_x;
1702 result[
"qoffset_y"] = header.qoffset_y;
1703 result[
"qoffset_z"] = header.qoffset_z;
1704 result[
"srow_x"] = std::vector<float>(header.srow_x, header.srow_x+4);
1705 result[
"srow_y"] = std::vector<float>(header.srow_y, header.srow_y+4);
1706 result[
"srow_z"] = std::vector<float>(header.srow_z, header.srow_z+4);
1708 result[
"intent_name"] = std::string(header.intent_name, 16);
1709 result[
"magic"] = std::string(header.magic, 4);
1711 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:1135
NiftiImage()
Default constructor.
Definition: NiftiImage.h:256
const int index
The location along dimension.
Definition: NiftiImage.h:56
static std::string xformToString(const mat44 matrix)
Convert a 4x4 xform matrix to a string describing its canonical axes.
Definition: NiftiImage.h:129
std::vector< int > dim() const
Return the dimensions of the image.
Definition: NiftiImage.h:442
const nifti_image * operator->() const
Allows a NiftiImage object to be treated as a pointer to a const nifti_image.
Definition: NiftiImage.h:364
bool persistent
Marker of persistence, which determines whether the nifti_image should be freed on destruction...
Definition: NiftiImage.h:188
std::vector< float > pixdim() const
Return the dimensions of the pixels or voxels in the image.
Definition: NiftiImage.h:454
NiftiImage(const std::string &path, const bool readData=true)
Initialise using a path string.
Definition: NiftiImage.h:309
NiftiImage & rescale(const std::vector< float > &scales)
Rescale the image, changing its image dimensions and pixel dimensions.
Definition: NiftiImage.h:1109
NiftiImage(nifti_image *const image, const bool copy=false)
Initialise using an existing nifti_image pointer.
Definition: NiftiImage.h:291
Definition: NiftiImage.h:39
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:80
bool isNull() const
Determine whether or not the internal pointer is NULL.
Definition: NiftiImage.h:415
Inner class referring to a subset of an image.
Definition: NiftiImage.h:52
Block slice(const int i)
Extract a slice block from a 3D image.
Definition: NiftiImage.h:596
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:1552
std::vector< TargetType > getData() const
Extract a vector of data from the image, casting it to any required element type. ...
Definition: NiftiImage.h:1465
bool isDataScaled() const
Determine whether nontrivial scale and slope parameters are set.
Definition: NiftiImage.h:425
const Block slice(const int i) const
Extract a slice block from a 3D image.
Definition: NiftiImage.h:589
const Block volume(const int i) const
Extract a volume block from a 4D image.
Definition: NiftiImage.h:603
bool isPersistent() const
Determine whether or not the image is marked as persistent.
Definition: NiftiImage.h:420
void setPixunits(const std::vector< std::string > &pixunits)
Modify the pixel dimension units.
Definition: NiftiImage.h:1084
NiftiImage(const Block &source)
Initialise from a block, copying in the data.
Definition: NiftiImage.h:276
virtual ~NiftiImage()
Destructor which frees the wrapped pointer, unless the object is marked as persistent.
Definition: NiftiImage.h:340
Thin wrapper around a C-style nifti_image struct that allows C++-style destruction.
Definition: NiftiImage.h:45
NiftiImage(const NiftiImage &source)
Copy constructor.
Definition: NiftiImage.h:263
int nBlocks() const
Return the number of blocks in the image.
Definition: NiftiImage.h:558
const int dimension
The dimension along which the block applies (which should be the last)
Definition: NiftiImage.h:55
const NiftiImage & image
The parent image.
Definition: NiftiImage.h:54
NiftiImage & operator=(const NiftiImage &source)
Copy assignment operator, which copies from its argument.
Definition: NiftiImage.h:375
static int fileVersion(const std::string &path)
Get the NIfTI format version used by the file at the specified path.
Definition: NiftiImage.h:157
void toFile(const std::string fileName, const short datatype=DT_NONE) const
Write the image to a NIfTI-1 file.
Definition: NiftiImage.h:1578
void updatePixdim(const std::vector< float > &pixdim)
Modify the pixel dimensions, and potentially the xform matrices to match.
Definition: NiftiImage.h:1031
const Block block(const int i) const
Extract a block from the image.
Definition: NiftiImage.h:573
NiftiImage & setPersistence(const bool persistent)
Marked the image as persistent, so that it can be passed back to R.
Definition: NiftiImage.h:402
void copy(const nifti_image *source)
Copy the contents of a nifti_image to create a new image.
Definition: NiftiImage.h:662
mat44 xform(const bool preferQuaternion=true) const
Obtain an xform matrix, indicating the orientation of the image.
Definition: NiftiImage.h:1409
nifti_image * image
The wrapped nifti_image pointer.
Definition: NiftiImage.h:187
NiftiImage & changeDatatype(const short datatype)
Change the datatype of the image, casting the pixel data if present.
Definition: NiftiImage.h:1479
std::vector< TargetType > getData() const
Extract a vector of data from a block, casting it to any required element type.
Definition: NiftiImage.h:1446
int nDims() const
Return the number of dimensions in the image.
Definition: NiftiImage.h:430
Block volume(const int i)
Extract a volume block from a 4D image.
Definition: NiftiImage.h:610
NiftiImage & drop()
Drop unitary dimensions.
Definition: NiftiImage.h:468
Block(const NiftiImage &image, const int dimension, const int index)
Standard constructor for this class.
Definition: NiftiImage.h:65
NiftiImage & dropData()
Drop the data from the image, retaining only the metadata.
Definition: NiftiImage.h:509
Block block(const int i)
Extract a block from the image.
Definition: NiftiImage.h:582