diff --git a/applications/gui2/src/modules/camera.cpp b/applications/gui2/src/modules/camera.cpp index 368ead646abf793e580eeef9b185ea619b472baa..25b5a813b634bfc714dbe95ef0f910ea35fadcd7 100644 --- a/applications/gui2/src/modules/camera.cpp +++ b/applications/gui2/src/modules/camera.cpp @@ -541,6 +541,20 @@ Eigen::Vector3f Camera::worldAt(int x, int y) { return res; } +Eigen::Vector3f fitPlane(const std::vector<float3>& pts) { + // PCA: calculate covariance matrix and its eigenvectors. Last eigenvector + // is the plane normal. + + Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, 3, Eigen::RowMajor>> + mat((float*)(pts.data()), pts.size(), 3); + + Eigen::MatrixXf centered = mat.rowwise() - mat.colwise().mean(); + Eigen::MatrixXf cov = (centered.adjoint() * centered) / double(mat.rows() - 1); + Eigen::EigenSolver<Eigen::MatrixXf> es(cov); + Eigen::Vector3cf n(es.eigenvectors().col(2)); // already normalized + return n.real(); +} + void Camera::setCursor(int x, int y) { auto ptr = std::atomic_load(&latest_); @@ -554,35 +568,50 @@ void Camera::setCursor(int x, int y) { const auto &intrins = frame.getLeft(); Eigen::Matrix4f posef = frame.getPose().cast<float>(); - float3 CC = getWorldPoint(depth, x, y, intrins, posef); cursor_pos_[0] = CC.x; cursor_pos_[1] = CC.y; cursor_pos_[2] = CC.z; - // Now find normal - float3 PC = getWorldPoint(depth, x, y+4, intrins, posef); - float3 CP = getWorldPoint(depth, x+4, y, intrins, posef); - float3 MC = getWorldPoint(depth, x, y-4, intrins, posef); - float3 CM = getWorldPoint(depth, x-4, y, intrins, posef); - const float3 n = cross(PC-MC, CP-CM); - const float l = length(n); - - if (l > 0.0f) { - cursor_normal_[0] = n.x / -l; - cursor_normal_[1] = n.y / -l; - cursor_normal_[2] = n.z / -l; - } - + // some valid value as initial value + const float3 CP = getWorldPoint(depth, x+4, y, intrins, posef); cursor_target_[0] = CP.x; cursor_target_[1] = CP.y; cursor_target_[2] = CP.z; + + // get points around the selected point. candidates are selected in + // from square [-range, range] around (x, y) and points which are + // closer than max_distance are used. TODO: check bounds (depth map + // size) + const int range = 30; + const float max_distance = 0.50; + std::vector<float3> pts; + for (int xi = -range; xi <= range; xi++) { + for (int yi = -range; yi <= range; yi++) { + auto p = getWorldPoint(depth, x + xi, y + yi, intrins, posef); + if (p.x == 0 && p.y == 0 && p.z == 0.0) { + continue; + } + const float3 d = p - CC; + if (sqrtf(d.x*d.x + d.y*d.y + d.z*d.z) < max_distance) { + pts.push_back(p); + } + }} + if (pts.size() == 0) { return; } + + cursor_normal_ = fitPlane(pts); } } cursor_ = _cursor(); } +void Camera::setCursorTarget(const Eigen::Vector3f &p) { + cursor_target_ = + p - cursor_normal_.dot(p - cursor_pos_) * cursor_normal_; + cursor_ = _cursor(); +} + void Camera::setOriginToCursor() { using ftl::calibration::transform::inverse; diff --git a/applications/gui2/src/modules/camera.hpp b/applications/gui2/src/modules/camera.hpp index 21ed70dc34a986b034a28e246897fbcd55e4e384..fa0d756ceee92545de014b1be9848b9e29004ae8 100644 --- a/applications/gui2/src/modules/camera.hpp +++ b/applications/gui2/src/modules/camera.hpp @@ -75,7 +75,7 @@ public: void setCursorPosition(const Eigen::Vector3f &pos) { cursor_pos_ = pos; cursor_ = _cursor(); } void setCursorNormal(const Eigen::Vector3f &norm) { cursor_normal_ = norm; cursor_ = _cursor(); } - void setCursorTarget(const Eigen::Vector3f &targ) { cursor_target_ = targ; cursor_ = _cursor(); } + void setCursorTarget(const Eigen::Vector3f &targ); void setCursor(int x, int y); const Eigen::Vector3f getCursorPosition() const { return cursor_pos_; }