23 return MatFilePtr(Mat_Open(path.c_str(), MAT_ACC_RDONLY));
28 return MatVarPtr(Mat_VarRead(matfp, name.c_str()));
33 return MatVarPtr(Mat_VarRead(matfp,
nullptr));
40matvar_t* MatReader::getStructField(matvar_t* s,
const char* field,
size_t index)
noexcept {
41 if (!s || s->class_type != MAT_C_STRUCT)
return nullptr;
42 return Mat_VarGetStructFieldByName(s, field, index);
45bool MatReader::readTopInt(mat_t* matfp,
const char* name,
int& out)
noexcept {
47 MatVarPtr v(Mat_VarRead(matfp, name));
48 if (!v || !v->data)
return false;
49 out =
static_cast<int>(readScalar(v.get()));
53bool MatReader::readTopDouble(mat_t* matfp,
const char* name,
double& out)
noexcept {
55 MatVarPtr v(Mat_VarRead(matfp, name));
56 if (!v || !v->data)
return false;
57 out = readScalar(v.get());
61bool MatReader::readTopString(mat_t* matfp,
const char* name, std::string& out) {
64 if (!v || !v->data)
return false;
69bool MatReader::readStructInt(matvar_t* s,
const char* field,
int& out,
size_t index)
noexcept {
71 matvar_t* f = getStructField(s, field, index);
72 if (!f || !f->data)
return false;
73 out =
static_cast<int>(readScalar(f));
77bool MatReader::readStructDouble(matvar_t* s,
const char* field,
double& out,
size_t index)
noexcept {
79 matvar_t* f = getStructField(s, field, index);
80 if (!f || !f->data)
return false;
85bool MatReader::readStructBool(matvar_t* s,
const char* field,
bool& out,
size_t index)
noexcept {
87 matvar_t* f = getStructField(s, field, index);
88 if (!f || !f->data)
return false;
89 out =
static_cast<int>(readScalar(f)) != 0;
93bool MatReader::readStructString(matvar_t* s,
const char* field, std::string& out,
size_t index) {
95 matvar_t* f = getStructField(s, field, index);
96 if (!f || !f->data)
return false;
106 auto matfp =
openMat(mat_path);
108 LOG_ERROR <<
"Failed to open DLT calibration file: " << mat_path;
112 auto dlt_var =
readVar(matfp.get(),
"DLTstructCam");
113 if (!dlt_var || dlt_var->class_type != MAT_C_STRUCT) {
114 LOG_ERROR <<
"Variable 'DLTstructCam' not found or not a struct in " << mat_path;
121 if (matvar_t* f = getStructField(dlt_var.get(),
"DLTparams", 0)) {
125 LOG_ERROR <<
"DLTparams has " << calib.
DLTparams.size() <<
" elements (expected 11) in " << mat_path;
130 readStructInt(dlt_var.get(),
"indCam", calib.
indCam);
133 if (matvar_t* f = getStructField(dlt_var.get(),
"imageCentroids", 0)) {
134 if (f->data && f->class_type == MAT_C_DOUBLE && f->rank >= 2) {
135 size_t rows = f->dims[0];
136 size_t cols = f->dims[1];
138 const double* d =
static_cast<const double*
>(f->data);
141 for (
size_t r = 0; r < rows; ++r) {
142 for (
size_t c = 0; c < cols; ++c) {
150 if (matvar_t* f = getStructField(dlt_var.get(),
"columns", 0)) {
153 for (
int i = 0; i < f->rank; ++i) n *= f->dims[i];
154 if (f->class_type == MAT_C_UINT8) {
155 const uint8_t* d =
static_cast<const uint8_t*
>(f->data);
156 calib.
columns.assign(d, d + n);
157 }
else if (f->class_type == MAT_C_DOUBLE) {
158 const double* d =
static_cast<const double*
>(f->data);
160 for (
size_t i = 0; i < n; ++i) calib.
columns[i] =
static_cast<uint8_t
>(d[i]);
166 if (matvar_t* f = getStructField(dlt_var.get(),
"C3Dtrue", 0)) {
167 if (f->data && f->class_type == MAT_C_DOUBLE && f->rank >= 3) {
168 size_t d0 = f->dims[0];
169 size_t d1 = f->dims[1];
170 size_t d2 = f->dims[2];
173 const double* src =
static_cast<const double*
>(f->data);
175 calib.
C3Dtrue.resize(d0 * d1 * d2);
176 for (
size_t k = 0; k < d2; ++k) {
177 for (
size_t j = 0; j < d1; ++j) {
178 for (
size_t i = 0; i < d0; ++i) {
179 calib.
C3Dtrue[(i * d1 + j) * d2 + k] = src[k * d1 * d0 + j * d0 + i];
187 <<
", " << calib.
DLTparams.size() <<
" DLT params"
189 <<
" from " << mat_path;
199 auto matfp =
openMat(protocol_path);
201 LOG_ERROR <<
"Failed to open protocol file: " << protocol_path;
206 MatVarPtr cond_var(Mat_VarRead(matfp.get(),
"cond"));
208 cond_var.reset(Mat_VarRead(matfp.get(),
"protocol"));
211 if (!cond_var || cond_var->class_type != MAT_C_STRUCT) {
216 if (matvar_t* titles_var = getStructField(cond_var.get(),
"titles", 0)) {
217 if (titles_var->class_type == MAT_C_CELL) {
223 matvar_t* table_var = getStructField(cond_var.get(),
"table", 0);
224 if (!table_var || table_var->class_type != MAT_C_CELL || table_var->rank < 2) {
228 const size_t n_trials = table_var->dims[0];
229 const size_t n_fields = table_var->dims[1];
234 protocol.
trials.reserve(n_trials);
237 int dir_idx = -1, nf_idx = -1, spd_idx = -1, rep_idx = -1;
238 for (
size_t i = 0; i < protocol.
titles.size(); ++i) {
239 if (protocol.
titles[i] ==
"dir") dir_idx =
static_cast<int>(i);
240 else if (protocol.
titles[i] ==
"nf") nf_idx =
static_cast<int>(i);
241 else if (protocol.
titles[i] ==
"spd") spd_idx =
static_cast<int>(i);
242 else if (protocol.
titles[i] ==
"rep") rep_idx =
static_cast<int>(i);
246 for (
size_t trial = 0; trial < n_trials; ++trial) {
250 auto readCellAt = [&](
size_t col) -> matvar_t* {
255 const size_t idx = trial + col * n_trials;
256 return Mat_VarGetCell(table_var, idx);
261 matvar_t* cell = readCellAt(
static_cast<size_t>(dir_idx));
262 if (cell && cell->class_type == MAT_C_CHAR && cell->data) {
263 std::string s(
static_cast<char*
>(cell->data), cell->nbytes);
264 s.erase(s.find_last_not_of(
" \t\n\r") + 1);
265 info.direction = std::move(s);
271 matvar_t* cell = readCellAt(
static_cast<size_t>(nf_idx));
272 if (cell && cell->class_type == MAT_C_DOUBLE && cell->data) {
273 info.force = *
static_cast<double*
>(cell->data);
279 matvar_t* cell = readCellAt(
static_cast<size_t>(spd_idx));
280 if (cell && cell->class_type == MAT_C_DOUBLE && cell->data) {
281 info.speed = *
static_cast<double*
>(cell->data);
287 matvar_t* cell = readCellAt(
static_cast<size_t>(rep_idx));
288 if (cell && cell->class_type == MAT_C_DOUBLE && cell->data) {
289 info.repetition =
static_cast<int>(*
static_cast<double*
>(cell->data));
293 protocol.
trials.push_back(std::move(info));
300 std::vector<std::string> strings;
301 if (!cell_var || cell_var->class_type != MAT_C_CELL)
return strings;
304 strings.reserve(n_cells);
306 for (
size_t i = 0; i < n_cells; ++i) {
307 matvar_t* cell = Mat_VarGetCell(cell_var, i);
308 if (cell && cell->class_type == MAT_C_CHAR && cell->data) {
309 std::string str(
static_cast<char*
>(cell->data), cell->nbytes);
310 str.erase(str.find_last_not_of(
" \t\n\r") + 1);
311 strings.push_back(std::move(str));
313 strings.emplace_back();
324 const std::string& struct_name,
325 const std::string& field_name) {
326 if (!matfp)
return nullptr;
328 MatVarPtr struct_var(Mat_VarRead(matfp, struct_name.c_str()));
329 if (!struct_var || struct_var->class_type != MAT_C_STRUCT)
return nullptr;
331 matvar_t* field = Mat_VarGetStructFieldByName(struct_var.get(), field_name.c_str(), 0);
332 if (!field)
return nullptr;
335 return Mat_VarDuplicate(field, 1);
339 if (!matfp)
return nullptr;
342 std::vector<std::string> parts;
343 std::istringstream iss(path);
345 while (std::getline(iss, part,
'.')) parts.push_back(part);
346 if (parts.empty())
return nullptr;
348 MatVarPtr current(Mat_VarRead(matfp, parts[0].c_str()));
349 if (!current)
return nullptr;
351 for (
size_t i = 1; i < parts.size(); ++i) {
352 if (current->class_type != MAT_C_STRUCT)
return nullptr;
354 matvar_t* field = Mat_VarGetStructFieldByName(current.get(), parts[i].c_str(), 0);
355 if (!field)
return nullptr;
357 MatVarPtr next(Mat_VarDuplicate(field, 1));
361 return current.release();
369 std::vector<double> result;
370 if (!var || !var->data)
return result;
372 if (var->class_type != MAT_C_DOUBLE) {
373 LOG_WARN <<
"Variable is not double type";
377 size_t n_elements = 1;
378 for (
int i = 0; i < var->rank; ++i) n_elements *= var->dims[i];
380 const double* data =
static_cast<const double*
>(var->data);
381 result.assign(data, data + n_elements);
386 std::vector<double> result;
390 if (!var || !var->data || var->rank < 2)
return result;
392 if (var->class_type != MAT_C_DOUBLE) {
393 LOG_WARN <<
"Variable is not double type";
400 const double* data =
static_cast<const double*
>(var->data);
401 const size_t n_elements = rows * cols;
404 result.resize(n_elements);
405 for (
size_t r = 0; r < rows; ++r) {
406 for (
size_t c = 0; c < cols; ++c) {
407 result[r * cols + c] = data[c * rows + r];
414 if (!var || !var->data)
return cv::Mat();
416 if (var->rank != 2) {
417 LOG_WARN <<
"Expected 2D array for image";
421 const size_t rows = var->dims[0];
422 const size_t cols = var->dims[1];
424 if (var->class_type == MAT_C_DOUBLE) {
425 const double* data =
static_cast<const double*
>(var->data);
426 cv::Mat img(rows, cols, CV_64F);
427 for (
size_t r = 0; r < rows; ++r) {
428 for (
size_t c = 0; c < cols; ++c) {
429 img.at<
double>(r, c) = data[c * rows + r];
435 if (var->class_type == MAT_C_UINT8) {
436 const uint8_t* data =
static_cast<const uint8_t*
>(var->data);
437 cv::Mat img(rows, cols, CV_8U);
439 const bool is_logical = (var->isLogical != 0);
440 for (
size_t r = 0; r < rows; ++r) {
441 for (
size_t c = 0; c < cols; ++c) {
442 const uint8_t value = data[c * rows + r];
443 img.at<uint8_t>(r, c) = is_logical ? (value * 255) : value;
449 LOG_WARN <<
"Unsupported image data type";
454 if (!var || !var->data)
return 0.0;
456 if (var->class_type == MAT_C_DOUBLE) {
457 return static_cast<const double*
>(var->data)[0];
459 if (var->class_type == MAT_C_INT32) {
460 return static_cast<double>(
static_cast<const int32_t*
>(var->data)[0]);
462 if (var->class_type == MAT_C_UINT8) {
463 return static_cast<double>(
static_cast<const uint8_t*
>(var->data)[0]);
466 LOG_WARN <<
"Unsupported scalar type";
471 if (!var || !var->data)
return "";
474 for (
int i = 0; i < var->rank; ++i) len *= var->dims[i];
476 if (var->class_type == MAT_C_CHAR) {
477 return std::string(
static_cast<const char*
>(var->data), len);
480 if (var->class_type == MAT_C_UINT16) {
481 const uint16_t* data =
static_cast<const uint16_t*
>(var->data);
484 for (
size_t i = 0; i < len; ++i) out.push_back(
static_cast<char>(data[i]));
488 LOG_WARN <<
"Unsupported string type";
493 if (!var || var->class_type != MAT_C_CELL)
return 0;
496 for (
int i = 0; i < var->rank; ++i) n_cells *= var->dims[i];
501 if (!var || var->class_type != MAT_C_CELL)
return nullptr;
503 if (index >= n_cells)
return nullptr;
506 return Mat_VarGetCell(var, index);
514 std::map<std::string, double> params;
515 if (!matfp)
return params;
518 if (!dispinfo || dispinfo->class_type != MAT_C_STRUCT)
return params;
520 const std::vector<std::string> field_names = {
521 "cutoff_corrcoef",
"cutoff_diffnorm",
"cutoff_iteration",
522 "radius",
"spacing",
"subsettrunc",
"total_threads"
525 for (
const auto& field : field_names) {
526 matvar_t* field_var = Mat_VarGetStructFieldByName(dispinfo.get(), field.c_str(), 0);
527 if (field_var && field_var->data) {
536 if (!matfp)
return false;
537 MatVarPtr var(Mat_VarRead(matfp, var_name.c_str()));
538 return static_cast<bool>(var);
542 auto matfp =
openMat(mat_path);
544 LOG_ERROR <<
"Failed to open MAT file: " << mat_path;
548 auto var =
readVar(matfp.get(), var_name);
550 LOG_ERROR <<
"Variable '" << var_name <<
"' not found in " << mat_path;
558 auto matfp =
openMat(mat_path);
560 LOG_ERROR <<
"Failed to open MAT file: " << mat_path;
567 const char* var_names[] = {
"DIC3Dcombined",
"DIC3D",
"data"};
568 for (
const char* name : var_names) {
569 dic3d_var.reset(Mat_VarRead(matfp.get(), name));
570 if (dic3d_var)
break;
574 if (!dic3d_var || dic3d_var->class_type != MAT_C_STRUCT) {
575 LOG_ERROR <<
"DIC3Dcombined structure not found in " << mat_path;
579 auto ensure_loaded = [&](matvar_t* var,
const char* label) {
583 if (Mat_VarReadDataAll(matfp.get(), var) != 0) {
584 if (!var->data && var->class_type != MAT_C_STRUCT && var->class_type != MAT_C_CELL) {
585 LOG_WARN <<
"failed to fully load '" << label <<
"' from " << mat_path;
595 std::function<void(matvar_t*)> deep_load = [&](matvar_t* v) {
597 Mat_VarReadDataAll(matfp.get(), v);
599 for (
int i = 0; i < v->rank; ++i) ne *= v->dims[i];
600 if (v->class_type == MAT_C_STRUCT) {
601 unsigned nf = Mat_VarGetNumberOfFields(v);
602 char*
const* names = Mat_VarGetStructFieldnames(v);
603 for (
size_t e = 0; e < ne; ++e)
604 for (
unsigned i = 0; i < nf; ++i)
605 deep_load(Mat_VarGetStructFieldByName(v, names[i], e));
606 }
else if (v->class_type == MAT_C_CELL) {
607 for (
size_t e = 0; e < ne; ++e) deep_load(Mat_VarGetCell(v, static_cast<int>(e)));
610 deep_load(dic3d_var.get());
613 if (matvar_t* faces_var = getStructField(dic3d_var.get(),
"Faces", 0)) {
614 if (faces_var->data && faces_var->rank >= 2) {
615 const size_t dim0 = faces_var->dims[0];
616 const size_t dim1 = faces_var->dims[1];
618 size_t nFaces = (dim1 == 3) ? dim0 : dim1;
620 combined.
Faces.clear();
621 combined.
Faces.reserve(nFaces * 3);
623 if (faces_var->class_type == MAT_C_INT32) {
625 const int32_t* d =
static_cast<const int32_t*
>(faces_var->data);
628 for (
size_t i = 0; i < nFaces * 3; ++i) {
629 combined.
Faces.push_back(d[i] - 1);
633 for (
size_t r = 0; r < nFaces; ++r) {
634 for (
size_t c = 0; c < 3; ++c) {
635 combined.
Faces.push_back(d[c * nFaces + r] - 1);
639 }
else if (faces_var->class_type == MAT_C_DOUBLE) {
641 const double* d =
static_cast<const double*
>(faces_var->data);
644 for (
size_t r = 0; r < nFaces; ++r) {
645 combined.
Faces.push_back(
static_cast<int>(d[r]) - 1);
646 combined.
Faces.push_back(
static_cast<int>(d[r + nFaces]) - 1);
647 combined.
Faces.push_back(
static_cast<int>(d[r + 2 * nFaces]) - 1);
651 for (
size_t r = 0; r < nFaces; ++r) {
652 for (
size_t c = 0; c < 3; ++c) {
653 combined.
Faces.push_back(
static_cast<int>(d[c + r * 3]) - 1);
658 LOG_WARN <<
"Unexpected Faces class_type " << faces_var->class_type;
660 LOG_DEBUG <<
"Loaded " << nFaces <<
" faces (type=" << faces_var->class_type
661 <<
", dims=" << dim0 <<
"x" << dim1 <<
")";
668 if (matvar_t* points3d_var = getStructField(dic3d_var.get(),
"Points3D", 0)) {
669 ensure_loaded(points3d_var,
"Points3D");
670 if (points3d_var->class_type == MAT_C_CELL) {
674 for (
size_t frame = 0; frame < nFrames; ++frame) {
675 matvar_t* frame_var = Mat_VarGetCell(points3d_var, frame);
676 if (!frame_var)
continue;
677 ensure_loaded(frame_var,
"Points3D cell");
679 if (frame_var->class_type == MAT_C_STRUCT) {
681 matvar_t* x_var = getStructField(frame_var,
"x", 0);
682 matvar_t* y_var = getStructField(frame_var,
"y", 0);
683 matvar_t* z_var = getStructField(frame_var,
"z", 0);
684 ensure_loaded(x_var,
"Points3D.x");
685 ensure_loaded(y_var,
"Points3D.y");
686 ensure_loaded(z_var,
"Points3D.z");
688 if (!x_var || !y_var || !z_var ||
689 x_var->class_type != MAT_C_DOUBLE || !x_var->data ||
690 y_var->class_type != MAT_C_DOUBLE || !y_var->data ||
691 z_var->class_type != MAT_C_DOUBLE || !z_var->data) {
698 }
else if (frame_var->class_type == MAT_C_DOUBLE && frame_var->data && frame_var->rank >= 2) {
699 const size_t dim0 = frame_var->dims[0];
700 const size_t dim1 = frame_var->dims[1];
701 const double* d =
static_cast<const double*
>(frame_var->data);
704 const size_t nPts = dim0;
705 combined.
Points3D[frame].x.assign(d, d + nPts);
706 combined.
Points3D[frame].y.assign(d + nPts, d + 2 * nPts);
707 combined.
Points3D[frame].z.assign(d + 2 * nPts, d + 3 * nPts);
708 }
else if (dim0 == 3) {
710 const size_t nPts = dim1;
711 combined.
Points3D[frame].x.resize(nPts);
712 combined.
Points3D[frame].y.resize(nPts);
713 combined.
Points3D[frame].z.resize(nPts);
714 for (
size_t p = 0; p < nPts; ++p) {
715 combined.
Points3D[frame].x[p] = d[p * 3 + 0];
716 combined.
Points3D[frame].y[p] = d[p * 3 + 1];
717 combined.
Points3D[frame].z[p] = d[p * 3 + 2];
720 LOG_WARN <<
"Points3D frame " << frame <<
" has dims "
721 << dim0 <<
"x" << dim1 <<
" (expected Nx3 or 3xN) in " << mat_path;
729 if (combined.
Points3D[frame].x.size() != combined.
Points3D[frame].y.size() ||
731 LOG_WARN <<
"inconsistent x/y/z sizes in Points3D frame "
732 << frame <<
" in " << mat_path;
740 LOG_DEBUG <<
"Loaded Points3D: " << nFrames <<
" frames, "
741 << combined.
Points3D[0].x.size() <<
" points per frame";
747 if (matvar_t* corr_var = getStructField(dic3d_var.get(),
"corrComb", 0)) {
748 ensure_loaded(corr_var,
"corrComb");
749 if (corr_var->class_type == MAT_C_CELL) {
752 for (
size_t frame = 0; frame < nFrames; ++frame) {
753 matvar_t* cell = Mat_VarGetCell(corr_var, frame);
754 ensure_loaded(cell,
"corrComb cell");
755 if (cell && cell->class_type == MAT_C_DOUBLE && cell->data) {
763 if (matvar_t* face_corr_var = getStructField(dic3d_var.get(),
"FaceCorrComb", 0)) {
764 if (face_corr_var->class_type == MAT_C_CELL) {
767 for (
size_t frame = 0; frame < nFrames; ++frame) {
768 matvar_t* cell = Mat_VarGetCell(face_corr_var, frame);
769 if (cell && cell->class_type == MAT_C_DOUBLE && cell->data) {
778 if (matvar_t* fc_var = getStructField(dic3d_var.get(),
"FaceCentroids", 0)) {
779 if (fc_var->class_type == MAT_C_CELL) {
782 for (
size_t frame = 0; frame < nFrames; ++frame) {
783 matvar_t* cell = Mat_VarGetCell(fc_var, frame);
784 if (cell && cell->class_type == MAT_C_DOUBLE && cell->data && cell->rank >= 2) {
785 size_t dim0 = cell->dims[0];
786 size_t dim1 = cell->dims[1];
787 const double* d =
static_cast<const double*
>(cell->data);
791 }
else if (dim1 == 3) {
793 size_t nFaces = dim0;
795 for (
size_t f = 0; f < nFaces; ++f) {
809 if (matvar_t* disp_var = getStructField(dic3d_var.get(),
"Disp", 0)) {
810 if (disp_var->class_type == MAT_C_STRUCT) {
813 if (matvar_t* dv_var = getStructField(disp_var,
"DispVec", 0)) {
814 if (dv_var->class_type == MAT_C_CELL) {
817 for (
size_t frame = 0; frame < nFrames; ++frame) {
818 matvar_t* cell = Mat_VarGetCell(dv_var, frame);
819 if (cell && cell->class_type == MAT_C_DOUBLE && cell->data && cell->rank >= 2) {
820 size_t dim0 = cell->dims[0];
821 size_t dim1 = cell->dims[1];
822 const double* d =
static_cast<const double*
>(cell->data);
825 combined.
Disp.
DispVec[frame].assign(d, d + dim0 * dim1);
826 }
else if (dim1 == 3) {
830 for (
size_t p = 0; p < nPts; ++p) {
833 combined.
Disp.
DispVec[frame][p*3+2] = d[p + 2*nPts];
836 combined.
Disp.
DispVec[frame].assign(d, d + dim0 * dim1);
843 if (matvar_t* dm_var = getStructField(disp_var,
"DispMgn", 0)) {
844 if (dm_var->class_type == MAT_C_CELL) {
847 for (
size_t frame = 0; frame < nFrames; ++frame) {
848 matvar_t* cell = Mat_VarGetCell(dm_var, frame);
849 if (cell && cell->class_type == MAT_C_DOUBLE && cell->data) {
859 if (matvar_t* facepair_var = getStructField(dic3d_var.get(),
"FacePairInds", 0)) {
863 if (matvar_t* pointpair_var = getStructField(dic3d_var.get(),
"PointPairInds", 0)) {
867 if (matvar_t* dic2d_info_var = getStructField(dic3d_var.get(),
"DIC2Dinfo", 0)) {
868 if (dic2d_info_var->class_type == MAT_C_CELL) {
872 for (
size_t i = 0; i < nEntries; ++i) {
873 matvar_t* cell = Mat_VarGetCell(dic2d_info_var, i);
874 if (cell && cell->class_type == MAT_C_STRUCT) {
875 readDIC2DPairResultsFromStruct(cell, combined.
DIC2Dinfo[i]);
881 LOG_INFO <<
"Loaded DIC3Dcombined: "
882 << combined.
Points3D.size() <<
" frames, "
883 << (combined.
Points3D.empty() ? 0 : combined.
Points3D[0].x.size()) <<
" points, "
884 << combined.
Faces.size() / 3 <<
" faces";
890 std::vector<int> result;
891 if (!var || !var->data)
return result;
893 size_t n_elements = 1;
894 for (
int i = 0; i < var->rank; ++i) n_elements *= var->dims[i];
896 result.reserve(n_elements);
898 if (var->class_type == MAT_C_DOUBLE) {
899 const double* data =
static_cast<const double*
>(var->data);
900 for (
size_t i = 0; i < n_elements; ++i) result.push_back(
static_cast<int>(data[i]));
904 if (var->class_type == MAT_C_INT32) {
905 const int32_t* data =
static_cast<const int32_t*
>(var->data);
906 result.assign(data, data + n_elements);
910 if (var->class_type == MAT_C_UINT16) {
911 const uint16_t* data =
static_cast<const uint16_t*
>(var->data);
912 for (
size_t i = 0; i < n_elements; ++i) result.push_back(
static_cast<int>(data[i]));
916 if (var->class_type == MAT_C_UINT32) {
917 const uint32_t* data =
static_cast<const uint32_t*
>(var->data);
918 for (
size_t i = 0; i < n_elements; ++i) result.push_back(
static_cast<int>(data[i]));
929bool MatReader::readDIC2DPairResultsFromStruct(matvar_t* dic2d_root,
DIC2DPairResults& result) {
930 if (!dic2d_root || dic2d_root->class_type != MAT_C_STRUCT) {
934 result = DIC2DPairResults{};
936 readStructInt(dic2d_root,
"nCamRef", result.
nCamRef);
937 readStructInt(dic2d_root,
"nCamDef", result.
nCamDef);
938 readStructInt(dic2d_root,
"nImages", result.
nImages);
939 if (matvar_t* pair_order_var = getStructField(dic2d_root,
"pairOrder", 0)) {
944 readStructBool(dic2d_root,
"pairForced", result.
pairForced);
946 if (matvar_t* roi_var = getStructField(dic2d_root,
"ROImask", 0)) {
950 if (matvar_t* faces_var = getStructField(dic2d_root,
"Faces", 0)) {
951 if (faces_var->data && faces_var->rank >= 2) {
952 const size_t rows = faces_var->dims[0];
953 const size_t cols = faces_var->dims[1];
954 const size_t nFaces = (cols == 3) ? rows : ((rows == 3) ? cols : 0);
956 result.
Faces.clear();
958 result.
Faces.reserve(nFaces * 3);
960 auto pushFaces = [&](
auto* dataPtr) {
962 for (
size_t f = 0; f < nFaces; ++f) {
963 result.
Faces.push_back(
static_cast<int>(dataPtr[f]) - 1);
964 result.
Faces.push_back(
static_cast<int>(dataPtr[f + nFaces]) - 1);
965 result.
Faces.push_back(
static_cast<int>(dataPtr[f + 2 * nFaces]) - 1);
968 for (
size_t f = 0; f < nFaces; ++f) {
969 result.
Faces.push_back(
static_cast<int>(dataPtr[f * 3 + 0]) - 1);
970 result.
Faces.push_back(
static_cast<int>(dataPtr[f * 3 + 1]) - 1);
971 result.
Faces.push_back(
static_cast<int>(dataPtr[f * 3 + 2]) - 1);
976 if (faces_var->class_type == MAT_C_DOUBLE) {
977 pushFaces(
static_cast<const double*
>(faces_var->data));
978 }
else if (faces_var->class_type == MAT_C_UINT16) {
979 pushFaces(
static_cast<const uint16_t*
>(faces_var->data));
980 }
else if (faces_var->class_type == MAT_C_UINT32) {
981 pushFaces(
static_cast<const uint32_t*
>(faces_var->data));
982 }
else if (faces_var->class_type == MAT_C_INT32) {
983 pushFaces(
static_cast<const int32_t*
>(faces_var->data));
989 if (matvar_t* facecolors_var = getStructField(dic2d_root,
"FaceColors", 0)) {
993 if (matvar_t* points_var = getStructField(dic2d_root,
"Points", 0)) {
994 if (points_var->class_type == MAT_C_CELL) {
996 result.
Points.resize(nFrames);
997 for (
size_t frame = 0; frame < nFrames; ++frame) {
998 matvar_t* frame_var = Mat_VarGetCell(points_var, frame);
999 if (!frame_var || frame_var->class_type != MAT_C_DOUBLE || !frame_var->data || frame_var->rank < 2) {
1003 const size_t dim0 = frame_var->dims[0];
1004 const size_t dim1 = frame_var->dims[1];
1005 const double* pts_data =
static_cast<const double*
>(frame_var->data);
1008 const size_t nPoints = dim0;
1009 result.
Points[frame].x.resize(nPoints);
1010 result.
Points[frame].y.resize(nPoints);
1011 for (
size_t i = 0; i < nPoints; ++i) {
1012 result.
Points[frame].x[i] = pts_data[i];
1013 result.
Points[frame].y[i] = pts_data[i + nPoints];
1015 }
else if (dim0 == 2) {
1016 const size_t nPoints = dim1;
1017 result.
Points[frame].x.resize(nPoints);
1018 result.
Points[frame].y.resize(nPoints);
1019 for (
size_t i = 0; i < nPoints; ++i) {
1020 result.
Points[frame].x[i] = pts_data[i * 2 + 0];
1021 result.
Points[frame].y[i] = pts_data[i * 2 + 1];
1028 if (matvar_t* corr_var = getStructField(dic2d_root,
"CorCoeffVec", 0)) {
1029 if (corr_var->class_type == MAT_C_CELL) {
1032 for (
size_t frame = 0; frame < nFrames; ++frame) {
1033 matvar_t* frame_var = Mat_VarGetCell(corr_var, frame);
1034 if (frame_var && frame_var->class_type == MAT_C_DOUBLE && frame_var->data) {
1041 if (matvar_t* ncorr_var = getStructField(dic2d_root,
"ncorrInfo", 0)) {
1042 if (ncorr_var->class_type == MAT_C_STRUCT) {
1043 if (matvar_t* cutoff_cc = getStructField(ncorr_var,
"cutoff_corrcoef", 0)) {
1058 matvar_t* step_var = getStructField(ncorr_var,
"stepanalysis", 0);
1059 if (step_var && step_var->class_type == MAT_C_STRUCT) {
1072 auto matfp =
openMat(mat_path);
1074 LOG_ERROR <<
"Failed to open MAT file: " << mat_path;
1080 auto first_var =
readVar(matfp.get(),
"DIC2DpairResults");
1081 matvar_t* dic2d_root =
nullptr;
1082 if (first_var && first_var->name && std::strcmp(first_var->name,
"DIC2DpairResults") == 0 &&
1083 first_var->class_type == MAT_C_STRUCT) {
1084 dic2d_root = first_var.get();
1088 if (!readDIC2DPairResultsFromStruct(dic2d_root, result)) {
1091 LOG_INFO <<
"Loaded DIC2DPairResults: "
1092 <<
"nCamRef=" << result.
nCamRef
1093 <<
", nCamDef=" << result.
nCamDef
1094 <<
", nImages=" << result.
nImages
1095 <<
", pairOrder=" << result.
pairOrder.size()
1096 <<
", " << result.
Points.size() <<
" frames"
1097 <<
", " << result.
Faces.size() / 3 <<
" faces";
1101 std::vector<MatVarPtr> owned_vars;
1102 owned_vars.reserve(10);
1104 auto readAnyVar = [&](
const char* name) -> matvar_t* {
1106 return getStructField(dic2d_root, name, 0);
1108 owned_vars.push_back(
readVar(matfp.get(), name));
1109 return owned_vars.back().get();
1112 auto readAnyInt = [&](
const char* name,
int& out) {
1114 readStructInt(dic2d_root, name, out);
1116 readTopInt(matfp.get(), name, out);
1120 readAnyInt(
"nCamRef", result.
nCamRef);
1121 readAnyInt(
"nCamDef", result.
nCamDef);
1122 readAnyInt(
"nImages", result.
nImages);
1123 if (matvar_t* pair_order_var = readAnyVar(
"pairOrder")) {
1128 if (matvar_t* pair_forced_var = readAnyVar(
"pairForced")) {
1134 if (matvar_t* roi_var = readAnyVar(
"ROImask")) {
1138 if (matvar_t* faces_var = readAnyVar(
"Faces")) {
1139 if (faces_var->data && faces_var->rank >= 2) {
1140 const size_t rows = faces_var->dims[0];
1141 const size_t cols = faces_var->dims[1];
1142 const size_t nFaces = (cols == 3) ? rows : ((rows == 3) ? cols : 0);
1144 result.
Faces.clear();
1147 LOG_WARN <<
"Faces has unexpected shape (" << rows <<
"x" << cols
1148 <<
"), expected Nx3 or 3xN in " << mat_path;
1150 result.
Faces.reserve(nFaces * 3);
1152 auto pushFaces = [&](
auto* dataPtr) {
1155 for (
size_t f = 0; f < nFaces; ++f) {
1156 result.
Faces.push_back(
static_cast<int>(dataPtr[f]) - 1);
1157 result.
Faces.push_back(
static_cast<int>(dataPtr[f + nFaces]) - 1);
1158 result.
Faces.push_back(
static_cast<int>(dataPtr[f + 2 * nFaces]) - 1);
1162 for (
size_t f = 0; f < nFaces; ++f) {
1163 result.
Faces.push_back(
static_cast<int>(dataPtr[f * 3 + 0]) - 1);
1164 result.
Faces.push_back(
static_cast<int>(dataPtr[f * 3 + 1]) - 1);
1165 result.
Faces.push_back(
static_cast<int>(dataPtr[f * 3 + 2]) - 1);
1170 if (faces_var->class_type == MAT_C_DOUBLE) {
1171 pushFaces(
static_cast<const double*
>(faces_var->data));
1172 }
else if (faces_var->class_type == MAT_C_UINT16) {
1173 pushFaces(
static_cast<const uint16_t*
>(faces_var->data));
1174 }
else if (faces_var->class_type == MAT_C_UINT32) {
1175 pushFaces(
static_cast<const uint32_t*
>(faces_var->data));
1176 }
else if (faces_var->class_type == MAT_C_INT32) {
1177 pushFaces(
static_cast<const int32_t*
>(faces_var->data));
1179 LOG_WARN <<
"Faces has unsupported type (class_type=" << faces_var->class_type
1180 <<
") in " << mat_path;
1186 if (matvar_t* facecolors_var = readAnyVar(
"FaceColors")) {
1190 if (matvar_t* points_var = readAnyVar(
"Points")) {
1191 if (points_var->class_type == MAT_C_CELL) {
1193 result.
Points.resize(nFrames);
1195 for (
size_t frame = 0; frame < nFrames; ++frame) {
1196 matvar_t* frame_var = Mat_VarGetCell(points_var, frame);
1197 if (!frame_var || frame_var->class_type != MAT_C_DOUBLE || !frame_var->data || frame_var->rank < 2)
1200 const size_t dim0 = frame_var->dims[0];
1201 const size_t dim1 = frame_var->dims[1];
1202 const double* pts_data =
static_cast<const double*
>(frame_var->data);
1206 const size_t nPoints = dim0;
1207 result.
Points[frame].x.resize(nPoints);
1208 result.
Points[frame].y.resize(nPoints);
1209 for (
size_t i = 0; i < nPoints; ++i) {
1210 result.
Points[frame].x[i] = pts_data[i];
1211 result.
Points[frame].y[i] = pts_data[i + nPoints];
1213 }
else if (dim0 == 2) {
1215 const size_t nPoints = dim1;
1216 result.
Points[frame].x.resize(nPoints);
1217 result.
Points[frame].y.resize(nPoints);
1218 for (
size_t i = 0; i < nPoints; ++i) {
1219 result.
Points[frame].x[i] = pts_data[i * 2 + 0];
1220 result.
Points[frame].y[i] = pts_data[i * 2 + 1];
1227 if (matvar_t* corr_var = readAnyVar(
"CorCoeffVec")) {
1228 if (corr_var->class_type == MAT_C_CELL) {
1232 for (
size_t frame = 0; frame < nFrames; ++frame) {
1233 matvar_t* frame_var = Mat_VarGetCell(corr_var, frame);
1234 if (frame_var && frame_var->class_type == MAT_C_DOUBLE && frame_var->data) {
1241 if (matvar_t* ncorr_var = readAnyVar(
"ncorrInfo")) {
1242 if (ncorr_var->class_type == MAT_C_STRUCT) {
1243 if (matvar_t* cutoff_cc = getStructField(ncorr_var,
"cutoff_corrcoef", 0)) {
1258 matvar_t* step_var = getStructField(ncorr_var,
"stepanalysis", 0);
1259 if (step_var && step_var->class_type == MAT_C_STRUCT) {
1268 LOG_INFO <<
"Loaded DIC2DPairResults: "
1269 <<
"nCamRef=" << result.
nCamRef
1270 <<
", nCamDef=" << result.
nCamDef
1271 <<
", nImages=" << result.
nImages
1272 <<
", pairOrder=" << result.
pairOrder.size()
1273 <<
", " << result.
Points.size() <<
" frames"
1274 <<
", " << result.
Faces.size() / 3 <<
" faces";
static bool loadDLTCalibration(const std::string &mat_path, DLTCalibrationData &calib)
static matvar_t * readStructField(mat_t *matfp, const std::string &struct_name, const std::string &field_name)
Read a specific field from a struct variable.
std::unique_ptr< matvar_t, MatVarDeleter > MatVarPtr
static cv::Mat readImage(matvar_t *var)
Read image from matvar.
static matvar_t * getCellElement(matvar_t *var, size_t index)
Get cell from cell array.
static bool variableExists(mat_t *matfp, const std::string &var_name)
Check if variable exists in MAT file.
static std::vector< double > readDoubleArray(matvar_t *var)
Read double array from matvar.
static MatVarPtr readVar(mat_t *matfp, const std::string &name)
static cv::Mat readROIMask(const std::string &mat_path, const std::string &var_name="refmask")
Read ROI mask from MAT file.
static std::vector< double > readDouble2DArray(matvar_t *var, size_t &rows, size_t &cols)
Read 2D double array from matvar.
static bool readDIC2DPairResults(const std::string &mat_path, struct DIC2DPairResults &result)
Read DIC2DPairResults structure from MAT file Loads myDIC2DpairResults_C_X_C_Y.mat files.
static bool readDIC3Dcombined(const std::string &mat_path, struct DIC3Dcombined &combined)
Read DIC3Dcombined structure from MAT file.
static std::string readString(matvar_t *var)
Read string from matvar.
static std::vector< int > readIntArray(matvar_t *var)
Read int array from matvar.
static matvar_t * readNestedField(mat_t *matfp, const std::string &path)
Read a nested struct field (e.g., "data_dic_save.dispinfo.radius")
static double readScalar(matvar_t *var)
Read scalar from matvar.
static std::vector< std::string > readCellStrings(matvar_t *cell_var)
static MatVarPtr readFirstVar(mat_t *matfp)
static std::map< std::string, double > readDispInfo(mat_t *matfp)
Read dispinfo parameters from MAT file.
static MatFilePtr openMat(const std::string &path)
std::unique_ptr< mat_t, MatFileDeleter > MatFilePtr
static bool loadProtocol(const std::string &protocol_path, ProtocolFileData &protocol)
static size_t getCellArraySize(matvar_t *var)
Get cell array size.
Lightweight, dependency-free logging facility for CPPxDIC.
DataSerializer - Abstract base class for format-agnostic DIC data I/O.
DIC 2D Pair Results Equivalent to MATLAB's DIC2DpairResults.
std::vector< int > pairOrder
std::vector< Points2D > Points
std::vector< std::vector< double > > CorCoeffVec
std::vector< double > FaceColors
DIC 3D Combined Results Equivalent to MATLAB's DIC3Dcombined This represents stitched results from mu...
std::vector< int > FacePairInds
std::vector< int > PointPairInds
std::vector< Point3DFrame > Points3D
std::vector< std::vector< double > > FaceCorrComb
std::vector< std::vector< double > > corrComb
std::vector< DIC2DPairResults > DIC2Dinfo
std::vector< std::vector< double > > FaceCentroids
std::vector< double > cutoff_corrcoef
StepAnalysisInfo stepanalysis
std::vector< double > imageCentroids
std::vector< uint8_t > columns
size_t imageCentroids_rows
std::vector< double > C3Dtrue
std::vector< double > DLTparams
std::vector< std::vector< double > > DispVec
std::vector< std::vector< double > > DispMgn
std::vector< TrialEntry > trials
std::vector< std::string > titles