Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Support for binary point file (.bin) reading and writing #943

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ CMakeFiles/
CTestTestfile.cmake
.idea/
.githooks/clangFormatMac
/cmake-build-debug/
/build/
Comment on lines +41 to +42
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to see this as a separate commit. (Following the principle of "One Commit. One Change.") Why do those directories appear in your source tree? Are they specific to a particular tool or platform? I'm fine with the change itself, but I would like some clarification!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, sorry for the late reply due to other obligations.
This can be safely ignored, i did not realize this change in .gitignore

6 changes: 6 additions & 0 deletions Common/itkTransformixInputPointFileReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ class ITK_TEMPLATE_EXPORT TransformixInputPointFileReader : public MeshFileReade
*/
itkGetConstMacro(NumberOfPoints, unsigned long);

/** Get and set whether to read the points from a binary file.
*/
itkGetConstMacro(Binary, bool);
itkSetMacro(Binary, bool);

/** Prepare the allocation of the output mesh during the first back
* propagation of the pipeline. Updates the PointsAreIndices and NumberOfPoints.
*/
Expand All @@ -100,6 +105,7 @@ class ITK_TEMPLATE_EXPORT TransformixInputPointFileReader : public MeshFileReade
private:
unsigned long m_NumberOfPoints{ 0 };
bool m_PointsAreIndices{ false };
bool m_Binary{ false };

std::ifstream m_Reader{};
};
Expand Down
91 changes: 71 additions & 20 deletions Common/itkTransformixInputPointFileReader.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -40,30 +40,60 @@ TransformixInputPointFileReader<TOutputMesh>::GenerateOutputInformation()
{
this->m_Reader.close();
}
this->m_Reader.open(this->m_FileName);

/** Read the first entry */
std::string indexOrPoint;
this->m_Reader >> indexOrPoint;
if( this->m_Binary ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use automatic code formatting by clang-format. It looks like this code still needs to be formatted that way, as I would expect it to place the { on the next line. (We use a specific (old) version of clang-format: 8.0.1) No problem, of course, we can fix that later.

this->m_Reader.open(this->m_FileName, std::ios::binary);

/** Read index flag */
unsigned long isindex;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry to say the use of (signed or unsigned) long is problematic, because its size differs between currently supported platforms and compilers. On Linux/GCC it's typically 64 bits, whereas on Windows/MSVC, it's 32 bits. If you want to write unsigned integers on one platform and read them back on another, better use either uint32_t or uint64_t. (In general use cases, unsigned int is usually fine, as it is the "default" unsigned type, and it's 32-bits on all supported platforms.)

this->m_Reader.read((char*) &isindex, sizeof(isindex));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of C-style (char*) cast, please use reinterpret_cast<const char *>. C-style casts are generally considered unsafe.


if( isindex == 0 )
{
/** Input points are specified in world coordinates. */
this->m_PointsAreIndices = false;
}
else //if( indexOrPoint == 1 )
{
/** Input points are specified as image indices. */
this->m_PointsAreIndices = true;
}

/** Read number of points */
unsigned long nrofpoints;
this->m_Reader.read((char*) &nrofpoints, sizeof(nrofpoints));
this->m_NumberOfPoints = nrofpoints;

/** Leave the file open for the generate data method */

/** Set the IsIndex bool and the number of points.*/
if (indexOrPoint == "point")
{
/** Input points are specified in world coordinates. */
this->m_PointsAreIndices = false;
this->m_Reader >> this->m_NumberOfPoints;
}
else if (indexOrPoint == "index")
{
/** Input points are specified as image indices. */
this->m_PointsAreIndices = true;
this->m_Reader >> this->m_NumberOfPoints;
}
else
else //if( this->m_Binary )
{
/** Input points are assumed to be specified as image indices. */
this->m_PointsAreIndices = true;
this->m_NumberOfPoints = atoi(indexOrPoint.c_str());
this->m_Reader.open(this->m_FileName);

/** Read the first entry */
std::string indexOrPoint;
this->m_Reader >> indexOrPoint;

/** Set the IsIndex bool and the number of points.*/
if (indexOrPoint == "point")
{
/** Input points are specified in world coordinates. */
this->m_PointsAreIndices = false;
this->m_Reader >> this->m_NumberOfPoints;
}
else if (indexOrPoint == "index")
{
/** Input points are specified as image indices. */
this->m_PointsAreIndices = true;
this->m_Reader >> this->m_NumberOfPoints;
}
else
{
/** Input points are assumed to be specified as image indices. */
this->m_PointsAreIndices = true;
this->m_NumberOfPoints = atoi(indexOrPoint.c_str());
}
}

/** Leave the file open for the generate data method */
Expand All @@ -90,6 +120,27 @@ TransformixInputPointFileReader<TOutputMesh>::GenerateData()
/** Read the file */
if (this->m_Reader.is_open())
{
if( this->m_Binary ){
for( unsigned long i = 0; i < this->m_NumberOfPoints; ++i )
{
// read point from binary file
PointType point;

if( !this->m_Reader.eof() )
{
this->m_Reader.read((char*) &point, sizeof(point));
} else {
std::ostringstream msg;
msg << "The file is not large enough. "
<< std::endl << "Filename: " << this->m_FileName
<< std::endl;
MeshFileReaderException e( __FILE__, __LINE__, msg.str().c_str(), ITK_LOCATION );
throw e;
}
points->push_back( point );
}
}
else //if( this->m_Binary ){
for (unsigned int i = 0; i < this->m_NumberOfPoints; ++i)
{
// read point from textfile
Expand Down
2 changes: 1 addition & 1 deletion Core/ComponentBaseClasses/elxTransformBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ class ITK_TEMPLATE_EXPORT TransformBase : public BaseComponentSE<TElastix>

/** Function to transform coordinates from fixed to moving image. */
void
TransformPointsSomePoints(const std::string & filename) const;
TransformPointsSomePoints(const std::string & filename, const bool binary = false) const;

/** Function to transform coordinates from fixed to moving image, given as VTK file. */
void
Expand Down
125 changes: 83 additions & 42 deletions Core/ComponentBaseClasses/elxTransformBase.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,11 @@ TransformBase<TElastix>::TransformPoints() const
log::info(" The transform is evaluated on some points, specified in a VTK input point file.");
this->TransformPointsSomePointsVTK(def);
}
else if (itksys::SystemTools::StringEndsWith(def, ".bin") || itksys::SystemTools::StringEndsWith(def, ".BIN"))
{
log::info(" The transform is evaluated on some points, specified in a binary input point file.");
this->TransformPointsSomePoints(def, true);
}
else
{
log::info(" The transform is evaluated on some points, specified in the input point file.");
Expand Down Expand Up @@ -721,7 +726,7 @@ TransformBase<TElastix>::TransformPoints() const

template <class TElastix>
void
TransformBase<TElastix>::TransformPointsSomePoints(const std::string & filename) const
TransformBase<TElastix>::TransformPointsSomePoints(const std::string & filename, const bool binary) const
{
/** Typedef's. */
using FixedImageRegionType = typename FixedImageType::RegionType;
Expand All @@ -739,6 +744,7 @@ TransformBase<TElastix>::TransformPointsSomePoints(const std::string & filename)
/** Construct an ipp-file reader. */
const auto ippReader = itk::TransformixInputPointFileReader<PointSetType>::New();
ippReader->SetFileName(filename);
ippReader->SetBinary(binary);

/** Read the input points. */
log::info(std::ostringstream{} << " Reading input point file: " << filename);
Expand All @@ -760,7 +766,7 @@ TransformBase<TElastix>::TransformPointsSomePoints(const std::string & filename)
{
log::info(" Input points are specified in world coordinates.");
}
const unsigned int nrofpoints = ippReader->GetNumberOfPoints();
const unsigned long nrofpoints = ippReader->GetNumberOfPoints();
log::info(std::ostringstream{} << " Number of specified input points: " << nrofpoints);

/** Get the set of input points. */
Expand Down Expand Up @@ -794,7 +800,7 @@ TransformBase<TElastix>::TransformPointsSomePoints(const std::string & filename)
/** Read the input points, as index or as point. */
if (ippReader->GetPointsAreIndices())
{
for (unsigned int j = 0; j < nrofpoints; ++j)
for (unsigned long j = 0; j < nrofpoints; ++j)
{
/** The read point from the inutPointSet is actually an index
* Cast to the proper type.
Expand All @@ -811,7 +817,7 @@ TransformBase<TElastix>::TransformPointsSomePoints(const std::string & filename)
}
else
{
for (unsigned int j = 0; j < nrofpoints; ++j)
for (unsigned long j = 0; j < nrofpoints; ++j)
{
/** Compute index of nearest voxel in fixed image. */
InputPointType point{};
Expand All @@ -827,7 +833,7 @@ TransformBase<TElastix>::TransformPointsSomePoints(const std::string & filename)

/** Apply the transform. */
log::info(" The input points are transformed.");
for (unsigned int j = 0; j < nrofpoints; ++j)
for (unsigned long j = 0; j < nrofpoints; ++j)
{
/** Call TransformPoint. */
outputpointvec[j] = this->GetAsITKBaseType()->TransformPoint(inputpointvec[j]);
Expand Down Expand Up @@ -860,50 +866,85 @@ TransformBase<TElastix>::TransformPointsSomePoints(const std::string & filename)
!outputDirectoryPath.empty())
{
/** Create filename and file stream. */
const std::string outputPointsFileName = outputDirectoryPath + "outputpoints.txt";
std::ofstream outputPointsFile(outputPointsFileName);
const std::string extension = binary ? "bin" : "txt";
const std::string outputPointsFileName = outputDirectoryPath + "outputpoints." + extension;

std::ofstream outputPointsFile(outputPointsFileName, binary ? std::ios_base::binary : std::ios_base::out );
outputPointsFile << std::showpoint << std::fixed;
log::info(std::ostringstream{} << " The transformed points are saved in: " << outputPointsFileName);

const auto writeToFile = [&outputPointsFile](const auto & rangeOfElements) {
for (const auto element : rangeOfElements)
{
outputPointsFile << element << ' ';
}
};

/** Print the results. */
for (unsigned int j = 0; j < nrofpoints; ++j)
{
/** The input index. */
outputPointsFile << "Point\t" << j << "\t; InputIndex = [ ";
writeToFile(inputindexvec[j]);

/** The input point. */
outputPointsFile << "]\t; InputPoint = [ ";
writeToFile(inputpointvec[j]);

/** The output index in fixed image. */
outputPointsFile << "]\t; OutputIndexFixed = [ ";
writeToFile(outputindexfixedvec[j]);
/** Write the results. */

/** The output point. */
outputPointsFile << "]\t; OutputPoint = [ ";
writeToFile(outputpointvec[j]);
if (binary) { //Note: for speed, write the transformed points only
unsigned long isindex = ippReader->GetPointsAreIndices() ? 1 : 0;
outputPointsFile.write((char*) &isindex, sizeof(isindex));

/** The output point minus the input point. */
outputPointsFile << "]\t; Deformation = [ ";
writeToFile(deformationvec[j]);
unsigned long numberOfPoints = nrofpoints;
outputPointsFile.write((char*) &numberOfPoints, sizeof(numberOfPoints));

if (alsoMovingIndices)
for( unsigned int j = 0; j < nrofpoints; j++ )
{
/** The output index in moving image. */
outputPointsFile << "]\t; OutputIndexMoving = [ ";
writeToFile(outputindexmovingvec[j]);
}

outputPointsFile << "]" << std::endl;
} // end for nrofpoints
if ( ippReader->GetPointsAreIndices() )
{
/** The output index in fixed image. */
for( unsigned int i = 0; i < FixedImageDimension; i++ )
{
outputPointsFile.write((char*) &outputindexfixedvec[ j ][ i ], sizeof(outputindexfixedvec[ j ][ i ]) );
}
}
else
{
/** The output point. */
for( unsigned int i = 0; i < FixedImageDimension; i++ )
{
outputPointsFile.write((char*) &outputpointvec[ j ][ i ], sizeof(outputpointvec[ j ][ i ]) );
}
}
} // end for nrofpoints

outputPointsFile.close();
}
else //if (binary)
{
const auto writeToFile = [&outputPointsFile](const auto & rangeOfElements) {
for (const auto element : rangeOfElements)
{
outputPointsFile << element << ' ';
}
};

for (unsigned int j = 0; j < nrofpoints; ++j)
{
/** The input index. */
outputPointsFile << "Point\t" << j << "\t; InputIndex = [ ";
writeToFile(inputindexvec[j]);

/** The input point. */
outputPointsFile << "]\t; InputPoint = [ ";
writeToFile(inputpointvec[j]);

/** The output index in fixed image. */
outputPointsFile << "]\t; OutputIndexFixed = [ ";
writeToFile(outputindexfixedvec[j]);

/** The output point. */
outputPointsFile << "]\t; OutputPoint = [ ";
writeToFile(outputpointvec[j]);

/** The output point minus the input point. */
outputPointsFile << "]\t; Deformation = [ ";
writeToFile(deformationvec[j]);

if (alsoMovingIndices)
{
/** The output index in moving image. */
outputPointsFile << "]\t; OutputIndexMoving = [ ";
writeToFile(outputindexmovingvec[j]);
}

outputPointsFile << "]" << std::endl;
} // end for nrofpoints
}
}

} // end TransformPointsSomePoints()
Expand Down