64 hid_t obj_id =
h5Deref(file_id, ref);
65 if (obj_id < 0)
return {};
67 hid_t space = H5Dget_space(obj_id);
68 int ndims = H5Sget_simple_extent_ndims(space);
76 H5Sget_simple_extent_dims(space, dims,
nullptr);
80 const size_t cols = dims[0];
81 const size_t rows = dims[1];
83 std::vector<double> buf(rows * cols);
84 herr_t err = H5Dread(obj_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, buf.data());
87 if (err < 0)
return {};
89 Array2D<double> arr(
static_cast<std::ptrdiff_t
>(rows),
90 static_cast<std::ptrdiff_t
>(cols));
92 for (
size_t c = 0; c < cols; ++c)
93 for (
size_t r = 0; r < rows; ++r)
94 arr(
static_cast<std::ptrdiff_t
>(r),
95 static_cast<std::ptrdiff_t
>(c)) = buf[c * rows + r];
100static Array2D<bool>
h5ReadMask2D(hid_t file_id,
const hobj_ref_t& ref) {
101 hid_t obj_id =
h5Deref(file_id, ref);
102 if (obj_id < 0)
return {};
104 hid_t space = H5Dget_space(obj_id);
105 int ndims = H5Sget_simple_extent_ndims(space);
113 H5Sget_simple_extent_dims(space, dims,
nullptr);
116 const size_t cols = dims[0];
117 const size_t rows = dims[1];
120 hid_t dtype = H5Dget_type(obj_id);
121 H5T_class_t tclass = H5Tget_class(dtype);
124 Array2D<bool> mask(
static_cast<std::ptrdiff_t
>(rows),
125 static_cast<std::ptrdiff_t
>(cols),
false);
127 if (tclass == H5T_INTEGER) {
128 std::vector<uint8_t> buf(rows * cols);
129 H5Dread(obj_id, H5T_NATIVE_UINT8, H5S_ALL, H5S_ALL, H5P_DEFAULT, buf.data());
130 for (
size_t c = 0; c < cols; ++c)
131 for (
size_t r = 0; r < rows; ++r)
132 mask(
static_cast<std::ptrdiff_t
>(r),
133 static_cast<std::ptrdiff_t
>(c)) = (buf[c * rows + r] != 0);
134 }
else if (tclass == H5T_FLOAT) {
135 std::vector<double> buf(rows * cols);
136 H5Dread(obj_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, buf.data());
137 for (
size_t c = 0; c < cols; ++c)
138 for (
size_t r = 0; r < rows; ++r)
139 mask(
static_cast<std::ptrdiff_t
>(r),
140 static_cast<std::ptrdiff_t
>(c)) = (buf[c * rows + r] != 0.0);
142 LOG_WARN <<
"unsupported HDF5 type class " << tclass <<
" for ROI mask";
152 hid_t dset = H5Dopen2(file_id, path, H5P_DEFAULT);
153 if (dset < 0)
return {};
155 hid_t space = H5Dget_space(dset);
156 hssize_t nElems = H5Sget_simple_extent_npoints(space);
164 count =
static_cast<size_t>(nElems);
165 std::vector<hobj_ref_t> refs(
count);
166 herr_t err = H5Dread(dset, H5T_STD_REF_OBJ, H5S_ALL, H5S_ALL, H5P_DEFAULT, refs.data());
180int main(
int argc,
char* argv[]) {
182 std::cerr <<
"Usage: generate_ncorr_bin <input.mat> [output.bin]\n";
186 std::string input_path = argv[1];
187 std::string output_path;
189 output_path = argv[2];
192 output_path = input_path;
193 auto pos = output_path.rfind(
".mat");
194 if (pos != std::string::npos) {
195 output_path.replace(pos, 4,
".bin");
197 output_path +=
".bin";
201 if (!std::filesystem::exists(input_path)) {
202 LOG_ERROR <<
"input file not found: " << input_path;
206 LOG_INFO <<
"Input: " << input_path;
207 LOG_INFO <<
"Output: " << output_path;
214 LOG_ERROR <<
"failed to open MAT file: " << input_path;
219 if (!data_dic_save || data_dic_save->class_type != MAT_C_STRUCT) {
220 LOG_ERROR <<
"'data_dic_save' not found or not a struct";
227 matvar_t* dispinfo =
getField(data_dic_save.get(),
"dispinfo");
228 if (!dispinfo || dispinfo->class_type != MAT_C_STRUCT) {
229 LOG_ERROR <<
"'data_dic_save.dispinfo' not found";
234 if (matvar_t* f =
getField(dispinfo,
"spacing")) {
237 const std::ptrdiff_t scalefactor = spacing + 1;
239 double pixtounits = 1.0;
240 if (matvar_t* f =
getField(dispinfo,
"pixtounits")) {
244 std::string units =
"pixels";
245 if (matvar_t* f =
getField(dispinfo,
"units")) {
249 std::string type_str =
"Lagrangian";
250 if (matvar_t* f =
getField(dispinfo,
"type")) {
254 PERSPECTIVE perspective = PERSPECTIVE::LAGRANGIAN;
255 if (type_str.find(
"ulerian") != std::string::npos ||
256 type_str.find(
"Eulerian") != std::string::npos) {
257 perspective = PERSPECTIVE::EULERIAN;
260 LOG_INFO <<
" spacing=" << spacing <<
" (scalefactor=" << scalefactor <<
")";
261 LOG_INFO <<
" pixtounits=" << pixtounits;
262 LOG_INFO <<
" units=\"" << units <<
"\"";
263 LOG_INFO <<
" type=\"" << type_str <<
"\"";
266 data_dic_save.reset();
272 hid_t file_id = H5Fopen(input_path.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
274 LOG_ERROR <<
"failed to open HDF5 file: " << input_path;
278 const char* u_path =
"/data_dic_save/displacements/plot_u_dic";
279 const char* v_path =
"/data_dic_save/displacements/plot_v_dic";
280 const char* cc_path =
"/data_dic_save/displacements/plot_corrcoef_dic";
281 const char* roi_path =
"/data_dic_save/displacements/roi_dic";
286 LOG_ERROR <<
"plot_u_dic has 0 references";
292 size_t nV = 0, nCC = 0, nROI = 0;
298 LOG_ERROR <<
"plot_v_dic has " << nV <<
" refs (expected " << nFrames <<
")";
308 bool roi_usable =
false;
311 Array2D<bool> test_mask =
h5ReadMask2D(file_id, roi_refs[0]);
314 if (!test_mask.empty() && !u0.empty() &&
315 test_mask.height() == u0.height() && test_mask.width() == u0.width()) {
319 if (!roi_usable && nROI > 0) {
320 LOG_INFO <<
" Note: roi_dic contains ncorr_class_roi objects (not simple masks). "
321 <<
"Using all-true ROI masks — non-ROI pixels have zero displacement.";
327 std::vector<Disp2D> disps;
328 disps.reserve(nFrames);
331 for (
size_t f = 0; f < nFrames; ++f) {
335 if (u_arr.empty() || v_arr.empty()) {
336 LOG_WARN <<
"frame " << f <<
" has empty u or v array, skipping";
338 disps.emplace_back();
343 Array2D<double> cc_arr;
347 if (cc_arr.empty()) {
348 cc_arr = Array2D<double>(u_arr.height(), u_arr.width(), 0.0);
353 if (roi_usable && f < nROI) {
356 if (mask.empty() || mask.height() != u_arr.height() || mask.width() != u_arr.width()) {
357 mask = Array2D<bool>(u_arr.height(), u_arr.width(),
true);
361 if (u_arr.height() != v_arr.height() || u_arr.width() != v_arr.width()) {
362 LOG_WARN <<
"frame " << f <<
" u/v dimension mismatch ("
363 << u_arr.height() <<
"x" << u_arr.width() <<
" vs "
364 << v_arr.height() <<
"x" << v_arr.width() <<
")";
366 disps.emplace_back();
370 ROI2D roi(std::move(mask));
371 disps.emplace_back(std::move(v_arr), std::move(u_arr), std::move(cc_arr),
376 << disps.back().data_height() <<
"x"
377 << disps.back().data_width()
378 <<
", ROI regions: " << disps.back().get_roi().size_regions()
379 <<
", scalefactor: " << disps.back().get_scalefactor();
385 LOG_INFO <<
" Built " << disps.size() <<
" displacement frames ("
386 << skipped <<
" skipped)";
388 DIC_analysis_output output(disps, perspective, units, pixtounits);
393 save(output, output_path);
394 LOG_INFO <<
"Saved: " << output_path;
399 LOG_INFO <<
"Verifying round-trip...";
400 auto reloaded = DIC_analysis_output::load(output_path);
402 if (reloaded.disps.size() != output.disps.size()) {
403 LOG_ERROR <<
"FAIL: frame count mismatch: " << reloaded.disps.size()
404 <<
" vs " << output.disps.size();
407 if (reloaded.units != output.units) {
408 LOG_ERROR <<
"FAIL: units mismatch: \"" << reloaded.units
409 <<
"\" vs \"" << output.units <<
"\"";
412 if (std::abs(reloaded.units_per_pixel - output.units_per_pixel) > 1e-12) {
413 LOG_ERROR <<
"FAIL: units_per_pixel mismatch: " << reloaded.units_per_pixel
414 <<
" vs " << output.units_per_pixel;
417 if (ok && !reloaded.disps.empty()) {
418 for (
size_t f = 0; f < reloaded.disps.size(); ++f) {
419 if (reloaded.disps[f].data_height() > 0) {
420 LOG_INFO <<
" Reloaded frame " << f <<
" size: "
421 << reloaded.disps[f].data_height() <<
"x"
422 << reloaded.disps[f].data_width()
423 <<
", ROI regions: " << reloaded.disps[f].get_roi().size_regions()
424 <<
", scalefactor: " << reloaded.disps[f].get_scalefactor();