35 constexpr float parallelEpsilon = 1e-9F;
39 faceAxis(
int faceIndex)
46 faceSign(
int faceIndex)
48 return (faceIndex % 2 == 0) ? 1.0F : -1.0F;
64 std::array<Eigen::Vector3f, 8>
67 const Eigen::Matrix3f r =
axes();
68 const Eigen::Vector3f
c =
center();
70 std::array<Eigen::Vector3f, 8> result;
71 for (
int i = 0; i < 8; ++i)
74 const Eigen::Vector3f signs{
75 (i & 1) ? 1.0F : -1.0F, (i & 2) ? 1.0F : -1.0F, (i & 4) ? 1.0F : -1.0F};
76 result[
static_cast<std::size_t
>(i)] =
c + r * signs.cwiseProduct(
halfExtents);
90 const Eigen::Matrix3f r =
cameraPose.topLeftCorner<3, 3>();
91 const Eigen::Vector3f local = r.transpose() * (globalPoint -
position());
93 const Eigen::Vector3f forward =
forwardLocal.normalized();
94 const Eigen::Vector3f up =
upLocal.normalized();
96 const Eigen::Vector3f right = forward.cross(up);
98 return {local.dot(forward), local.dot(right), local.dot(up)};
104 const Eigen::Vector3f view =
toViewFrame(globalPoint);
105 const float depth = view.x();
113 const float horizontalAngle = std::atan2(view.y(), depth);
114 const float verticalAngle = std::atan2(view.z(), depth);
123 const std::array<Eigen::Vector3f, 8> corners = box.
corners();
125 if (requireFullyInside)
127 return std::all_of(corners.begin(),
129 [&frustum](
const Eigen::Vector3f&
c)
130 { return frustum.contains(c); });
133 if (std::any_of(corners.begin(),
135 [&frustum](
const Eigen::Vector3f&
c) { return frustum.contains(c); }))
147 return faceSign(faceIndex) * box.
axes().col(faceAxis(faceIndex)).normalized();
153 const int axis = faceAxis(faceIndex);
160 const Eigen::Vector3f cameraPosition = frustum.
position();
163 float bestScore = -std::numeric_limits<float>::infinity();
165 for (
int face = 0; face < 6; ++face)
167 const Eigen::Vector3f toFace =
faceCenter(box, face) - cameraPosition;
168 const float distance = toFace.norm();
177 if (score > bestScore)
186 std::vector<Eigen::Vector3f>
189 std::vector<Eigen::Vector3f> samples;
195 const int axis = faceAxis(faceIndex);
196 const int axisU = (axis + 1) % 3;
197 const int axisV = (axis + 2) % 3;
199 const Eigen::Matrix3f r = box.
axes();
200 const Eigen::Vector3f center =
faceCenter(box, faceIndex);
202 samples.reserve(
static_cast<std::size_t
>(gridN) *
static_cast<std::size_t
>(gridN));
203 for (
int i = 0; i < gridN; ++i)
206 const float u = ((i + 0.5F) / gridN) * 2.0F - 1.0F;
207 for (
int j = 0; j < gridN; ++j)
209 const float v = ((j + 0.5F) / gridN) * 2.0F - 1.0F;
210 samples.push_back(center + r.col(axisU) * (u * box.
halfExtents(axisU)) +
219 const Eigen::Vector3f& unitDirection,
222 const Eigen::Matrix3f r = box.
axes();
225 const Eigen::Vector3f localOrigin = r.transpose() * (origin - box.
center());
226 const Eigen::Vector3f localDirection = r.transpose() * unitDirection;
228 float tMin = -std::numeric_limits<float>::infinity();
229 float tMax = std::numeric_limits<float>::infinity();
231 for (
int i = 0; i < 3; ++i)
234 if (std::abs(localDirection(i)) < parallelEpsilon)
237 if (std::abs(localOrigin(i)) > half)
244 float tEnter = (-half - localOrigin(i)) / localDirection(i);
245 float tExit = (half - localOrigin(i)) / localDirection(i);
248 std::swap(tEnter, tExit);
251 tMin = std::max(tMin, tEnter);
252 tMax = std::min(tMax, tExit);
266 return std::max(tMin, 0.0F);
272 const std::vector<Box>& occluders,
276 const std::vector<Eigen::Vector3f> samples =
283 const Eigen::Vector3f cameraPosition = frustum.
position();
285 std::size_t visible = 0;
286 for (
const Eigen::Vector3f& sample : samples)
288 const Eigen::Vector3f toSample = sample - cameraPosition;
289 const float distance = toSample.norm();
297 const Eigen::Vector3f direction = toSample /
distance;
298 const bool blocked = std::any_of(
301 [&](
const Box& occluder)
303 const std::optional<float> hit =
304 intersectRayBox(cameraPosition, direction, occluder);
307 return hit.has_value() and *hit > epsilon and *hit < distance - epsilon;
316 return static_cast<float>(visible) /
static_cast<float>(samples.size());
Geometric primitives for the fake object detector.
std::vector< Eigen::Vector3f > sampleFace(const Box &box, int faceIndex, int gridN)
A gridN x gridN grid of sample points spread over faceIndex, in global coordinates.
int frontFaceIndex(const Frustum &frustum, const Box &box)
The face of box that points most directly at the camera - the "front facing side" of the bounding box...
std::optional< float > intersectRayBox(const Eigen::Vector3f &origin, const Eigen::Vector3f &unitDirection, const Box &box)
Intersect a ray with an oriented box using the slab method.
bool isInFieldOfView(const Frustum &frustum, const Box &box, bool requireFullyInside)
Whether box is inside the camera frustum.
float visibleFraction(const Frustum &frustum, const Box &target, const std::vector< Box > &occluders, int gridN, float epsilon)
The fraction of sample points on the front face of target that the camera can see.
Eigen::Vector3f faceNormal(const Box &box, int faceIndex)
Outward unit normal of faceIndex in global coordinates.
Eigen::Vector3f faceCenter(const Box &box, int faceIndex)
Centre of faceIndex in global coordinates.
double distance(const Point &a, const Point &b)
double dot(const Point &x, const Point &y)
An oriented bounding box in the global frame.
std::array< Eigen::Vector3f, 8 > corners() const
The eight corners in global coordinates.
Eigen::Matrix4f centerPose
Rotation = box axes, translation = box centre.
Eigen::Vector3f halfExtents
Half the extent along each local box axis.
Eigen::Matrix3f axes() const
Eigen::Vector3f center() const
A symmetric view frustum.
bool contains(const Eigen::Vector3f &globalPoint) const
Whether a global point lies inside the frustum.
Eigen::Vector3f upLocal
Up axis in the camera's local frame.
Eigen::Matrix4f cameraPose
Camera -> global.
Eigen::Vector3f toViewFrame(const Eigen::Vector3f &globalPoint) const
Express a global point in the camera's viewing frame.
float horizontalFov
Full opening angles [rad].
Eigen::Vector3f forwardLocal
Viewing axis in the camera's local frame.
Eigen::Vector3f position() const