1 #ifndef _NIFTI_IMAGE_H_ 2 #define _NIFTI_IMAGE_H_ 11 #define R_NegInf -INFINITY 28 #include "niftilib/nifti1_io.h" 46 vec3 operator-()
const 56 inline mat33 topLeftCorner (
const mat44 &matrix)
59 for (
int i=0; i<3; i++)
61 for (
int j=0; j<3; j++)
62 newMatrix.m[i][j] = matrix.m[i][j];
67 inline vec3 matrixVectorProduct (
const mat33 &matrix,
const vec3 &vector)
70 for (
int i=0; i<3; i++)
73 for (
int j=0; j<3; j++)
74 newVector.v[i] += matrix.m[i][j] * vector.v[j];
109 throw std::runtime_error(
"Blocks must be along the last dimension in the image");
122 if (source->datatype !=
image->datatype)
123 throw std::runtime_error(
"New data does not have the same datatype as the target block");
124 if (source->scl_slope !=
image->scl_slope || source->scl_inter !=
image->scl_inter)
125 throw std::runtime_error(
"New data does not have the same scale parameters as the target block");
127 size_t blockSize = 1;
131 if (blockSize != source->nvox)
132 throw std::runtime_error(
"New data does not have the same size as the target block");
134 blockSize *=
image->nbyper;
135 memcpy(static_cast<char*>(
image->data) + blockSize*
index, source->data, blockSize);
146 template <
typename TargetType>
147 std::vector<TargetType>
getData (
const bool useSlope =
true)
const;
157 static short sexpTypeToNiftiType (
const int sexpType)
159 if (sexpType == INTSXP || sexpType == LGLSXP)
161 else if (sexpType == REALSXP)
164 throw std::runtime_error(
"Array elements must be numeric");
175 float qb, qc, qd, qfac;
176 nifti_mat44_to_quatern(matrix, &qb, &qc, &qd, NULL, NULL, NULL, NULL, NULL, NULL, &qfac);
177 mat44 rotationMatrix = nifti_quatern_to_mat44(qb, qc, qd, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, qfac);
178 return internal::topLeftCorner(rotationMatrix);
188 int icode, jcode, kcode;
189 nifti_mat44_to_orientation(matrix, &icode, &jcode, &kcode);
191 int codes[3] = { icode, jcode, kcode };
192 std::string result(
"---");
193 for (
int i=0; i<3; i++)
197 case NIFTI_L2R: result[i] =
'R';
break;
198 case NIFTI_R2L: result[i] =
'L';
break;
199 case NIFTI_P2A: result[i] =
'A';
break;
200 case NIFTI_A2P: result[i] =
'P';
break;
201 case NIFTI_I2S: result[i] =
'S';
break;
202 case NIFTI_S2I: result[i] =
'I';
break;
216 nifti_1_header *header = nifti_read_header(path.c_str(), NULL,
false);
221 int version = NIFTI_VERSION(*header);
225 if (header->sizeof_hdr == 540 || header->sizeof_hdr == 469893120)
228 const char *magic = (
char *) header + 4;
229 if (strncmp(magic,
"ni2",3) == 0 || strncmp(magic,
"n+2",3) == 0)
232 else if (!nifti_hdr_looks_good(header))
251 void copy (
const nifti_image *source);
273 void initFromNiftiS4 (
const Rcpp::RObject &
object,
const bool copyData =
true);
280 void initFromMriImage (
const Rcpp::RObject &
object,
const bool copyData =
true);
286 void initFromList (
const Rcpp::RObject &
object);
293 void initFromArray (
const Rcpp::RObject &
object,
const bool copyData =
true);
307 void setPixunits (
const std::vector<std::string> &pixunits);
325 Rprintf(
"Creating NiftiImage with pointer %p (from NiftiImage)\n", this->image);
338 Rprintf(
"Creating NiftiImage with pointer %p (from Block)\n", this->image);
356 Rprintf(
"Creating NiftiImage with pointer %p (from pointer)\n", this->image);
366 NiftiImage (
const std::string &path,
const bool readData =
true)
369 this->image = nifti_image_read(path.c_str(), readData);
370 if (this->image == NULL)
371 throw std::runtime_error(
"Failed to read image from path " + path);
373 Rprintf(
"Creating NiftiImage with pointer %p (from string)\n", this->image);
383 NiftiImage (
const std::string &path,
const std::vector<int> &volumes);
391 NiftiImage (
const SEXP
object,
const bool readData =
true);
402 Rprintf(
"Freeing NiftiImage with pointer %p\n", this->image);
404 nifti_image_free(
image);
411 operator const nifti_image* ()
const {
return image; }
416 operator nifti_image* () {
return image; }
436 Rprintf(
"Creating NiftiImage with pointer %p (from NiftiImage)\n", this->image);
450 Rprintf(
"Creating NiftiImage with pointer %p (from Block)\n", this->image);
464 Rprintf(
"Setting NiftiImage with pointer %p to be persistent\n", this->image);
499 std::vector<int>
dim ()
const 502 return std::vector<int>();
514 return std::vector<float>();
516 return std::vector<float>(
image->pixdim+1,
image->pixdim+
image->ndim+1);
527 int ndim =
image->ndim;
528 while (
image->dim[ndim] < 2)
542 template <
typename TargetType>
543 std::vector<TargetType>
getData (
const bool useSlope =
true)
const;
568 template <
typename SourceType>
576 nifti_image_unload(
image);
610 NiftiImage & update (
const Rcpp::RObject &
object);
618 mat44
xform (
const bool preferQuaternion =
true)
const;
682 void toFile (
const std::string fileName,
const short datatype = DT_NONE)
const;
689 void toFile (
const std::string fileName,
const std::string &datatype)
const;
697 Rcpp::RObject toArray ()
const;
704 Rcpp::RObject toPointer (
const std::string label)
const;
712 Rcpp::RObject toArrayOrPointer (
const bool internal,
const std::string label)
const;
718 Rcpp::RObject headerToList ()
const;
725 #include "lib/NiftiImage_internal.h" 730 nifti_image_free(
image);
736 image = nifti_copy_nim_info(source);
737 if (source->data != NULL)
739 size_t dataSize = nifti_get_volsize(source);
740 image->data = calloc(1, dataSize);
741 memcpy(
image->data, source->data, dataSize);
750 const nifti_image *sourceStruct = source;
757 nifti_image_free(
image);
759 const nifti_image *sourceStruct = source.
image;
760 if (sourceStruct == NULL)
764 image = nifti_copy_nim_info(sourceStruct);
768 nifti_update_dims_from_array(
image);
770 if (sourceStruct->data != NULL)
772 size_t blockSize = nifti_get_volsize(
image);
773 image->data = calloc(1, blockSize);
774 memcpy(
image->data, static_cast<char*>(source.
image->data) + blockSize*source.
index, blockSize);
784 inline void NiftiImage::initFromNiftiS4 (
const Rcpp::RObject &
object,
const bool copyData)
786 nifti_1_header header;
787 header.sizeof_hdr = 348;
789 const std::vector<short> dims =
object.slot(
"dim_");
790 for (
int i=0; i<8; i++)
791 header.dim[i] = dims[i];
793 header.intent_p1 =
object.slot(
"intent_p1");
794 header.intent_p2 =
object.slot(
"intent_p2");
795 header.intent_p3 =
object.slot(
"intent_p3");
796 header.intent_code =
object.slot(
"intent_code");
798 header.datatype =
object.slot(
"datatype");
799 header.bitpix =
object.slot(
"bitpix");
801 header.slice_start =
object.slot(
"slice_start");
802 header.slice_end =
object.slot(
"slice_end");
803 header.slice_code = Rcpp::as<int>(
object.slot(
"slice_code"));
804 header.slice_duration =
object.slot(
"slice_duration");
806 const std::vector<float> pixdims =
object.slot(
"pixdim");
807 for (
int i=0; i<8; i++)
808 header.pixdim[i] = pixdims[i];
809 header.xyzt_units = Rcpp::as<int>(
object.slot(
"xyzt_units"));
811 header.vox_offset =
object.slot(
"vox_offset");
813 header.scl_slope =
object.slot(
"scl_slope");
814 header.scl_inter =
object.slot(
"scl_inter");
815 header.toffset =
object.slot(
"toffset");
817 header.cal_max =
object.slot(
"cal_max");
818 header.cal_min =
object.slot(
"cal_min");
819 header.glmax = header.glmin = 0;
821 strncpy(header.descrip, Rcpp::as<std::string>(
object.slot(
"descrip")).c_str(), 79);
822 header.descrip[79] =
'\0';
823 strncpy(header.aux_file, Rcpp::as<std::string>(
object.slot(
"aux_file")).c_str(), 23);
824 header.aux_file[23] =
'\0';
825 strncpy(header.intent_name, Rcpp::as<std::string>(
object.slot(
"intent_name")).c_str(), 15);
826 header.intent_name[15] =
'\0';
827 strncpy(header.magic, Rcpp::as<std::string>(
object.slot(
"magic")).c_str(), 3);
828 header.magic[3] =
'\0';
830 header.qform_code =
object.slot(
"qform_code");
831 header.sform_code =
object.slot(
"sform_code");
833 header.quatern_b =
object.slot(
"quatern_b");
834 header.quatern_c =
object.slot(
"quatern_c");
835 header.quatern_d =
object.slot(
"quatern_d");
836 header.qoffset_x =
object.slot(
"qoffset_x");
837 header.qoffset_y =
object.slot(
"qoffset_y");
838 header.qoffset_z =
object.slot(
"qoffset_z");
840 const std::vector<float> srow_x =
object.slot(
"srow_x");
841 const std::vector<float> srow_y =
object.slot(
"srow_y");
842 const std::vector<float> srow_z =
object.slot(
"srow_z");
843 for (
int i=0; i<4; i++)
845 header.srow_x[i] = srow_x[i];
846 header.srow_y[i] = srow_y[i];
847 header.srow_z[i] = srow_z[i];
850 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)
851 header.datatype = DT_INT32;
852 else if (header.datatype == DT_FLOAT32 || header.datatype == DT_FLOAT64)
853 header.datatype = DT_FLOAT64;
855 throw std::runtime_error(
"Data type is not supported");
857 this->
image = nifti_convert_nhdr2nim(header, NULL);
859 const SEXP data = PROTECT(
object.slot(
".Data"));
860 if (!copyData || Rf_length(data) <= 1)
861 this->
image->data = NULL;
864 const size_t dataSize = nifti_get_volsize(this->
image);
865 this->
image->data = calloc(1, dataSize);
866 if (header.datatype == DT_INT32)
868 Rcpp::IntegerVector intData(data);
869 std::copy(intData.begin(), intData.end(),
static_cast<int32_t*
>(this->
image->data));
873 Rcpp::DoubleVector doubleData(data);
874 std::copy(doubleData.begin(), doubleData.end(),
static_cast<double*
>(this->
image->data));
880 inline void NiftiImage::initFromMriImage (
const Rcpp::RObject &
object,
const bool copyData)
882 Rcpp::Reference mriImage(
object);
883 Rcpp::Function getXform = mriImage.field(
"getXform");
884 Rcpp::NumericMatrix
xform = getXform();
888 if (Rf_length(mriImage.field(
"tags")) > 0)
889 initFromList(mriImage.field(
"tags"));
891 Rcpp::RObject data = mriImage.field(
"data");
892 if (data.inherits(
"SparseArray"))
894 Rcpp::Language call(
"as.array", data);
898 const short datatype = (Rf_isNull(data) ? DT_INT32 : sexpTypeToNiftiType(data.sexp_type()));
900 int dims[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
901 const std::vector<int> dimVector = mriImage.field(
"imageDims");
902 const int nDims = std::min(7,
int(dimVector.size()));
905 for (
int i=0; i<
nDims; i++)
907 dims[i+1] = dimVector[i];
908 nVoxels *= dimVector[i];
911 if (this->
image == NULL)
912 this->
image = nifti_make_new_nim(dims, datatype, FALSE);
915 std::copy(dims, dims+8, this->
image->dim);
916 this->
image->datatype = datatype;
917 nifti_datatype_sizes(
image->datatype, &
image->nbyper, NULL);
920 if (copyData && !Rf_isNull(data))
923 const size_t dataSize = nVoxels *
image->nbyper;
924 this->
image->data = calloc(1, dataSize);
925 if (datatype == DT_INT32)
926 memcpy(this->
image->data, INTEGER(data), dataSize);
928 memcpy(this->
image->data, REAL(data), dataSize);
931 this->
image->data = NULL;
933 const std::vector<float> pixdimVector = mriImage.field(
"voxelDims");
934 const int pixdimLength = pixdimVector.size();
935 for (
int i=0; i<std::min(pixdimLength,
nDims); i++)
936 this->
image->pixdim[i+1] = std::abs(pixdimVector[i]);
938 const std::vector<std::string> pixunitsVector = mriImage.field(
"voxelDimUnits");
942 this->
image->qform_code = this->image->sform_code = 0;
946 for (
int i=0; i<4; i++)
948 for (
int j=0; j<4; j++)
949 matrix.m[i][j] = static_cast<float>(
xform(i,j));
952 this->
image->qto_xyz = matrix;
953 this->
image->qto_ijk = nifti_mat44_inverse(
image->qto_xyz);
954 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);
956 this->
image->sto_xyz = matrix;
957 this->
image->sto_ijk = nifti_mat44_inverse(
image->sto_xyz);
959 this->
image->qform_code = this->
image->sform_code = 2;
963 inline void NiftiImage::initFromList (
const Rcpp::RObject &
object)
965 Rcpp::List list(
object);
966 nifti_1_header *header = nifti_make_new_header(NULL, DT_FLOAT64);
968 internal::updateHeader(header, list);
970 this->image = nifti_convert_nhdr2nim(*header, NULL);
971 this->image->data = NULL;
975 inline void NiftiImage::initFromArray (
const Rcpp::RObject &
object,
const bool copyData)
977 int dims[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
978 const std::vector<int> dimVector =
object.attr(
"dim");
980 const int nDims = std::min(7,
int(dimVector.size()));
982 for (
int i=0; i<nDims; i++)
983 dims[i+1] = dimVector[i];
985 const short datatype = sexpTypeToNiftiType(
object.sexp_type());
986 this->image = nifti_make_new_nim(dims, datatype,
int(copyData));
990 const size_t dataSize = nifti_get_volsize(image);
991 if (datatype == DT_INT32)
992 memcpy(this->image->data, INTEGER(
object), dataSize);
994 memcpy(this->image->data, REAL(
object), dataSize);
997 this->image->data = NULL;
999 if (
object.hasAttribute(
"pixdim"))
1001 const std::vector<float> pixdimVector =
object.attr(
"pixdim");
1002 const int pixdimLength = pixdimVector.size();
1003 for (
int i=0; i<std::min(pixdimLength,nDims); i++)
1004 this->image->pixdim[i+1] = pixdimVector[i];
1007 if (
object.hasAttribute(
"pixunits"))
1009 const std::vector<std::string> pixunitsVector =
object.attr(
"pixunits");
1010 setPixunits(pixunitsVector);
1014 inline NiftiImage::NiftiImage (
const SEXP
object,
const bool readData)
1017 Rcpp::RObject imageObject(
object);
1018 bool resolved =
false;
1020 if (imageObject.hasAttribute(
".nifti_image_ptr"))
1022 Rcpp::XPtr<NiftiImage> imagePtr(SEXP(imageObject.attr(
".nifti_image_ptr")));
1026 this->
image = ptr->image;
1030 if (imageObject.hasAttribute(
"dim"))
1031 update(imageObject);
1033 else if (Rf_isString(
object))
1034 throw std::runtime_error(
"Internal image is not valid");
1036 Rf_warning(
"Ignoring invalid internal pointer");
1041 if (Rf_isNull(
object))
1043 else if (Rf_isString(
object))
1045 const std::string path = Rcpp::as<std::string>(object);
1046 this->
image = nifti_image_read(path.c_str(), readData);
1047 if (this->image == NULL)
1048 throw std::runtime_error(
"Failed to read image from path " + path);
1050 else if (imageObject.inherits(
"nifti"))
1051 initFromNiftiS4(imageObject, readData);
1052 else if (imageObject.inherits(
"anlz"))
1053 throw std::runtime_error(
"Cannot currently convert objects of class \"anlz\"");
1054 else if (imageObject.inherits(
"MriImage"))
1055 initFromMriImage(imageObject, readData);
1056 else if (Rf_isVectorList(
object))
1057 initFromList(imageObject);
1058 else if (imageObject.hasAttribute(
"dim"))
1059 initFromArray(imageObject, readData);
1061 throw std::runtime_error(
"Cannot convert object of class \"" + Rcpp::as<std::string>(imageObject.attr(
"class")) +
"\" to a nifti_image");
1064 if (this->image != NULL)
1065 nifti_update_dims_from_array(this->image);
1068 Rprintf(
"Creating NiftiImage with pointer %p (from SEXP)\n", this->image);
1077 if (volumes.empty())
1078 throw std::runtime_error(
"The vector of volumes is empty");
1080 nifti_brick_list brickList;
1081 this->image = nifti_image_read_bricks(path.c_str(), volumes.size(), &volumes[0], &brickList);
1082 if (this->image == NULL)
1083 throw std::runtime_error(
"Failed to read image from path " + path);
1086 image->data = calloc(1, nifti_get_volsize(
image));
1087 for (
int i=0; i<brickList.nbricks; i++)
1088 memcpy((
char *)
image->data + i * brickSize, brickList.bricks[i], brickSize);
1089 nifti_free_NBL(&brickList);
1092 Rprintf(
"Creating NiftiImage with pointer %p (from string and volume vector)\n", this->image);
1099 const std::vector<float> origPixdim(
image->pixdim+1,
image->pixdim+4);
1101 for (
int i=1; i<8; i++)
1102 image->pixdim[i] = 0.0;
1104 const int pixdimLength =
pixdim.size();
1105 for (
int i=0; i<std::min(pixdimLength,
nDims); i++)
1108 if (!std::equal(origPixdim.begin(), origPixdim.begin() + std::min(3,
nDims),
pixdim.begin()))
1111 for (
int i=0; i<3; i++)
1113 for (
int j=0; j<3; j++)
1116 scaleMatrix.m[i][j] = 0.0;
1117 else if (i >=
nDims)
1118 scaleMatrix.m[i][j] = 1.0;
1120 scaleMatrix.m[i][j] =
pixdim[i] / origPixdim[i];
1124 if (
image->qform_code > 0)
1126 mat33 prod = nifti_mat33_mul(scaleMatrix, internal::topLeftCorner(
image->qto_xyz));
1127 for (
int i=0; i<3; i++)
1129 for (
int j=0; j<3; j++)
1130 image->qto_xyz.m[i][j] = prod.m[i][j];
1132 image->qto_ijk = nifti_mat44_inverse(
image->qto_xyz);
1133 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);
1136 if (
image->sform_code > 0)
1138 mat33 prod = nifti_mat33_mul(scaleMatrix, internal::topLeftCorner(
image->sto_xyz));
1139 for (
int i=0; i<3; i++)
1141 for (
int j=0; j<3; j++)
1142 image->sto_xyz.m[i][j] = prod.m[i][j];
1144 image->sto_ijk = nifti_mat44_inverse(
image->sto_xyz);
1151 for (
size_t i=0; i<pixunits.size(); i++)
1153 if (pixunits[i] ==
"m")
1154 image->xyz_units = NIFTI_UNITS_METER;
1155 else if (pixunits[i] ==
"mm")
1156 image->xyz_units = NIFTI_UNITS_MM;
1157 else if (pixunits[i] ==
"um")
1158 image->xyz_units = NIFTI_UNITS_MICRON;
1159 else if (pixunits[i] ==
"s")
1160 image->time_units = NIFTI_UNITS_SEC;
1161 else if (pixunits[i] ==
"ms")
1162 image->time_units = NIFTI_UNITS_MSEC;
1163 else if (pixunits[i] ==
"us")
1164 image->time_units = NIFTI_UNITS_USEC;
1165 else if (pixunits[i] ==
"Hz")
1166 image->time_units = NIFTI_UNITS_HZ;
1167 else if (pixunits[i] ==
"ppm")
1168 image->time_units = NIFTI_UNITS_PPM;
1169 else if (pixunits[i] ==
"rad/s")
1170 image->time_units = NIFTI_UNITS_RADS;
1178 for (
int i=0; i<std::min(3,
int(scales.size())); i++)
1180 if (scales[i] != 1.0)
1183 image->dim[i+1] =
static_cast<int>(std::floor(
image->dim[i+1] * scales[i]));
1188 nifti_update_dims_from_array(
image);
1192 nifti_image_unload(
image);
1194 image->scl_slope = 0.0;
1195 image->scl_inter = 0.0;
1204 if (image->qform_code == 0 &&
image->sform_code == 0)
1206 Rf_warning(
"Image qform and sform codes are both zero, so it cannot be reoriented");
1210 int used[6] = { 0, 0, 0, 0, 0, 0 };
1214 if (used[0]+used[1] != 1 || used[2]+used[3] != 1 || used[4]+used[5] != 1)
1215 throw std::runtime_error(
"Each canonical axis should be used exactly once");
1217 const int codes[3] = { icode, jcode, kcode };
1218 const mat44 native = this->
xform();
1223 for (
int i=0; i<3; i++)
1224 origin.v[i] = native.m[i][3];
1225 origin = -internal::matrixVectorProduct(nifti_mat33_inverse(internal::topLeftCorner(native)), origin);
1229 for (
int j=0; j<3; j++)
1231 for (
int i=0; i<3; i++)
1232 target.m[i][j] = 0.0;
1236 case NIFTI_L2R: target.m[0][j] = 1.0;
break;
1237 case NIFTI_R2L: target.m[0][j] = -1.0;
break;
1238 case NIFTI_P2A: target.m[1][j] = 1.0;
break;
1239 case NIFTI_A2P: target.m[1][j] = -1.0;
break;
1240 case NIFTI_I2S: target.m[2][j] = 1.0;
break;
1241 case NIFTI_S2I: target.m[2][j] = -1.0;
break;
1246 int nicode, njcode, nkcode;
1247 nifti_mat44_to_orientation(native, &nicode, &njcode, &nkcode);
1248 int ncodes[3] = { nicode, njcode, nkcode };
1249 mat33 nativeAxesTransposed;
1250 for (
int i=0; i<3; i++)
1252 for (
int j=0; j<3; j++)
1253 nativeAxesTransposed.m[i][j] = 0.0;
1257 case NIFTI_L2R: nativeAxesTransposed.m[i][0] = 1.0;
break;
1258 case NIFTI_R2L: nativeAxesTransposed.m[i][0] = -1.0;
break;
1259 case NIFTI_P2A: nativeAxesTransposed.m[i][1] = 1.0;
break;
1260 case NIFTI_A2P: nativeAxesTransposed.m[i][1] = -1.0;
break;
1261 case NIFTI_I2S: nativeAxesTransposed.m[i][2] = 1.0;
break;
1262 case NIFTI_S2I: nativeAxesTransposed.m[i][2] = -1.0;
break;
1267 if (icode == nicode && jcode == njcode && kcode == nkcode)
1273 mat33 transform = nifti_mat33_mul(nativeAxesTransposed, target);
1275 for (
int i=0; i<4; i++)
1277 for (
int j=0; j<3; j++)
1278 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];
1280 result.m[3][i] = i == 3 ? 1.0 : 0.0;
1285 int locs[3], signs[3], newdim[3], revsigns[3];
1287 double maxes[3] = { R_NegInf, R_NegInf, R_NegInf };
1289 for (
int j=0; j<3; j++)
1292 for (
int i=0; i<3; i++)
1294 const double value =
static_cast<double>(transform.m[i][j]);
1295 if (fabs(value) > maxes[j])
1297 maxes[j] = fabs(value);
1298 signs[j] = value > 0.0 ? 1 : -1;
1304 revsigns[locs[j]] = signs[j];
1307 newdim[j] =
image->dim[locs[j]+1];
1308 newpixdim[j] =
image->pixdim[locs[j]+1];
1312 offset.v[j] =
image->dim[locs[j]+1] - origin.v[locs[j]] - 1.0;
1314 offset.v[j] = origin.v[locs[j]];
1318 offset = -internal::matrixVectorProduct(internal::topLeftCorner(result), offset);
1319 for (
int i=0; i<3; i++)
1320 result.m[i][3] = offset.v[i];
1323 if (
image->qform_code > 0)
1325 image->qto_xyz = result;
1326 image->qto_ijk = nifti_mat44_inverse(
image->qto_xyz);
1327 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);
1329 if (
image->sform_code > 0)
1331 image->sto_xyz = result;
1332 image->sto_ijk = nifti_mat44_inverse(
image->sto_xyz);
1336 ptrdiff_t strides[3];
1337 strides[locs[0]] = 1;
1338 strides[locs[1]] = strides[locs[0]] *
image->dim[locs[0]+1];
1339 strides[locs[2]] = strides[locs[1]] *
image->dim[locs[1]+1];
1342 if (
image->data != NULL)
1345 size_t nVolumes = std::max(
size_t(1),
image->nvox / volSize);
1347 const std::vector<double> oldData = this->getData<double>();
1348 std::vector<double> newData(
image->nvox);
1351 size_t volStart = 0;
1352 for (
int i=0; i<3; i++)
1354 if (revsigns[i] < 0)
1355 volStart += (
image->dim[i+1] - 1) * strides[i];
1359 std::vector<double>::const_iterator it = oldData.begin();
1360 for (
size_t v=0; v<nVolumes; v++)
1362 for (
int k=0; k<
image->nz; k++)
1364 ptrdiff_t offset = k * strides[2] * revsigns[2];
1365 for (
int j=0; j<
image->ny; j++)
1367 for (
int i=0; i<
image->nx; i++)
1369 newData[volStart + offset] = *it++;
1370 offset += strides[0] * revsigns[0];
1372 offset += strides[1] * revsigns[1] -
image->nx * strides[0] * revsigns[0];
1375 volStart += volSize;
1384 std::copy(newdim, newdim+3,
image->dim+1);
1385 std::copy(newpixdim, newpixdim+3,
image->pixdim+1);
1386 nifti_update_dims_from_array(
image);
1393 if (orientation.length() != 3)
1394 throw std::runtime_error(
"Orientation string should have exactly three characters");
1397 for (
int i=0; i<3; i++)
1399 switch(orientation[i])
1401 case 'r':
case 'R': codes[i] = NIFTI_L2R;
break;
1402 case 'l':
case 'L': codes[i] = NIFTI_R2L;
break;
1403 case 'a':
case 'A': codes[i] = NIFTI_P2A;
break;
1404 case 'p':
case 'P': codes[i] = NIFTI_A2P;
break;
1405 case 's':
case 'S': codes[i] = NIFTI_I2S;
break;
1406 case 'i':
case 'I': codes[i] = NIFTI_S2I;
break;
1409 throw std::runtime_error(
"Orientation string is invalid");
1413 return reorient(codes[0], codes[1], codes[2]);
1418 inline NiftiImage & NiftiImage::update (
const Rcpp::RObject &
object)
1420 if (Rf_isVectorList(
object))
1422 Rcpp::List list(
object);
1423 nifti_1_header *header = NULL;
1426 header = nifti_make_new_header(NULL, DT_FLOAT64);
1427 internal::updateHeader(header, list,
true);
1431 header = (nifti_1_header *) calloc(1,
sizeof(nifti_1_header));
1432 *header = nifti_convert_nim2nhdr(
image);
1433 internal::updateHeader(header, list,
true);
1438 nifti_image *newImage = nifti_convert_nhdr2nim(*header, NULL);
1439 if (this->image->data != NULL)
1441 size_t dataSize = nifti_get_volsize(
image);
1442 newImage->data = calloc(1, dataSize);
1443 memcpy(newImage->data,
image->data, dataSize);
1446 nifti_image_free(
image);
1447 this->image = newImage;
1450 else if (
object.hasAttribute(
"dim"))
1452 for (
int i=0; i<8; i++)
1454 const std::vector<int> dimVector =
object.attr(
"dim");
1456 const int nDims = std::min(7,
int(dimVector.size()));
1458 for (
int i=0; i<
nDims; i++)
1459 image->dim[i+1] = dimVector[i];
1461 if (
object.hasAttribute(
"pixdim"))
1463 const std::vector<float> pixdimVector =
object.attr(
"pixdim");
1467 if (
object.hasAttribute(
"pixunits"))
1469 const std::vector<std::string> pixunitsVector =
object.attr(
"pixunits");
1474 nifti_update_dims_from_array(
image);
1477 image->datatype = NiftiImage::sexpTypeToNiftiType(
object.sexp_type());
1478 nifti_datatype_sizes(
image->datatype, &
image->nbyper, NULL);
1481 nifti_image_unload(
image);
1483 const size_t dataSize = nifti_get_volsize(
image);
1484 image->data = calloc(1, dataSize);
1485 if (
image->datatype == DT_INT32)
1486 memcpy(
image->data, INTEGER(
object), dataSize);
1488 memcpy(
image->data, REAL(
object), dataSize);
1490 image->scl_slope = 0.0;
1491 image->scl_inter = 0.0;
1504 for (
int i=0; i<4; i++)
1506 for (
int j=0; j<4; j++)
1507 matrix.m[i][j] = 0.0;
1511 else if (
image->qform_code <= 0 &&
image->sform_code <= 0)
1515 for (
int i=0; i<4; i++)
1517 for (
int j=0; j<4; j++)
1520 matrix.m[i][j] = 0.0;
1522 matrix.m[3][3] = 1.0;
1524 matrix.m[i][j] = (
image->pixdim[i+1]==0.0 ? 1.0 :
image->pixdim[i+1]);
1529 else if ((preferQuaternion &&
image->qform_code > 0) ||
image->sform_code <= 0)
1530 return image->qto_xyz;
1532 return image->sto_xyz;
1535 template <
typename TargetType>
1539 return std::vector<TargetType>();
1541 size_t blockSize = 1;
1545 std::vector<TargetType> data(blockSize);
1546 internal::DataConverter<TargetType> *converter = NULL;
1548 converter =
new internal::DataConverter<TargetType>(
image->scl_slope,
image->scl_inter);
1550 internal::convertData<TargetType>(
image->data,
image->datatype, blockSize, data.begin(), blockSize*
index, converter);
1556 template <
typename TargetType>
1560 return std::vector<TargetType>();
1562 std::vector<TargetType> data(
image->nvox);
1563 internal::DataConverter<TargetType> *converter = NULL;
1565 converter =
new internal::DataConverter<TargetType>(
image->scl_slope,
image->scl_inter);
1567 internal::convertData<TargetType>(
image->data,
image->datatype,
image->nvox, data.begin(), 0, converter);
1575 if (this->
isNull() || image->datatype == datatype)
1579 throw std::runtime_error(
"Resetting the slope and intercept for an image with them already set is not supported");
1581 if (
image->data != NULL)
1584 nifti_datatype_sizes(datatype, &bytesPerPixel, NULL);
1585 void *data = calloc(
image->nvox, bytesPerPixel);
1591 internal::DataConverter<uint8_t> converter(useSlope ? internal::DataConverter<uint8_t>::IndexMode : internal::DataConverter<uint8_t>::CastMode);
1592 internal::convertData(
image->data,
image->datatype,
image->nvox, static_cast<uint8_t *>(data), 0, &converter);
1593 image->scl_slope =
static_cast<float>(converter.getSlope());
1594 image->scl_inter =
static_cast<float>(converter.getIntercept());
1600 internal::DataConverter<int16_t> converter(useSlope ? internal::DataConverter<int16_t>::IndexMode : internal::DataConverter<int16_t>::CastMode);
1601 internal::convertData(
image->data,
image->datatype,
image->nvox, static_cast<int16_t *>(data), 0, &converter);
1602 image->scl_slope =
static_cast<float>(converter.getSlope());
1603 image->scl_inter =
static_cast<float>(converter.getIntercept());
1609 internal::DataConverter<int32_t> converter(useSlope ? internal::DataConverter<int32_t>::IndexMode : internal::DataConverter<int32_t>::CastMode);
1610 internal::convertData(
image->data,
image->datatype,
image->nvox, static_cast<int32_t *>(data), 0, &converter);
1611 image->scl_slope =
static_cast<float>(converter.getSlope());
1612 image->scl_inter =
static_cast<float>(converter.getIntercept());
1618 internal::DataConverter<float> converter(useSlope ? internal::DataConverter<float>::IndexMode : internal::DataConverter<float>::CastMode);
1619 internal::convertData(
image->data,
image->datatype,
image->nvox, static_cast<float *>(data), 0, &converter);
1620 image->scl_slope =
static_cast<float>(converter.getSlope());
1621 image->scl_inter =
static_cast<float>(converter.getIntercept());
1627 internal::DataConverter<double> converter(useSlope ? internal::DataConverter<double>::IndexMode : internal::DataConverter<double>::CastMode);
1628 internal::convertData(
image->data,
image->datatype,
image->nvox, static_cast<double *>(data), 0, &converter);
1629 image->scl_slope =
static_cast<float>(converter.getSlope());
1630 image->scl_inter =
static_cast<float>(converter.getIntercept());
1636 internal::DataConverter<int8_t> converter(useSlope ? internal::DataConverter<int8_t>::IndexMode : internal::DataConverter<int8_t>::CastMode);
1637 internal::convertData(
image->data,
image->datatype,
image->nvox, static_cast<int8_t *>(data), 0, &converter);
1638 image->scl_slope =
static_cast<float>(converter.getSlope());
1639 image->scl_inter =
static_cast<float>(converter.getIntercept());
1645 internal::DataConverter<uint16_t> converter(useSlope ? internal::DataConverter<uint16_t>::IndexMode : internal::DataConverter<uint16_t>::CastMode);
1646 internal::convertData(
image->data,
image->datatype,
image->nvox, static_cast<uint16_t *>(data), 0, &converter);
1647 image->scl_slope =
static_cast<float>(converter.getSlope());
1648 image->scl_inter =
static_cast<float>(converter.getIntercept());
1654 internal::DataConverter<uint32_t> converter(useSlope ? internal::DataConverter<uint32_t>::IndexMode : internal::DataConverter<uint32_t>::CastMode);
1655 internal::convertData(
image->data,
image->datatype,
image->nvox, static_cast<uint32_t *>(data), 0, &converter);
1656 image->scl_slope =
static_cast<float>(converter.getSlope());
1657 image->scl_inter =
static_cast<float>(converter.getIntercept());
1663 internal::DataConverter<int64_t> converter(useSlope ? internal::DataConverter<int64_t>::IndexMode : internal::DataConverter<int64_t>::CastMode);
1664 internal::convertData(
image->data,
image->datatype,
image->nvox, static_cast<int64_t *>(data), 0, &converter);
1665 image->scl_slope =
static_cast<float>(converter.getSlope());
1666 image->scl_inter =
static_cast<float>(converter.getIntercept());
1672 internal::DataConverter<uint64_t> converter(useSlope ? internal::DataConverter<uint64_t>::IndexMode : internal::DataConverter<uint64_t>::CastMode);
1673 internal::convertData(
image->data,
image->datatype,
image->nvox, static_cast<uint64_t *>(data), 0, &converter);
1674 image->scl_slope =
static_cast<float>(converter.getSlope());
1675 image->scl_inter =
static_cast<float>(converter.getIntercept());
1680 throw std::runtime_error(
"Unsupported data type (" + std::string(nifti_datatype_string(datatype)) +
")");
1683 nifti_image_unload(
image);
1687 image->datatype = datatype;
1688 nifti_datatype_sizes(datatype, &
image->nbyper, &
image->swapsize);
1695 return changeDatatype(internal::stringToDatatype(datatype), useSlope);
1698 template <
typename SourceType>
1703 else if (data.size() !=
image->nvox)
1704 throw std::runtime_error(
"New data length does not match the number of voxels in the image");
1706 if (datatype != DT_NONE)
1708 nifti_image_unload(
image);
1709 image->datatype = datatype;
1710 nifti_datatype_sizes(datatype, &
image->nbyper, &
image->swapsize);
1713 if (
image->data == NULL)
1715 internal::replaceData<SourceType>(data.begin(), data.end(),
image->data,
image->datatype);
1717 image->scl_slope = 0.0;
1718 image->scl_inter = 0.0;
1719 image->cal_min =
static_cast<float>(*std::min_element(data.begin(), data.end()));
1720 image->cal_max =
static_cast<float>(*std::max_element(data.begin(), data.end()));
1727 const bool changingDatatype = (datatype != DT_NONE && !this->
isNull() && datatype !=
image->datatype);
1732 if (changingDatatype)
1737 const int status = nifti_set_filenames(imageToWrite, fileName.c_str(),
false,
true);
1739 throw std::runtime_error(
"Failed to set filenames for NIfTI object");
1740 nifti_image_write(imageToWrite);
1745 toFile(fileName, internal::stringToDatatype(datatype));
1750 inline Rcpp::RObject NiftiImage::toArray ()
const 1752 Rcpp::RObject array;
1758 array = internal::imageDataToArray<REALSXP>(
image);
1759 std::transform(REAL(array), REAL(array)+Rf_length(array), REAL(array), internal::DataConverter<double>(
image->scl_slope,
image->scl_inter));
1763 switch (
image->datatype)
1773 array = internal::imageDataToArray<INTSXP>(
image);
1778 array = internal::imageDataToArray<REALSXP>(
image);
1782 throw std::runtime_error(
"Unsupported data type (" + std::string(nifti_datatype_string(
image->datatype)) +
")");
1786 internal::addAttributes(array,
image);
1787 array.attr(
"class") = Rcpp::CharacterVector::create(
"niftiImage",
"array");
1791 inline Rcpp::RObject NiftiImage::toPointer (
const std::string label)
const 1794 return Rcpp::RObject();
1797 Rcpp::RObject
string = Rcpp::wrap(label);
1798 internal::addAttributes(
string,
image,
false);
1799 string.attr(
"class") = Rcpp::CharacterVector::create(
"internalImage",
"niftiImage");
1804 inline Rcpp::RObject NiftiImage::toArrayOrPointer (
const bool internal,
const std::string label)
const 1806 return (
internal ? toPointer(label) : toArray());
1809 inline Rcpp::RObject NiftiImage::headerToList ()
const 1811 if (this->image == NULL)
1812 return Rcpp::RObject();
1814 nifti_1_header header = nifti_convert_nim2nhdr(this->image);
1817 result[
"sizeof_hdr"] = header.sizeof_hdr;
1819 result[
"dim_info"] = int(header.dim_info);
1820 result[
"dim"] = std::vector<short>(header.dim, header.dim+8);
1822 result[
"intent_p1"] = header.intent_p1;
1823 result[
"intent_p2"] = header.intent_p2;
1824 result[
"intent_p3"] = header.intent_p3;
1825 result[
"intent_code"] = header.intent_code;
1827 result[
"datatype"] = header.datatype;
1828 result[
"bitpix"] = header.bitpix;
1830 result[
"slice_start"] = header.slice_start;
1831 result[
"pixdim"] = std::vector<float>(header.pixdim, header.pixdim+8);
1832 result[
"vox_offset"] = header.vox_offset;
1833 result[
"scl_slope"] = header.scl_slope;
1834 result[
"scl_inter"] = header.scl_inter;
1835 result[
"slice_end"] = header.slice_end;
1836 result[
"slice_code"] = int(header.slice_code);
1837 result[
"xyzt_units"] = int(header.xyzt_units);
1838 result[
"cal_max"] = header.cal_max;
1839 result[
"cal_min"] = header.cal_min;
1840 result[
"slice_duration"] = header.slice_duration;
1841 result[
"toffset"] = header.toffset;
1842 result[
"descrip"] = std::string(header.descrip, 80);
1843 result[
"aux_file"] = std::string(header.aux_file, 24);
1845 result[
"qform_code"] = header.qform_code;
1846 result[
"sform_code"] = header.sform_code;
1847 result[
"quatern_b"] = header.quatern_b;
1848 result[
"quatern_c"] = header.quatern_c;
1849 result[
"quatern_d"] = header.quatern_d;
1850 result[
"qoffset_x"] = header.qoffset_x;
1851 result[
"qoffset_y"] = header.qoffset_y;
1852 result[
"qoffset_z"] = header.qoffset_z;
1853 result[
"srow_x"] = std::vector<float>(header.srow_x, header.srow_x+4);
1854 result[
"srow_y"] = std::vector<float>(header.srow_y, header.srow_y+4);
1855 result[
"srow_z"] = std::vector<float>(header.srow_z, header.srow_z+4);
1857 result[
"intent_name"] = std::string(header.intent_name, 16);
1858 result[
"magic"] = std::string(header.magic, 4);
1860 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:1200
NiftiImage()
Default constructor.
Definition: NiftiImage.h:313
const int index
The location along dimension.
Definition: NiftiImage.h:96
static std::string xformToString(const mat44 matrix)
Convert a 4x4 xform matrix to a string describing its canonical axes.
Definition: NiftiImage.h:186
std::vector< int > dim() const
Return the dimensions of the image.
Definition: NiftiImage.h:499
const nifti_image * operator->() const
Allows a NiftiImage object to be treated as a pointer to a const nifti_image.
Definition: NiftiImage.h:421
bool persistent
Marker of persistence, which determines whether the nifti_image should be freed on destruction...
Definition: NiftiImage.h:245
std::vector< float > pixdim() const
Return the dimensions of the pixels or voxels in the image.
Definition: NiftiImage.h:511
NiftiImage(const std::string &path, const bool readData=true)
Initialise using a path string.
Definition: NiftiImage.h:366
NiftiImage & rescale(const std::vector< float > &scales)
Rescale the image, changing its image dimensions and pixel dimensions.
Definition: NiftiImage.h:1174
NiftiImage(nifti_image *const image, const bool copy=false)
Initialise using an existing nifti_image pointer.
Definition: NiftiImage.h:348
Definition: NiftiImage.h:38
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:120
bool isNull() const
Determine whether or not the internal pointer is NULL.
Definition: NiftiImage.h:472
Inner class referring to a subset of an image.
Definition: NiftiImage.h:92
Block slice(const int i)
Extract a slice block from a 3D image.
Definition: NiftiImage.h:661
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:1699
bool isDataScaled() const
Determine whether nontrivial scale and slope parameters are set.
Definition: NiftiImage.h:482
const Block slice(const int i) const
Extract a slice block from a 3D image.
Definition: NiftiImage.h:654
const Block volume(const int i) const
Extract a volume block from a 4D image.
Definition: NiftiImage.h:668
bool isPersistent() const
Determine whether or not the image is marked as persistent.
Definition: NiftiImage.h:477
std::vector< TargetType > getData(const bool useSlope=true) const
Extract a vector of data from the image, casting it to any required element type. ...
Definition: NiftiImage.h:1557
std::vector< TargetType > getData(const bool useSlope=true) const
Extract a vector of data from a block, casting it to any required element type.
Definition: NiftiImage.h:1536
void setPixunits(const std::vector< std::string > &pixunits)
Modify the pixel dimension units.
Definition: NiftiImage.h:1149
NiftiImage(const Block &source)
Initialise from a block, copying in the data.
Definition: NiftiImage.h:333
virtual ~NiftiImage()
Destructor which frees the wrapped pointer, unless the object is marked as persistent.
Definition: NiftiImage.h:397
Thin wrapper around a C-style nifti_image struct that allows C++-style destruction.
Definition: NiftiImage.h:85
NiftiImage(const NiftiImage &source)
Copy constructor.
Definition: NiftiImage.h:320
int nBlocks() const
Return the number of blocks in the image.
Definition: NiftiImage.h:623
const int dimension
The dimension along which the block applies (which should be the last)
Definition: NiftiImage.h:95
const NiftiImage & image
The parent image.
Definition: NiftiImage.h:94
NiftiImage & operator=(const NiftiImage &source)
Copy assignment operator, which copies from its argument.
Definition: NiftiImage.h:432
static int fileVersion(const std::string &path)
Get the NIfTI format version used by the file at the specified path.
Definition: NiftiImage.h:214
static mat33 xformToRotation(const mat44 matrix)
Extract the pure rotation part of a 4x4 xform matrix.
Definition: NiftiImage.h:173
void toFile(const std::string fileName, const short datatype=DT_NONE) const
Write the image to a NIfTI-1 file.
Definition: NiftiImage.h:1725
void updatePixdim(const std::vector< float > &pixdim)
Modify the pixel dimensions, and potentially the xform matrices to match.
Definition: NiftiImage.h:1096
const Block block(const int i) const
Extract a block from the image.
Definition: NiftiImage.h:638
NiftiImage & setPersistence(const bool persistent)
Marked the image as persistent, so that it can be passed back to R.
Definition: NiftiImage.h:459
Definition: NiftiImage.h:42
NiftiImage & changeDatatype(const short datatype, const bool useSlope=false)
Change the datatype of the image, casting the pixel data if present.
Definition: NiftiImage.h:1573
void copy(const nifti_image *source)
Copy the contents of a nifti_image to create a new image.
Definition: NiftiImage.h:727
mat44 xform(const bool preferQuaternion=true) const
Obtain an xform matrix, indicating the orientation of the image.
Definition: NiftiImage.h:1499
nifti_image * image
The wrapped nifti_image pointer.
Definition: NiftiImage.h:244
int nDims() const
Return the number of dimensions in the image.
Definition: NiftiImage.h:487
Block volume(const int i)
Extract a volume block from a 4D image.
Definition: NiftiImage.h:675
NiftiImage & drop()
Drop unitary dimensions.
Definition: NiftiImage.h:525
Block(const NiftiImage &image, const int dimension, const int index)
Standard constructor for this class.
Definition: NiftiImage.h:105
NiftiImage & dropData()
Drop the data from the image, retaining only the metadata.
Definition: NiftiImage.h:574
Block block(const int i)
Extract a block from the image.
Definition: NiftiImage.h:647