Skip to content

Commit 61cc38d

Browse files
committed
ENH: Use NumberToString for lossless float metadata in NIfTI reader
SetImageIOMetadataFromNIfTI() was using std::ostringstream to convert float fields from the nifti_image struct to strings, which applies the default 6-digit stream precision and silently truncates values. Replace all float-field conversions with itk::ConvertNumberToString(), which uses Google's double-conversion ToShortest() algorithm (via ITKDoubleConversion) to produce the shortest decimal string that round-trips exactly through strtof. Integer and char* fields are unchanged. Fields corrected: intent_p1/p2/p3, pixdim[0-7], scl_slope, scl_inter, cal_max, cal_min, slice_duration, toffset, quatern_b/c/d, qoffset_x/y/z, srow_x/y/z. Fixes: #3249
1 parent fdd8f7f commit 61cc38d

1 file changed

Lines changed: 33 additions & 73 deletions

File tree

Modules/IO/NIFTI/src/itkNiftiImageIO.cxx

Lines changed: 33 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "itkNiftiImageIO.h"
2020
#include "itkIOCommon.h"
2121
#include "itkMetaDataObject.h"
22+
#include "itkNumberToString.h"
2223
#include "itkAnatomicalOrientation.h"
2324
#include <nifti1_io.h>
2425
#include "itkNiftiImageIOConfigurePrivate.h"
@@ -645,17 +646,9 @@ NiftiImageIO::SetImageIOMetadataFromNIfTI()
645646
EncapsulateMetaData<std::string>(thisDic, dimKey.str(), dim.str());
646647
}
647648

648-
std::ostringstream intent_p1;
649-
intent_p1 << m_Holder->ptr->intent_p1;
650-
EncapsulateMetaData<std::string>(thisDic, "intent_p1", intent_p1.str());
651-
652-
std::ostringstream intent_p2;
653-
intent_p2 << m_Holder->ptr->intent_p2;
654-
EncapsulateMetaData<std::string>(thisDic, "intent_p2", intent_p2.str());
655-
656-
std::ostringstream intent_p3;
657-
intent_p3 << m_Holder->ptr->intent_p3;
658-
EncapsulateMetaData<std::string>(thisDic, "intent_p3", intent_p3.str());
649+
EncapsulateMetaData<std::string>(thisDic, "intent_p1", itk::ConvertNumberToString(m_Holder->ptr->intent_p1));
650+
EncapsulateMetaData<std::string>(thisDic, "intent_p2", itk::ConvertNumberToString(m_Holder->ptr->intent_p2));
651+
EncapsulateMetaData<std::string>(thisDic, "intent_p3", itk::ConvertNumberToString(m_Holder->ptr->intent_p3));
659652

660653
std::ostringstream intent_code;
661654
intent_code << m_Holder->ptr->intent_code;
@@ -675,24 +668,17 @@ NiftiImageIO::SetImageIOMetadataFromNIfTI()
675668

676669
for (int idx = 0; idx < 8; ++idx)
677670
{
678-
std::ostringstream pixdim;
679-
pixdim << m_Holder->ptr->pixdim[idx];
680671
std::ostringstream pixdimKey;
681672
pixdimKey << "pixdim[" << idx << ']';
682-
EncapsulateMetaData<std::string>(thisDic, pixdimKey.str(), pixdim.str());
673+
EncapsulateMetaData<std::string>(thisDic, pixdimKey.str(), itk::ConvertNumberToString(m_Holder->ptr->pixdim[idx]));
683674
}
684675

685676
std::ostringstream vox_offset;
686677
vox_offset << m_Holder->ptr->iname_offset;
687678
EncapsulateMetaData<std::string>(thisDic, "vox_offset", vox_offset.str());
688679

689-
std::ostringstream scl_slope;
690-
scl_slope << m_Holder->ptr->scl_slope;
691-
EncapsulateMetaData<std::string>(thisDic, "scl_slope", scl_slope.str());
692-
693-
std::ostringstream scl_inter;
694-
scl_inter << m_Holder->ptr->scl_inter;
695-
EncapsulateMetaData<std::string>(thisDic, "scl_inter", scl_inter.str());
680+
EncapsulateMetaData<std::string>(thisDic, "scl_slope", itk::ConvertNumberToString(m_Holder->ptr->scl_slope));
681+
EncapsulateMetaData<std::string>(thisDic, "scl_inter", itk::ConvertNumberToString(m_Holder->ptr->scl_inter));
696682

697683
std::ostringstream slice_end;
698684
slice_end << m_Holder->ptr->slice_end;
@@ -706,21 +692,11 @@ NiftiImageIO::SetImageIOMetadataFromNIfTI()
706692
xyzt_units << SPACE_TIME_TO_XYZT(m_Holder->ptr->xyz_units, m_Holder->ptr->time_units);
707693
EncapsulateMetaData<std::string>(thisDic, "xyzt_units", xyzt_units.str());
708694

709-
std::ostringstream cal_max;
710-
cal_max << m_Holder->ptr->cal_max;
711-
EncapsulateMetaData<std::string>(thisDic, "cal_max", cal_max.str());
712-
713-
std::ostringstream cal_min;
714-
cal_min << m_Holder->ptr->cal_min;
715-
EncapsulateMetaData<std::string>(thisDic, "cal_min", cal_min.str());
716-
717-
std::ostringstream slice_duration;
718-
slice_duration << m_Holder->ptr->slice_duration;
719-
EncapsulateMetaData<std::string>(thisDic, "slice_duration", slice_duration.str());
720-
721-
std::ostringstream toffset;
722-
toffset << m_Holder->ptr->toffset;
723-
EncapsulateMetaData<std::string>(thisDic, "toffset", toffset.str());
695+
EncapsulateMetaData<std::string>(thisDic, "cal_max", itk::ConvertNumberToString(m_Holder->ptr->cal_max));
696+
EncapsulateMetaData<std::string>(thisDic, "cal_min", itk::ConvertNumberToString(m_Holder->ptr->cal_min));
697+
EncapsulateMetaData<std::string>(
698+
thisDic, "slice_duration", itk::ConvertNumberToString(m_Holder->ptr->slice_duration));
699+
EncapsulateMetaData<std::string>(thisDic, "toffset", itk::ConvertNumberToString(m_Holder->ptr->toffset));
724700

725701
std::ostringstream descrip;
726702
descrip << m_Holder->ptr->descrip;
@@ -740,44 +716,28 @@ NiftiImageIO::SetImageIOMetadataFromNIfTI()
740716
EncapsulateMetaData<std::string>(thisDic, "sform_code", sform_code.str());
741717
EncapsulateMetaData<std::string>(thisDic, "sform_code_name", std::string(str_xform(m_Holder->ptr->sform_code)));
742718

743-
std::ostringstream quatern_b;
744-
quatern_b << m_Holder->ptr->quatern_b;
745-
EncapsulateMetaData<std::string>(thisDic, "quatern_b", quatern_b.str());
746-
747-
std::ostringstream quatern_c;
748-
quatern_c << m_Holder->ptr->quatern_c;
749-
EncapsulateMetaData<std::string>(thisDic, "quatern_c", quatern_c.str());
750-
751-
std::ostringstream quatern_d;
752-
quatern_d << m_Holder->ptr->quatern_d;
753-
EncapsulateMetaData<std::string>(thisDic, "quatern_d", quatern_d.str());
754-
755-
std::ostringstream qoffset_x;
756-
qoffset_x << m_Holder->ptr->qoffset_x;
757-
EncapsulateMetaData<std::string>(thisDic, "qoffset_x", qoffset_x.str());
758-
759-
std::ostringstream qoffset_y;
760-
qoffset_y << m_Holder->ptr->qoffset_y;
761-
EncapsulateMetaData<std::string>(thisDic, "qoffset_y", qoffset_y.str());
719+
EncapsulateMetaData<std::string>(thisDic, "quatern_b", itk::ConvertNumberToString(m_Holder->ptr->quatern_b));
720+
EncapsulateMetaData<std::string>(thisDic, "quatern_c", itk::ConvertNumberToString(m_Holder->ptr->quatern_c));
721+
EncapsulateMetaData<std::string>(thisDic, "quatern_d", itk::ConvertNumberToString(m_Holder->ptr->quatern_d));
722+
EncapsulateMetaData<std::string>(thisDic, "qoffset_x", itk::ConvertNumberToString(m_Holder->ptr->qoffset_x));
723+
EncapsulateMetaData<std::string>(thisDic, "qoffset_y", itk::ConvertNumberToString(m_Holder->ptr->qoffset_y));
724+
EncapsulateMetaData<std::string>(thisDic, "qoffset_z", itk::ConvertNumberToString(m_Holder->ptr->qoffset_z));
762725

763-
std::ostringstream qoffset_z;
764-
qoffset_z << m_Holder->ptr->qoffset_z;
765-
EncapsulateMetaData<std::string>(thisDic, "qoffset_z", qoffset_z.str());
766-
767-
std::ostringstream srow_x;
768-
srow_x << m_Holder->ptr->sto_xyz.m[0][0] << ' ' << m_Holder->ptr->sto_xyz.m[0][1] << ' '
769-
<< m_Holder->ptr->sto_xyz.m[0][2] << ' ' << m_Holder->ptr->sto_xyz.m[0][3];
770-
EncapsulateMetaData<std::string>(thisDic, "srow_x", srow_x.str());
771-
772-
std::ostringstream srow_y;
773-
srow_y << m_Holder->ptr->sto_xyz.m[1][0] << ' ' << m_Holder->ptr->sto_xyz.m[1][1] << ' '
774-
<< m_Holder->ptr->sto_xyz.m[1][2] << ' ' << m_Holder->ptr->sto_xyz.m[1][3];
775-
EncapsulateMetaData<std::string>(thisDic, "srow_y", srow_y.str());
776-
777-
std::ostringstream srow_z;
778-
srow_z << m_Holder->ptr->sto_xyz.m[2][0] << ' ' << m_Holder->ptr->sto_xyz.m[2][1] << ' '
779-
<< m_Holder->ptr->sto_xyz.m[2][2] << ' ' << m_Holder->ptr->sto_xyz.m[2][3];
780-
EncapsulateMetaData<std::string>(thisDic, "srow_z", srow_z.str());
726+
for (int row = 0; row < 3; ++row)
727+
{
728+
std::string srowStr;
729+
for (int col = 0; col < 4; ++col)
730+
{
731+
if (col > 0)
732+
{
733+
srowStr += ' ';
734+
}
735+
srowStr += itk::ConvertNumberToString(m_Holder->ptr->sto_xyz.m[row][col]);
736+
}
737+
std::ostringstream srowKey;
738+
srowKey << "srow_" << "xyz"[row];
739+
EncapsulateMetaData<std::string>(thisDic, srowKey.str(), srowStr);
740+
}
781741

782742
std::ostringstream intent_name;
783743
intent_name << m_Holder->ptr->intent_name;

0 commit comments

Comments
 (0)