CPPxDIC
C++ Digital Image Correlation library for fingertip 3D reconstruction
Loading...
Searching...
No Matches
cuncorr_dic.cpp
Go to the documentation of this file.
1
6#include "dic/cuncorr_dic.h"
7
8#include <cmath>
9#include <cstdint>
10#include <stdexcept>
11
12#include "cuncorr/backend.h"
13#include "cuncorr/session.h"
14
15namespace cppxdic {
16namespace {
17
18using diff_t = ncorr::ROI2D::difference_type;
19
21std::vector<std::uint8_t> image_to_u8(const ncorr::Image2D& img, int& W, int& H) {
22 const ncorr::Array2D<double> gs = img.get_gs(); // (height, width), values in [0,1]
23 H = static_cast<int>(gs.height());
24 W = static_cast<int>(gs.width());
25 std::vector<std::uint8_t> buf(static_cast<std::size_t>(W) * H);
26 for (int r = 0; r < H; ++r)
27 for (int c = 0; c < W; ++c) {
28 double v = gs(r, c) * 255.0;
29 v = v < 0 ? 0 : (v > 255 ? 255 : v);
30 buf[static_cast<std::size_t>(r) * W + c] = static_cast<std::uint8_t>(v + 0.5);
31 }
32 return buf;
33}
34
36std::vector<std::uint8_t> roi_to_u8(const ncorr::ROI2D& roi) {
37 const ncorr::Array2D<bool>& m = roi.get_mask();
38 const int H = static_cast<int>(m.height()), W = static_cast<int>(m.width());
39 std::vector<std::uint8_t> buf(static_cast<std::size_t>(W) * H);
40 for (int r = 0; r < H; ++r)
41 for (int c = 0; c < W; ++c) buf[static_cast<std::size_t>(r) * W + c] = m(r, c) ? 255 : 0;
42 return buf;
43}
44
45} // namespace
46
48 auto be = cuncorr::make_default_backend();
49 const auto info = be->info();
50 return info.kind == cuncorr::Backend::CUDA && info.device_available;
51}
52
53ncorr::DIC_analysis_output run_cuncorr_dic(const std::vector<ncorr::Image2D>& imgs,
54 const ncorr::ROI2D& roi, const CuncorrDicConfig& cfg,
55 CuncorrDicInfo* info) {
56 if (imgs.size() < 2)
57 throw std::invalid_argument("run_cuncorr_dic: need at least a reference + one frame");
58 const int sf = cfg.scalefactor > 0 ? cfg.scalefactor : 1;
59
60 cuncorr::SessionConfig scfg;
61 scfg.scalefactor = sf;
62 scfg.subregion_radius = cfg.subregion_radius;
63 scfg.num_threads = cfg.num_threads;
64 scfg.seed_x = cfg.seed_x;
65 scfg.seed_y = cfg.seed_y;
66 scfg.seed_search = cfg.seed_search;
67 scfg.debug = cfg.debug;
68
69 if (info) {
70 auto be = cuncorr::make_default_backend();
71 const auto bi = be->info();
72 info->backend = bi.name;
73 info->cuda = bi.kind == cuncorr::Backend::CUDA && bi.device_available;
74 }
75
76 cuncorr::NcorrSession session(scfg);
77
78 int W = 0, H = 0;
79 const auto ref_buf = image_to_u8(imgs[0], W, H);
80 session.set_reference(cuncorr::ImageBuffer(ref_buf.data(), W, H, 1));
81
82 const auto roi_buf = roi_to_u8(roi);
83 session.set_roi(cuncorr::ImageBuffer(roi_buf.data(), static_cast<int>(roi.width()),
84 static_cast<int>(roi.height()), 1));
85
86 // Reduced ROI/grid — cuNCorr's reduced dims equal these by construction.
87 const ncorr::ROI2D roi_reduced = roi.reduce(sf);
88 const diff_t gH = roi_reduced.height();
89 const diff_t gW = roi_reduced.width();
90
91 session.reset_sequence();
92 std::vector<ncorr::Disp2D> disps;
93 disps.reserve(imgs.size() - 1);
94
95 for (std::size_t f = 1; f < imgs.size(); ++f) {
96 int dW = 0, dH = 0;
97 const auto def_buf = image_to_u8(imgs[f], dW, dH);
98 const cuncorr::DICResult res =
99 session.process_frame_sequence(cuncorr::ImageBuffer(def_buf.data(), dW, dH, 1));
100
101 ncorr::Array2D<double> A_v(gH, gW), A_u(gH, gW), A_cc(gH, gW);
102 ncorr::Array2D<bool> A_vp(gH, gW, false);
103
104 const int rgW = res.width, rgH = res.height;
105 for (int gi = 0; gi < static_cast<int>(gH); ++gi)
106 for (int gj = 0; gj < static_cast<int>(gW); ++gj) {
107 if (gi >= rgH || gj >= rgW) continue;
108 const std::size_t idx = static_cast<std::size_t>(gi) * rgW + gj;
109 const double v = res.v[idx], u = res.u[idx], cc = res.corrcoef[idx];
110 if (std::isnan(v) || std::isnan(u)) continue;
111 A_v(gi, gj) = v; // v = row/y displacement (matches ncorr)
112 A_u(gi, gj) = u; // u = col/x displacement
113 A_cc(gi, gj) = std::isnan(cc) ? 0.0 : cc;
114 A_vp(gi, gj) = true;
115 }
116
117 const ncorr::ROI2D roi_valid = roi_reduced.form_union(A_vp);
118 disps.emplace_back(std::move(A_v), std::move(A_u), std::move(A_cc), roi_valid,
119 static_cast<diff_t>(sf));
120 }
121
122 return ncorr::DIC_analysis_output(disps, ncorr::PERSPECTIVE::LAGRANGIAN, "pixels", 1.0);
123}
124
125} // namespace cppxdic
Adapter: run the cuNCorr DIC engine and package results as ncorr::DIC_analysis_output.
DataSerializer - Abstract base class for format-agnostic DIC data I/O.
bool cuncorr_cuda_available()
True if cuNCorr was built with CUDA and a device is present at runtime.
ncorr::DIC_analysis_output run_cuncorr_dic(const std::vector< ncorr::Image2D > &imgs, const ncorr::ROI2D &roi, const CuncorrDicConfig &cfg, CuncorrDicInfo *info=nullptr)
Run cuNCorr in sequence mode (fixed reference = imgs[0], warm-start each frame) over the stack agains...
Tunables mirrored onto cuncorr::SessionConfig.
Definition cuncorr_dic.h:26
int seed_search
coarse ZNCC seed search radius (px)
Definition cuncorr_dic.h:32
int seed_y
manual seed row (full-image px); -1 = auto
Definition cuncorr_dic.h:31
int seed_x
manual seed column (full-image px); -1 = auto
Definition cuncorr_dic.h:30
Which backend actually ran.
Definition cuncorr_dic.h:37
bool cuda
true if a CUDA device executed the run
Definition cuncorr_dic.h:39
std::string backend
e.g. "cpu" or "cuda:0 <name>"
Definition cuncorr_dic.h:38