Skip to content

Commit

Permalink
Update "tutorial-mb-generic-tracker-full.cpp" to export poses into np…
Browse files Browse the repository at this point in the history
…z file.

Update "script/PlotCameraTrajectory.py" to read poses from npz file.
Add documentation to "visp::cnpy::npz_load()", "npz_save()", [...] functions.
  • Loading branch information
s-trinh committed Jan 29, 2024
1 parent 9a6bbcf commit 4cfa003
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 50 deletions.
58 changes: 47 additions & 11 deletions modules/core/include/visp3/core/vpIoTools.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,18 +136,14 @@ VISP_EXPORT NpyArray npy_load(std::string fname);

template<typename T> std::vector<char> &operator+=(std::vector<char> &lhs, const T rhs)
{
//write in little endian
//write in little endian
for (size_t byte = 0; byte < sizeof(T); byte++) {
char val = *((char *)&rhs+byte);
lhs.push_back(val);
}
return lhs;
}

//template<> std::vector<char> &operator+=(std::vector<char> &lhs, const std::string rhs);
//template<> std::vector<char> &operator+=(std::vector<char> &lhs, const char *rhs);


template<> inline std::vector<char> &operator+=(std::vector<char> &lhs, const std::string rhs)
{
lhs.insert(lhs.end(), rhs.begin(), rhs.end());
Expand All @@ -165,6 +161,16 @@ template<> inline std::vector<char> &operator+=(std::vector<char> &lhs, const ch
return lhs;
}

/*!
Save an array of data (\p data) into the \p fname npy file. This function is similar to the
<a href="https://numpy.org/doc/stable/reference/generated/numpy.save.html">numpy.save</a> function.
\param[in] fname : Path to the npy file.
\param[in] data : Pointer to an array of basic datatype (int, float, double, std::complex<double>, ...).
\param[in] shape : Shape of the array, e.g. Nz x Ny x Nx.
\param[in] mode : Writing mode, i.e. overwrite (w) or append (a) to the file.
\warning This function has only been tested on little endian platform.
\note Original library: <a href="https://github.com/rogersce/cnpy">cnpy</a> with MIT license.
*/
template<typename T> void npy_save(std::string fname, const T *data, const std::vector<size_t> shape, std::string mode = "w")
{
FILE *fp = NULL;
Expand All @@ -173,7 +179,7 @@ template<typename T> void npy_save(std::string fname, const T *data, const std::
if (mode == "a") fp = fopen(fname.c_str(), "r+b");

if (fp) {
//file exists. we need to append to it. read the header, modify the array size
//file exists. we need to append to it. read the header, modify the array size
size_t word_size;
bool fortran_order;
parse_npy_header(fp, word_size, true_data_shape, fortran_order);
Expand Down Expand Up @@ -211,9 +217,20 @@ template<typename T> void npy_save(std::string fname, const T *data, const std::
fclose(fp);
}

/*!
Save the specified \p fname array of data (\p data) into the \p zipname npz file. This function is similar to the
<a href="https://numpy.org/doc/stable/reference/generated/numpy.savez.html">numpy.savez</a> function.
\param[in] zipname : Path to the npz file.
\param[in] fname : Identifier for the corresponding array of data.
\param[in] data : Pointer to an array of basic datatype (int, float, double, std::complex<double>, ...).
\param[in] shape : Shape of the array, e.g. Nz x Ny x Nx.
\param[in] mode : Writing mode, i.e. overwrite (w) or append (a) to the file.
\warning This function has only been tested on little endian platform.
\note Original library: <a href="https://github.com/rogersce/cnpy">cnpy</a> with MIT license.
*/
template<typename T> void npz_save(std::string zipname, std::string fname, const T *data, const std::vector<size_t> &shape, std::string mode = "w")
{
//first, append a .npy to the fname
//first, append a .npy to the fname
fname += ".npy";

//now, on with the show
Expand All @@ -225,10 +242,10 @@ template<typename T> void npz_save(std::string zipname, std::string fname, const
if (mode == "a") fp = fopen(zipname.c_str(), "r+b");

if (fp) {
//zip file exists. we need to add a new npy file to it.
//first read the footer. this gives us the offset and size of the global header
//then read and store the global header.
//below, we will write the the new data at the start of the global header then append the global header and footer below it
//zip file exists. we need to add a new npy file to it.
//first read the footer. this gives us the offset and size of the global header
//then read and store the global header.
//below, we will write the the new data at the start of the global header then append the global header and footer below it
size_t global_header_size;
parse_zip_footer(fp, nrecs, global_header_size, global_header_offset);
fseek(fp, global_header_offset, SEEK_SET);
Expand Down Expand Up @@ -301,13 +318,32 @@ template<typename T> void npz_save(std::string zipname, std::string fname, const
fclose(fp);
}

/*!
Save the specified 1-D array of data (\p data) into the \p fname npz file. This function is similar to the
<a href="https://numpy.org/doc/stable/reference/generated/numpy.save.html">numpy.save</a> function.
\param[in] fname : Path to the npy file.
\param[in] data : Pointer to a 1-D array of basic datatype (int, float, double, std::complex<double>, ...).
\param[in] mode : Writing mode, i.e. overwrite (w) or append (a) to the file.
\warning This function has only been tested on little endian platform.
\note Original library: <a href="https://github.com/rogersce/cnpy">cnpy</a> with MIT license.
*/
template<typename T> void npy_save(std::string fname, const std::vector<T> data, std::string mode = "w")
{
std::vector<size_t> shape;
shape.push_back(data.size());
npy_save(fname, &data[0], shape, mode);
}

/*!
Save the specified \p fname 1-D array of data (\p data) into the \p zipname npz file. This function is similar to the
<a href="https://numpy.org/doc/stable/reference/generated/numpy.savez.html">numpy.savez</a> function.
\param[in] zipname : Path to the npz file.
\param[in] fname : Identifier for the corresponding array of data.
\param[in] data : Pointer to a 1-D array of basic datatype (int, float, double, std::complex<double>, ...).
\param[in] mode : Writing mode, i.e. overwrite (w) or append (a) to the file.
\warning This function has only been tested on little endian platform.
\note Original library: <a href="https://github.com/rogersce/cnpy">cnpy</a> with MIT license.
*/
template<typename T> void npz_save(std::string zipname, std::string fname, const std::vector<T> data, std::string mode = "w")
{
std::vector<size_t> shape;
Expand Down
38 changes: 32 additions & 6 deletions modules/core/src/tools/file/vpIoTools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@
#define USE_ZLIB_API 0

#if !USE_ZLIB_API
// See: https://github.com/BinomialLLC/basis_universal/blob/master/encoder/basisu_miniz.h
// Apache License, Version 2.0
#include "basisu_miniz.h"

using namespace buminiz;
Expand Down Expand Up @@ -158,9 +160,9 @@ char visp::cnpy::map_type(const std::type_info &t)

void visp::cnpy::parse_npy_header(unsigned char *buffer, size_t &word_size, std::vector<size_t> &shape, bool &fortran_order)

Check warning on line 161 in modules/core/src/tools/file/vpIoTools.cpp

View check run for this annotation

Codecov / codecov/patch

modules/core/src/tools/file/vpIoTools.cpp#L161

Added line #L161 was not covered by tests
{
//std::string magic_string(buffer,6);
// uint8_t major_version = *reinterpret_cast<uint8_t*>(buffer+6);
// uint8_t minor_version = *reinterpret_cast<uint8_t*>(buffer+7);
//std::string magic_string(buffer,6);
// uint8_t major_version = *reinterpret_cast<uint8_t*>(buffer+6);
// uint8_t minor_version = *reinterpret_cast<uint8_t*>(buffer+7);
uint16_t header_len = *reinterpret_cast<uint16_t *>(buffer+8);
std::string header(reinterpret_cast<char *>(buffer+9), header_len);

Check warning on line 167 in modules/core/src/tools/file/vpIoTools.cpp

View check run for this annotation

Codecov / codecov/patch

modules/core/src/tools/file/vpIoTools.cpp#L166-L167

Added lines #L166 - L167 were not covered by tests

Expand Down Expand Up @@ -288,7 +290,6 @@ visp::cnpy::NpyArray load_the_npy_file(FILE *fp)

visp::cnpy::NpyArray load_the_npz_array(FILE *fp, uint32_t compr_bytes, uint32_t uncompr_bytes)

Check warning on line 291 in modules/core/src/tools/file/vpIoTools.cpp

View check run for this annotation

Codecov / codecov/patch

modules/core/src/tools/file/vpIoTools.cpp#L291

Added line #L291 was not covered by tests
{

std::vector<unsigned char> buffer_compr(compr_bytes);
std::vector<unsigned char> buffer_uncompr(uncompr_bytes);

Check warning on line 294 in modules/core/src/tools/file/vpIoTools.cpp

View check run for this annotation

Codecov / codecov/patch

modules/core/src/tools/file/vpIoTools.cpp#L293-L294

Added lines #L293 - L294 were not covered by tests
size_t nread = fread(&buffer_compr[0], 1, compr_bytes, fp);
Expand Down Expand Up @@ -328,6 +329,14 @@ visp::cnpy::NpyArray load_the_npz_array(FILE *fp, uint32_t compr_bytes, uint32_t
return array;

Check warning on line 329 in modules/core/src/tools/file/vpIoTools.cpp

View check run for this annotation

Codecov / codecov/patch

modules/core/src/tools/file/vpIoTools.cpp#L329

Added line #L329 was not covered by tests
}

/*!
Load the specified \p fname filepath as arrays of data. This function is similar to the
<a href="https://numpy.org/doc/stable/reference/generated/numpy.load.html">numpy.load</a> function.
\param[in] fname : Path to the npz file.
\return A map of arrays data. The key represents the variable name, the value is an array of basic data type.
\warning This function has only been tested on little endian platform.
\note Original library: <a href="https://github.com/rogersce/cnpy">cnpy</a> with MIT license.
*/
visp::cnpy::npz_t visp::cnpy::npz_load(std::string fname)

Check warning on line 340 in modules/core/src/tools/file/vpIoTools.cpp

View check run for this annotation

Codecov / codecov/patch

modules/core/src/tools/file/vpIoTools.cpp#L340

Added line #L340 was not covered by tests
{
FILE *fp = fopen(fname.c_str(), "rb");

Check warning on line 342 in modules/core/src/tools/file/vpIoTools.cpp

View check run for this annotation

Codecov / codecov/patch

modules/core/src/tools/file/vpIoTools.cpp#L342

Added line #L342 was not covered by tests
Expand All @@ -344,7 +353,7 @@ visp::cnpy::npz_t visp::cnpy::npz_load(std::string fname)
if (headerres != 30)
throw std::runtime_error("npz_load: failed fread");

Check warning on line 354 in modules/core/src/tools/file/vpIoTools.cpp

View check run for this annotation

Codecov / codecov/patch

modules/core/src/tools/file/vpIoTools.cpp#L353-L354

Added lines #L353 - L354 were not covered by tests

//if we've reached the global header, stop reading
//if we've reached the global header, stop reading
if (local_header[2] != 0x03 || local_header[3] != 0x04) break;

Check warning on line 357 in modules/core/src/tools/file/vpIoTools.cpp

View check run for this annotation

Codecov / codecov/patch

modules/core/src/tools/file/vpIoTools.cpp#L357

Added line #L357 was not covered by tests

//read in the variable name
Expand All @@ -354,7 +363,7 @@ visp::cnpy::npz_t visp::cnpy::npz_load(std::string fname)
if (vname_res != name_len)
throw std::runtime_error("npz_load: failed fread");

Check warning on line 364 in modules/core/src/tools/file/vpIoTools.cpp

View check run for this annotation

Codecov / codecov/patch

modules/core/src/tools/file/vpIoTools.cpp#L363-L364

Added lines #L363 - L364 were not covered by tests

//erase the lagging .npy
//erase the lagging .npy
varname.erase(varname.end()-4, varname.end());

Check warning on line 367 in modules/core/src/tools/file/vpIoTools.cpp

View check run for this annotation

Codecov / codecov/patch

modules/core/src/tools/file/vpIoTools.cpp#L367

Added line #L367 was not covered by tests

//read in the extra field
Expand All @@ -378,6 +387,15 @@ visp::cnpy::npz_t visp::cnpy::npz_load(std::string fname)
return arrays;

Check warning on line 387 in modules/core/src/tools/file/vpIoTools.cpp

View check run for this annotation

Codecov / codecov/patch

modules/core/src/tools/file/vpIoTools.cpp#L386-L387

Added lines #L386 - L387 were not covered by tests
}

/*!
Load the specified \p varname array of data from the \p fname npz file. This function is similar to the
<a href="https://numpy.org/doc/stable/reference/generated/numpy.load.html">numpy.load</a> function.
\param[in] fname : Path to the npz file.
\param[in] varname : Identifier for the requested array of data.
\return An array of basic data type.
\warning This function has only been tested on little endian platform.
\note Original library: <a href="https://github.com/rogersce/cnpy">cnpy</a> with MIT license.
*/
visp::cnpy::NpyArray visp::cnpy::npz_load(std::string fname, std::string varname)

Check warning on line 399 in modules/core/src/tools/file/vpIoTools.cpp

View check run for this annotation

Codecov / codecov/patch

modules/core/src/tools/file/vpIoTools.cpp#L399

Added line #L399 was not covered by tests
{
FILE *fp = fopen(fname.c_str(), "rb");

Check warning on line 401 in modules/core/src/tools/file/vpIoTools.cpp

View check run for this annotation

Codecov / codecov/patch

modules/core/src/tools/file/vpIoTools.cpp#L401

Added line #L401 was not covered by tests
Expand Down Expand Up @@ -427,6 +445,14 @@ visp::cnpy::NpyArray visp::cnpy::npz_load(std::string fname, std::string varname
throw std::runtime_error("npz_load: Variable name "+varname+" not found in "+fname);

Check warning on line 445 in modules/core/src/tools/file/vpIoTools.cpp

View check run for this annotation

Codecov / codecov/patch

modules/core/src/tools/file/vpIoTools.cpp#L445

Added line #L445 was not covered by tests
}

/*!
Load the specified npy \p fname filepath as one array of data. This function is similar to the
<a href="https://numpy.org/doc/stable/reference/generated/numpy.load.html">numpy.load</a> function.
\param[in] fname : Path to the npy file.
\return An array of basic data type.
\warning This function has only been tested on little endian platform.
\note Original library: <a href="https://github.com/rogersce/cnpy">cnpy</a> with MIT license.
*/
visp::cnpy::NpyArray visp::cnpy::npy_load(std::string fname)

Check warning on line 456 in modules/core/src/tools/file/vpIoTools.cpp

View check run for this annotation

Codecov / codecov/patch

modules/core/src/tools/file/vpIoTools.cpp#L456

Added line #L456 was not covered by tests
{

Expand Down
33 changes: 23 additions & 10 deletions script/PlotCameraTrajectory.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,27 @@ def visp_rotation_to_thetau(R):
thetau[0,2] = -thetau[0,2]
return thetau

def load_camera_poses(filename, use_thetau=False):
if use_thetau:
camera_poses_raw = np.loadtxt(filename)
def load_camera_poses(filename, use_thetau=False, use_npz_file_format=False):
if use_npz_file_format:
datafile = np.load(filename)
camera_poses_raw = datafile['vec_poses']
camera_poses = np.zeros((4*camera_poses_raw.shape[0], 4))
for i in range(camera_poses_raw.shape[0]):
camera_poses[i*4:i*4+3, 0:3] = visp_thetau_to_rotation(camera_poses_raw[i, 3:])
camera_poses[i*4:i*4+3, 3] = camera_poses_raw[i,0:3].T
camera_poses[i*4:i*4+3, 0:3] = visp_thetau_to_rotation(camera_poses_raw[i,3:])
camera_poses[i*4:i*4+3, 3] = camera_poses_raw[i,:3].T
camera_poses[i*4+3, 3] = 1
return camera_poses
else:
return np.loadtxt(filename)
if use_thetau:
camera_poses_raw = np.loadtxt(filename)
camera_poses = np.zeros((4*camera_poses_raw.shape[0], 4))
for i in range(camera_poses_raw.shape[0]):
camera_poses[i*4:i*4+3, 0:3] = visp_thetau_to_rotation(camera_poses_raw[i,3:])
camera_poses[i*4:i*4+3, 3] = camera_poses_raw[i,:3].T
camera_poses[i*4+3, 3] = 1
return camera_poses
else:
return np.loadtxt(filename)

def inverse_homogeneoux_matrix(M):
R = M[0:3, 0:3]
Expand Down Expand Up @@ -713,6 +723,8 @@ def main():
parser.add_argument('-p', type=str, nargs=1, required=True, help='Path to poses file.')
parser.add_argument('--theta-u', action='store_true', default=False,
help='If true, camera poses are expressed using [tx ty tz tux tuy tuz] formalism, otherwise in homogeneous form.')
parser.add_argument('--npz', action='store_true', default=False,
help='If true, poses are loaded from a npz file with \"vec_poses\" field and [tx ty tz tux tuy tuz] data.')
parser.add_argument('-m', type=str, nargs=1, required=True, help='Path to CAO model file.')
parser.add_argument('--colormap', default='gist_rainbow', type=str, help='Colormap to use for the camera path.')
parser.add_argument('--save', action='store_true', help='If true, save the figures on disk.')
Expand Down Expand Up @@ -774,8 +786,9 @@ def main():
# Load camera poses
camera_pose_filename = args.p[0]
use_thetau = args.theta_u
print(f"Load camera poses from: {camera_pose_filename} ; Use theta-u? {use_thetau}")
camera_poses = load_camera_poses(camera_pose_filename, use_thetau)
use_npz_file_format = args.npz
print(f"Load camera poses from: {camera_pose_filename} ; Use theta-u? {use_thetau} ; Load from npz file? {use_npz_file_format}")
camera_poses = load_camera_poses(camera_pose_filename, use_thetau, use_npz_file_format)
print("poses: ", camera_poses.shape)

colormap = args.colormap
Expand Down Expand Up @@ -854,12 +867,12 @@ def main():

for i in range(0, inverse_camera_poses.shape[0], 4*pose_step):
camera_pose = inverse_camera_poses[i:i+4,:]
draw_camera(ax, camera_pose, cam_width, cam_height, cam_focal, cam_scale)
draw_camera(ax, camera_pose, cam_width, cam_height, cam_focal, cam_scale, camera_colors[i//4])

# Draw the last camera pose
for i in range(inverse_camera_poses.shape[0]-4, inverse_camera_poses.shape[0], 4):
camera_pose = inverse_camera_poses[i:i+4,:]
draw_camera(ax, camera_pose, cam_width, cam_height, cam_focal, cam_scale)
draw_camera(ax, camera_pose, cam_width, cam_height, cam_focal, cam_scale, camera_colors[-1])

draw_camera_path(ax, inverse_camera_poses, camera_colors)
draw_model(ax, model)
Expand Down
Loading

0 comments on commit 4cfa003

Please sign in to comment.