libsdfio 1.0.0
Reader and writer for ISO 25178-71 SDF surface data files in C
Loading...
Searching...
No Matches
eigen.cpp
// Remove the best-fit plane of an SDF file with Eigen.
#include <sdfio/file.h>
#include <sdfio/sdfio.h>
#include <Eigen/Dense>
#include <cmath>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
namespace
{
using Reader = std::unique_ptr<sdfio_reader, decltype(&sdfio_reader_free)>;
using Writer = std::unique_ptr<sdfio_writer, decltype(&sdfio_writer_free)>;
using Header = std::unique_ptr<sdfio_header, decltype(&sdfio_header_free)>;
void check(sdfio_status status, const char *message)
{
if (status != SDFIO_OK)
{
throw std::runtime_error(std::string(sdfio_status_string(status)) + ": "
+ message);
}
}
Header copy_header(const sdfio_header *source)
{
sdfio_header *header = nullptr;
sdfio_header_copy(header, source);
return {header, sdfio_header_free};
}
Reader create_reader()
{
sdfio_reader *reader = nullptr;
return {reader, sdfio_reader_free};
}
Writer create_writer()
{
sdfio_writer *writer = nullptr;
return {writer, sdfio_writer_free};
}
// Eigen stores a matrix column by column: the elements of a row are one column
// apart, and the rows are one element apart. The matrix is double, whatever the
// data type of the file, and the library converts the heights.
sdfio_layout layout_of(const Eigen::MatrixXd &matrix)
{
layout.element_stride =
static_cast<ptrdiff_t>(matrix.outerStride() * sizeof(double));
layout.row_stride =
static_cast<ptrdiff_t>(matrix.innerStride() * sizeof(double));
return layout;
}
Eigen::MatrixXd read_matrix(sdfio_reader *reader)
{
const sdfio_header *header = sdfio_reader_header(reader);
Eigen::MatrixXd heights(
static_cast<Eigen::Index>(sdfio_header_rows(header)),
static_cast<Eigen::Index>(sdfio_header_columns(header)));
const sdfio_layout layout = layout_of(heights);
check(sdfio_read_heights(reader, &layout, heights.data()),
return heights;
}
// z = offset + slope_x * x + slope_y * y, with x and y counted in points
struct Plane
{
double offset;
double slope_x;
double slope_y;
};
Plane fit_plane(const Eigen::MatrixXd &heights)
{
// A point that is not measured is NaN and is left out.
const Eigen::Index count = (heights.array() == heights.array()).count();
Eigen::MatrixXd design(count, 3);
Eigen::VectorXd z(count);
Eigen::Index row = 0;
const Eigen::Index columns = heights.cols();
const Eigen::Index rows = heights.rows();
for (Eigen::Index x = 0; x < columns; ++x)
{
const double point_x = static_cast<double>(x);
for (Eigen::Index y = 0; y < rows; ++y)
{
if (!std::isnan(heights(y, x)))
{
design.row(row) << 1.0, point_x, static_cast<double>(y);
z(row++) = heights(y, x);
}
}
}
const Eigen::Vector3d solution = design.colPivHouseholderQr().solve(z);
return {solution(0), solution(1), solution(2)};
}
void remove_plane(Eigen::MatrixXd &heights, const Plane &plane)
{
const Eigen::Index columns = heights.cols();
const Eigen::Index rows = heights.rows();
for (Eigen::Index x = 0; x < columns; ++x)
{
const double column_offset =
plane.offset + plane.slope_x * static_cast<double>(x);
for (Eigen::Index y = 0; y < rows; ++y)
{
heights(y, x) -=
column_offset + plane.slope_y * static_cast<double>(y);
}
}
}
void write_matrix(const char *path, const sdfio_header *source,
const Eigen::MatrixXd &heights)
{
Header header = copy_header(source);
Writer writer = create_writer();
check(sdfio_create_file(writer.get(), path, header.get()),
const sdfio_layout layout = layout_of(heights);
check(sdfio_write_heights(writer.get(), &layout, heights.data()),
check(sdfio_end_file(writer.get()),
}
} // namespace
int main(int argc, char **argv)
{
if (argc < 3)
{
std::cerr << "usage: " << argv[0] << " input.sdf output.sdf\n";
return 1;
}
const char *input = argv[1];
const char *output = argv[2];
try
{
Reader reader = create_reader();
check(sdfio_open_file(reader.get(), input),
Eigen::MatrixXd heights = read_matrix(reader.get());
remove_plane(heights, fit_plane(heights));
write_matrix(output, sdfio_reader_header(reader.get()), heights);
}
catch (const std::exception &error)
{
std::cerr << error.what() << "\n";
return 1;
}
}
Files by path, and the stream over a C file.
sdfio_status sdfio_open_file(sdfio_reader *reader, const char *path)
Opens the file at a path as the source and reads its header, as sdfio_open_stream() does for a stream...
sdfio_status sdfio_create_file(sdfio_writer *writer, const char *path, const sdfio_header *header)
Creates the file at a path and starts it as sdfio_create_stream() does.
struct sdfio_header sdfio_header
Header of a file, with the field names of the standard.
Definition header.h:42
void sdfio_header_free(sdfio_header *header)
Releases a header that sdfio_header_create() created.
uint32_t sdfio_header_rows(const sdfio_header *header)
Returns the number of rows N of the data matrix: 1 for a profile.
sdfio_status sdfio_header_create(const sdfio_allocator *allocator, sdfio_header **header)
Creates a header with default values.
sdfio_status sdfio_header_copy(sdfio_header *dst, const sdfio_header *src)
Copies every field of a header into another header, which keeps its allocator.
uint32_t sdfio_header_columns(const sdfio_header *header)
Returns the number of columns M of the data matrix: the points along x.
sdfio_status sdfio_header_set_format(sdfio_header *header, sdfio_format format)
Sets the format of the file.
sdfio_status sdfio_header_set_data_type(sdfio_header *header, sdfio_data_type data_type)
Sets the data type of the coded values in the data area.
const sdfio_layout * sdfio_layout_packed(sdfio_data_type type)
Returns the layout of a plain array: the elements follow each other and the rows follow each other.
sdfio_status sdfio_reader_add_binary(sdfio_reader *reader)
Adds the binary format to the formats that the reader reads, the version numbers bISO-1....
const sdfio_header * sdfio_reader_header(const sdfio_reader *reader)
Returns the header of the opened file, or null if no file is open.
struct sdfio_reader sdfio_reader
Opaque handle of a reader.
Definition reader.h:58
sdfio_status sdfio_reader_add_ascii(sdfio_reader *reader)
Adds the ASCII format to the formats that the reader reads, the version numbers aISO-1....
sdfio_status sdfio_reader_create(const sdfio_allocator *allocator, sdfio_reader **reader)
Creates a reader.
sdfio_status sdfio_read_heights(sdfio_reader *reader, const sdfio_layout *layout, void *dst)
Reads the remaining rows as heights in metres into a buffer that lies in memory as the layout describ...
const char * sdfio_reader_error_message(const sdfio_reader *reader)
Returns a description of the most recent failure of the reader, or an empty string if none occurred.
void sdfio_reader_free(sdfio_reader *reader)
Releases a reader and closes the file that sdfio_open_file() opened, if any.
Reading and writing of ISO 25178-71 SDF surface data files.
Description of a buffer of the caller for heights or for the values of the data area: the type of its...
Definition layout.h:45
ptrdiff_t element_stride
Distance in bytes between consecutive elements within a row.
Definition layout.h:54
ptrdiff_t row_stride
Distance in bytes between consecutive rows.
Definition layout.h:62
const char * sdfio_status_string(sdfio_status status)
Returns a constant description of a status.
@ SDFIO_BINARY
Binary format.
Definition types.h:83
sdfio_status
Result of a function call.
Definition types.h:31
@ SDFIO_OK
The call succeeded.
Definition types.h:33
const sdfio_allocator * sdfio_default_allocator(void)
Returns the allocator that uses malloc() and free().
@ SDFIO_TYPE_DOUBLE
double: IEEE 754 binary64.
Definition types.h:122
@ SDFIO_TYPE_FLOAT
float: IEEE 754 binary32.
Definition types.h:114
sdfio_status sdfio_writer_add_binary(sdfio_writer *writer)
Adds the binary format to the formats that the writer writes.
sdfio_status sdfio_writer_create(const sdfio_allocator *allocator, sdfio_writer **writer)
Creates a writer.
struct sdfio_writer sdfio_writer
Opaque handle of a writer.
Definition writer.h:69
const char * sdfio_writer_error_message(const sdfio_writer *writer)
Returns a description of the most recent failure of the writer, or an empty string if none occurred.
void sdfio_writer_free(sdfio_writer *writer)
Releases a writer.
sdfio_status sdfio_write_heights(sdfio_writer *writer, const sdfio_layout *layout, const void *src)
Writes the whole data area from a matrix of heights in metres that lies in memory as the layout descr...
sdfio_status sdfio_end_file(sdfio_writer *writer)
Ends the file: closes the trailer, the last record, flushes the stream and, for a file that sdfio_cre...